☰
探矿文档清洗实战:RAG数据预处理的四类数据源方案
2026/10/27 15:33:39 网站建设 项目流程

1. 从一次失败检索说起:为什么探矿文档总是"搜不到"?

我接手过一个老矿区历史资料整理项目。项目组想做一个内部知识库,把过去三十年的地质报告、钻孔数据、勘察规范、政策文件统一做成RAG检索系统,让技术员不用再翻档案柜就能找到"某矿区1998年的化探报告里是怎么描述异常区的"。

需求很明确,数据也摆在那里——几台旧电脑里的TXT、Word文档,NAS上一大堆扫描版PDF,不同网页上抓取的政策文件和地质资料。结果第一次测试就翻车了。

我问系统:"某某矿区钻孔ZK301在120米处的岩芯描述是什么?"RAG模型一本正经地返回了一段文字。我对照原始报告一看,文字本身没错,但它是从两页不同内容里被拼接出来的——检索器把"岩芯描述"和"钻孔基本信息"两段内容都判成了高相关,然后把它们拼在了一起。追溯原因,问题出在一份从老系统导出的Word文件里。那个Word文档根本没有什么结构化信息,整个钻孔报告是一张大表格,表格里的单元格还嵌了很多换行符和制表符,分块器一处理,内容就碎成了渣。

这次的教训让我意识到:探矿业务里做RAG,难点根本不在模型选型、向量数据库调参这些"前端动作",而在于一个特别容易被低估的环节——文档清洗。如果喂给RAG的源文档是脏的,后面不管是做embedding、chunk切分还是重排序,都是在垃圾进、垃圾出。而探矿业务的数据源又有它非常特殊的"脏法":老设备导出的TXT编码混乱、扫描PDF没有文本层、Word里充满表格和域代码、网页抓取回来全是导航栏和广告。这些场景和通用文档清洗的思路完全不是一回事。

这篇文章就把我在这个项目里踩过的坑,以及对TXT、Word、PDF、网页这四类数据源的清洗思路完整梳理一遍。核心就一句话:清洗不是简单地去掉空格和换行,而是要让每一段文本能够被检索器稳定、准确地切分和召回。下面从数据源分析开始,逐类拆解。

2. 先认清探矿文档的特殊性:数据源画像与清洗目标

在动手写清洗脚本之前,我花了三天时间把项目里的原始数据过了一遍,做了一个很粗的"数据源画像"。这是整个项目里最有价值的一项工作,因为你只有知道每类数据源脏在哪里,才能设计对应的清洗策略。

2.1 探矿业务四类数据源的典型特征

项目里的数据大概分成四类,每类都有各自的"历史包袱":

数据源典型内容常见脏数据特征清洗难点
TXT文件老式电传记录、钻孔编录表、化探数据导出GBK/GB2312编码、乱码符号、无段落结构、杂散符号(通常是打印控制符残留)编码无法自动识别、纯文本没有语义分隔
Word文档勘察报告、储量计算说明、会议纪要、规程规范表格嵌套、文本框、域代码、页眉页脚污染、目录页码混入正文结构信息(表格、标题)和文本内容混在一起,直接分段会撕碎语义
PDF文件扫描版历史图件、规范扫描件、打印版报告无文本层(纯图片)、版面栏数混乱、页眉页脚反复出现必须OCR,但OCR结果断行严重,精度参差不齐
网页抓取地质云平台资料、政策法规、行业新闻、矿业企业公告导航栏、广告、页脚链接、相关推荐混入正文正文提取难,抓取内容时效性不稳定,重复内容多

2.2 清洗目标的三层定义

很多RAG项目把清洗的目标定义为"把文字提取出来",这太浅了。在我的项目里,清洗目标分三层:

第一层:能读。也就是把文档里的文字内容无损提取出来,不丢字、不乱码。这一层主要是格式转换的活儿,TXT要解决编码问题,PDF要解决文字层问题,Word要解决正文提取问题。

