在 RAG 之前清理文件

為檢索增強生成(RAG)清理文件,意味著在剩餘文字被切分為塊、嵌入並儲存到向量索引之前,移除一切不屬於內容的東西——PDF 在每一頁上攜帶的頁眉、頁腳、頁碼和重複樣板。這是 RAG 流水線中最不起眼的一步,卻悄悄決定了檢索是否有效:用未清理的文字構建的索引會把同樣的版面雜訊儲存數百次,並把它當作知識檢索出來。本文解釋為什麼版面雜訊會毒害嵌入、分塊實際上如何運作、為什麼去重屬於嵌入之前、什麼該剝掉什麼該保留,以及如何評估清理是否真的有幫助。要實際動手清理,請使用文件清理器,並對照你自己的結果閱讀本指南。

為什麼頁面雜訊會毒害嵌入

嵌入模型把一塊文字變成一個應當捕捉這塊文字主題的向量。餵給它一個一半是報告標題、保密頁腳和頁碼的塊,這個向量也會忠實地捕捉那些東西——這意味著長文件中的每一個塊都會向同樣的雜訊漂移,本應在向量空間中相距很遠的塊,因為共享一個頁眉而人為地靠得很近。於是檢索恰恰做了它被要求做的事:問退款政策那一節,它返回五個塊,它們最強的共同訊號是頁眉中的公司名稱。沒有人犯建模錯誤。索引以完整強度塞滿了在每一頁上重複的雜訊,而重複的雜訊對一個計算相似度的系統來說,與訊號無法區分。

損害比稀釋更嚴重。雜訊塊可以乾脆贏得檢索:一個措辭像頁腳的問題——關於保密、文件控制或版本——與被嵌入了兩百次的頁腳塊的比對,會勝過真正回答它的那一個段落。頁碼造成一種更微妙的失敗,它教會模型裸數字就是內容,於是一個以「42」結尾的塊可能被當作有意義的數字引用回來。而且每個雜訊塊都佔據有限的 top-k 結果集中的一個位置,把真正相關的塊擠出模型被給到的上下文。因此清理不是美化性的整理;它是在你自己的索引內部,移除你自己內容的一個系統性競爭者。

分塊基礎:大小、重疊與邊界

分塊是關於什麼單位被嵌入的決定,它有三個旋鈕。大小在聚焦與上下文之間權衡:小塊,幾百個字元,銳利地嵌入一個想法、精確地檢索,但可能缺少賦予這個想法條件的句子;大塊,幾千個字元,帶著上下文,卻把幾個想法混成一個平均向量,於是沒有一個能強力檢索。大多數文件語料落在中間某處——大約 500 到 1,000 個字元是一個合理的起始區間——而正確答案由用你自己的問題測試你自己的文件決定,而不是抄一個框架預設值。LangChain 文字切分文件很好地梳理了切分器家族,從字元和遞歸切分器到結構感知切分器。

重疊是針對第二個旋鈕邊界的保險。一個不管結構、每 800 字元就切一刀的切分器,遲早會把一個定義切成兩半,而兩半都既不好檢索也不好讀。按邊界切分——先段落、再句子、再空格,正如遞歸切分器所做的——大多數時候能保持單位完整,而百分之十到二十的適度重疊會把一個塊的邊緣重複到下一個塊的開頭,這樣即使切分還是發生了,也不會讓上下文成為孤兒。LlamaIndex 節點解析器文件中的層級從載入器一側說明了同一點:先解析成尊重文件的節點,再切分。但這一切都救不了雜訊:重疊會忠實地把頁眉也重複進下一個塊,這就是為什麼清理在分塊之前、永遠不在之後。

在嵌入之前去重

重複是第二種系統性污染物,它有兩種。第一種是整篇文件的重複:同一 PDF 以不同檔名被索引兩次,舊版本留在新版本旁邊,郵件附件保存在共用磁碟副本旁邊。第二種在文件內部:標題頁上的免責聲明又出現在附錄中,摘要在每一章開頭重複,表格既貼進正文又貼進附件。兩種對檢索的影響相同——重複文字被反覆嵌入,於是被反覆檢索,用一個段落的副本擠滿 top-k,而其他段落永遠浮不上來——對成本的影響也相同,因為你要為每一份副本的嵌入和儲存付費。

