在建立專業的檢索增強生成(Retrieval-Augmented Generation,RAG)應用程式時,LangChain 提供了一系列豐富的內建元件。然而,有時我們需要根據特定需求客製化這些元件。本文將探討如何客製化 LangChain 元件,特別是檔案載入器(Document Loader)、文字分割器(Text Splitter)和檢索器(Retriever),以建立更個人化與高效的 RAG 應用程式。

客製化檔案載入器

LangChain 的檔案載入器負責從各種來源載入檔案。雖然內建的載入器涵蓋了大多數常見格式,但有時我們需要處理特殊格式或來源的檔案。

為什麼要客製化檔案載入器?

  • 處理特殊檔案格式: 例如,處理專有的檔案格式或特定的資料函式庫。
  • 整合專有資料來源: 連線至公司內部的資料儲存系統或 API。
  • 實作具體的預處理邏輯: 在載入檔案時進行特定的資料清洗或轉換。

自訂檔案載入器的步驟

  1. 繼承自 BaseLoader 類別: 這是所有 LangChain 檔案載入器的基底類別。
  2. 實作 load() 方法: 這個方法負責實際載入檔案並將其轉換為 LangChain 的 Document 物件。
  3. 傳回 Document 物件列表: Document 物件包含頁面內容(page_content)和元資料(metadata),例如檔案來源、建立日期等。

範例:自訂 CSV 檔案載入器

以下是一個客製化的 CSV 檔案載入器範例,它可以讀取 CSV 檔案並將每列資料轉換為 Document 物件:

from langchain.document_loaders.base import BaseLoader
from langchain.schema import Document
import csv

class CustomCSVLoader(BaseLoader):
    def __init__(self, file_path):
        self.file_path = file_path

    def load(self):
        documents = []
        with open(self.file_path, 'r') as csv_file:
            csv_reader = csv.DictReader(csv_file)
            for row in csv_reader:
                content = f"Name: {row['name']}, Age: {row['age']}, City: {row['city']}"
                metadata = {"source": self.file_path, "row": csv_reader.line_num}
                documents.append(Document(page_content=content, metadata=metadata))
        return documents

# 使用範例
loader = CustomCSVLoader("path/to/your/file.csv")
documents = loader.load()

這個範例展示瞭如何建立一個簡單的客製化檔案載入器,你可以根據自己的需求進一步擴充套件和修改。

客製化文字分割器

文字分割是 RAG 系統中至關重要的一步。雖然 LangChain 提供了各種內建的分割器,但我們可能需要針對特定場景客製化分割器,以滿足特殊要求。

為什麼要客製化文字分割器?

  • 處理特殊文字格式: 例如,程式碼、表格或特定領域的專業檔案。
  • 實施特定的拆分規則: 例如,按章節、段落或特定標記拆分。
  • 最佳化分割結果的品質和語義完整性: 確保分割後的文字塊具有良好的語義連貫性,以便檢索器能夠更準確地找到相關資訊。

自訂文字分割器的基本架構

自訂文字分割器通常需要繼承自 TextSplitter 基底類別,並實作 split_text() 方法。

繼承自 TextSplitter 基礎類別

from langchain.text_splitter import TextSplitter
from typing import List

class CustomTextSplitter(TextSplitter):
    def __init__(self, chunk_size: int = 1000, chunk_overlap: int = 200):
        super().__init__(chunk_size=chunk_size, chunk_overlap=chunk_overlap)

    def split_text(self, text: str) -> List[str]:
        """實作特定的文字分割邏輯"""
        # 自訂分割規則
        chunks = []
        # 處理文字並回傳分割後的片段
        return chunks

chunk_size 引數定義了每個文字塊的最大長度,而 chunk_overlap 引數定義了相鄰文字塊之間的重疊長度。適當的 chunk_sizechunk_overlap 可以提高檢索的準確性。

例項:自訂分割器

以下是一些客製化分割器的例項,你可以根據自己的需求進行修改和調整。

1. 根據標記的分割器

這種分割器根據特定的標記(例如標題、分隔符號)來分割文字。