第二层:能分。这是RAG项目特有的要求。清洗后的文本要有清晰的结构边界——标题、段落、表格、列表要能被一个简单的分块器正确识别。打个比方,一份勘察报告如果是一个几千字的大段,分块器只能靠截断硬切,切出来的每一块都说不清是讲什么的;但如果你把报告按"工程概况、地质条件、岩土参数、结论建议"这类结构拆好,分块器拿到的每个块都是自洽的。探矿报告的表格式、列表式内容特别多,这一层的难度比普通文本大很多。

第三层:能召回。清洗时要想清楚,用户的检索意图通常落在哪个粒度上。比如"ZK301号钻孔的涌水量",用户希望召回的是该钻孔那一节的内容,而不是整份报告。所以清洗时下意识的判断标准是:把文档拆成最小的语义单元(章节段落、表格行、数据条目)时,该单元是否能让检索器给出足够精确的语义匹配。这决定了哪些内容要保留原样,哪些内容要重组。

用一句话概括我在这个项目里的清洗哲学:清洗不是做减法,而是做结构化。目标是把杂乱无章的原始文档转译成一种"中间态"——这种中间态有稳定的编码、有清晰的段落边界、有保留语义的表格转写,可以让下游的RAG组件不费力地使用。

3. TXT乱码的根源与实战清洗:四大编码陷阱和两种修复路径

TXT文件在普通人眼里是最简单的格式,但在探矿业务里,TXT恰恰是最容易让人崩溃的数据源。因为这个行业的TXT文件大多是从老设备、老系统里导出的,历史跨度二三十年,编码问题极为突出。

3.1 乱码是怎样产生的:一个必要的编码知识铺垫

TXT文件本身只存字节,不存编码声明。Windows简体中文系统里,记事本默认保存用的是ANSI编码,对中文来说实际是GBK(GB2312的扩展)。Linux系统里很多老程序的默认编码是UTF-8。如果产生文件的时代再早一点,还可能碰到GB2312、GB18030、HZ编码,甚至Shift-JIS这类日文编码(有些老仪器的上位机软件是日本进口的,导出的数据就是Shift-JIS)。

检索程序读取文件时,如果按UTF-8去解码一个GBK编码的文件,就会出现典型的乱码:中文变成"锟斤拷"(UTF-8解码GBK字节时最常见的错误输出),或者"�"这类替代符。更头疼的是,有些文件里是混合编码——绝大部分是GBK,个别字段是UTF-8,或者中间夹着ASCII的英文和数字。这种情况下,单纯指定一个编码去读,永远是部分正确、部分乱。

3.2 编码探测的实战方案:不要全信chardet

说到编码探测,很多人的第一反应是用chardet库。我的经验是:chardet只能当参考,不能当结论。它对于长文本、编码特征明显的文件准确率尚可,但探矿行业的文件有两个特点让它失灵:一是文件可能很短(比如一个钻孔的编录表只有几百字节),短文本的统计特征不明显;二是文件是混合编码的。chardet的单文件单编码模型处理不了这种情况。

我实际的方案是三步走:

第一步:多重探测,结果交叉验证。同时用chardet和charset_normalizer两个库对文件做探测,结合文件头的字节特征做判断。GBK编码的中文文本,如果按GBK解码,字符分布会比较均匀;如果按UTF-8解码,很大概率出现解码错误(UnicodeDecodeError)。所以一个更靠谱的流程是穷举候选编码列表,逐个尝试解码,统计谁的错误最少。

import charset_normalizer import chardet def detect_encoding(file_bytes): candidates = ["utf-8", "gbk", "gb2312", "gb18030", "shift_jis", "big5"] results = [] for enc in candidates: try: decoded = file_bytes.decode(enc) errors = 0 # 这里可以细化,统计替换字符数量 results.append((enc, decoded)) except UnicodeDecodeError: continue # 综合判断 + 依赖 charset_normalizer 给出置信度 guess = charset_normalizer.from_bytes(file_bytes).best() return guess.encoding if guess else "gbk"

