RAG 完整指南:檢索增強生成的原理、架構與實作¶
摘要¶
大型語言模型(LLM)天生有兩個限制:知識截止日與幻覺。RAG(Retrieval-Augmented Generation,檢索增強生成)是目前業界最主流的解決方案——在生成答案之前,先從外部知識庫撈出相關文件,再把它們塞進 Prompt,讓模型「有憑有據」地回答。
| 維度 | 純 LLM | RAG |
|---|---|---|
| 知識來源 | 訓練資料(固定) | 外部知識庫(可更新) |
| 幻覺風險 | 高 | 低(有文件支撐) |
| 可追溯性 | 無 | 可引用來源 |
| 更新成本 | 重新訓練(高) | 更新索引(低) |
| 延遲 | 低 | 較高(多一次檢索) |
RAG 不是萬能的。它適合「知識密集型」任務(文件問答、客服、法規查詢),不適合純推理或創作任務。
本篇在系列的哪一站¶
本站的 RAG 筆記是一個五站的系列。本篇是第 1 站:把整條管線走一遍,建立骨架與詞彙,各層的選型與調優細節交給後面的專篇,不在這裡重複。
| 站 | 你在問的問題 | 讀哪篇 |
|---|---|---|
| 0 | 我到底該用 RAG,還是 prompting / fine-tuning? | Fine-tuning vs RAG vs Prompting |
| 1 | RAG 整條管線長什麼樣? | 本篇 |
| 2 | embedding 和向量庫怎麼選? | Embeddings 與向量資料庫實務 |
| 3 | naive RAG 效果不好,怎麼推到生產級? | RAG 進階系列 |
| 4 | 加槓桿還不夠,要不要換架構? | GraphRAG(換索引標的)/Agentic RAG(換控制流) |
第 4 站的兩篇是平行的分岔,不是先後:GraphRAG 改的是「索引什麼」,Agentic RAG 改的是「誰決定何時檢索」。
重要提醒:第 0 站其實該最先讀。很多 RAG 專案的失敗不是做得不好,而是根本不該做 RAG——靜態小語料直接塞長 context 更省事,要改的是模型行為就該 fine-tune。
為什麼需要 RAG¶
LLM 在訓練後知識便固定下來。面對以下情境,單靠模型本身無法可靠回答:
- 時效性問題:「今天台股大盤收幾點?」
- 私有領域知識:公司內部文件、個人筆記、私有資料庫
- 長尾細節:訓練資料中罕見的專業技術文件
- 可信度要求:需要引用具體來源的法律、醫療場景
RAG 的核心思路:不改模型,改 Prompt。把外部知識即時注入 Prompt,讓模型用提供的文件作答,而不是憑記憶猜測。
RAG 三階段架構¶
整個 RAG 系統分為兩個時間維度:
離線(Indexing):資料準備階段,只需執行一次(或增量更新)。
線上(Query → Retrieve → Generate):每次用戶提問都會走一遍。
[離線] Document → Chunk → Embed → Vector DB
↓
[線上] Query → Embed → 相似度搜尋 → Top-K Chunks → Prompt → LLM → Answer
這條管線是整個系列的骨架。後面每一篇專篇,都是在把其中一段換成更好的做法。
第一階段:索引(Indexing)¶
1.1 文件載入(Document Loading)¶
原始文件格式多元,需轉換為純文字:
| 格式 | 工具 |
|---|---|
pypdf, pdfplumber, unstructured |
|
| Word/Excel | python-docx, openpyxl |
| HTML/網頁 | BeautifulSoup, Playwright |
| 資料庫 | SQL → 文字摘要 |
| 程式碼 | 直接讀取 + AST 解析 |
這一步的損失是不可逆的:parsing 丟掉的表格結構與圖表數值,後面再強的檢索也救不回來。若文件視覺結構豐富,考慮本頁後段的 PixelRAG。
1.2 文件分塊(Chunking)¶
文件通常比 LLM 的 context window 大,必須切分。切塊的核心張力是大小:切太大會把多個主題塞進同一塊,稀釋檢索訊號;切太小則失去上下文,答案拼不起來。相鄰 chunk 通常保留一段重疊(overlap),避免跨邊界的重要資訊被切斷。
不要抄別人的 chunk 大小。 最佳值取決於你的語料與 embedding 模型,必須用 recall@K 實測。切塊策略(fixed-size / sentence / recursive / semantic / heading-aware)的取捨與實測方法見 RAG 進階系列 §1。
1.3 嵌入(Embedding)¶
用 Embedding Model 把每個 chunk 轉成向量,語意相近的 chunk 向量距離相近。
兩條不能違反的規則:
- 查詢與文件必須用同一個 Embedding Model。 不同模型的向量空間不相通,混用等於在比較兩種不同的座標系。
- 換模型就要全量重嵌。 舊向量與新向量不可混存,這是常態維運成本,選型時就要算進去。
模型怎麼挑(MTEB 排行榜怎麼用、中文語料看什麼、維度與成本的取捨)見 Embeddings 與向量資料庫實務。
1.4 向量資料庫(Vector Database)¶
儲存向量並支援快速近似最近鄰搜尋(ANN)——用一點點準確率換數量級的速度。除了向量本身,還應該把 source、date、authority 等 metadata 一起存,查詢時先過濾再比相似度,時效與權限控管都靠它。
選型(Chroma / FAISS / Qdrant / Pinecone / pgvector / Weaviate / Milvus)、HNSW 與 IVF 的取捨、量化壓縮與生產考量,同樣見 Embeddings 與向量資料庫實務。
第二階段:檢索(Retrieval)¶
2.1 基礎向量檢索¶
用戶問題 → Embed → 向量相似度搜尋 → 取回 Top-K chunks。
相似度計算方式:
- Cosine Similarity(方向相似,最常用)
- Dot Product(向量已正規化時等同 Cosine)
- L2 Distance(歐氏距離,適合密集向量)
這就是 naive RAG 的檢索層。它的三個典型弱點,正好對應下面三個補強手法。
2.2 混合搜尋(Hybrid Search)¶
弱點:純向量搜尋擅長語意相近,但會漏掉需要精確字面命中的查詢(產品型號、人名、錯誤碼)。
補法:把向量檢索(dense)與關鍵字檢索 BM25(sparse)兩條排名合併,常用 RRF(Reciprocal Rank Fusion)。細節與評估方式見 RAG 進階系列 §2。
2.3 Query Transformation(查詢轉換)¶
弱點:用戶的問法常和文件的用詞對不上。
補法:在檢索之前改寫或擴展查詢。
| 手法 | 做什麼 |
|---|---|
| Multi-Query | 把一個問題改寫成數個不同角度的問法,分別搜尋再去重 |
| HyDE | 先讓 LLM 假設一個理想答案,用答案去搜尋——答案的語意空間與文件更接近 |
| Step-Back | 先問更廣泛的背景問題,再搜尋 |
| Decomposition | 把複雜問題拆解為子問題,各自搜尋後合併 |
各手法的適用時機見 RAG 進階系列 §4。在 agentic 架構裡,這會進一步變成「檢索失敗 → 改寫 → 重試」的迴圈,見 Agentic RAG。
2.4 重排序(Re-ranking)¶
弱點:初次 Top-K 撈得夠廣,但順序不夠準。
補法:兩階段。先用 hybrid 廣撈候選(追 recall),再用 Cross-Encoder 逐一打分重排、截到最終進 context 的小集合(追 precision)。
Cross-Encoder 比向量內積準,是因為它讓「查詢 + 候選」一起進模型算相關性,而非各自編碼再比距離;代價是慢,所以只用在縮小後的候選集上。Reranker 選型與「別把 recall 殺掉」的調法見 RAG 進階系列 §3。
第三階段:生成(Generation)¶
檢索做得再好,生成端沒約束住,模型照樣可以無視文件亂答。這一階段全系列只有本篇談。
3.1 Prompt 設計¶
將檢索到的 chunks 組合進 Prompt,標準模板:
你是一個知識問答助理。請根據以下「參考資料」回答用戶問題。
如果參考資料中沒有足夠資訊,請明確說「資料中未找到相關內容」,不要憑空猜測。
## 參考資料
{retrieved_chunks}
## 用戶問題
{user_query}
## 回答
關鍵設計原則:
- 明確限制來源:防止模型混用訓練知識
- 處理無資訊情境:讓模型說「不知道」而非幻覺
- 可引用來源:要求模型標注引用的文件編號
第三點不只是體貼使用者。沒有 citation,當 faithfulness 分數偏低時,你連「它在瞎掰哪一句」都定位不了——見 RAG 進階系列 §5。
3.2 Context 放置位置¶
研究顯示 LLM 對 Prompt 中間位置的文字注意力最低(Lost in the Middle 現象)。將最相關的文件放在最前面或最後面。
這也是為什麼「檢索少而準」勝過「檢索多而雜」——同樣的道理在 Context Engineering 實作篇 有更完整的討論。
超越 Naive RAG:先診斷,再決定加槓桿還是換架構¶
naive RAG 效果不好時,先確認是哪一種不好,再選對應的解法。多數問題出在檢索端:
| 症狀 | 原因 | 解法 | 屬於 |
|---|---|---|---|
| 召回率低 | 問題與文件語意差異大 | HyDE、Multi-Query、Hybrid Search | 加槓桿(第 3 站) |
| 精確度低 | Top-K 有雜訊 | Re-ranking | 加槓桿(第 3 站) |
| 答案用了舊資料 | 索引過期 | Freshness 維運 | 加槓桿(第 3 站) |
| 跨文件推理失敗 | 資訊分散在多份文件 | GraphRAG | 換架構(第 4 站) |
| 需要多輪自我修正 | 一次檢索不足以判斷 | Agentic RAG | 換架構(第 4 站) |
| 回答不忠實 | 模型忽視上下文 | Self-RAG、CRAG | 見下 |
| 視覺內容遺失 | 文字 parsing 丟棄表格/圖表 | PixelRAG | 見下 |
值得記住的經驗法則:「80% 的 RAG 問題是檢索問題,不是生成問題。」 除錯先從 chunk 相關性分數逐段追,別急著換更大的生成模型。
Self-RAG¶
讓 LLM 自己決定:「這個問題需要檢索嗎?檢索到的結果有用嗎?我的回答是否忠實於來源?」
引入三個特殊 Token:
[Retrieve]:判斷是否需要檢索[IsRel]:判斷檢索結果是否相關[IsSup]:判斷回答是否被來源支持
Corrective RAG(CRAG)¶
加入「反思」步驟:評估檢索到的文件品質。
- 品質高 → 直接生成
- 品質低 → 用搜尋引擎補充
- 模糊 → 混合兩者
PixelRAG(Berkeley SkyLab / BAIR)¶
傳統 RAG 把文件 parse 成文字——這個過程會丟棄表格結構、圖表數值、排版資訊。PixelRAG 的做法完全相反:不解析,直接截圖。
核心流程:
文件 / 網頁
↓ pixelshot(Playwright / CDP)
截圖 Tiles(圖片分塊)
↓ Qwen3-VL-Embedding(LoRA fine-tuned on screenshot data)
視覺向量
↓ FAISS 相似度索引
↓ VLM 讀取截圖回答問題
傳統 RAG 碰到「表格第三欄第五行的數值是多少?」這類問題會失敗,因為 HTML parsing 後表格結構已不存在。PixelRAG 保留了整頁的視覺結構,VLM 直接看圖就能找到答案。
技術細節:
- Embedding 模型:
Qwen/Qwen3-VL-Embedding-2B,在 Wikipedia 截圖資料上 LoRA fine-tune - 預建索引:8.28M 筆 Wikipedia 頁面,提供公開 API(
api.pixelrag.ai) - 索引大小:FAISS 索引約 217 GB
快速試用:
pip install pixelrag
# 截圖並搜尋 Wikipedia 預建索引(無需 API key)
pixelshot https://en.wikipedia.org/wiki/Python --output ./tiles
curl -X POST https://api.pixelrag.ai/search \
-H "Content-Type: application/json" \
-d '{"queries": [{"text": "Python 的發明者是誰?"}], "n_docs": 5}'
和 GraphRAG 的差異——兩者都是「換索引標的」,但換的方向不同:
| PixelRAG | GraphRAG | |
|---|---|---|
| 索引的是什麼 | 頁面截圖(像素) | 實體關係圖譜 |
| 解決的問題 | 視覺內容被 parsing 丟失 | 跨文件多跳推理 |
| 讀取模型 | VLM(看圖) | LLM(讀文字+圖譜) |
| 適合場景 | 含大量表格/圖表的文件 | 需理解實體關係的大量文件 |
適合:含豐富視覺結構的文件(財報表格、技術規格書、網頁截圖)。GraphRAG 的建圖四階段與成本代價見 GraphRAG 專篇。
RAG 評估框架¶
評估 RAG 系統需要同時衡量檢索品質與生成品質。沒有評估就沒有儀表板,後面所有調優都是盲調——這是進到第 3 站之前的前置條件。
RAGAS 指標¶
| 指標 | 評估對象 | 含義 |
|---|---|---|
| Faithfulness | 生成 | 答案是否完全基於檢索到的上下文(不幻覺) |
| Answer Relevancy | 生成 | 答案是否回應了用戶問題 |
| Context Precision | 檢索 | 檢索到的文件有多少是真正相關的(精確度) |
| Context Recall | 檢索 | 真正相關的文件有多少被成功檢索(召回率) |
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision
results = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy, context_precision],
)
黃金測試集(Golden Dataset)¶
自動評估需要事先準備「問題-答案-來源」三元組。方法:
- 從文件中讓 LLM 自動生成 Q&A 對(合成測試集)
- 人工標注少量高品質測試案例作為核心基準
生產環境的目標值、指標掉了該往哪查,見 Agentic RAG 與 LLM Evaluation 實務。
工具生態與選型¶
主流框架比較¶
| 框架 | 定位 | 優點 | 缺點 |
|---|---|---|---|
| LangChain | 通用 LLM 應用框架 | 生態最大、整合最多 | 抽象層厚、debug 困難 |
| LlamaIndex | 專注於資料索引與查詢 | RAG 功能最完整 | 較 LangChain 小衆 |
| Haystack | 企業級搜尋+RAG | 生產成熟度高 | 學習曲線較陡 |
| DSPy | 自動最佳化 Prompt | 系統性調優 | 概念抽象,新穎 |
最小可行 RAG 實作(LangChain)¶
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_anthropic import ChatAnthropic
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 1. 載入文件
loader = PyPDFLoader("document.pdf")
docs = loader.load()
# 2. 分塊
splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# 3. 建立索引
vectordb = Chroma.from_documents(chunks, OpenAIEmbeddings())
# 4. 建立 RAG Chain
qa = RetrievalQA.from_chain_type(
llm=ChatAnthropic(model="claude-opus-5"),
retriever=vectordb.as_retriever(search_kwargs={"k": 5}),
)
# 5. 查詢
answer = qa.invoke("這份文件的主要結論是什麼?")
上面的 chunk_size=512, chunk_overlap=50 只是讓程式跑起來的預設值,不是建議值——正式使用前務必對自己的語料實測。
下一步¶
這一站建立了骨架。往下走的三個方向:
- 底層選型還沒決定 → Embeddings 與向量資料庫實務(第 2 站)
- 跑起來了但效果不好 → RAG 進階系列(第 3 站,六個槓桿與落地優先序)
- 槓桿加滿仍不夠 → Agentic RAG 有完整的架構光譜比較(Naive / Advanced / Agentic / GraphRAG / Adaptive),含延遲與成本量級
其他相關:Prompt Engineering 核心技術 | Context Engineering | AI Agent Harness Engineering

