探矿文档RAG清洗实战:从乱码到高精度检索的分块策略

发布时间:2026/10/6 15:17:54
探矿文档RAG清洗实战:从乱码到高精度检索的分块策略 从乱码到高精度检索探矿业务中 TXT、Word、PDF 与网页的 RAG 清洗之道做探矿业务的老哥们应该都有过这种体验项目的文字资料比矿体还难啃。几十年前的手写报告扫描件、同事从现场发来的Word勘探记录、从合作单位拷过来的PDF储量估算表、还有零散的TXT坐标数据文件全都堆在一个共享盘里。平时没人看等真要写报告、做立项论证、或者搞知识库检索的时候才发现这些资料根本没法用——不是打开乱码就是检索系统返回一堆牛头不对马嘴的结果。所以我一直强调一个观点RAG项目里真正决定上限的不是模型不是向量库而是清洗环节。尤其是探矿这种文档结构松散、格式混乱、专业术语密集的领域清洗没做到位后面的检索和生成全是空中楼阁。这篇文章就把我在探矿业务中处理TXT、Word、PDF和网页这四类来源文件时的完整思路和实操细节做一次复盘从乱码的根源一路讲到高精度检索的分块策略全是踩坑踩出来的经验。1. 探矿文档为什么难啃乱码只是最表层的麻烦很多人一听说清洗就想到编码、乱码、去空格实际上探矿领域的文档问题远不止这些。我先从最常见也最容易被忽视的几个层面说起。1.1 编码乱码TXT文件里被折叠的矿化信息探矿资料里TXT格式大量存在坐标数据、测井记录、化学分析原始数据很多都是老仪器直接导出的编码方式千奇百怪。最常见的是GBK和UTF-8之间的错位——你用UTF-8的解析器打开一个GBK编码的文件满屏锟斤拷反过来用GBK打开UTF-8文件又会出现涓€开头的中文乱码。但真正难的还不是这种肉眼可见的乱码而是那些能正常打开、实际内容已经被破坏的文件。举个例子我遇到过一批野外荧光分析仪导出的TXT文件文件头声明是UTF-8但中文字段名实际上是GBK编码Python默认读取后字段名变成了一堆非法字符程序直接报错。这种情况下不能光靠chardet自动检测得结合文件来源和仪器型号做硬性映射。1.2 排版与语义错位PDF解析后表格全散架探矿报告里最有价值的信息往往是各类表格——钻孔编录表、样品分析结果表、矿权范围坐标表。可PDF一旦经过扫描归档或者排版软件的导出文字层的顺序就和视觉排布脱节了。用PDFPlumber解析坐标表经常出现表头和数据错位、同一行的数据被拆到两行、坐标值小数点被当成句号切断这类问题。数据一旦错位检索系统还浑然不觉照样把Au 12.35g/t和深度区间145-148m错误地关联起来。1.3 网页资料导航栏和正文混在一起探矿业务也会大量引用网页资料比如矿产资源法、地方政府的矿权公示、勘探规范条文、行业协会公告。直接从网页抓下来的HTML里全是导航菜单、广告、页脚版权声明。要是不做正文提取和超链接内容解析只管整段塞进知识库检索的时候搜采矿权能返回十条八条是导航栏重复文本。这属于文本层面肉眼看不到、向量检索却会被反复干扰的隐性脏数据。1.4 检索噪声探矿术语里的绝对高频词探矿文档里有大量高频出现的通用词——矿区钻孔样品分析报告这些词在普通文档里可能还算有区分度但在探矿领域的语料库里几乎是无差别高频词。如果不做词频分析和领域停用词处理向量化的结果会被严重稀释导致检索精度上不去。这一点后面在分块策略里还会细说但清洗阶段就得先有意识地把这些词标注出来而不是等检索阶段再补救。2. 分格式解析TXT、Word、PDF、网页各自的处理路径不同格式有各自的解析难点不能用一套代码通吃。我的经验是分四条管线处理每条管线单独写解析脚本输出统一格式的中间态JSON或Markdown再进入清洗阶段。2.1 TXT管线先认编码再清符号对于TXT文件第一步永远是编码判定。我在实践中用的是编码检测加人工兜底的双重策略。先用chardet和charset_normalizer分别检测两者结果一致就直接用不一致就触发人工判定流程让熟悉这批数据来源的工程师指定编码。import chardet from charset_normalizer import from_bytes def detect_encoding(file_path): raw open(file_path, rb).read(4096) result1 chardet.detect(raw) result2 from_bytes(raw).best() if result1[encoding] result2.encoding: return result1[encoding] else: # 触发人工判断这里记录到日志等待处理 return None第二步是符号清理。探矿TXT里常见的符号垃圾包括制表符混杂空格造成的列错位、ANSI转义序列老设备输出的\x1b[开头的内容、不可见控制字符、重复的换行符和全角半角混排。这些符号不需要设计师级别的精细处理正则批量清洗就行。import re def clean_control_chars(text): # 去除控制字符但保留 \n \t text re.sub(r[\x00-\x08\x0b\x0c\x0e-\x1f\x7f], , text) # 合并连续空行 text re.sub(r\n\s*\n, \n\n, text) # 去掉行尾多余空格 text re.sub(r[ \t]$, , text, flagsre.M) return text还有一个探矿场景特有的问题坐标TXT文件里通常有大量度分秒格式的数据比如116°2345.6清洗时千万别把这些符号当作特殊字符删掉——那是坐标数据的一部分删了坐标就废了。我就吃过这个亏当时图省事用统一正则清掉了所有非字母数字字符结果一批钻孔坐标的秒符号全没了数据直接没法用只能从备份重新清洗。从这个教训之后我的清洗规则里永远把坐标转义符号放在白名单里。2.2 Word管线表格、公式、批注各走各路Word文档在探矿业务里的角色很特殊往往是中间成果——野外记录整理版、评审前的修改稿、数据汇总表。处理Word主流的方案是python-docx光靠它也能解决大半问题但有几个坑必须单独处理。第一正文段落和表格要分开提取。python-docx里document.paragraphs只返回正文段落表格要用document.tables单独遍历。如果混在一起读表格内容和正文内容交错语义就乱了。我封装了一个遍历函数把段落和表格做一个时间线合并保留它们在文档中的实际出现顺序from docx import Document from docx.table import Table from docx.text.paragraph import Paragraph def iter_block_items(parent): from docx.oxml.ns import qn parent_elm parent.element.body for child in parent_elm.iterchildren(): if child.tag qn(w:p): yield Paragraph(child, parent) elif child.tag qn(w:tbl): yield Table(child, parent)第二公式问题。探矿报告里的公式虽然不多但很重要——资源储量估算公式、品位计算公式、变异系数公式等。用python-docx读不到公式内容因为公式是OLE对象或者OMML格式。我在这个环节的实践是OMML格式的公式用latex2mathml配合mammoth做转换转成LaTeX文本如果是嵌入的Equation对象赶时间就直接标注[公式占位]在后续处理里人工补录。如果你不需要对公式做语义检索占位符方案完全够用别在上面耗太多时间。第三批注和修订。评审稿的批注往往包含专家的修改意见这些内容有没有价值取决于场景——如果建设的是最终成果知识库批注应该剥离如果建设的是项目过程档案库批注反而是精华。我的做法是用python-docx的comments属性把批注单独抽取出来和正文分开存储打上不同的元数据标签这样同一个文档在知识库里可以被灵活检索。2.3 PDF管线文字层、物理层和OCR三层递进PDF是探矿文档的大头也是最难处理的格式。我把PDF解析分成三个层级递进处理。第一层是文字层解析。用pdfplumber提取文本和表格适合电子版PDF。这里有个关键技巧extract_tables的table_settings参数能大幅提升表格提取准确率尤其是对探矿报告里常见的竖线表、三线表。我用的是import pdfplumber settings { vertical_strategy: lines, horizontal_strategy: lines, snap_tolerance: 3, } with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: tables page.extract_tables(table_settingssettings)第二层是物理层解析针对那些文字层位置异常的情况。有些PDF是排版软件导出的文字虽然可选但顺序完全错乱表格被拆成碎片。这种情况下pdfplumber的文字层解析基本失灵就得用PyMuPDF按坐标块提取文本再根据坐标位置重排。建议在PyMuPDF的page.get_text(blocks)里提取包含坐标的文本块然后按垂直和水平坐标聚类恢复表格的行列结构。第三层是OCR针对扫描件。探矿老资料里扫描件比例极高早期地质报告大多是纸质档案扫描的有些甚至是黑白低分辨率的老旧扫描。OCR这一层我用PaddleOCR加pdf2image配pytesseract个人经验是PaddleOCR对中文专业术语的识别率明显高于Tesseract。识别完之后一定要做的不是直接入库而是再做一次文本校验——把识别出的专业术语和领域词库比对发现置信度低的词要标记出来。探矿领域有大量品位蚀变矽卡岩安山岩这类词OCR一旦把矽卡岩识别成砂卡岸检索系统是认不出来的。2.4 网页管线去噪与主体提取网页资料的清洗用trafilatura比较好。这个库专门做正文提取能自动去除导航、页脚、侧栏。它在处理政府网站、行业资讯这类结构相对规范的页面时效果不错但对于用JS动态渲染的页面无能为力那种情况得改用playwright先渲染再提取。网页提取完之后还有一个容易被忽略的步骤——链接解析。探矿资料里大量网页正文中提到的法规名称、政策文件编号、矿床名称往往附有超链接这些链接指向的内容可能是附件PDF或者下级页面。我会用爬虫递归地抓取这些链接指向的文档和原始网页关联存储构建一个简单的文档关系网。比如某个矿权公示页面正文提到了评价报告的PDF附件点击链接才能看到完整内容如果不递归抓取知识库里就只有公示页面碎片的公告信息检索详查报告结论时永远找不到答案。3. 从文本到语料的清洗细节探矿领域特有的脏数据原始格式解析派生出文本之后真正的重头戏才开始。这一节讲的都是探矿领域特有、通用清洗教程里不会写的内容。3.1 表格行序保护动过排序就毁了数据关联探矿文档里的表格行顺序就是数据关系本身。你可能觉得这是废话但清洗时很容易在无意识中破坏行序。比如批量清洗正则把两个相邻行匹配到一个模式然后去重合并或者为了去空白把包含大量空单元格的表格行误判为空行删掉。钻孔编录表里一个空单元格不代表这一行没意义——可能是未见矿化而非数据缺失。我的经验是凡是从表格提取的内容每一行都保留原始行号和页眉信息清洗过程中绝不删整行只允许修改单元格内容。这样出问题还能回溯到源头。3.2 单位与数值的标准化Au 12.35g/t vs Au 12.35探矿文档里同一个数值多种写法是常态。金品位12.35g/t可能写成12.35 g/t12.35克/吨12.35×10-6。不统一检索时查品位12.35和品位12.35克/吨就完全匹配不上。我的标准化思路是做一个单位映射表把常见的中英文单位变体统一到一个标准表示并在保留原始文本的同时额外生成一个标准化字段存到元数据里。检索时优先用标准化字段做匹配原文只做展示。另外要处理数值格式的一致性例如统一千分位分隔符把全角数字转半角科学计数法转十进制。这里有个教训要提醒千万别用简单正则把×10-6这种写法里的负号给删了要写成完整的浮点数不然数值语义直接变错。3.3 探矿特有噪声页眉、水印、图例标注探矿PDF特别爱在每页顶部放矿权名称和报告编号的页眉页脚放页码有些图件页面还有内部资料注意保密的水印。这些文字在PDF解析时会混入正文形成全篇重复的噪声。对于这类内容用规则匹配即可因为格式往往高度固定可以针对每个项目定制页眉正则精准删除。但水印就要小心了。很多水印是矢量文字叠加在正文上的PDF解析时会反复出现在每一页的同一位置提取的文本坐标一旦和水印坐标重叠正文可能被水印文字打断。我的做法是先统计全篇文本坐标的分布识别出固定坐标反复出现的字符串标记为水印在执行正文提取时按坐标剔除。还有一个探矿特有的坑——图例标注。地质图附录里会有大量Q 第四系J3 上侏罗统γ 花岗岩这类图例说明在文字版文档里它们通常是独立的术语表清洗时会被误当作乱码或噪声删掉。实际上这些图例术语是构建领域词典的黄金素材我会专门把它们抽取出来进领域词库而不是删除。3.4 坐标和方位信息的保护性抽取探矿文档绕不开坐标。经纬度坐标有十几种写法度分秒、十进制度、UTM投影坐标各有适用场景。在清洗阶段就要识别并保护这些坐标不要在通用清洗流程里把它们当作累赘符号删掉更不要试图强制统一格式——UTM坐标和经纬度坐标本身不能混用。我的方案是抽取出坐标后单独存储到字段里标注坐标系WGS84、北京54、西安80等并保留原始投影信息。这样后续如果接GIS工具可以直接从字段里读取坐标做空间检索不做空间功能时这些字段也不会干扰文本向量化。另外探矿文本里大量出现方位描述比如沿NE向断裂带……矿体倾向NW300°——这些实际上是空间关系的文本表达保留原文即可但千万别把它们当作无关方位词清洗掉。4. 高精度检索的关键清洗后的分块与元数据策略解析和清洗做完接下来是RAG检索精度最敏感的部分——分块和向量化。这一节的经验直接决定了高精度检索能不能落地。4.1 按语义边界分块而不是按字数硬切很多RAG教程默认用固定窗口分块比如每块500字加50字重叠。这个方案在探矿场景效果很差。原因很简单探矿报告的核心信息高度集中在表格、图件说明、规范条文和储量数据里固定窗口分块会把一张完整的钻孔编录表切得七零八落检索时只能召回表格的碎片模型拿到的是残缺数据。我的做法是优先按语义边界分块——段落、表格、列表各自独立成块并把块之间的引用关系记录下来。表格单独存一块是原则性的要求因为表格本身就是一个完整的语义单元。同时表格块要保留表头和表首行的描述防止独立成块后丢失上下文。比如一张ZK01钻孔岩心编录表如果表头信息没有随内容一起存储检索系统就不知道这块数据是哪个钻孔的。def semantic_chunk(doc): chunks [] for block in doc.blocks: if block.type table: chunk { content: table_to_text(block), head: block.table_header, meta: block.meta } chunks.append(chunk) elif block.type paragraph: # 按段落语义完整保留不做硬切 chunks.append({content: block.text, meta: block.meta}) return chunks4.2 元数据标签检索之前先过滤探矿知识库的元数据应该怎么设计我的经验是至少包含五类资料类型勘探报告、储量评审、采样数据、航测图件说明、法律法规、矿区/矿种标签、时间范围报告覆盖年份、地理位置描述、文件来源内部编制/外部引进/公开网络。元数据的作用在于检索时可以先做粗过滤再做向量检索。举个例子用户问XX矿区2023年最新储量数据有了时间和矿区的元数据标签系统先用标签过滤掉所有2022年及之前的报告只把最近的储量评审报告送入向量召回检索精度和速度同时提升。如果全部文档一股脑向量化只靠语义相似度召回即使TopK设到20候选里也大量混入旧版本数据。4.3 领域停用词与高频词处理前面提到探矿文档里的通用高频词问题分块阶段要正式处理。以我处理过的某铜矿知识库为例矿区钻孔样品ppm报告这类词的文档频率超过60%它们在语义向量中会把更有区分度的词汇权重稀释掉。针对这个问题除了嵌入模型自带的停用词机制我还会在构建检索语料时额外维护一个领域停用词表同时对品位储量矿体蚀变这类核心术语增加加权。需要说明的是领域停用词表不能一刀切删词而是要在检索打分阶段做调整。文本内容里保留原词向量化时对领域词赋予更高的权重或单独建索引字段。我用过的方法是把领域术语单独抽取出来存入标签字段检索时在向量相似度和术语标签匹配之间做加权融合效果比单纯删词好很多。4.4 嵌入模型的选型通用模型在探矿场景哪里不够用清洗得再好嵌入模型选不对检索精度也起不来。通用中文嵌入模型在普通新闻、百科领域表现很好但在探矿专业语料上我实测的相似度召回效果很一般——矽卡岩型铜矿和矽卡岩型矿床这种语义相近但字面差异明显的文本通用模型召回排序不稳定。我的建议是本地微调一个领域嵌入模型。用探矿领域的公开报告文本做数据配合bge-large-zh或m3e-base做继续预训练和对比学习微调领域术语之间的语义相似度会有明显提升。如果你的团队没有条件微调退而求其次的做法是使用基于bge-m3的模型加领域同义词词典扩展检索——用户查矽卡岩型时先扩展出矽卡岩接触交代热液交代等同义概念再进向量库检索副作用是召回率升高、精度略下降但比纯通用模型好一些。5. 实测效果清洗前后的检索精度差距与后续扩展最后聊一段实测数据也顺便交代这套方案后续还能怎么扩展。5.1 一个真实的检索对比案例我用某金矿项目做了一次对比测试。语料库包含历史地质报告、化探数据、钻孔编录、评审意见等约300份文档原始处理方式是直接解析PDF后按500字固定窗口切块入库清洗优化后的方式是本文讲的这套流程——按语义分块、元数据过滤、术语加权、领域词表修正。测试问题是矿区蚀变带内金品位分布与矿体走向的关系。原始方案的检索结果Top5里有两条来自报告页眉的重复文本两条是无关的矿权公告只有一条勉强相关且信息碎片化。清洗优化后的系统Top5全部来自钻孔编录表和化探分析报告且返回块的语义完整度明显更高有一条直接包含了需要的蚀变带品位数据和对应矿体走向描述。这轮对比下来Top5相关度从基本不可用提升到可直接辅助报告编写差距可以说是质变的。5.2 工程化落地的几个建议如果你要把这套清洗流程落地成生产线有几点工程上的建议。第一全流程做日志留痕记录每份文件的编码判定结果、清洗规则命中情况、分块数量、元数据标签出问题才能回溯。第二原始文件永远保留备份清洗流程只产出新文件不改原文件这样参数调整可以随时重跑。第三清洗脚本全部入库并用DAG调度工具编排每个文件的处理管线互相独立有一个环节挂掉不影响其他管线。5.3 后续的扩展方向关于扩展我目前在尝试两个方向。一个是在清洗阶段接入GIS属性数据——把坐标字段、矿权边界矢量数据也纳入知识库统一管理这样用户既能文本检索又能画个范围框做空间检索文本和空间两条召回链路融合。另一个是对OCR识别结果做领域模型的二次校正目前用的是规则加词库匹配准确率在85%左右后续打算用N-gram比对加语言模型对整个识别结果做后处理进一步压低专业术语的误识别率。那套清洗管线说到底每一层都是在跟探矿领域里脏乱差的历史资料做对抗。从编码乱码到表格错位再到检索噪声每解决一层知识库的可用性就上一格台阶。做探矿数据处理的人经常得在维护原始资料的完整性和让机器读懂资料之间找平衡我的体会是前者一定不能牺牲——原始文本哪怕是乱码也要留着清洗结果只是它的一个可检索视图。这不仅仅是个技术问题更是对几十年地质资料价值的一种尊重。