class MarkerBasedSplitter(TextSplitter):
    def __init__(self, markers: List[str], **kwargs):
        super().__init__(**kwargs)
        self.markers = markers

    def split_text(self, text: str) -> List[str]:
        chunks = []
        current_chunk = ""
        for line in text.split('\n'):
            if any(marker in line for marker in self.markers):
                if current_chunk.strip():
                    chunks.append(current_chunk.strip())
                current_chunk = line
            else:
                current_chunk += '\n' + line
        if current_chunk.strip():
            chunks.append(current_chunk.strip())
        return chunks

# 使用範例
splitter = MarkerBasedSplitter(markers=["## ", "# ", "### "], chunk_size=1000, chunk_overlap=200)

這個分割器會根據 markers 列表中定義的標記來分割文字。你可以根據自己的需求修改 markers 列表。

2. 程式碼感知分割器

這種分割器專門用於處理程式碼,可以根據程式語言的語法結構來分割程式碼。

class CodeAwareTextSplitter(TextSplitter):
    def __init__(self, language: str, **kwargs):
        super().__init__(**kwargs)
        self.language = language

    def split_text(self, text: str) -> List[str]:
        # 根據程式語言的語法結構來分割程式碼
        chunks = []
        # 處理程式碼並回傳分割後的片段
        return chunks

你可以使用程式語言的解析器(例如 Python 的 ast 模組)來分析程式碼的語法結構,並根據語法結構來分割程式碼。

客製化 LangChain 元件可以讓你根據特定需求建立更高效和個人化的 RAG 應用程式。透過客製化檔案載入器、文字分割器和檢索器,你可以更好地處理特殊格式的檔案、實施特定的拆分規則,並最佳化檢索結果的品質和準確性。

RAG 應用程式的效能取決於多個因素,包括檔案載入的效率、文字分割的品質以及檢索器的準確性。因此,客製化這些元件是提高 RAG 應用程式效能的關鍵步驟。除了上述範例之外,你還可以客製化檢索器,以實作更複雜的檢索策略,例如根據語義相似性的檢索或根據知識圖譜的檢索。

透過不斷的實驗和調整,你可以找到最適合自己需求的 LangChain 元件客製化方案,並建立出色的 RAG 應用程式。

在建構高效能的「檢索增強生成(Retrieval-Augmented Generation,RAG)」系統時,文字分割(Text Splitting)與客製化檢索器(Custom Retrievers)扮演著至關重要的角色。一個良好的文字分割策略,能確保檢索器能夠找到最相關的檔案片段,而客製化檢索器則能進一步最佳化檢索邏輯,以適應特定的應用場景。本文將由玄貓(BlackCat)我,帶領大家深入瞭解這兩個關鍵技術,並分享如何在 LangChain 中實作。

文字分割:RAG 系統的根本

文字分割是將大型檔案切割成更小、更易於管理的片段的過程。這些片段隨後會被轉換為向量嵌入(Vector Embeddings),儲存在向量資料函式庫ector Database)中。當使用者提出查詢時,系統會將查詢轉換為向量,並在向量資料函式庫找與查詢向量最相似的片段。

文字分割的重要性

  • 提升檢索精準度:將檔案分割成更小的片段,可以提高檢索的精準度。如果檔案過大,檢索器可能會找到包含關鍵資訊的檔案,但無法準確定位到相關的段落。
  • 降低運算成本:處理較小的片段可以降低運算成本。計算大型檔案的向量嵌入需要更多的資源和時間。
  • 支援多樣性檢索:不同的分割策略可以支援不同類別的檢索。例如,按照句子分割可以更好地捕捉語意資訊,而按照固定長度分割則適用於需要連貫的背景與環境資訊的場景。