去重在嵌入之前最便宜,那時它還只是一次字串比較。完全重複可以靠規範化解決:摺疊空白,忽略你已經剝掉的頁面雜訊,對結果做雜湊並只保留一份。近似重複——同一段落只改了日期或重編了標題——需要一次相似度檢查,但即使粗糙的檢查也能抓住危害最大的樣板。有兩條提醒讓去重保持誠實。第一,保留第一次出現,並記錄副本在哪裡;一個在兩章中重複的警告可能是刻意的安全裝置,把它從記錄中徹底移除,與把它從索引中移除是不同的決定。第二,在清理之後去重,而不是之前:兩頁如果只差頁碼,那就是被不同雜訊包裹的內容重複,只有清理後的文字才能揭示這一點。

什麼該剝掉,什麼該保留

剝掉那些重複卻不提供資訊的東西:頁眉頁腳、頁碼、水印、保密橫幅、產生的「列印於」行,以及網頁轉 PDF 匯出在每一頁上蓋的導覽屑。保留那些提供結構的東西:真正的標題,因為它們是你擁有的最強的分塊邊界訊號,並且常常應進入塊的中繼資料;圖注,因為圖題往往是唯一說明圖裡有什麼的文字;表格內容,但要小心處理,因為被剝過的表格會失去讓其數字有意義的行列標題;還有清單,其條目是天然的單位。臨界案例是目錄。作為塊,目錄行是雜訊——一串與每個查詢都隱約比對、卻回答不了任何一個的標題和頁碼——但作為中繼資料,同一頁目錄是文件的地圖。把它從被嵌入的文字中剝掉,把它留在你關於語料的筆記中。

同樣的判斷也適用於文件清理器本身:雜訊和頁碼預設被勾選,因為它們幾乎總是可以安全移除,而重複段落從不預設勾選,因為重複有時正是目的所在。擷取的誠實在這裡也重要。PDF 是一種結構化格式——ISO PDF 標準 ISO 32000 把頁面定義為繪圖和文字操作的內容流,而不是流動的文字——因此任何擷取器都在近似閱讀順序,而掃描版 PDF 在 OCR 給它文字層之前根本沒有文字層。如果你的來源文件出於其他原因也重要,配套文章你的 PDF 是網站無障礙的一部分介紹了同時幫助螢幕閱讀器和擷取器的標籤結構。

清理後評估檢索效果

清理是一個假設——檢索會變好——而關於搜尋品質的假設可以用少得令人尷尬的機械來檢驗。收集二十到五十個你的使用者真正會問的問題,並手工註明每個問題應當由哪段文字回答。用同一個檢索器、同樣的塊設定和同樣的 top-k,把它們跑在未清理的索引和清理後的索引上,比較兩件事:正確的段落是否出現在結果中,以及它出現時排名有多高。這一對——召回與排名——就是記分牌。如果清理在起作用,雜訊塊會停止出現在結果中,正確的段落會上升,而那些過去因為頁腳壓過答案而失敗的問題會開始通過。

然後閱讀失敗案例,因為數字本身會奉承你。一個清理後的索引可能召回變好,卻丟掉一個問題——它的答案確實住在你去重去掉的一個重複摘要裡,或者住在一個為每個塊帶來所需產品名的頁眉裡。這些損失是資訊:它們告訴你把那個元素保留為中繼資料,或者有意地把文件標題前置到每個塊,而不是意外地把它嵌入兩百次。每當語料、切分器或嵌入模型改變時,重新運行評估,並把問題集與索引一起版本化。RAG 品質不是發布時的一次性屬性;它是一種維護屬性,一個小的固定測試集正是把「感覺變好了」變成可以辯護的東西的關鍵。

來源與延伸閱讀

常見問題

清理到底能把檢索改善多少?

這取決於你的文件有多重的雜訊,而這是可測量的:在清理前數一數跨頁重複的行,並用一組固定的真實問題在索引的前後各測一次。帶有強頁眉的長報告收益最大,因為雜訊可以佔據每個塊的十分之一或更多;短而乾淨的文件可能收益甚微。誠實的方法是最後一節中的前後對比評估,而不是承諾某個百分比。

我應該在分塊之前還是之後清理?

永遠在之前。對未清理的文件分塊,會把雜訊嵌入每一個塊,把它重疊進下一個塊,並把頁碼散布在整個索引中,之後的任何步驟都無法可靠地移除它們。先清理擷取出的文字,再對結果分塊——文件清理器正是按這個順序產出清理後的文字和現成的 JSONL 塊的。

我應該從什麼分塊大小和重疊開始?

從大約 800 個字元、按段落邊界切分、帶適度重疊開始,然後針對你自己的問題集調優。較小的塊檢索更精確但上下文更少;較大的塊恰好相反。像來源中連結的 LangChain 文字切分文件這樣的框架指南描述了切分器家族,但適合你語料的數字來自評估,而不是來自預設值。