本文 AI 產出,尚未審核

LangChain 教學:RAG 基本 Pipeline

簡介

在資訊爆炸的時代,單純的 大型語言模型 (LLM) 雖然擁有強大的生成能力,卻往往缺乏對最新或專業領域知識的即時存取。檢索增強生成(Retrieval‑Augmented Generation,簡稱 RAG)正是為了解決這個痛點而設計的技術:先從外部文件或向量資料庫中檢索相關片段,然後把檢索結果作為上下文交給 LLM,讓模型在產出答案時能夠「站在」正確的知識基礎上。

LangChain 作為 LLM 應用的開發框架,已經把 RAG 所需的 檢索器文件載入器向量嵌入模型、以及 LLM 呼叫等元件抽象化,讓開發者只需要組合幾個簡單的類別,就能快速搭建起一條完整的 RAG pipeline。本文將從概念說明、程式碼範例、常見陷阱與最佳實踐,最後延伸到實務應用場景,帶你一步步完成「基本 RAG Pipeline」的建置。


核心概念

1. RAG Pipeline 的四大步驟

步驟 功能說明 LangChain 對應元件
文件載入 把原始資料(PDF、txt、網頁等)轉成文字 DocumentLoader(如 PyPDFLoaderTextLoader
切分 & 向量化 把長文本切成較小的 Chunk,並產生向量嵌入 TextSplitter + Embeddings(如 OpenAIEmbeddings
建立向量資料庫 把向量與原始文字一起儲存,支援相似度搜尋 FAISS, Chroma, Pinecone 等 VectorStore
檢索 & 生成 依據使用者查詢在向量庫中找出相關 Chunk,並把結果傳給 LLM 產生最終答案 Retriever + LLMChain(或 ConversationalRetrievalChain

重點:在 LangChain 中,Retriever 負責 檢索LLMChain 負責 生成,兩者的組合即構成完整的 RAG 流程。


2. 文件載入與切分

2.1 載入 PDF 範例

from langchain.document_loaders import PyPDFLoader

# 讀取本機的 PDF 檔案
loader = PyPDFLoader("data/annual_report_2023.pdf")
documents = loader.load()
print(f"載入了 {len(documents)} 個頁面")

load() 回傳的是 List[Document],每個 Document 包含 page_content(文字)與 metadata(如頁碼、檔名)。

2.2 切分成 Chunk

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 設定每個 Chunk 最多 1000 個字,重疊 200 個字
splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    separators=["\n\n", "\n", " "]
)

chunks = splitter.split_documents(documents)
print(f"切分後共有 {len(chunks)} 個 Chunk")

技巧chunk_overlap 可以避免重要資訊被切斷,提升檢索的召回率。


3. 向量化與向量資料庫

3.1 使用 OpenAI Embeddings

from langchain.embeddings import OpenAIEmbeddings

# 需要先在環境變數設定 OPENAI_API_KEY
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")

3.2 建立 FAISS 向量庫

from langchain.vectorstores import FAISS

# 把文字 Chunk 與其向量一起存入 FAISS
vectorstore = FAISS.from_documents(chunks, embeddings)

# 檢查向量庫大小
print(f"FAISS 資料庫內有 {vectorstore.index.ntotal} 個向量")

FAISS 是本機端的向量搜尋引擎,適合開發階段或小型專案;如果要上線服務,可考慮雲端向量服務如 Pinecone、Weaviate。


4. 檢索與生成

4.1 建立 Retriever

# 這裡使用相似度搜尋 top_k=4
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

4.2 組合成完整的 RAG Chain

from langchain.llms import OpenAI
from langchain.chains import RetrievalQA

# LLM 設定(同樣需要 OPENAI_API_KEY)
llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0)

# RetrievalQA 會自動把檢索結果拼接成 prompt,送給 LLM
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",          # 直接把所有檢索結果塞入 prompt
    retriever=retriever,
    return_source_documents=True   # 回傳原始 Chunk,方便除錯
)