常見的文字分割策略

  1. 固定尺寸分割(Fixed-Size Splitting):將檔案按照固定的字元數或 Token 數進行分割。這種方法簡單直接,但可能會破壞句子的完整性,影響語意理解。

    class FixedSizeTextSplitter(TextSplitter):
        def __init__(self, chunk_size: int = 200, **kwargs):
            super().__init__(**kwargs)
            self.chunk_size = chunk_size
    
        def split_text(self, text: str) -> List[str]:
            chunks = []
            start = 0
            while start < len(text):
                end = start + self.chunk_size
                chunks.append(text[start:end])
                start = end
            return chunks
    
  2. 遞迴字元分割(Recursive Character Splitting):這種方法會嘗試按照特定的分隔符(例如:換行符、句號)進行分割,以保持句子的完整性。如果分割後的片段仍然過大,則會遞迴地使用更小的分隔符進行分割。

  3. 語意感知分割(Semantic-Aware Splitting):這種方法會嘗試理解句子的語意,並在語意完整的地方進行分割。例如,可以使用自然語言處理(Natural Language Processing,NLP)技術來識別句子的邊界,或者使用 Transformer 模型來計算句子的語意相似度,並在相似度較低的地方進行分割。

    class SemanticAwareTextSplitter(TextSplitter):
        def __init__(self, sentence_endings: List[str] = ['.', '!', '?'], **kwargs):
            super().__init__(**kwargs)
            self.sentence_endings = sentence_endings
    
        def split_text(self, text: str) -> List[str]:
            chunks = []
            current_chunk = ""
            for sentence in self._split_into_sentences(text):
                if len(current_chunk) + len(sentence) > self.chunk_size:
                    if current_chunk:
                        chunks.append(current_chunk.strip())
                    current_chunk = sentence
                else:
                    current_chunk += ' ' + sentence
            if current_chunk:
                chunks.append(current_chunk.strip())
            return chunks
    
        def _split_into_sentences(self, text: str) -> List[str]:
            sentences = []
            current_sentence = ""
            for char in text:
                current_sentence += char
                if char in self.sentence_endings:
                    sentences.append(current_sentence.strip())
                    current_sentence = ""
            if current_sentence:
                sentences.append(current_sentence.strip())
            return sentences
    

最佳化文字分割的技巧

  • 保持語意完整性:盡可能地保持句子的完整性,避免在句子中間進行分割。
  • 處理程式碼區塊:對於包含程式碼的檔案,需要特殊處理程式碼區塊,避免將程式碼分割成無效的片段。
  • 最佳化重疊處理:在分割時,可以設定一定的重疊區域,以確保片段之間具有足夠的連貫的背景與環境資訊。

客製化檢索器:開發獨一無二的檢索體驗

檢索器是 RAG 系統的核心元件,負責從向量資料函式庫索相關檔案。LangChain 提供了多種內建檢索器,例如 SimilaritySearchMMR(最大邊際相關性)。然而,在某些情況下,我們需要自訂檢索器來實作特定的檢索邏輯或整合專有檢索演算法。

為什麼要客製化檢索器?

  • 實施具體的相關性計算方法:例如,可以使用 TF-IDF 或 BM25 等傳統的資訊檢索模型來計算相關性。
  • 整合專有檢索演算法:例如,可以整合企業內部的知識圖譜或搜尋引擎。
  • 最佳化檢索結果的多樣性和相關性:例如,可以使用 MMR 演算法來平衡檢索結果的多樣性和相關性。
  • 實作特定領域的連貫的背景與環境感知檢索:例如,可以根據使用者的歷史行為或偏好來調整檢索結果。

自訂檢索器的基本架構

from langchain.retrievers import BaseRetriever
from langchain.schema import Document
from typing import List
import asyncio

class CustomRetriever(BaseRetriever):
    def __init__(self, vectorstore):
        self.vectorstore = vectorstore

    def get_relevant_documents(self, query: str) -> List[Document]:
        # Implement custom retrieval logic
        results = []
        #...retrieval process...
        return results

    async def aget_relevant_documents(self, query: str) -> List[Document]:
        # Asynchronous version of retrieval logic
        return await asyncio.to_thread(self.get_relevant_documents, query)

實作客製化檢索器的步驟

  1. 繼承 BaseRetriever 類別BaseRetriever 是 LangChain 中所有檢索器的基礎類別。
  2. 實作 get_relevant_documents 方法:這個方法接收一個查詢字串,並回傳一個包含相關檔案的列表。
  3. 定義檢索邏輯:在這個方法中,可以實作任何自定義的檢索邏輯。例如,可以使用不同的相關性計算方法、整合外部資料來源,或者調整檢索結果的排序方式。
  4. (可選)實作 aget_relevant_documents 方法:這個方法是 get_relevant_documents 的非同步版本,可以用於提升效能。

文字分割與客製化檢索器是 RAG 系統中不可或缺的關鍵技術。透過選擇合適的文字分割策略,並根據特定的應用場景客製化檢索器,我們可以開發出更精準、高效的 RAG 系統,為使用者提供更好的資訊檢索體驗。作為玄貓(BlackCat),我建議大家在實作 RAG 系統時,務必重視這兩個環節,並不斷探索新的最佳化方法。只有不斷精進技術,才能在人工智慧的道路上走得更遠。

