ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

从乱码到高精度检索:探矿文档RAG清洗实战指南

从乱码到高精度检索:探矿文档RAG清洗实战指南 前几天帮一个矿产勘查团队搭 RAG 知识库拉回来的第一批资料直接让我破防TXT 文件打开是“锟斤拷锟斤拷”Word 里藏着一堆修订记录PDF 有扫描版有电子版网页正文粘出来还带了一堆导航栏。这些材料要是直接喂给检索增强生成RAG系统别说高精度检索连基本的“找到对的那句话”都做不到。探矿业务天然就是多种格式文档混战的场景。地质报告、钻孔编录、化验数据、行业规范、期刊论文往往横跨十几年甚至几十年有的来自老式 GIS 系统导出有的是仪器直接输出还有的是扫描存档。这些资料要变成 RAG 能用的知识第一道坎就是清洗。这篇文章我把自己在探矿业务里折腾 TXT、Word、PDF 和网页清洗的完整方案、踩坑记录和调优经验都梳理出来适合正在做垂直领域知识库、处理非结构化文档的工程师和数据人员参考。1. 地质资料堆成山RAG 却“检索不动”锅大半在清洗1.1 探矿文档究竟长什么样四种格式的“野生形态”探矿业务里的文档和我们平时处理的互联网文章完全是两码事。先说 TXT这玩意看着最“干净”实际最容易乱码。物探仪器导出的测量数据、化验室输出的元素含量表、老式地理信息系统导出坐标经常是 GBK、GB2312、UTF-8 混着来有的还带 BOM。更头疼的是数据文件的字段分隔符不统一有的是 Tab有的是竖线|有的是连续空格解析不对就是灾难。Word 文档在探矿业务里通常是报告正文、设计书和野外记录整理稿。问题不在于段落文字本身而是内部夹杂了公式对象、表格、文本框、页眉页脚还有“修订模式”留下的红字和批注。我见过一份储量估算报告正文里稀稀拉拉穿插着十几处批注和修订痕迹清洗的时候不处理这些检索到的内容就会被过期信息污染。PDF 是最分裂的一类。电子版 PDF 还算友好扫描版才是真正的“硬骨头”。早期地质报告很多是扫描件纸张泛黄、字迹模糊、表格线扭曲更别提地质图件里那些手写标注和图例。这类材料不做 OCR 就只能是“图片”RAG 完全抓不到内容。即便是电子版 PDF双栏排版的期刊、带复杂表格的测试报告也会让常规解析器出错。网页资料同样棘手。地质类资讯、地学数据库页面、标准规范查询页结构五花八门。有的页面是老式 GB2312 编码有的正文藏在嵌套表格里有的内容分多页显示直接抓下载体把导航、广告、版权声明一起塞进知识库检索时噪声极大。1.2 RAG 清洗洗的是什么字符、版面、语义三层过滤很多团队把 RAG 的重点放在向量模型和检索参数上实际跑下来才发现数据没洗干净后面全白搭。所谓清洗本质上是把一份“给人看的文档”改造成“给机器精确理解的结构化文本”。我习惯把它拆成三层来做。字符层是最基础的。乱码要修、编码要统一、全角半角要规范化、不可见字符要去掉。这一层不做后面的切分和向量化就是在垃圾上盖楼。版面层解决的是“页眉页脚、页码、目录、水印”等页面装饰物以及双栏顺序、表格结构这类版式问题。至于语义层则是去掉与主题无关的章节、重复的段落提取真正有意义的知识内容。打个比方清洗不是把一件旧衣服裁成新衣服而是把上面的污渍洗掉、线头剪干净、纽扣钉牢让衣服原本的版型和材质显现出来。RAG 检索靠的是语义相似度一堆乱码和噪声文本在高维向量空间里只会把“有用的信号”淹没掉。所以探矿业务 RAG 的精度瓶颈往往不在大模型而在我们把资料送进模型之前有没有完成这三层过滤。2. 逐个击破TXT、Word、PDF、网页的清洗实操2.1 TXT 清洗乱码三兄弟“锟斤拷”“烫烫烫”“”是怎样炼成的TXT 清洗的第一步永远是“猜编码”。常见乱码里“锟斤拷”是 UTF-8 的替换字符 UFFFD 被 GBK 二次解码后的产物本质上是因为原文件是 GBK 编码但工具按 UTF-8 去读遇到无法处理的字节就替换成“”再用 GBK 读回来就成了“锟斤拷”。“烫烫烫”则更经典这是 Visual C 调试模式下未初始化栈内存被填成 0xCC按 GBK 解码得到的汉字通常意味着数据本身就是半成品。“”单个出现则多半是二进制文件混入或单字节截断。处理时我一般不靠肉眼去猜直接用 chardet 或 charset-normalizer 检测字节流检测到 GB2312 就升级成 GB18030 再解码尽量降低单字节损失。先读前 10KB 做检测命中率已经能覆盖绝大多数场景速度还快。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 统一用 GB18030 解码兼容生僻字 if enc.lower() in (gb2312, gbk): enc gb18030 return raw.decode(enc, errorsreplace)解码之后还要做几件小事。一是统一换行符把\r\n、\r全转成\n二是把全角标点转半角中文内容保留全角没问题但英文、数字和代码片段一定要转半角三是按字段分隔符清理解析比如 GIS 导出的“ID, X, Y, Z, 岩性编码”这类结构要确认坐标和属性的分隔逻辑是否一致。提示TXT 里的行去重也要分场景。探矿数据的“重复行”可能是同一钻孔不同深度的记录值一样但深度不同不能简单按整行 hash 去重。我一般会带上“钻孔编号 深度区间”这么一组业务主键来判断才敢放心去重。2.2 Word 清洗标题层级、修订模式、公式对象三个坑逐个填Word 清洗的优先度高于 TXT 和 PDF因为 Word 自带结构信息——标题样式、段落层级都是现成的这对后续切分非常有价值。我用 python-docx 按段落流读取先判断样式名把 Heading 1/2/3 的层级记录下来再按正文段落继续提取这样清洗完得到的不是一坨纯文本而是一棵“章节树”。修订模式和批注是探矿报告里最常见的“隐藏污染”。一份经历了多人审阅的方案正文里可能残留大量删除线文字和新插入文字。我用 python-docx 读取时要遍历文档的修订状态优先取“最终版本”的内容批注一律丢弃。如果文档实在结构混乱也可以用 LibreOffice 命令行转成 docx 再处理兼容性会好很多。公式对象是第二个坑。探矿业务里的公式多出现在资源量估算、化学分析数据处理这些章节。Word 里的公式可能是 MathType、AxMath 或 OMML 格式提取后大多是 OMML想转 LaTeX 还得挂个转换器。我的建议是清洗阶段不强求公式完美转写先把公式周围的主干文本提取出来保留公式的编号或名称比如“式3-2— 储量计算公式”再单独把公式转 LaTeX 存元数据。这样既能保证正文语义不丢又不会因为某个公式解析失败卡断整个流程。表格在 Word 里也要单独抽出来我用 python-docx 遍历 table拆成 Markdown 或 JSON 格式保存。探矿报告里的化验结果表、钻孔柱状表行列结构往往很规整直接转成 Markdown 表格后RAG 在召回时会更容易命中“数值型”问题。from docx import Document doc Document(某矿区勘探报告.docx) tree_lines [] for block in doc.element.body.iterchildren(): tag block.tag.split(})[-1] if tag p: para doc.paragraphs # 实际用业务逻辑映射 elif tag tbl: # 表格单独走 CSV/Markdown 分支 pass2.3 PDF 清洗先分扫描版和电子版再谈双栏与表格PDF 清洗的第一件事不是调参而是判断“这个文件到底是文本层 PDF 还是扫描版”。拿 PyMuPDF 打开文件后抽取某一页的文本长度如果整页提取出来不到十几个字符基本可以断定是扫描件得走 OCR 路线。电子版 PDF 我用 PyMuPDF 提取块级信息用page.get_text(dict)拿到坐标和字体大小。双栏期刊的判断很简单如果一个页面的文字块 x 坐标分布明显分成两个簇左右两列宽度相似就按中线切分然后按“左列从上到下、右列从上到下”的顺序重排。这里容易踩的坑是“图注和表格穿插在栏间”跨栏的大表格要单独识别出来否则文字顺序一乱语义就断了。扫描版 PDF 走 OCR 时我试验过 PaddleOCR 和 Tesseract 两套方案。PaddleOCR 中文版面还原度明显更好但资源占用高Tesseract 部署轻量对印刷体识别也够用。探矿扫描件还有一个地学场景特有的麻烦有些页面是地质图、剖面图图里的大段文字标注根本不是正文OCR 出来后是破碎的坐标轴标签、图例说明放进知识库只会添乱。我的办法是先用版面分析把“图区”和“文本区”分开只保留文本区的识别结果。PDF 表格清洗也比 Word 复杂。电子版可以用 pdfplumber 抽取表格结构扫描版就得靠 OCR 后的版面还原。不管哪种方式清洗后都要做“抽样校验”随机挑 5 页人工比对原文和提取结果看段落顺序有没有乱、表格列有没有错位、公式有没有丢失。这一步看着笨却是省后面调试检索精度最有效的手段。2.4 网页清洗正文抽取之外还藏着老编码和分页问题网页清洗的目标是从 HTML 里把“真正有用的正文”捞出来。探矿业务里爬得最多的几类页面行业资讯、地学数据库查询结果、标准规范页面。这些页面的共同问题是导航、侧栏、页脚、上下篇链接、广告脚本占了大半个 DOM。我用 BeautifulSoup 或 lxml 先把主要结构抽出来优先用article、main这类语义标签定位正文容器定位不到就回退到正文密度算法。所谓正文密度就是统计每个 div 块里文本长度和链接文本长度的比例正文块通常是“文字多、链接少”导航块则相反。这个思路简单粗暴但能解决绝大多数页面。注意网页清洗时最容易忽略的是老页面的编码声明。有些地方门户或期刊网站至今还是 GB2312 的 HTML用 charset 属性识别后要统一转回 UTF-8。另外网页内容改了排版后正文容器选择器随时可能失效所以我一般会把“正文抽取”做成可配置的规则集而不是在代码里写死。还有两个探矿业务里很常见的网页形态分页文章和动态加载列表。分页文章要把每一页的正文都抓下来再拼接避免知识库里有“残缺片段”动态加载的数据表比如地质资料目录查询结果如果页面是用 JavaScript 异步渲染的靠 requests 直接抓只能拿到空壳。我的做法是优先找页面底部的接口请求直接拿 JSON 数据实在找不到再用无头浏览器渲染后抓取少走弯路。3. 清洗之后才是 RAG 的起点切片、元数据与混合检索3.1 切片别只看字符数要顺着文档结构走清洗完的文本还不能直接丢给向量库。有一次我图省事把整篇 PDF 报告按 500 字一段硬切结果把“矿区地质背景”切成两半前面讲地层后面讲构造语义被拦腰折断检索命中率直线下降。从那以后我坚持按“文档结构”去切。Word 文档本身就是一棵结构树章节、小节、段落。清洗时我把 Heading 层级提取出来了切片时就沿着这个层级往下沉。如果某个二级章节内容太长再按三级标题或段落边界二次切分每个切片控制在 300 到 800 字之间同时让相邻切片有 10% 到 15% 的重叠避免关键句落在边界处。PDF 没有天然的标题层级但可以利用书签和目录。PyMuPDF 可以读出文档书签或按字体大小推断标题层级。扫描版 OCR 出来的文本则要结合版面分析里的标题位置来切分。探矿资料里的段落往往很长一段地质描述可能上百字全部塞进一个切片会导致信息密度下降所以我会在段落内部再按句号、分号做“语义边界分割”。表格切片也要单独处理。表格一旦转成 Markdown 或 JSON就不太适合和正文混在一起切块。我通常把每个表格独立成一个切片前面加上表格标题和所在章节的上下文摘要。这样检索“XX 矿区铜品位数据”这类问题可以直接命中表格而不必先翻过几大段文字。3.2 元数据让检索命中率肉眼可见地变高清洗时同步提取元数据是探矿 RAG 项目里性价比最高的一件事。所谓元数据就是给每个切片附上“矿区名称、矿种、报告编号、编制单位、年份、图幅号、坐标范围、文献类型”这些标签。这些字段不用复杂模型去抽大部分能从文件名、封面页、目录页里规则式提取。元数据在检索链路上的价值是“过滤器”。用户问“某某矿区 2023 年储量报告里的品位数据”如果有独立的矿区名和年份字段就可以先做过滤再向量检索把候选范围从几十万个切片缩小到几百个精度自然高。元数据还能用来做展示来源让回答下面附上“摘自 XX 报告第 XX 页”增强可信度。我遇到过最典型的一个案例两份报告都提到“铅锌矿化”这个词一份是 A 矿区的勘查报告一份是 B 矿区的物探成果内容不同但因为措辞相似导致互相污染。加上矿区元数据做过滤后这类跨报告串扰基本消失检索结果一下就从“相关”变成了“精准”。3.3 混合检索与重排把清洗优势彻底用起来清洗干净 按结构切片只是把“料”备齐了真正把料炒出味道的是检索策略。探矿领域的查询语言“很不 AI”工程师问东西经常是零散的名词组合比如“铜多金属 蚀变带 钻孔”或者直接来一个矿区编号。这类查询靠单一的向量检索经常召回偏了因为向量模型对短查询不够敏感。混合检索是通用解BM25 处理关键词精确匹配向量检索处理语义相似度。两条路的召回结果按权重融合我常用 0.3 比 0.7具体看资料类型调。清洗质量越高BM25 的权重就可以稍微调高因为文本噪声少了关键词本身就更可信。召回之后再加一道重排rerank用 Cross-Encoder 对候选切片逐条打分。这一步很值因为向量检索返回的 Top 几个结果往往不是最贴切的重排能把“真正回答问题的那一段”选到最前面。RAG 的瓶颈很多时候不在生成模型而在于召回到的资料压根对了没有。一个测试集多跑几轮清洗前后的 Top 命中率差距能到 20 个点以上差距就是这么出来的。4. 踩坑实录与排查速查从乱码到高精度的最后一步4.1 乱码与解析问题的现象速查表实际操作里很多问题虽然看起来五花八门根因其实是有限的几个。下面这张表是我在探矿资料清洗中反复用到的排查表直接按“现象找原因”就行。现象典型原因处理办法文本显示“锟斤拷”UTF-8 替换符被 GBK 二次解码统一按 GB18030 重新解码源文件文本开头多一个“锘”或“”UTF-8 BOM 被误读为字节内容去掉 BOM 标志再解码Word 打开提示宏错误或兼容问题旧版 .doc 模板或嵌入宏组件用 LibreOffice 批量转存 docx 后再提取PDF 提取的文本全是空字符串扫描版 PDF没有文本层走 OCR 管线先做版面分析OCR 后序号混乱、语义跳跃双栏排版未按阅读顺序重排按文字块坐标切分中线并重排读取顺序表格提取后列错位扫描件表格线变形或跨页断线用版面分析定位表格边界表格单独切片网页正文混入大量导航链接正文容器定位失败改用正文密度算法重新抽取配置 fallback 规则网页抓回来乱码老页面编码为 GB2312 且未声明通过 charset 或字节流检测识别后转 UTF-8切片后语义断层固定长度硬切切断段落按标题层级和句边界切分加重叠窗口这类问题最忌讳的是“逐个文件手动救”。我的建议是做一个清洗流水线把编码检测、格式解析、版面重排、元数据抽取串起来每次报错就补一条规则慢慢形成探矿资料专属的清洗过滤器。4.2 检索不准时的排查顺序先查清洗再查参数很多团队一看到检索结果变差第一反应是换向量模型、调 top_k、改 prompt但真正的问题往往出在更上游。我自己的排查顺序是固定的先看切片内容干不干净再看元数据有没有生效最后才调检索参数。第一步把用户检索命中率最低的那几个问题拿出来看召回切片原文。如果切片里是页码、页眉、扫描噪声那说明清洗环节还有漏网之鱼参数调得再好也没用。如果切片本身干净但内容不匹配那就去看元数据过滤条件比如矿区名、年份这些标签有没有抽错。最后才轮到调 BM25 和向量的权重比例、重排候选数。我还踩过一个很隐蔽的坑OCR 成功但“字对了、意不对”。扫描版报告里“品位”被识别成“品位”可能还好但“蚀变”识别成“浊变”、“花岗岩”识别成“花岩”这种错误在向量模型看来就是完全不同的语义检索自然失准。所以清洗完我会对 OCR 文本做一次关键词校正用探矿领域的词表做正则替换把高频错误映射回标准术语。这一步效果立竿见影但前提是得积累一份项目专属词表。提示探矿资料里经常出现同一实体多种写法比如“铅锌矿”和“铅-锌矿”、“多金属矿”和“有色金属矿”。清洗时做一轮同义替换或保存同义词表能显著提升检索对“口语化提问”的抗性。把这份同义词表同步给重排模型做上下文注入比单纯扩召回的收益更稳。5. 写在最后RAG 清洗没有银弹但有方法论做探矿业务 RAG 这段时间我最大的体会是高精度检索不是靠某个神奇的模型参数而是靠把“资料”当“数据”来认真对待。探矿资料里那些乱码、扫描件、老网页本质上都是可以被规则和工具驯服的真正难的是你有没有耐心把每一个格式的脾气摸清楚。最后分享一个我很受用的习惯把清洗流水线做成“可观测”的。每一步结果都抽样留档每处理一个 PDF、一个乱码 TXT都记录当时的处理规则和效果。这样项目跑过一轮之后下轮遇到新材料就能直接复用规则集而不是从头开始盲试。RAG 的清洗道说到底就是一条“从乱码到可信知识”的流水线跑通了检索精度自然就上来了。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表