# 測試問答
query = "2023 年公司營收的主要成長動力是什麼?"
result = qa_chain(query)
print("答案:", result["result"])
print("\n參考來源:")
for doc in result["source_documents"]:
    print("- ", doc.metadata.get("source", "unknown"))

提示chain_type 除了 "stuff",還有 "map_reduce""refine" 等更進階的組合方式,可依需求調整。


5. 完整範例:從檔案到問答的端到端流程

# 1️⃣ 載入文件
loader = PyPDFLoader("data/annual_report_2023.pdf")
documents = loader.load()

# 2️⃣ 切分
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(documents)

# 3️⃣ 向量化 & 建立向量庫
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
vectorstore = FAISS.from_documents(chunks, embeddings)

# 4️⃣ 建立 Retriever
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

# 5️⃣ 組合 RetrievalQA
llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0)
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever)

# 6️⃣ 問答測試
while True:
    query = input("\n請輸入問題 (enter 離開): ")
    if not query:
        break
    answer = qa(query)
    print("\n🗨️ 答案:", answer["result"])
    print("\n🔎 參考來源:")
    for doc in answer["source_documents"]:
        print(f"  - {doc.metadata.get('source', 'unknown')} (第 {doc.metadata.get('page', '?')} 頁)")

只要把 data/annual_report_2023.pdf 換成自己的檔案,即可快速得到一個 可互動的問答介面


常見陷阱與最佳實踐

陷阱 說明 最佳實踐
Chunk 大小過小 會導致檢索結果缺乏足夠上下文,LLM 產生的答案可能斷裂。 依據模型上下文長度(如 GPT‑3.5 為 4k token)設定 chunk_size,通常在 800~1500 字之間較佳。
向量模型與 LLM 不匹配 使用不同領域的嵌入模型(如科學領域的專屬模型)卻搭配通用 LLM,檢索結果可能不相關。 盡量選擇 與 LLM 兼容的嵌入模型(如 OpenAI text-embedding-ada-002),或自行微調向量模型。
檢索結果過多 (k 設太大) 會把太多不相關的文字塞進 prompt,超過 token 限制,甚至降低答案品質。 先從 k=3~5 起步,觀察效果後再微調;可使用 重排 (re‑ranking) 進一步篩選。
忘記傳遞 Metadata 若要在答案中標示來源,必須在 Documentmetadata 中保留檔名、頁碼等資訊。 載入時使用 metadata,或在切分後手動加入 metadata["source"]metadata["page"]
向量庫未持久化 開發階段每次重啟程式都重新建庫,浪費時間。 使用 FAISS.save_local() 或雲端向量服務的持久化功能,讓向量庫只建一次。

實際應用場景

  1. 企業內部知識庫
    • 把公司手冊、會議記錄、技術文件匯入,員工只要輸入問題,即可即時取得最新的官方答案。
  2. 客服支援
    • 結合 FAQ、產品說明書,讓聊天機器人提供 參考來源 的精準回覆,降低誤導風險。
  3. 法律或醫療文件檢索
    • 針對大量條款或病例報告,RAG 能在保密前提下快速定位相關條文,協助專業人員決策。
  4. 教育平台
    • 把教材、課程筆記轉成向量庫,學生提問時可得到結合課本內容的個性化解答。
  5. 多語言文件搜尋
    • 透過多語言嵌入模型(如 sentence-transformers),同時支援中文、英文等語言的跨語檢索。

總結

檢索增強生成(RAG)是將 外部知識大型語言模型 有機結合的關鍵技術。透過 LangChain,我們只需要:

  1. 載入 & 切分 文件
  2. 向量化 並建立 向量資料庫
  3. 設定 Retriever 取得相關 Chunk
  4. 將檢索結果交給 LLM 完成生成

只要掌握上述四個步驟,並留意 Chunk 大小、k 值、向量模型匹配 等細節,即可在短時間內打造出 可靠、可追溯 的問答系統。未來,隨著向量資料庫服務與嵌入模型的持續進化,RAG 的應用範圍將更加廣闊,從企業內部到跨領域的知識服務,都能受惠於這條 基本 RAG Pipeline

祝你在 LangChain 的旅程中,玩得開心、寫得順手! 🎉