在資訊爆炸的時代,如何快速與精準地從海量資料中找到所需資訊,成為一項重要的技術挑戰。檢索器(Retriever)作為搜尋引擎和問答系統的核心元件,其效能直接影響使用者經驗。本文將探討自訂檢索器的實作與最佳化策略,協助開發者開發更卓越的檢索體驗。

開發獨特檢索能力:自訂檢索器例項

Langchain 提供了彈性的框架,讓開發者可以根據特定需求建立自訂檢索器。以下將介紹幾種常見的自訂檢索器例項,並提供程式碼範例。

1. 混血檢索器(Hybrid Retriever)

混血檢索器結合多種檢索方式,例如關鍵字搜尋和向量相似度搜尋,以提升檢索的準確性和召回率。以下範例結合了 BM25 關鍵字檢索和 FAISS 向量檢索:

from langchain.retrievers import BM25Retriever
from langchain.vectorstores import FAISS
from langchain.schema import BaseRetriever, Document
from typing import List

class HybridRetriever(BaseRetriever):
    def __init__(self, vectorstore, documents):
        self.vectorstore = vectorstore
        self.bm25 = BM25Retriever.from_documents(documents)

    def get_relevant_documents(self, query: str) -> List[Document]:
        bm25_results = self.bm25.get_relevant_documents(query)
        vector_results = self.vectorstore.similarity_search(query)

        # 合併結果並移除重複項
        all_results = bm25_results + vector_results
        unique_results = list({doc.page_content: doc for doc in all_results}.values())
        return unique_results[:5]  # 回傳前 5 個結果

混血檢索器利用 BM25 快速篩選出相關檔案,再透過向量相似度搜尋找到語義上相似的結果,最後合併兩種結果並去除重複項,從而提升檢索的全面性。

2. 連貫的背景與環境感知檢索器(Context-Aware Retriever)

連貫的背景與環境感知檢索器在檢索過程中考慮查詢的連貫的背景與環境資訊,例如使用者先前的查詢或對話歷史,以更精準地理解使用者的意圖。以下範例展示如何將連貫的背景與環境資訊融入查詢中:

from langchain.schema import BaseRetriever, Document
from typing import List

class ContextAwareRetriever(BaseRetriever):
    def __init__(self, vectorstore):
        self.vectorstore = vectorstore

    def get_relevant_documents(self, query: str, context: str = "") -> List[Document]:
        # 結合查詢和連貫的背景與環境
        enhanced_query = f"{context} {query}".strip()

        # 使用增強後的查詢進行檢索
        results = self.vectorstore.similarity_search(enhanced_query, k=5)

        # 根據連貫的背景與環境對結果進行後處理
        processed_results = self._post_process(results, context)
        return processed_results

    def _post_process(self, results: List[Document], context: str) -> List[Document]:
        # 實作根據連貫的背景與環境的後處理邏輯
        # 例如,根據連貫的背景與環境調整檔案的相關性分數
        return results

透過將連貫的背景與環境資訊融入查詢中,連貫的背景與環境感知檢索器可以更準確地理解使用者的意圖,並提供更相關的檢索結果。

提升檢索效能:最佳化技巧

除了實作自訂檢索器,還可以透過以下技巧來最佳化檢索效能:

  1. 動態權重調整(Dynamic Weight Adjustment):根據查詢類別或領域,動態調整不同檢索方法的權重。例如,對於技術性查詢,可以增加向量相似度搜尋的權重;對於一般性查詢,可以增加關鍵字搜尋的權重。
  2. 結果多樣性(Result Diversity):實作類別似最大邊緣關聯(MMR, Maximum Marginal Relevance)的演算法,確保檢索結果的多樣性。MMR 演算法在選擇結果時,不僅考慮結果與查詢的相關性,還考慮結果之間的差異性,從而避免回傳過於相似的結果。
  3. 效能最佳化(Performance Optimization):對於大規模資料集,可以考慮使用近似最近鄰(ANN, Approximate Nearest Neighbor)演算法,例如 HNSW(Hierarchical Navigable Small World),以加速向量搜尋的速度。
  4. 快取機制(Caching Mechanism):實作智慧型快取來儲存常見查詢的結果,以減少重複計算,提升檢索速度。
  5. 回饋學習(Feedback Learning):根據使用者回饋或系統效能指標,不斷最佳化檢索策略。例如,可以透過使用者點選或評分來調整檢索模型的引數,或調整不同檢索方法的權重。