第二步:针对混合编码做分段修复。我遇到过一个最典型的场景:一份钻孔数据库导出的TXT,表头是中文GBK,表体里夹杂着从某个老旧SQL Server里导出的UTF-8字段。这种情况下,一次性解码必然有部分是乱的。我的办法是先按"\t"分割字段,对每个字段单独做编码探测和转换,把乱码字段"溶解"后重新拼装。这个思路有点粗糙,但实操中非常管用——因为表格类的TXT文件,字段边界天然存在,按字段修比按全文修成功率高得多。

第三步:乱码模式字典修复。还有一种情况是文件本身解码没有报错,但内容里出现了"锟斤拷""烫烫烫""屯屯屯"这类经典乱码字符串。这些是GBK字节被按UTF-8解码后再重新按GBK编码产生的"二次乱码"。我建了一个常见乱码模式与还原结果的对照字典,对文本做正则替换。比如"锟斤拷"这个模式,对应的是连续重复的GBK编码字节0xEF 0xBF 0xBD,它在原文理应是某些连续的中文字符,具体是什么需要结合上下文判断。

常见乱码模式速查 - 锟斤拷:GBK字节流被误按UTF-8解码后产生的替代字符序列 - 烫烫烫:VC++ Debug模式下未初始化内存的填充字节()对应的GBK显示 - 屯屯屯:类似"烫烫烫",是另一种未初始化内存填充字节的显示结果

3.3 顽固乱码的处理:降级转码与人工标注

总有一些文件是机器修不回来的。我的原则是:不追求100%修复,追求100%可控。如果一个文件用所有候选编码都无法完整解码,我会把它单独归入"待人工处理"目录,在清洗报告中标记问题字节的位置,而不是让它带着乱码进入RAG。原因很简单:乱码文本进入向量数据库以后,检索时会产生语义噪声,它会让模型产生幻觉,或者把毫无意义的字节和正常文本关联起来。宁可让这部分内容缺失,也不能让脏数据污染整个知识库。

这里有个小技巧:对于实在无法确定编码的文件,可以尝试用iconv在GBK和GB18030之间互转,看是否比直接解码效果更好。GB18030是GBK的超集,很多老系统导出的所谓GBK文件里藏着GB18030扩展区字符,直接用GB18030解码的成功率往往高于GBK。

4. Word清洗的重头戏:结构噪声、表格转写与域代码处理

如果说TXT是编码重灾区,那Word文档就是结构重灾区。探矿业务里的Word文档,几乎都是"表格开会"——勘察数据一览表、地层岩性描述表、岩石力学参数表、水文地质观测表,通篇都是表格。坦白说,直接把Word文档往分块器里丢,是所有RAG项目最容易犯的错误,没有之一。

4.1 Word清洗的三大痛点

痛点一:表格内容被撕裂。python-docx读取文档时,表格和段落是两套独立的迭代对象。很多清洗脚本只处理段落,遇到表格就简单跳过,或者把表格每个单元格当独立文本提出来。结果就是一个完整的"钻孔地层表"被打散成上百个孤立单元格,每个单元格是零碎的几个字。embedding模型在这种细碎文本上的表现很差,检索时几乎必然召回不完整的语义单元。

痛点二:页眉页脚、目录、域代码混入正文。老文档的页眉里写着"第X页共Y页"或项目名称,Word的目录域会在每次打开时重新生成页码。如果你用某些库直接把文档转成纯文本,页眉页脚、目录这些噪声会穿插在正文里。分块器切分时,两块正文内容之间夹着一个页眉,语义连续性就被打断了。

痛点三:文本框和嵌入对象无法被常规API读取。探矿报告里经常有地质剖面图、柱状图、照片等嵌入对象,这些对象的题注往往放在文本框里。python-docx的默认遍历路径拿不到文本框里的文字,如果忽略这一步,报告中一部分只有文本框才承载的描述性内容会静默丢失。

