從零做一個真的能用的 RAG 知識庫:框架不是第一步,資料清理才是
很多 RAG 教學一開始就裝 LangChain、LlamaIndex、向量資料庫,然後十幾行 code 跑出一個聊天介面。
這種 demo 很適合截圖,不太適合你拿去給公司同事用。
因為真實知識庫最難的不是把 query 丟進 vector DB。最難的是你要讓模型拿到乾淨、正確、版本清楚的資料。資料層沒處理好,後面換什麼模型都只是把垃圾講得更順。
RAG 最早被系統化討論時,重點就是把生成模型和可檢索的外部記憶結合起來。Lewis 等人在 RAG paper 裡講的是 parametric memory + non-parametric memory。今天我們做企業知識庫,也是在做同一件事:模型負責語言和推理,外部資料庫負責可更新事實。
所以第一步不是選框架。
第一步是問:你的資料能不能被機器穩定讀懂?
先定義你的文件物件
不要把 PDF、Notion、網頁、Google Docs 直接丟進去。
先轉成一個標準格式。最少要有:
{
"doc_id": "pricing_policy_2026_06",
"title": "Pricing and refund policy",
"source_url": "https://example.com/pricing",
"updated_at": "2026-06-01",
"owner": "billing-team",
"status": "active",
"text": "...clean markdown..."
}
為什麼要這麼麻煩?
因為 RAG 壞掉時,你要能追。它引用的是哪份文件?是不是舊版?誰負責維護?這份文件還有效嗎?
如果你只存一段文字,模型答錯時你只能看著向量資料庫發呆。
清理資料比 embedding 更重要
PDF 抽文字會有頁首頁尾。網頁會有導覽列、推薦文章、cookie banner。Notion 匯出會有奇怪的 nested block。客服紀錄裡會混入簽名檔和模板話術。
這些東西如果不清掉,embedding 會很認真地把它們也編進去。
到時候使用者問「退款期限」,系統可能撈到每頁重複的 footer,或一段完全無關的法律聲明。不是模型笨,是你餵它的索引太髒。
建議先做三層處理:
- extraction:把來源轉成 markdown 或純文字
- cleaning:移除 boilerplate、重複區塊、亂碼
- normalization:統一標題、日期、URL、版本欄位
如果你要處理 PDF,至少抽樣人工檢查幾份。尤其是表格和雙欄排版。很多 RAG 在財報、合約、研究報告上出錯,就是 PDF 抽取階段已經壞了。
Chunk 要帶著標題走
切片不要只按 token 數。
比較好的順序是:先照 H1/H2/H3 切,再看段落,最後才按 token 長度補切。每個 chunk 都要保留上層標題。
例如:
{
"chunk_id": "pricing_policy_2026_06#refund#003",
"doc_id": "pricing_policy_2026_06",
"section_path": ["Pricing", "Refund policy", "Enterprise exceptions"],
"source_url": "https://example.com/pricing#refund",
"updated_at": "2026-06-01",
"text": "Enterprise contracts may override the standard 14-day refund policy..."
}
這樣模型看到的不只是文字,還知道這段在哪個脈絡下。
很多人會加 overlap。可以,但不要亂加。Overlap 太多會讓 retrieval 結果看起來有五段,其實都在講同一件事。最後 prompt 被重複內容塞滿,真正需要的補充資料反而進不來。
Retrieval 不是一次搜尋就結束
最簡版 RAG 是 query embedding -> top k chunks。
但實務上常常不夠。
例如使用者問:「Pro 和 Enterprise 的 SSO 權限差在哪?」
這需要找 Pro,也要找 Enterprise,還要找 SSO,不是單一語意相似就能處理。你可以用 multi-query retrieval:先讓模型拆成幾個查詢,再各自檢索。也可以用 HyDE,先生成一段假想答案,再用那段去做 dense retrieval。HyDE 這個方法在 Precise Zero-Shot Dense Retrieval without Relevance Labels 裡有比較完整的討論。它的直覺是:短 query 有時太瘦,先讓模型生成一個「可能相關文件」可以幫 embedding 找到更好的鄰近區域。
但 HyDE 也有風險。它生成的假文件可能有錯,所以後面一定要回到真實 corpus。不要把 HyDE 生成內容當答案,它只是搜尋輔助。
不同 retrieval 做法要拿結果比較,不要只看架構圖
RAG 文章如果只畫「文件 -> embedding -> vector DB -> LLM」那張圖,讀者看完其實還是不知道怎麼選。
比較好的寫法,是直接放一張小型 eval table。不是為了假裝很科學,而是逼自己承認:不同 retrieval 策略在不同題型上差很多。
假設你做一個 50 題內部知識庫評估集,題型包含單點查詢、比較題、版本題、拒答題。你可以用下面這種表格記結果。數字不是通用 benchmark,因為每個 corpus 都不一樣;這種表的價值是讓讀者看到「你真的有測」,不是只憑感覺推薦工具。
| Retrieval 設計 | Top-5 命中正確 chunk | 答案引用正確率 | 平均延遲 | 最適合場景 | 最常見失敗 |
|---|---|---|---|---|---|
| 只用 dense vector search | 68% | 54% | 低 | FAQ、語意接近的短問題 | 語意像但不是答案,尤其容易抓錯版本 |
| BM25 + dense hybrid | 78% | 63% | 中低 | 有產品名、API 參數、錯誤碼、專有名詞的文件 | query 太口語時,keyword match 仍然抓不到 |
| Multi-query retrieval | 82% | 67% | 中 | 比較題、多條件問題,例如 Pro vs Enterprise | 生成的子查詢太散,會帶入雜訊 |
| HyDE + dense retrieval | 80% | 61% | 中 | 原始 query 太短、使用者描述很模糊時 | 假想文件會引入不存在的細節,需要後面 rerank 壓住 |
| Hybrid + reranker | 88% | 76% | 中高 | 要上線的客服、sales enablement、內部 policy bot | 成本和延遲上升,但通常值得 |
| Hybrid + metadata filter + reranker | 91% | 82% | 中高 | 有版本、權限、地區、產品線差異的知識庫 | metadata 維護很煩,髒資料會直接毀掉結果 |
這張表的結論很無聊,但很實際:如果只是 demo,dense vector search 很快;如果要上線,通常要 hybrid + rerank + metadata filter。
BM25 這種老方法不要太早丟掉。BEIR benchmark 那篇 A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models 其實也提醒過,retrieval 沒有單一模型能在所有資料集上穩贏。dense retrieval 很會抓語意,但遇到 SKU、錯誤碼、函式名、法條編號、產品版本,keyword signal 仍然有用。
所以我會這樣看:
- 問題很短、文件很乾淨:dense search 可以先跑
- 文件有大量專有名詞:加 BM25 / hybrid
- 問題常常要比較兩個東西:加 multi-query
- 使用者問法很模糊:可以測 HyDE
- 要上線給真人用:加 reranker
- 文件有新舊版本和權限差異:metadata filter 不可省
如果你懶得做完整 benchmark,至少手工測 30 題。每一題記下 top-5 chunks 和最後答案。你會很快看到問題不是「模型不夠強」,而是 retrieval 根本沒把正確資料撈上來。
第一版技術選型不要太華麗
如果你本來就有 Postgres,先用 pgvector 很合理。少一個系統要維護。
如果你想快速做本地 demo,可以用 Chroma 或 Qdrant。資料量很小時,甚至 numpy + cosine similarity 都可以。
我會把常見選項粗暴分成這樣:
| 選項 | 適合誰 | 優點 | 缺點 | 我會怎麼選 |
|---|---|---|---|---|
| numpy / 本地檔案 | 只想理解 RAG 流程的人 | 最透明,沒有黑盒 | 不適合多人、更新、權限 | 教學或 spike 用 |
| Chroma | 快速 demo、小型內部工具 | 開發快,本地很好跑 | production 維運能力要自己評估 | 原型可以用 |
| pgvector | 本來就有 Postgres 的團隊 | 少維護一套系統,metadata / transaction 好處理 | 超大規模向量搜尋不是它最強項 | 多數 SaaS 第一版我會選這個 |
| Qdrant | 想要正式 vector DB 但又不想太重 | filter、payload、部署彈性不錯 | 還是多一個服務要管 | 中型 RAG 產品很合理 |
| Weaviate | 需要比較完整的 semantic search stack | 功能多,生態成熟 | 對小團隊可能太重 | 資料團隊較完整再考慮 |
| Pinecone | 想少管 infra、直接用 managed vector DB | 省維運,scale 較輕鬆 | 成本和資料控制要評估 | 有預算、要快上線可選 |
重點不是一開始選最強 vector DB,而是把資料流打通:
- ingest 能不能重跑?
- 文件更新後舊 chunk 會不會失效?
- chunk_id 是否穩定?
- metadata 能不能 filter?
- retrieval 結果能不能 debug?
很多團隊過早上重型架構,結果連哪個 chunk 被命中都看不到。
Prompt 要把引用變成硬規則
一個基本模板可以長這樣:
你是內部知識庫助手。只能根據 CONTEXT 回答。
如果 CONTEXT 沒有明確答案,回答「目前資料不足」。
每個關鍵結論後面都要附 source_url 或 chunk_id。
不要使用未提供的外部知識補充。
這不會完美,但比「請回答使用者問題」好很多。
更進一步,你可以要求輸出 JSON:answer、citations、missing_info、confidence。這樣前端可以檢查 citation 是否存在,沒有 citation 就不顯示答案。
一開始就要記 log
每次問答都要記:
- user question
- rewritten query
- retrieved chunks
- reranked chunks
- final context
- model answer
- citations
- user feedback
這些 log 不是之後有空再做。
沒有 log,你就不知道失敗是 retrieval 問題、資料問題、prompt 問題,還是模型問題。RAG 會變成玄學。
結論
RAG 知識庫不是把文件丟進向量資料庫。
它是一套資料產品:文件標準化、清理、切片、metadata、召回、重排、引用、拒答、log。
框架可以幫你省 code,但不能幫你定義資料品質。
所以第一次做 RAG,不要急著問「LangChain 還是 LlamaIndex」。先問你的資料是不是乾淨、可追、可更新。如果不是,後面的 AI 都只是在替爛資料化妝。