以下範例展示如何實作一個具有快取和回饋學習功能的自適應檢索器(Adaptive Retriever):

from langchain.schema import BaseRetriever, Document
from typing import List, Tuple

class AdaptiveRetriever(BaseRetriever):
    def __init__(self, vectorstore):
        self.vectorstore = vectorstore
        self.cache = {}
        self.feedback_data = []

    def get_relevant_documents(self, query: str) -> List[Document]:
        if query in self.cache:
            return self.cache[query]

        results = self.vectorstore.similarity_search(query, k=10)
        diverse_results = self._apply_mmr(results, query)
        self.cache[query] = diverse_results[:5]
        return self.cache[query]

    def _apply_mmr(self, results, query, lambda_param=0.5):
        # 實作 MMR 演算法
        # ...
        return results

    def add_feedback(self, query: str, doc_id: str, relevant: bool):
        self.feedback_data.append((query, doc_id, relevant))
        if len(self.feedback_data) > 1000:
            self._update_retrieval_strategy()

    def _update_retrieval_strategy(self):
        # 根據回饋資料更新檢索策略
        # ...
        pass

自適應檢索器透過快取機制加速常見查詢的檢索,並透過回饋學習不斷最佳化檢索策略,從而提升檢索的效能和準確性。

確保品質:測試與驗證

在實作自訂元件時,建議執行以下測試,以確保品質:

  1. 單元測試(Unit Testing):針對每個元件的功能進行單元測試,例如測試檢索器是否能正確地檢索到相關檔案。
  2. 整合測試(Integration Testing):將不同的元件整合在一起進行測試,例如測試檢索器、向量資料函式庫言模型的整合效果。
  3. 端對端測試(End-to-End Testing):模擬使用者使用情境進行端對端測試,例如測試問答系統是否能正確地回答使用者的問題。
  4. 效能測試(Performance Testing):測試檢索器的效能指標,例如檢索速度、準確性和召回率。

透過充分的測試與驗證,可以確保自訂檢索器的品質,並提供更卓越的檢索體驗。

自訂檢索器是開發卓越檢索體驗的關鍵。透過結合多種檢索方式、融入連貫的背景與環境資訊、最佳化檢索效能和充分的測試與驗證,開發者可以開發出更精準、高效、智慧的檢索系統,滿足不同應用場景的需求。隨著人工智慧技術的不斷發展,檢索器將在資訊檢索、問答系統、智慧助理等領域發揮越來越重要的作用,為使用者帶來更便捷、高效的資訊取得體驗。

客製化 LangChain 元件以提升 RAG 應用靈活性與效率

在建構根據 LangChain 的檢索增強生成(Retrieval-Augmented Generation, RAG)應用程式時,客製化元件是實作靈活性與效率的關鍵。透過客製化,我們可以更精準地滿足特定領域或場景的需求。本文將探討如何客製化 LangChain 的檔案載入器(Document Loaders)、文字分割器(Text Splitters)與檢索器(Retrievers),並分享一些最佳實踐。

檔案載入器(Document Loaders)的客製化

檔案載入器負責從各種來源載入資料。LangChain 預設支援多種檔案格式,但針對特定需求,我們可能需要建立自訂的載入器。

自訂檔案載入器的步驟

  1. 繼承 BaseLoader 類別:建立一個新的類別,繼承自 BaseLoader
  2. 實作 load() 方法:在這個方法中,定義如何從特定來源載入資料,並將其轉換為 Document 物件。Document 物件通常包含 page_content(文字內容)和 metadata(元資料)。
  3. 處理特定格式:針對特殊的檔案格式或 API,編寫相應的解析邏輯。

範例:從資料函式庫資料

假設我們需要從一個資料函式庫資料,可以建立一個自訂的載入器,如下所示:

from langchain.document_loaders import BaseLoader
from langchain.docstore.document import Document
import sqlite3