4.2 实战方案:基于"叙述顺序"重建Word结构

经过几次失败尝试之后,我找到了一种比较有效的Word清洗策略:回归文档的"叙述顺序"。

Word文档的存储模型里,正文内容是有逻辑顺序的——先出现一个标题,再出现一段文字,然后一个表格,接着又一段文字。把这个顺序完整提取出来,才是文档真正的阅读体验。python-docx默认把paragraphs和tables分开遍历,这恰恰丢掉了顺序信息。我的做法是遍历文档的body元素,按照元素出现的前后顺序提取:

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) doc = Document("钻孔报告.docx") for block in iter_block_items(doc): if isinstance(block, Paragraph): text = block.text.strip() if text: # 判断是否是标题(可结合样式名称) # 输出带标记的段落文本 print(f"[P] {text}") elif isinstance(block, Table): # 调用表格转写函数,见4.3 print(f"[T] {table_to_text(block)}")

这样提取出来的内容,就还原了人们阅读报告时的真实顺序:标题、段落、表格、段落、下一个标题。

4.3 表格转写:核心技术环节

表格是探矿Word文档里信息密度最高、同时也是检索最容易失败的部分。我的表格转写方案经历了三个版本:

第一版:每行一列一行文本。把表格每行每列的单元格用分隔符拼起来。结果很糟糕——检索器召回的"表格片段"只有行信息,不可能把整张表的内容关联起来。

第二版:整表转为Markdown表格。效果好一些,因为模型能识别Markdown表格结构。但问题是探矿表格经常出现合并单元格、多级表头,转出来的Markdown表格要么结构畸形,要么表格太长被分块器从中间截断。

第三版(最终方案):语义块转写。这个方案的核心是:给每个表格生成一个"文字化描述",让表格作为一个整体成为一个可检索的语义单元。做法是:先提取表头作为字段列表,然后把每一行数据组织成"字段:值;字段:值"的格式,最后拼接成一个连贯的段落。例如:

"ZK301钻孔地层岩性表:深度范围0-2.5m,岩性为第四系粉质粘土,颜色黄褐色,状态可塑;深度范围2.5-8.0m,岩性为中风化砂岩,颜色灰白色,状态较完整..."

这种转写方式有三个好处:一是绕开了分块器对Markdown表格的暴力截断问题;二是每一行的语义是自洽的,检索"ZK301埋深多少米"可以在这一行里直接命中;三是embedding模型对这种"字段:值;字段:值"的文本结构理解得比二维表格好得多——它本质上把表格变成了一个语义序列,模型更容易捕捉到字段之间的关联。

这个方案唯一需要注意的是:表格列数太多、单行内容过长时,生成的描述段落会非常长,需要二次切分策略。我的经验是设置一个长表格拆分的阈值(比如行数超过20行就按深度范围或按分区段拆分),拆分时保留表头前缀,保证每一块都能独立理解。

4.4 Word域代码和自动编号的处理

另一个隐蔽的坑是Word的域代码。老文档里"自动编号"和"交叉引用"是通过域代码(field code)实现的。直接提取纯文本时,你看到的可能是"{ LISTNUM }"或"{ SEQ }"这类残留,或者看到一些空段落。我的做法是:提取文本后,用正则清理所有形如{ ... }的域代码残留,并压缩连续空行。同时注意:文档里如果有"更新时间:{ DATE }"这类动态域,取出来的时间是文档打开时的日期,不是真实内容,清洗时要把这类内容识别并删除。

5. PDF的高精度清洗:文本层提取、OCR策略与版面还原

探矿业务里的PDF有两个极端:一种是电子版生成、自带文本层的PDF(比如从Word打印生成的),处理起来和Word类似;另一种是历史扫描件PDF,一页就是一张图片,完全没有文本层。后者才是真正的难题。

