昨天我們把 RAG 理解成「回答前先查文件」,但真正開始做時,很快就會遇到下一個問題:PDF 本身並不是一個適合直接搜尋的知識庫。
即使人類可以打開一份 80 頁報告慢慢看,系統也不能每次收到問題,就把整份文件全部塞進模型。
因此,一份文件要變成 AI 可以搜尋的知識,通常會經過幾個步驟:
PDF
↓
Parse
解析文件內容
↓
Chunk
切成較小的文件片段
↓
Embedding
轉換成向量
↓
Index
建立可搜尋的索引
↓
Retrieval
找出與問題最相關的內容
RAG 的品質,往往不只取決於最後使用哪一個大型語言模型,而是在這些前處理步驟中,就已經決定了一大半。
PDF 看起來像一份文件,但它的內部可能包含:
解析器(Parser)的工作,就是盡可能把其中的文字與結構抽取出來,讓後面的系統可以繼續處理。
對純文字 PDF 來說,這件事相對簡單;但如果文件包含雙欄排版、複雜表格或大量圖片,解析結果就可能出現順序錯亂。
例如原始文件可能是:
退款期限:商品購買後 30 天內
退款條件:需保留原始發票
但解析錯誤後,可能變成:
退款期限:
退款條件:
商品購買後
需保留原始發票
30 天內
對人來說可能還猜得出意思,但對後面的搜尋與模型而言,文件語意已經開始被破壞。
這也是為什麼不能把:
「成功讀到 PDF」等同於「成功理解 PDF」。
後面真正動手實作時,第一件事不應該是立刻問模型問題,而是先檢查解析後的文字是否合理。
假設一份文件有幾萬字,直接把整份內容做成一個向量幾乎沒有意義。
系統需要把內容切成較小的文件片段(Chunk),讓每一個 Chunk 代表一段相對完整的語意。
例如原始文件:
員工差旅管理辦法
第一條:國內差旅住宿費每日上限為 3,000 元。
第二條:海外差旅住宿費依各國家與城市標準計算。
第三條:交通費需提供相關付款證明。
系統可能把它切成:
Chunk 1
第一條:國內差旅住宿費每日上限為 3,000 元。
Chunk 2
第二條:海外差旅住宿費依各國家與城市標準計算。
Chunk 3
第三條:交通費需提供相關付款證明。
這樣當使用者詢問:
「國內出差住宿可以報多少?」
系統就不需要搜尋整份文件,而是找出最可能包含答案的 Chunk。
但 Chunk 並不是越小越好。
如果一個 Chunk 包含很多不同主題,搜尋回來的內容可能混入大量不相關資訊。
例如:
Chunk 1
住宿規範 + 交通規範 + 餐費規範 + 海外差旅規範 + 請假規範
即使搜尋成功找到這一塊,模型仍然需要從很多資訊裡判斷哪一段才是真正的答案。
另一個極端是切得太細:
Chunk 1:國內差旅
Chunk 2:住宿費
Chunk 3:每日上限
Chunk 4:3,000 元
這時雖然每一塊都很小,但完整語意也被拆散了。
因此 Chunking 真正要解決的問題是:
如何讓每一個文件片段夠小,可以精準搜尋;又夠完整,可以保留原本的語意。
文件切割時,另一個常見概念叫做 Overlap(重疊區段)。
假設文件內容是:
員工申請海外差旅時,需要提前取得主管核准。
若住宿費超過當地標準,還需要取得部門主管的額外批准。
如果剛好從兩句中間切開:
Chunk 1
員工申請海外差旅時,需要提前取得主管核准。
Chunk 2
若住宿費超過當地標準,還需要取得部門主管的額外批准。
第二個 Chunk 中的「若」其實依賴前面的海外差旅情境。
因此實務上常讓相鄰 Chunk 保留一小段相同內容:
Chunk 1
員工申請海外差旅時,需要提前取得主管核准。
Chunk 2
員工申請海外差旅時,需要提前取得主管核准。
若住宿費超過當地標準,還需要取得部門主管的額外批准。
這就是 Overlap 的概念。
它的目的不是單純製造重複資料,而是降低重要語意剛好被切在邊界上的風險。
文件切成 Chunks 之後,下一步就是 Embedding。
Embedding 的目的,是把文字轉換成一組數字,讓系統可以用數學方式比較兩段文字的語意是否相近。
可以先不用管向量裡到底有幾百或幾千個數字,只要理解一個核心概念:
意思越接近的文字,在向量空間中的位置通常也會越接近。
例如文件中寫的是:
「顧客可於商品購買後 30 日內辦理退貨。」
使用者詢問:
「商品可以在幾天內退?」
兩邊沒有使用完全一樣的文字。
傳統只比對關鍵字的搜尋方式,可能因為「退貨」與「退」不同而漏掉結果;但 Embedding 會嘗試把文字轉換成語意表示,因此有機會判斷這兩段內容其實在討論相同的事情。
可以把它想成:
「商品可以在幾天內退?」
↓
Embedding
↓
[0.12, -0.47, 0.83, ...]
「商品購買後 30 日內可以退貨」
↓
Embedding
↓
[0.15, -0.43, 0.79, ...]
真正的向量會複雜得多,但我們現在不需要理解其中每個數字代表什麼。
只需要知道:
Embedding 讓系統從「文字長得像不像」,進一步比較「意思像不像」。
當所有 Chunks 都完成 Embedding 後,系統會把這些向量,以及對應的原始文字與來源資訊保存起來。這個可供查詢的結構,就是 Index(索引)。因此,每次收到新問題時,問題本身也會被轉成向量,再與文件 chunks 比較相似度,找出最相關的幾段。
可以想像成:
Chunk 001
文字:國內住宿費每日上限 3,000 元
來源:travel_policy.pdf
頁數:Page 3
向量:[......]
Chunk 002
文字:海外住宿費依各國家標準計算
來源:travel_policy.pdf
頁數:Page 3
向量:[......]
Chunk 003
文字:交通費需提供付款證明
來源:travel_policy.pdf
頁數:Page 4
向量:[......]
當使用者提出新的問題:
「國內出差住宿最多可以報多少?」
這個問題也會先被轉換成 Embedding。
接著系統比較:
Question Embedding
↓
與 Index 中的文件向量比較
↓
計算相似度
↓
找出最相關的 Chunks
最後可能找到:
Top 1
國內住宿費每日上限 3,000 元
Top 2
海外住宿費依各國家標準計算
Top 3
交通費需提供付款證明
其中排名最高的內容,就會成為後續交給大型語言模型的重要 Context。
因此完整流程其實可以拆成兩個階段。
PDF
↓
Parse
↓
Chunk
↓
Embedding
↓
Index
使用者問題
↓
Question Embedding
↓
搜尋 Index
↓
Relevant Chunks
↓
加入 Prompt Context
↓
LLM
↓
回答
到這裡,前一天介紹的 RAG 流程就完整串起來了。
很多 RAG 問題最後都會被誤判成:
「是不是模型回答能力不好?」
但真正的原因可能根本不在模型。
例如:
因此,除錯 RAG 時建議遵守一個非常重要的順序:
先看 Retrieval
↓
確認正確資料有沒有被找到
↓
再看 Generation
↓
確認模型有沒有正確使用資料
這個順序很重要。
假設使用者問:
「公司的國內住宿費上限是多少?」
正確答案明明在文件中,但 Retrieval 找回來的是:
Chunk 1:交通費規範
Chunk 2:海外住宿規範
Chunk 3:員工請假規範
這時候即使換成能力更強的 LLM,也解決不了真正的問題。
因為:
模型從一開始就沒有拿到正確資料。
如果這時一直修改 Prompt,只是在試圖讓模型更漂亮地回答錯誤的 Context。
開始實作 RAG 後,你很容易在網路上看到這類建議:
Chunk Size = 500
Overlap = 100
或:
Chunk Size = 1000
Overlap = 200
但真正重要的不是背下某一組數字。
Chunking 最重要的是理解片段大小與重疊區段之間的取捨。
可以把 Chunk 想成「把一本長文件切成一張張可以搜尋的小卡片」。
如果切太大:
一張卡片裡會混入太多不同資訊。
如果切太小:
同一條規則的前因後果可能被拆開。
Overlap 則像是讓相鄰卡片保留少量共同內容,避免重要資訊剛好被剪成兩半。
更重要的是,不同文件本來就可能適合不同的切割方式。
FAQ 通常天然就有:
Question
+
Answer
因此可以考慮以「一問一答」作為一個 Chunk。
這類內容往往需要保留:
條款名稱
+
適用條件
+
例外
+
完整規範
如果切得太細,很容易失去上下文。
操作文件則可能比較適合按照:
章節
→ 標題
→ 操作步驟
進行切割,而不是只按照固定字數。
因此真正的做法不是問:
「RAG 最佳 Chunk Size 是多少?」
而是問:
「針對我的真實問題,正確的文件片段能不能被搜尋回來?」
這也是後面我們設計 RAG 測試時真正需要驗證的事情。
知識庫並不是建立一次之後就永遠不用管理。
假設公司原本的政策是:
國內住宿費上限:3,000 元
半年後改成:
國內住宿費上限:3,500 元
如果只是把新版文件加入知識庫,卻沒有刪除舊版本,搜尋時就可能同時得到:
舊版:3,000 元
新版:3,500 元
模型這時就必須面對互相衝突的證據。
除此之外,還有幾個實務上很常見的問題:
尤其最後一點很容易在 Demo 階段被忽略。
如果 Index 只存在伺服器的暫存空間裡:
建立索引
↓
RAG 可以正常搜尋
↓
重新部署 Server
↓
暫存資料消失
↓
知識庫也跟著消失
這提醒我們:
建立索引只是知識庫的開始,版本、更新、刪除與持久化才是長期維運的一部分。
這些問題我們不需要在今天一次全部解決,但之後當 Data Machi 從 Demo 走向真正可以長期使用的系統時,就會再次遇到它們。
今天的重點:
RAG 不是把 PDF 丟給 AI 就結束,而是一條 Parse → Chunk → Embedding → Index → Retrieval 的資料流程。
下一篇,我們會更深入看 Retrieval:為什麼傳統關鍵字搜尋和語意搜尋各有優缺點,以及什麼時候應該混合使用。
我們下集見囉!