class DatabaseLoader(BaseLoader):
    def __init__(self, db_path, query):
        self.db_path = db_path
        self.query = query

    def load(self):
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute(self.query)
        results = cursor.fetchall()
        documents = [Document(page_content=str(row), metadata={"source": "database"}) for row in results]
        conn.close()
        return documents

這個自訂的 DatabaseLoader 允許我們從 SQLite 資料函式庫查詢,並將結果轉換為 LangChain 的 Document 物件。

文字分割器(Text Splitters)的客製化

文字分割器將大型文字分割成更小的區塊,以便於檢索和處理。LangChain 提供了多種預設的分割器,但客製化分割器可以更精準地控制分割的邏輯。

客製化文字分割器的考量

  1. 分割策略:根據文字的特性選擇合適的分割策略。例如,針對程式碼可以使用根據程式碼結構的分割器,針對文章可以使用根據段落或句子的分割器。
  2. 區塊大小:調整區塊的大小,以平衡檢索的精確性和效率。較小的區塊可以提高檢索的精確性,但可能降低效率。
  3. 重疊區塊:設定重疊區塊的大小,以確保語義的連貫性。重疊區塊可以幫助模型更好地理解連貫的背景與環境。

範例:自訂程式碼分割器

以下是一個自訂的程式碼分割器範例:

from langchain.text_splitter import RecursiveCharacterTextSplitter

class CodeSplitter(RecursiveCharacterTextSplitter):
    def __init__(self, language, chunk_size=400, chunk_overlap=20):
        super().__init__(
            chunk_size=chunk_size,
            chunk_overlap=chunk_overlap,
            separators=["\n\n", "\n", " ", ""],
            is_separator_regex=False
        )
        self.language = language

    def split_text(self, text):
        # 這裡可以加入特定於程式語言的分割邏輯
        return super().split_text(text)

這個 CodeSplitter 繼承自 RecursiveCharacterTextSplitter,並可以根據程式語言的特性加入特定的分割邏輯。

檢索器(Retrievers)的客製化

檢索器負責從向量資料函式庫索相關的檔案。LangChain 提供了多種預設的檢索器,但客製化檢索器可以提高檢索的準確性和效率。

客製化檢索器的策略

  1. 相似度量:選擇合適的相似度量方法,例如餘弦相似度或點積。
  2. 檢索策略:實作自訂的檢索策略,例如根據關鍵字的檢索或根據語義的檢索。
  3. 後處理:對檢索結果進行後處理,例如過濾或排序。

範例:自訂檢索器

以下是一個自訂的檢索器範例:

from langchain.vectorstores import FAISS
from langchain.chains.retrieval_qa.base import BaseRetriever
from langchain.schema import Document

class CustomRetriever(BaseRetriever):
    def __init__(self, vectorstore):
        self.vectorstore = vectorstore

    def get_relevant_documents(self, query):
        results = self.vectorstore.similarity_search(query)
        return results

這個 CustomRetriever 使用 FAISS 向量資料函式庫相似度搜尋,並回傳相關的檔案。

自訂元件的最佳實踐

  1. 模組化設計(Modular Design):設計可重複使用與可組合的自訂元件。
  2. 效能最佳化(Performance Optimization):考慮大規模資料處理的效能,使用非同步方法和批次處理。
  3. 錯誤處理(Error Handling):實作強大的錯誤處理機制,確保元件在各種場景下都能正常運作。
  4. 可設定性(Configurability):提供靈活的設定選項,以使元件適應不同的使用案例。
  5. 檔案和註解(Documentation and Comments):提供詳細的檔案和程式碼註解,方便團隊協作和維護。
  6. 測試覆寫率(Test Coverage):編寫全面的單元測試和整合測試,以確保元件的可靠性。
  7. 版本控制(Version Control):使用版本控制系統管理自訂元件程式碼,以便追蹤變更和回復。

透過客製化 LangChain 元件,我們可以建構更靈活、高效的 RAG 應用。無論是檔案載入器、分割器或檢索器,客製化都能幫助我們更好地滿足特定領域或特定場景的需求。在實踐中,平衡客製化靈活性和系統複雜性非常重要,確保開發的元件不僅功能強大,而與易於維護和擴充套件。客製化元件的過程涉及深入理解 LangChain 的架構,並結合實際應用場景進行調整。透過不斷的實驗和最佳化,我們可以開發出真正符合需求的 RAG 系統。