本文將深入探討檢索增強生成(Retrieval-Augmented Generation,RAG)的原理與應用。文末將提供一個簡要的 RAG 實作範例,引導讀者理解如何整合外部資料集,以提升 AI 模型回答問題的精準度。
什麼是 RAG?
RAG 之所以叫做 Retrieval-Augmented Generation,是因為它巧妙地結合了以下兩個核心步驟,以增強 AI 模型的生成能力:
- Retrieval(檢索):在 AI 模型生成答案之前,系統會從外部大型知識庫中檢索與使用者所查詢的問題高度相關的文本片段。
- Augmented Generation(增強生成):檢索到的資訊將作為額外的上下文,用於「增強」AI 模型的輸入。這意味著在向模型提供使用者問題的同時,也將相關的背景資訊一併納入,引導模型生成更精確的回答。
總而言之,RAG 是一種強化 AI 生成能力的技術,它使模型能夠綜合其內建知識與外部檢索到的資訊,從而產生更精準、更具體的最終答案。其核心機制是在模型生成回應前,先行從知識庫中檢索相關內容,並將這些內容作為問題的上下文輸入模型。
為什麼要 RAG?
在引入 RAG 技術之前,大型語言模型(LLM)面臨以下三大挑戰:
- 幻覺現象(Hallucination):模型有時會生成看似合理但實際上不正確或虛構的資訊。例如,在無法存取即時網路資訊的情況下,模型可能會憑空捏造不存在的餐廳推薦。
- 知識時效性與領域限制:模型的知識庫僅限於其訓練數據的截止日期,無法獲取最新資訊。此外,許多垂直領域或企業內部專有資料(如公司員工手冊、退換貨標準作業流程)通常未包含在公開訓練數據中。
- 長文本處理與精準度挑戰:儘管部分模型支援較長的上下文輸入,但其處理超長文本的能力仍有限制,且從冗長內容中精準提取所需答案的穩定性可能下降。
RAG 技術的引入,能有效緩解上述問題,因為系統會直接提供與問題高度相關的精簡文件片段,使 AI 模型能在聚焦且有限的資訊中生成精準答案。不過 RAG 並非是萬能的,也有存在其局限性,本文將暫不深入探討,僅專注於其基本技術內涵。未來再來寫另外一片文章來討論其中的限制和業界的進階解決方案。
RAG 的簡易實作
從上述討論可知,RAG 將典型的 AI 生成過程劃分為以下兩個關鍵步驟:
- Retrieval(檢索):從知識庫中,找到 n 筆與使用者輸入問題最相關的知識片段。
- Augmented Generation(增強生成):將使用者問題和檢索到的相關知識片段,同時輸入給 AI 模型,要求它根據這些相關知識片段回答使用者的問題。
因此,RAG 實作的核心在於建立一個高效的檢索機制,確保使用者輸入的問題能精準地關聯到知識庫中最相關的文本片段。
向量化的語意搜尋
在此,我們將引入一種基於向量的語義搜尋機制,以實現使用者問題與知識庫中相關知識片段的精準關聯。
其步驟如下:
- 將整個知識庫切分為多個獨立的知識片段(Chunking)。
- 利用嵌入模型(Embedding Model)將每個知識片段轉換為固定長度的多維數值向量。例如,來自《三國演義》的片段「
關公領諾而出,提青龍刀,上赤兔馬
」可被表示為[0.123, -0.456, 0.789, -0.101, 0.234, ..., 0.987]
。 - 將所有向量化的文本及其對應的原始文本存儲於向量資料庫中(Vector Database)。
- 將使用者的問題(User Prompt)同樣透過嵌入模型向量化,例如
「關羽的武器是什麼?」
可能被轉換為[0.130, -0.460, 0.795, -0.105, 0.240, ..., 0.990]
。 - 在向量資料庫中,系統會搜尋並檢索出與使用者問題向量最為相似的前 n 個文本向量,並返回其對應的原始文本內容。例如,當使用者向 AI 模型詢問「關羽的武器是什麼?」時,AI 模型會參考「關公領諾而出,提青龍刀,上赤兔馬」這段上下文文本,因為這兩個文本在向量空間中具有高度相似性。這樣,AI 模型就能夠根據使用者的問題提供相關且準確的答案。
步驟一至步驟三的過程統稱為語義索引(Indexing)。此階段旨在將整個知識庫的文本內容轉換為向量形式,並儲存於向量資料庫中,以便未來能高效地與使用者查詢的問題進行語義關聯與匹配。
以下將透過簡潔的 Python 程式碼,展示上述語義索引與 RAG 流程的具體實作。完整的程式碼範例可以參考筆者的 GitHub 專案 evanxd/rag。
建立向量資料庫
首先,我們將初始化一個基於 SQLite3 的向量資料庫物件。此步驟需指定資料庫檔案路徑、用於文本向量化的嵌入模型,以及儲存資料的表格名稱。
db = SQLiteVec(
connection=SQLiteVec.create_connection(db_file="vec.db"),
embedding=HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh"),
table="romance_of_the_three_kingdoms"
)
索引文本
此部分程式碼負責將《三國演義》的文本內容進行處理並索引至向量資料庫。具體步驟如下:
- 從
romance-of-the-three-kingdoms.txt
檔案載入文本內容。 - 將載入的文本以 1024 個字元為單位進行切片(chunk),以適應模型處理長文本的能力。
- 利用設定的
BAAI/bge-small-zh
嵌入模型將這些文本片段向量化。 - 最後,將向量化的文本及其原始內容存入
vec.db
資料庫中。
def index(file_name: str):
"""Indexes the content of a given file into the vector database."""
loader = TextLoader(file_name)
documents = loader.load()
texts = chunk(documents)
db.add_texts(texts)
def chunk(documents) -> list:
"""Splits documents into smaller chunks."""
splitter = CharacterTextSplitter(chunk_size=1024, chunk_overlap=0)
docs = splitter.split_documents(documents)
texts = [doc.page_content for doc in docs]
return texts
index("romance-of-the-three-kingdoms.txt")
檢索與使用者問題相關的文本
在此步驟中,我們將使用者的查詢進行向量化,並在已建立的向量資料庫中執行語義相似度搜尋,以找到最相似的四(k=4
)個文本片段。語義相似性是指文本在多維向量空間中位置的接近程度;距離越近,表示其語義越相似,這也意味著這些文本與使用者問題的相關性最高。
question = "孔明最大的貢獻是什麼?"
docs = db.similarity_search(question, k=4)
context = "".join([doc.page_content for doc in docs])
以搜尋結果回答問題
最後一步是將在知識庫中檢索到的相關文本和使用者問題一起提供給 AI 模型,讓其做為上下文參考生成精確的回答。
messages = [SYSTEM_PROMPT]
messages.append({
"role": "user",
"content": f"""Question: {question}
Context: {context}
Answer:"""
})
llm = ChatGoogleGenerativeAI(
model="gemini-2.5-flash-preview-05-20",
google_api_key=os.getenv("GOOGLE_API_KEY")
)
result = llm.invoke(messages)
模型將根據提供的上下文生成回答,例如:『孔明最大的貢獻在於他為蜀漢「恢復中原,鞠躬盡瘁,死而後已」的堅定意志與不懈努力。他不僅在戰敗後深切反省並積極規劃未來戰事,展現卓越的治軍與戰略能力。此外,他透過高超的辯才與外交手腕,為蜀漢爭取到關鍵的戰略要地荊州。
』
總結
- RAG 定義與運作原理:檢索增強生成(RAG)是一種結合了「檢索」與「增強生成」的技術。它首先從外部知識庫中找出與使用者問題相關的資訊,然後將這些資訊作為額外上下文提供給 AI 模型,以生成更精確的答案。
- RAG 的必要性:RAG 旨在解決大型語言模型(LLM)的常見挑戰,包括減少「幻覺」(生成不實資訊)、克服知識時效性與領域限制,以及提升從大量文本中精準提取答案的能力。
- RAG 簡易實作流程:RAG 的實作核心在於建立「向量化的語義搜尋」機制。這包括將知識庫文本切片、透過嵌入模型轉換為向量並存入向量資料庫(語義索引),隨後將使用者問題向量化並進行相似度搜尋,最終將檢索到的相關上下文提供給 AI 模型進行回答。