RAG 實戰筆記

RAG 基礎邏輯與實戰教學:從文件到答案的完整管線

2026-08-09 #教學#RAG#Qdrant#LLM#向量檢索

這是「RAG 實戰筆記」系列的第一篇。我正在打造一個企業文件問答平台(黃金問答集實測檢索命中率 100%、答案正確率 89%),這個系列會把過程中搞懂的觀念和踩過的坑整理成文。第一篇先把地基打好:RAG 到底在解決什麼問題、完整管線長什麼樣、以及每個環節的實戰細節。

一、RAG 在解決什麼問題?

LLM 很聰明,但有三個先天限制:

  1. 知識有截止日——模型訓練完成後發生的事它不知道。
  2. 不知道你的私有資料——公司的規章、合約、會議記錄,模型從沒看過。
  3. 會一本正經地胡說——被問到不知道的事,它傾向生成「看起來合理」的答案,而不是承認不知道。

RAG(Retrieval-Augmented Generation,檢索增強生成)的解法直觀得近乎樸素:回答之前,先把相關資料找出來塞給模型看。模型不再憑記憶作答,而是「開書考」——而且書是你指定的。

1
2
傳統 LLM:  問題 ──────────────────▶ 模型憑訓練記憶回答
RAG: 問題 ──▶ 檢索相關文件片段 ──▶ 模型根據片段回答(並標明出處)

這帶來三個直接的好處:答案有依據(可以強制標來源,錯了能追溯)、資料可以隨時更新(加文件就好,不用重訓模型)、私有資料不用送去訓練(我的專案裡 embedding 完全在本機跑,文件內容不出機器)。

二、完整管線:兩條路徑

RAG 系統有兩條獨立的資料流——建索引(把文件變成可檢索的形式,離線做)和查詢(把問題變成答案,即時做):

1
2
3
4
5
6
7
8
9
10
11
12
13
【建索引 · Ingestion】
文件(PDF/Word/Excel/PPT/掃描檔)
→ 載入(含 OCR 判斷)
→ 切塊(chunking)
→ 向量化(embedding:dense + sparse)
→ 寫入向量資料庫(附 metadata)

【查詢 · Query】
問題 →(多輪對話時先改寫)
→ 向量化(與建索引同一顆模型!)
→ 混合檢索(dense 20 + sparse 20 → RRF 融合 → 廣撈 30)
→ Rerank 重排 → 取 top-15
→ 丟給 LLM 生成(強制標來源)

以下逐段拆解,每一段都附上我實際的做法與理由。

三、載入:文件世界比你想的髒

教學文章的 RAG 都從乾淨的純文字開始,現實裡你面對的是:有文字層的 PDF、沒有文字層的掃描 PDF、Word 裡的表格、Excel 的多工作表、PPT 的文字框。每種都要有對應的處理:

類型 處理方式
PDF(有文字層) 直接抽文字(pypdf)
PDF(掃描檔) 轉圖 → OCR(tesseract,中文繁體+英文)
Word 段落 + 表格
Excel 逐工作表逐列串接
PPT 逐投影片文字框

一個實用的小設計:怎麼判斷 PDF 是不是掃描檔? 我的做法是「平均每頁字元數 < 20 就當掃描檔走 OCR」。閾值很土,但有效——有文字層的 PDF 每頁隨便都幾百字,掃描檔抽出來幾乎是空的。

四、切塊:RAG 品質的第一個決勝點

文件不能整份塞給模型(塞不下,也不精準),要切成小塊。切塊策略直接決定檢索品質,這是我整個專案裡投資報酬率最高的一個環節。

起手式:固定長度滑動視窗。 每塊 700 字、相鄰塊重疊 120 字,以字元數計(對中文最直覺),切點盡量落在自然斷點(換行 > 句末標點 > 逗號),避免硬切在詞中間。

為什麼從最笨的方法開始?先笨後巧——第一階段要驗證的是「檢索+切塊能不能回答問題」這個大方向,固定切塊最穩、最好除錯;結構感知切塊是優化題,過早引入只會多一個出錯變數。

進化:表格列感知切塊。 固定切塊遇到表格會出災難——表格被橫向硬切,切塊開頭是上一筆資料的尾巴,「不能安全駕駛」被切成「不能安全駕/駛」,表頭和內容分家,切塊語意完全破碎。解法是先把版面文字還原成「一筆一列」的紀錄,再以固定筆數(我用 12 筆)完整列組成一個切塊,每塊都冠上表頭與文件日期,讓每個切塊能被獨立讀懂:

1
2
3
4
臺灣桃園地方檢察署公告(民國115年03月10日)偵查案件一覽
股別 | 年度 | 字別 | 案號 | 案由 | 移送機關 | 被告姓名 | 偵結要旨
律 | 115年偵字第7615號 | 家庭暴力防治 | 楊梅分局 | 黃○育 | 起訴
…(共 12 筆完整案件)

偵測不到已知表格樣式時自動退回固定長度切塊,一般文件不受影響。實測效果:答案正確率 50% → 72%(+22 個百分點)、檢索命中率 93% → 100%。 一個切塊策略的改進,比換模型、調 prompt 都有效——這是我想強調的重點:RAG 的瓶頸常常在資料工程,不在模型。

五、Embedding:把語意變成座標

Embedding 模型把文字轉成高維向量,讓「語意相近」變成「距離相近」——這是整個檢索的數學基礎。我用 bge-m3(本機執行,文件不出機器),它一次產生兩種向量:

  • dense(稠密向量):1024 維的語意向量。「請假規定」和「休假辦法」字面不同但語意近,dense 向量抓得到。
  • sparse(稀疏向量):詞彙級的權重表。專有名詞、案號、代碼這種「一字不差才算命中」的查詢,sparse 才可靠——dense 對「115年偵字第7615號」這種字串是很遲鈍的。

⚠️ 單向門:embedding 模型一旦定案就很難換

查詢時的向量必須跟建索引時用同一顆模型產生,否則兩邊的向量空間對不上,檢索結果全錯。這意味著換 embedding 模型 = 整個向量庫重算。所以選型時要想清楚,而且開發與正式環境用同一顆模型——品質一致、零重算。這是 RAG 架構裡少數「決定了就回不太了頭」的選擇。

六、檢索:混合搜尋 + 重排

單靠 dense 或單靠 sparse 都有盲區,所以用混合檢索

  1. dense 搜 20 筆 + sparse 搜 20 筆
  2. RRF(Reciprocal Rank Fusion)融合兩份排名——不比較兩邊的分數(量綱不同沒法比),只看名次
  3. 廣撈 30 筆候選
  4. 交給 reranker(bge-reranker-v2-m3)精排——它把「問題+候選切塊」成對送進模型打分,比純向量距離準得多,但慢,所以只用在最後一哩
  5. 取 top-15 進生成

這個「粗篩廣撈 → 精排收斂」的兩段式,是檢索系統的經典架構:粗篩要快、召回要廣(寧可多撈);精排要準(把真正相關的排上來)。

七、生成:把模型鎖在證據裡

檢索到的切塊連同問題一起組成 prompt 送給 LLM(我用 Claude),關鍵的設計有兩個:

  • 強制標來源:要求模型每個論點都標註出處([1][2] 對應到具體文件與切塊)。這不只為了可信度——它實質上改變了模型的行為,讓它傾向「只說找得到依據的話」。
  • 多輪改寫:對話中的追問常常是「那第二種呢?」這種缺乏上下文的句子,直接拿去檢索必死。所以檢索前先用 LLM 把問題改寫成獨立完整的查詢(query rewrite),這一步用 LangGraph 把「改寫 → 檢索 → 生成」串成明確的流程圖。

八、沒有評測,一切都是感覺

最後、也是最容易被跳過的一環:黃金問答集。準備一組「問題 + 標準答案 + 答案所在文件」的測試集,每次改動(換切塊策略、調檢索參數、改 prompt)都跑一遍,量兩個數字:

  • 檢索命中率:正確答案所在的切塊,有沒有進到 top-k?
  • 答案正確率:模型的回答對不對?

沒有這個,你只能憑感覺判斷「好像有變好」;有了它,前面提到的「表格切塊 +22pt」才有辦法被驗證。RAG 是資料工程,資料工程要量測。

小結:一張圖記住 RAG

1
2
3
4
5
     品質好壞的責任分佈(個人體感)
切塊策略 ████████████ 40%
檢索設計 ████████░░░░ 30%(混合檢索、rerank)
Embedding 選型 ████░░░░ 15%
Prompt/生成 ███░░░░░░ 15%

下一篇會深入資料層的一個常見疑問:向量資料庫和 MongoDB 到底差在哪?為什麼我的專案兩個都用?

Creative Commons 姓名標示 非商業性

本文採用 CC BY-NC 4.0 授權

歡迎轉載與引用,請標明出處(RAG 基礎邏輯與實戰教學:從文件到答案的完整管線 — EmptyWu);禁止用於商業用途。

商業使用或合作提案,歡迎來信洽談:murmur20260202@gmail.com

留言
分享

留言