5.1 文本层PDF:别让分栏和页眉毁了你的召回精度

即使是带文本层的PDF,版面分析也逃不掉。探矿报告PDF经常是标准A4双栏打印的,"工程概况"和"岩土工程分析"两栏文字在物理上是并排的,但在文本流里是交错出现的。如果不做版面分析直接提取,你会得到一段跨栏混合的文本,语义错乱得离谱。

我推荐的方案是使用文档解析工具(比如pdfplumber或PyMuPDF)先做版面分析,按物理坐标把每一页划分为独立的文本块,再按阅读顺序重组这些文本块。pdfplumber里可以用extract_words(layout=True)拿到每个词的位置信息,根据x坐标把词分成左栏和右栏,然后分别按y坐标排序。

但更省力的方案是直接用成熟的PDF解析引擎做预处理。我的实际选择是:先跑一遍PyMuPDF按"块"提取,看结果能不能用;遇到分栏识别失败的页面,再降级到pdfplumber手工排布。两种工具的取舍在正式数据处理里很重要——PyMuPDF速度快、脚本简单,但对复杂版面的语义还原能力有限,推荐用来跑批;pdfplumber精确但慢,适合做精修。

页眉页脚的处理特别谈一点:勘察报告的页眉通常包含项目名称和章节号,页脚包含页码。这些内容在每个页面上重复出现,如果在清洗阶段不删除,RAG检索时检索器会频繁命中页眉内容,导致多个不相关的文档片断被错误关联。删除逻辑很简单:在版面分析的坐标系统里,页面上方和下方10%的区域内,那些在每一页重复出现的短文本,就是页眉页脚,直接剔除。

5.2 扫描版PDF:OCR不是万能的,要先补图

扫描版PDF是探矿历史资料的大头。八十年代到两千年初的很多地质报告,原件都是针式打印机打出来的,复印存档再扫描,清晰度感人。OCR这类文档,直接用开源OCR引擎跑,结果往往很惨烈。我总结了一个相对可靠的流程:

第1步:扫描图预处理。这一步的目标是:让图像里的文字更"干净"。用OpenCV做灰度化、去噪、二值化、倾斜校正。倾斜校正是关键——针式打印稿扫描的时候稍微歪一点,OCR引擎的识别率就大幅下降。做一次基于霍夫变换的倾斜检测,把图像旋转到水平,OCR准确率能提升不少。

第2步:OCR加字典约束。探矿术语极其专业,通用的OCR模型不太认识"矽卡岩""泥质粉砂岩""ZK305""qm(2)"这类词。我的做法是:先用OCR引擎过一次,拿到初步结果,然后结合一份自建的探矿术语词典做后处理修正。词典优先匹配覆盖率不高的话,再走一遍基于规则的数字、字母、单位修正。

第3步:OCR结果的版面重组。扫描版PDF识别完之后,出来的文本也是按物理位置排列的,同样需要做版面还原。如果原始扫描件是表格,OCR引擎识别出的表格边界往往不是特别稳定,所以我倾向于把扫描件里的大表格也转写为"字段:值"的语义块格式,而不是保留表格原样。

5.3 一个关键经验:先判断"这段内容要不要进RAG"

不是所有扫描PDF都值得清洗。探矿资料里有大量重复的附件、附表、甚至空白页和盖章页。我在清洗流程里加了一个轻量级的质量门槛:OCR完成后,计算文本长度、关键词覆盖率、无效字符比例。如果一个"报告"OCR出来的有效文本不足300字,或者有效文本里探矿领域关键词覆盖率为零,就直接放入"低质量待审"目录,不进入RAG数据管线。相比把所有内容都灌入知识库,这种有筛选的做法让最终检索精度提升非常明显。

6. 网页抓取的清洗思路:导航噪声、动态加载与去重陷阱

