指南)
前幾天幫一個礦產(chǎn)勘查團隊搭 RAG 知識庫拉回來的第一批資料直接讓我破防TXT 文件打開是“錕斤拷錕斤拷”Word 里藏著一堆修訂記錄PDF 有掃描版有電子版網(wǎng)頁正文粘出來還帶了一堆導航欄。這些材料要是直接喂給檢索增強生成RAG系統(tǒng)別說高精度檢索連基本的“找到對的那句話”都做不到。探礦業(yè)務天然就是多種格式文檔混戰(zhàn)的場景。地質(zhì)報告、鉆孔編錄、化驗數(shù)據(jù)、行業(yè)規(guī)范、期刊論文往往橫跨十幾年甚至幾十年有的來自老式 GIS 系統(tǒng)導出有的是儀器直接輸出還有的是掃描存檔。這些資料要變成 RAG 能用的知識第一道坎就是清洗。這篇文章我把自己在探礦業(yè)務里折騰 TXT、Word、PDF 和網(wǎng)頁清洗的完整方案、踩坑記錄和調(diào)優(yōu)經(jīng)驗都梳理出來適合正在做垂直領(lǐng)域知識庫、處理非結(jié)構(gòu)化文檔的工程師和數(shù)據(jù)人員參考。1. 地質(zhì)資料堆成山RAG 卻“檢索不動”鍋大半在清洗1.1 探礦文檔究竟長什么樣四種格式的“野生形態(tài)”探礦業(yè)務里的文檔和我們平時處理的互聯(lián)網(wǎng)文章完全是兩碼事。先說 TXT這玩意看著最“干凈”實際最容易亂碼。物探儀器導出的測量數(shù)據(jù)、化驗室輸出的元素含量表、老式地理信息系統(tǒng)導出坐標經(jīng)常是 GBK、GB2312、UTF-8 混著來有的還帶 BOM。更頭疼的是數(shù)據(jù)文件的字段分隔符不統(tǒng)一有的是 Tab有的是豎線|有的是連續(xù)空格解析不對就是災難。Word 文檔在探礦業(yè)務里通常是報告正文、設(shè)計書和野外記錄整理稿。問題不在于段落文字本身而是內(nèi)部夾雜了公式對象、表格、文本框、頁眉頁腳還有“修訂模式”留下的紅字和批注。我見過一份儲量估算報告正文里稀稀拉拉穿插著十幾處批注和修訂痕跡清洗的時候不處理這些檢索到的內(nèi)容就會被過期信息污染。PDF 是最分裂的一類。電子版 PDF 還算友好掃描版才是真正的“硬骨頭”。早期地質(zhì)報告很多是掃描件紙張泛黃、字跡模糊、表格線扭曲更別提地質(zhì)圖件里那些手寫標注和圖例。這類材料不做 OCR 就只能是“圖片”RAG 完全抓不到內(nèi)容。即便是電子版 PDF雙欄排版的期刊、帶復雜表格的測試報告也會讓常規(guī)解析器出錯。網(wǎng)頁資料同樣棘手。地質(zhì)類資訊、地學數(shù)據(jù)庫頁面、標準規(guī)范查詢頁結(jié)構(gòu)五花八門。有的頁面是老式 GB2312 編碼有的正文藏在嵌套表格里有的內(nèi)容分多頁顯示直接抓下載體把導航、廣告、版權(quán)聲明一起塞進知識庫檢索時噪聲極大。1.2 RAG 清洗洗的是什么字符、版面、語義三層過濾很多團隊把 RAG 的重點放在向量模型和檢索參數(shù)上實際跑下來才發(fā)現(xiàn)數(shù)據(jù)沒洗干凈后面全白搭。所謂清洗本質(zhì)上是把一份“給人看的文檔”改造成“給機器精確理解的結(jié)構(gòu)化文本”。我習慣把它拆成三層來做。字符層是最基礎(chǔ)的。亂碼要修、編碼要統(tǒng)一、全角半角要規(guī)范化、不可見字符要去掉。這一層不做后面的切分和向量化就是在垃圾上蓋樓。版面層解決的是“頁眉頁腳、頁碼、目錄、水印”等頁面裝飾物以及雙欄順序、表格結(jié)構(gòu)這類版式問題。至于語義層則是去掉與主題無關(guān)的章節(jié)、重復的段落提取真正有意義的知識內(nèi)容。打個比方清洗不是把一件舊衣服裁成新衣服而是把上面的污漬洗掉、線頭剪干凈、紐扣釘牢讓衣服原本的版型和材質(zhì)顯現(xiàn)出來。RAG 檢索靠的是語義相似度一堆亂碼和噪聲文本在高維向量空間里只會把“有用的信號”淹沒掉。所以探礦業(yè)務 RAG 的精度瓶頸往往不在大模型而在我們把資料送進模型之前有沒有完成這三層過濾。2. 逐個擊破TXT、Word、PDF、網(wǎng)頁的清洗實操2.1 TXT 清洗亂碼三兄弟“錕斤拷”“燙燙燙”“”是怎樣煉成的TXT 清洗的第一步永遠是“猜編碼”。常見亂碼里“錕斤拷”是 UTF-8 的替換字符 UFFFD 被 GBK 二次解碼后的產(chǎn)物本質(zhì)上是因為原文件是 GBK 編碼但工具按 UTF-8 去讀遇到無法處理的字節(jié)就替換成“”再用 GBK 讀回來就成了“錕斤拷”?!盃C燙燙”則更經(jīng)典這是 Visual C 調(diào)試模式下未初始化棧內(nèi)存被填成 0xCC按 GBK 解碼得到的漢字通常意味著數(shù)據(jù)本身就是半成品?!啊眴蝹€出現(xiàn)則多半是二進制文件混入或單字節(jié)截斷。處理時我一般不靠肉眼去猜直接用 chardet 或 charset-normalizer 檢測字節(jié)流檢測到 GB2312 就升級成 GB18030 再解碼盡量降低單字節(jié)損失。先讀前 10KB 做檢測命中率已經(jīng)能覆蓋絕大多數(shù)場景速度還快。import chardet def smart_read_text(file_path): with open(file_path, rb) as f: raw f.read() guess chardet.detect(raw[:10000]) or {encoding: utf-8} enc guess.get(encoding, utf-8) # GB2312/GBK 統(tǒng)一用 GB18030 解碼兼容生僻字 if enc.lower() in (gb2312, gbk): enc gb18030 return raw.decode(enc, errorsreplace)解碼之后還要做幾件小事。一是統(tǒng)一換行符把\r\n、\r全轉(zhuǎn)成\n二是把全角標點轉(zhuǎn)半角中文內(nèi)容保留全角沒問題但英文、數(shù)字和代碼片段一定要轉(zhuǎn)半角三是按字段分隔符清理解析比如 GIS 導出的“ID, X, Y, Z, 巖性編碼”這類結(jié)構(gòu)要確認坐標和屬性的分隔邏輯是否一致。提示TXT 里的行去重也要分場景。探礦數(shù)據(jù)的“重復行”可能是同一鉆孔不同深度的記錄值一樣但深度不同不能簡單按整行 hash 去重。我一般會帶上“鉆孔編號 深度區(qū)間”這么一組業(yè)務主鍵來判斷才敢放心去重。2.2 Word 清洗標題層級、修訂模式、公式對象三個坑逐個填Word 清洗的優(yōu)先度高于 TXT 和 PDF因為 Word 自帶結(jié)構(gòu)信息——標題樣式、段落層級都是現(xiàn)成的這對后續(xù)切分非常有價值。我用 python-docx 按段落流讀取先判斷樣式名把 Heading 1/2/3 的層級記錄下來再按正文段落繼續(xù)提取這樣清洗完得到的不是一坨純文本而是一棵“章節(jié)樹”。修訂模式和批注是探礦報告里最常見的“隱藏污染”。一份經(jīng)歷了多人審閱的方案正文里可能殘留大量刪除線文字和新插入文字。我用 python-docx 讀取時要遍歷文檔的修訂狀態(tài)優(yōu)先取“最終版本”的內(nèi)容批注一律丟棄。如果文檔實在結(jié)構(gòu)混亂也可以用 LibreOffice 命令行轉(zhuǎn)成 docx 再處理兼容性會好很多。公式對象是第二個坑。探礦業(yè)務里的公式多出現(xiàn)在資源量估算、化學分析數(shù)據(jù)處理這些章節(jié)。Word 里的公式可能是 MathType、AxMath 或 OMML 格式提取后大多是 OMML想轉(zhuǎn) LaTeX 還得掛個轉(zhuǎn)換器。我的建議是清洗階段不強求公式完美轉(zhuǎn)寫先把公式周圍的主干文本提取出來保留公式的編號或名稱比如“式3-2— 儲量計算公式”再單獨把公式轉(zhuǎn) LaTeX 存元數(shù)據(jù)。這樣既能保證正文語義不丟又不會因為某個公式解析失敗卡斷整個流程。表格在 Word 里也要單獨抽出來我用 python-docx 遍歷 table拆成 Markdown 或 JSON 格式保存。探礦報告里的化驗結(jié)果表、鉆孔柱狀表行列結(jié)構(gòu)往往很規(guī)整直接轉(zhuǎn)成 Markdown 表格后RAG 在召回時會更容易命中“數(shù)值型”問題。from docx import Document doc Document(某礦區(qū)勘探報告.docx) tree_lines [] for block in doc.element.body.iterchildren(): tag block.tag.split(})[-1] if tag p: para doc.paragraphs # 實際用業(yè)務邏輯映射 elif tag tbl: # 表格單獨走 CSV/Markdown 分支 pass2.3 PDF 清洗先分掃描版和電子版再談雙欄與表格PDF 清洗的第一件事不是調(diào)參而是判斷“這個文件到底是文本層 PDF 還是掃描版”。拿 PyMuPDF 打開文件后抽取某一頁的文本長度如果整頁提取出來不到十幾個字符基本可以斷定是掃描件得走 OCR 路線。電子版 PDF 我用 PyMuPDF 提取塊級信息用page.get_text(dict)拿到坐標和字體大小。雙欄期刊的判斷很簡單如果一個頁面的文字塊 x 坐標分布明顯分成兩個簇左右兩列寬度相似就按中線切分然后按“左列從上到下、右列從上到下”的順序重排。這里容易踩的坑是“圖注和表格穿插在欄間”跨欄的大表格要單獨識別出來否則文字順序一亂語義就斷了。掃描版 PDF 走 OCR 時我試驗過 PaddleOCR 和 Tesseract 兩套方案。PaddleOCR 中文版面還原度明顯更好但資源占用高Tesseract 部署輕量對印刷體識別也夠用。探礦掃描件還有一個地學場景特有的麻煩有些頁面是地質(zhì)圖、剖面圖圖里的大段文字標注根本不是正文OCR 出來后是破碎的坐標軸標簽、圖例說明放進知識庫只會添亂。我的辦法是先用版面分析把“圖區(qū)”和“文本區(qū)”分開只保留文本區(qū)的識別結(jié)果。PDF 表格清洗也比 Word 復雜。電子版可以用 pdfplumber 抽取表格結(jié)構(gòu)掃描版就得靠 OCR 后的版面還原。不管哪種方式清洗后都要做“抽樣校驗”隨機挑 5 頁人工比對原文和提取結(jié)果看段落順序有沒有亂、表格列有沒有錯位、公式有沒有丟失。這一步看著笨卻是省后面調(diào)試檢索精度最有效的手段。2.4 網(wǎng)頁清洗正文抽取之外還藏著老編碼和分頁問題網(wǎng)頁清洗的目標是從 HTML 里把“真正有用的正文”撈出來。探礦業(yè)務里爬得最多的幾類頁面行業(yè)資訊、地學數(shù)據(jù)庫查詢結(jié)果、標準規(guī)范頁面。這些頁面的共同問題是導航、側(cè)欄、頁腳、上下篇鏈接、廣告腳本占了大半個 DOM。我用 BeautifulSoup 或 lxml 先把主要結(jié)構(gòu)抽出來優(yōu)先用article、main這類語義標簽定位正文容器定位不到就回退到正文密度算法。所謂正文密度就是統(tǒng)計每個 div 塊里文本長度和鏈接文本長度的比例正文塊通常是“文字多、鏈接少”導航塊則相反。這個思路簡單粗暴但能解決絕大多數(shù)頁面。注意網(wǎng)頁清洗時最容易忽略的是老頁面的編碼聲明。有些地方門戶或期刊網(wǎng)站至今還是 GB2312 的 HTML用 charset 屬性識別后要統(tǒng)一轉(zhuǎn)回 UTF-8。另外網(wǎng)頁內(nèi)容改了排版后正文容器選擇器隨時可能失效所以我一般會把“正文抽取”做成可配置的規(guī)則集而不是在代碼里寫死。還有兩個探礦業(yè)務里很常見的網(wǎng)頁形態(tài)分頁文章和動態(tài)加載列表。分頁文章要把每一頁的正文都抓下來再拼接避免知識庫里有“殘缺片段”動態(tài)加載的數(shù)據(jù)表比如地質(zhì)資料目錄查詢結(jié)果如果頁面是用 JavaScript 異步渲染的靠 requests 直接抓只能拿到空殼。我的做法是優(yōu)先找頁面底部的接口請求直接拿 JSON 數(shù)據(jù)實在找不到再用無頭瀏覽器渲染后抓取少走彎路。3. 清洗之后才是 RAG 的起點切片、元數(shù)據(jù)與混合檢索3.1 切片別只看字符數(shù)要順著文檔結(jié)構(gòu)走清洗完的文本還不能直接丟給向量庫。有一次我圖省事把整篇 PDF 報告按 500 字一段硬切結(jié)果把“礦區(qū)地質(zhì)背景”切成兩半前面講地層后面講構(gòu)造語義被攔腰折斷檢索命中率直線下降。從那以后我堅持按“文檔結(jié)構(gòu)”去切。Word 文檔本身就是一棵結(jié)構(gòu)樹章節(jié)、小節(jié)、段落。清洗時我把 Heading 層級提取出來了切片時就沿著這個層級往下沉。如果某個二級章節(jié)內(nèi)容太長再按三級標題或段落邊界二次切分每個切片控制在 300 到 800 字之間同時讓相鄰切片有 10% 到 15% 的重疊避免關(guān)鍵句落在邊界處。PDF 沒有天然的標題層級但可以利用書簽和目錄。PyMuPDF 可以讀出文檔書簽或按字體大小推斷標題層級。掃描版 OCR 出來的文本則要結(jié)合版面分析里的標題位置來切分。探礦資料里的段落往往很長一段地質(zhì)描述可能上百字全部塞進一個切片會導致信息密度下降所以我會在段落內(nèi)部再按句號、分號做“語義邊界分割”。表格切片也要單獨處理。表格一旦轉(zhuǎn)成 Markdown 或 JSON就不太適合和正文混在一起切塊。我通常把每個表格獨立成一個切片前面加上表格標題和所在章節(jié)的上下文摘要。這樣檢索“XX 礦區(qū)銅品位數(shù)據(jù)”這類問題可以直接命中表格而不必先翻過幾大段文字。3.2 元數(shù)據(jù)讓檢索命中率肉眼可見地變高清洗時同步提取元數(shù)據(jù)是探礦 RAG 項目里性價比最高的一件事。所謂元數(shù)據(jù)就是給每個切片附上“礦區(qū)名稱、礦種、報告編號、編制單位、年份、圖幅號、坐標范圍、文獻類型”這些標簽。這些字段不用復雜模型去抽大部分能從文件名、封面頁、目錄頁里規(guī)則式提取。元數(shù)據(jù)在檢索鏈路上的價值是“過濾器”。用戶問“某某礦區(qū) 2023 年儲量報告里的品位數(shù)據(jù)”如果有獨立的礦區(qū)名和年份字段就可以先做過濾再向量檢索把候選范圍從幾十萬個切片縮小到幾百個精度自然高。元數(shù)據(jù)還能用來做展示來源讓回答下面附上“摘自 XX 報告第 XX 頁”增強可信度。我遇到過最典型的一個案例兩份報告都提到“鉛鋅礦化”這個詞一份是 A 礦區(qū)的勘查報告一份是 B 礦區(qū)的物探成果內(nèi)容不同但因為措辭相似導致互相污染。加上礦區(qū)元數(shù)據(jù)做過濾后這類跨報告串擾基本消失檢索結(jié)果一下就從“相關(guān)”變成了“精準”。3.3 混合檢索與重排把清洗優(yōu)勢徹底用起來清洗干凈 按結(jié)構(gòu)切片只是把“料”備齊了真正把料炒出味道的是檢索策略。探礦領(lǐng)域的查詢語言“很不 AI”工程師問東西經(jīng)常是零散的名詞組合比如“銅多金屬 蝕變帶 鉆孔”或者直接來一個礦區(qū)編號。這類查詢靠單一的向量檢索經(jīng)常召回偏了因為向量模型對短查詢不夠敏感?;旌蠙z索是通用解BM25 處理關(guān)鍵詞精確匹配向量檢索處理語義相似度。兩條路的召回結(jié)果按權(quán)重融合我常用 0.3 比 0.7具體看資料類型調(diào)。清洗質(zhì)量越高BM25 的權(quán)重就可以稍微調(diào)高因為文本噪聲少了關(guān)鍵詞本身就更可信。召回之后再加一道重排rerank用 Cross-Encoder 對候選切片逐條打分。這一步很值因為向量檢索返回的 Top 幾個結(jié)果往往不是最貼切的重排能把“真正回答問題的那一段”選到最前面。RAG 的瓶頸很多時候不在生成模型而在于召回到的資料壓根對了沒有。一個測試集多跑幾輪清洗前后的 Top 命中率差距能到 20 個點以上差距就是這么出來的。4. 踩坑實錄與排查速查從亂碼到高精度的最后一步4.1 亂碼與解析問題的現(xiàn)象速查表實際操作里很多問題雖然看起來五花八門根因其實是有限的幾個。下面這張表是我在探礦資料清洗中反復用到的排查表直接按“現(xiàn)象找原因”就行?,F(xiàn)象典型原因處理辦法文本顯示“錕斤拷”UTF-8 替換符被 GBK 二次解碼統(tǒng)一按 GB18030 重新解碼源文件文本開頭多一個“锘”或“”UTF-8 BOM 被誤讀為字節(jié)內(nèi)容去掉 BOM 標志再解碼Word 打開提示宏錯誤或兼容問題舊版 .doc 模板或嵌入宏組件用 LibreOffice 批量轉(zhuǎn)存 docx 后再提取PDF 提取的文本全是空字符串掃描版 PDF沒有文本層走 OCR 管線先做版面分析OCR 后序號混亂、語義跳躍雙欄排版未按閱讀順序重排按文字塊坐標切分中線并重排讀取順序表格提取后列錯位掃描件表格線變形或跨頁斷線用版面分析定位表格邊界表格單獨切片網(wǎng)頁正文混入大量導航鏈接正文容器定位失敗改用正文密度算法重新抽取配置 fallback 規(guī)則網(wǎng)頁抓回來亂碼老頁面編碼為 GB2312 且未聲明通過 charset 或字節(jié)流檢測識別后轉(zhuǎn) UTF-8切片后語義斷層固定長度硬切切斷段落按標題層級和句邊界切分加重疊窗口這類問題最忌諱的是“逐個文件手動救”。我的建議是做一個清洗流水線把編碼檢測、格式解析、版面重排、元數(shù)據(jù)抽取串起來每次報錯就補一條規(guī)則慢慢形成探礦資料專屬的清洗過濾器。4.2 檢索不準時的排查順序先查清洗再查參數(shù)很多團隊一看到檢索結(jié)果變差第一反應是換向量模型、調(diào) top_k、改 prompt但真正的問題往往出在更上游。我自己的排查順序是固定的先看切片內(nèi)容干不干凈再看元數(shù)據(jù)有沒有生效最后才調(diào)檢索參數(shù)。第一步把用戶檢索命中率最低的那幾個問題拿出來看召回切片原文。如果切片里是頁碼、頁眉、掃描噪聲那說明清洗環(huán)節(jié)還有漏網(wǎng)之魚參數(shù)調(diào)得再好也沒用。如果切片本身干凈但內(nèi)容不匹配那就去看元數(shù)據(jù)過濾條件比如礦區(qū)名、年份這些標簽有沒有抽錯。最后才輪到調(diào) BM25 和向量的權(quán)重比例、重排候選數(shù)。我還踩過一個很隱蔽的坑OCR 成功但“字對了、意不對”。掃描版報告里“品位”被識別成“品位”可能還好但“蝕變”識別成“濁變”、“花崗巖”識別成“花巖”這種錯誤在向量模型看來就是完全不同的語義檢索自然失準。所以清洗完我會對 OCR 文本做一次關(guān)鍵詞校正用探礦領(lǐng)域的詞表做正則替換把高頻錯誤映射回標準術(shù)語。這一步效果立竿見影但前提是得積累一份項目專屬詞表。提示探礦資料里經(jīng)常出現(xiàn)同一實體多種寫法比如“鉛鋅礦”和“鉛-鋅礦”、“多金屬礦”和“有色金屬礦”。清洗時做一輪同義替換或保存同義詞表能顯著提升檢索對“口語化提問”的抗性。把這份同義詞表同步給重排模型做上下文注入比單純擴召回的收益更穩(wěn)。5. 寫在最后RAG 清洗沒有銀彈但有方法論做探礦業(yè)務 RAG 這段時間我最大的體會是高精度檢索不是靠某個神奇的模型參數(shù)而是靠把“資料”當“數(shù)據(jù)”來認真對待。探礦資料里那些亂碼、掃描件、老網(wǎng)頁本質(zhì)上都是可以被規(guī)則和工具馴服的真正難的是你有沒有耐心把每一個格式的脾氣摸清楚。最后分享一個我很受用的習慣把清洗流水線做成“可觀測”的。每一步結(jié)果都抽樣留檔每處理一個 PDF、一個亂碼 TXT都記錄當時的處理規(guī)則和效果。這樣項目跑過一輪之后下輪遇到新材料就能直接復用規(guī)則集而不是從頭開始盲試。RAG 的清洗道說到底就是一條“從亂碼到可信知識”的流水線跑通了檢索精度自然就上來了。