网页抓取是探矿业务知识库的新鲜血液。不少资料只在网上有电子版,比如各地方发布的矿业权公示、勘查许可证信息、政策法规调整公告。抓下来之后的清洗,跟本地文件又不一样,它多了一层"网页结构噪声"的问题。

6.1 正文提取:不要简单地用BeautifulSoup爬文本

很多人的习惯是拿到HTML,BeautifulSoup一把梭,get_text()完事。结果正文、菜单、侧栏推荐、广告、备案号全部混在一起。这么做RAG一定准不了——模型会把导航栏里的"首页""关于我们""设为首页"当成知识文本召回。

我推荐直接采用正文提取算法,比如readability-lxml或trafilatura。trafilatura是我在多个项目里反复比较后选用的首选工具,它对不规范网页的容忍度高,提取效果稳定。特别是我们可以直接调用它的bare_extraction功能,只返回正文内容,连标题、作者、日期都可以一并提取出来:

import trafilatura downloaded = trafilatura.fetch_url(url) result = trafilatura.bare_extraction(downloaded) if result: text = result["text"]

需要留意的是:探矿业务网站里很多内容是以表格和附件形式呈现的,主流正文提取算法对table标签的处理普遍偏保守,可能会直接丢掉。所以我的流程是:trafilatura提取正文,同时用另一路解析提取HTML里的表格内容,两路结果合并,按页面结构顺序融合。凡网页正文质量太差或跟本地文档重复的,我也不会强行入库——联网抓取的内容往往有多个站点互相复制,内容去重必须单独做。

6.2 抓取后的三层去重

网页数据爬下来之后,去重不做,知识库就会被污染。三层去重逻辑如下:

  • URL级别的去重(一级):记录每个页面的规范化URL(去跟踪参数、去锚点),发现重复URL直接跳过。
  • 标题和指纹去重(二级):用hashlib对正文前几百个字符做SimHash,同一内容在不同站点上转载时,只需要简单的归一化(去掉全角/半角、空格、换行)后对比正文指纹,就能识别出来。探矿规范类内容在各省厅网站上经常互相转发,这篇文章的正文和一些省级网站上的转载文本,指纹相似度极高,去重之后知识库的体积能降不少。
  • 语义去重(三级):同一条矿业政策可能以不同标题出现,必须算一遍文本向量相似度。我设了一个经验阈值:MinHash相似度(或余弦相似度)超过0.85的内容判定为重复,只保留发布时间最早、页面排版最完整的那一版。

6.3 网页动态内容与时效性:一个常被忽略的环节

探矿网站的很多页面是动态加载的——初始HTML里没有正文,数据是JavaScript异步请求后渲染的。这类页面直接用Trafilatura抓,抓到的是空壳。判断办法很简单:抓回来后正文长度小于100字,多半是动态页面。简单补救是用无头浏览器(比如Playwright)渲染后再解析正文。另外原始抓取页面里检索不到内容的时候,还要顺手看一眼网页快照和发布时间,老政策是不是被新政策替代,这些属于业务层的清洗判断,可以留到后续人工微调。

做这一块的清洗,真正要提醒大家的是:网页抓取的数据,质量方差比本地文件大得多。同一天抓回来的网页,有的正文完整、有的带大量转载水印、有的是动态页空壳。所以在进入RAG管线前,我始终对所有网页抓取数据执行质量门槛逻辑——正文长度在300字以下的不要、重复率超过阈值的不进库、非探矿主题的页面(比如网站公告、招聘信息)不进库。

7. 清洗效果的验证:不是"看起来干净"就行,要能测

聊完四类数据源的清洗方案,最后花点篇幅说说验证环节。做RAG清洗,最大的挑战不是写出清洗代码,而是无法证明清洗真的有用。我用了一套基于回归测试思路的评测方法,分两层来验证清洗效果。

7.1 第一层:格式质量自动化检查

清洗完成后,每条记录自动生成一个质量报告,包含以下几个检查项:

检查项通过条件
编码无效字符率无效字符(U+FFFD等)比例低于0.1%
正文文本长度清洗后有效正文字数不低于原始文本的70%
结构标记覆盖率文档被正确标记出的标题、表格、段落数量与原始结构数量误差在10%以内
高频噪声残留不包含"页眉""联系电话""版权所有"等超过2次的高频噪声词
乱码模式残留不包含"锟斤拷""烫烫烫"等乱码模式

这个自动化检查的作用,本质上是防止清洗脚本在某个文件上悄悄失灵。实际项目中,格式质量检查单能保住"清洗结果不烂"的底线,但它证明不了检索质量的提升。

7.2 第二层:评测集的召回归回测试

我建了一个小的评测集,大约50组问题,全部来自探矿业务的真实检索场景。示例:

  • "某某矿区ZK301号钻孔涌水量是多少?"
  • "花岗岩残积土的承载力特征值经验值是多少?"
  • "2023年发布的固体矿产勘查规范里,关于风钻孔取样的间距要求是什么?"

每组问题对应一个标准答案,以及该答案在源文档中的位置。然后在同一套RAG管线里,分别用原始文档和清洗后文档做召回,计算Hit Rate(答案被正确召回的比例)和Answer Relevance(答案和问题的语义一致性)。这个测试跑下来,几乎所有文档类型都观测到了显著提升:整体Hit Rate从45%左右提升到了78%以上,TXT和Word类型提升幅度最大,扫描PDF的提升幅度相对小一些——毕竟OCR本身有精度天花板。

这里要强调的是:召回程度必须结合自己业务的评测集来调,不能拿别人的指标当标准。探矿领域的问题表述方式非常特定,通用领域的评测指标无法真实反映清洗效果的好坏。

7.3 清洗中间态的设计:一次清洗,多处复用

最后说一下我推荐的整体加工方法:清洗输出不要固化成"最终文本",而应形成一份带标记的中间态文件。我的中间态格式是JSON Lines,包含原始文件路径、清洗后文本、文档类型、标题层级信息、表格标记、原始元数据(日期、作者、来源)。这样当RAG管线升级、换了分块器或者embedding模型时,不需要重新清洗文档——只需要在中间态基础上重新切分就够了。对于探矿业务这种数据源种类多、经常需要反复迭代的项目,这种设计节省的工作量相当可观。

我把清洗脚本做成了定时任务,新入库的文档自动走完整条处理流程;如果某个文件在格式质检中不过关,会单独落到人工复核目录里,由业务人员和技术人员一起判断是扔、是修、还是等更新版本。这样既保证知识库随时可发布,也保证每一轮新增数据都在控。

8. 踩坑之后的一些实在建议

整个过程走完之后,我的体会是:探矿文档清洗,真正费时间的不是执行清洗,而是判断清洗质量。

不要迷信任何一个单一工具能解决所有格式问题——pdfplumber、PyMuPDF、Trafilatura、python-docx这些方案我都写过脚本,到实际业务里都会遇到意料之外的坑,现在项目里保留的是逐步验证出来的一套组合流程,而不是某一个"万能神器"。

也不要忽视元数据的管理。我所有的清洗环节都把元数据(文件生成时间、来源路径、清洗工具版本、处理时间戳)写进了中间态文件。这带来的好处是,当某一次清洗质量拉胯时,能快速回溯是哪一版脚本出的问题;当业务方质疑某个数据不准时,能翻出原始文档重新验证。这个过程在通用RAG项目上可能不算什么,但在探矿这种长期运行、数据链条长的系统里,干净、可追溯的数据管线是所有下游应用的生命线。

如果只让我总结三件事,我会说:第一,清洗前先做数据源画像,不要上来写代码;第二,表格转写、编码修复、噪声剔除这些细节比模型选择重要得多;第三,一定要建自己的评测集,否则清洗做得好不好你根本无从判断。这三件事做完,探矿业务里的RAG才真正有可能从"试试看"变成"能用了"。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询