
1. 探矿业务里的数据泥潭为什么通用RAG方案一上来就翻车做过矿业数字化项目的人都有一个共同体会这个行业的数据脏得很有层次感。地质报告是扫描件PDF钻孔编录是上世纪九十年代Word 97格式化探数据是TXT但编码是GBK矿区公示信息散落在十几个网页里还有一堆SHP转出来的坐标文本。你把这些东西一股脑丢给通用RAG框架检索出来的结果基本没法看——要么是乱码切片要么是表格结构全丢要么是同一份报告被切成几百个语义碎片问它“XX矿区铜品位多少”它给你返回一段讲地层年代的段落。这不是RAG本身的问题是清洗环节被严重低估了。通用方案默认你的文档是干净的、结构化的、编码统一的但探矿业务恰恰相反。我前后经手过三个地质资料知识库项目踩过的坑足够写一本避雷手册。这篇文章就把TXT、Word、PDF、网页这四类数据在探矿场景下的RAG清洗思路完整拆一遍从编码识别到版面还原从表格抽取到切片策略每个环节都给出可复现的操作方案。核心关键词RAG、TXT、Word、PDF、网页会贯穿全文但我不打算写成工具说明书而是按“为什么这么选—具体怎么做—踩过什么坑”的逻辑展开。适合正在做地质、矿业、勘探类知识库的工程师也适合任何被非结构化文档折磨过的RAG开发者。读完你至少能拿到一套可直接抄作业的清洗流水线以及一份价值不菲的避坑清单。2. 整体清洗架构设计四类数据源的分治策略2.1 为什么不能用一个解析器打天下很多人上来就想找一个“万能文档解析库”Python里试一圈pdfplumber、python-docx、BeautifulSoup然后写一个统一入口。这个思路在探矿业务里必然失败原因有三。第一编码问题在TXT里是致命的。地质行业大量老数据是GBK甚至GB2312编码有些化探数据文件还混着全角字符和特殊符号。你直接用UTF-8读轻则乱码重则抛异常中断整个流水线。而PDF和Word的编码是封装在文件格式内部的处理逻辑完全不同。第二版面信息的价值差异巨大。PDF里的钻孔柱状图、Word里的品位表格、网页里的矿权公示表格这些结构化信息是探矿RAG的核心检索目标。通用文本抽取会把表格拍扁成一行字符串坐标和品位数值混在一起检索精度直接归零。第三切片策略必须跟着数据源走。TXT化探数据适合按行或按块切Word地质报告适合按章节切PDF扫描件需要先做版面分析再切网页需要先做正文抽取再切。用同一套切片参数等于让所有数据都受最差那类数据的拖累。所以我的方案是分治架构四类数据源各自走独立的清洗管道最终汇入统一的向量化与索引层。下面这张表是我在实际项目中沉淀下来的分治策略对照。数据源核心难点首选解析方案切片粒度元数据重点TXT编码混乱、分隔符不统一chardet检测多编码回退按行/按记录块文件来源、坐标系统Word表格嵌套、公式对象、版本差异python-docx自定义表格抽取按章节表格独立报告编号、编写单位PDF扫描件无文本层、双栏排版PyMuPDFOCR回退版面分析按段落表格区域页码、图件编号网页正文噪声、动态渲染正文抽取算法结构化解析按语义块URL、发布时间2.2 清洗流水线的五个阶段不管哪类数据源清洗流水线都遵循同样的五阶段模型只是每个阶段的具体实现不同。阶段一格式识别与预处理。判断文件真实类型不能只看扩展名检测编码识别是否加密评估文本层质量。这一步的输出是一份“体检报告”决定后续走哪条分支。阶段二内容抽取。从原始格式中提取文本、表格、图片说明等可用信息。关键是保留结构信息不能只拿纯文本。阶段三噪声清洗。去掉页眉页脚、水印文字、重复的免责声明、网页导航栏残留。探矿报告里常见的“内部资料注意保密”这类页眉如果不清掉会在每个切片里出现严重干扰检索。阶段四结构化重组。把抽取出来的碎片按逻辑关系重新组织表格还原成Markdown或JSON章节建立层级关系坐标数据关联到对应矿区。阶段五切片与元数据注入。按数据源特性选择切片策略同时把来源、时间、矿区、坐标系统等元数据注入每个切片供后续过滤检索使用。注意阶段三和阶段四的顺序不能颠倒。我见过先重组再清洗的方案结果页眉文字被当成章节标题嵌进了层级结构里后面怎么改都别扭。2.3 元数据设计被大多数人忽略的检索加速器探矿RAG和通用RAG最大的区别在于用户查询几乎总是带条件的。“XX矿区”“XX年份”“铜品位”“钻孔编号”这些条件如果只靠向量相似度去匹配召回率会很难看。正确做法是把这些条件做成元数据字段检索时先做元数据过滤再做向量检索。我在项目里定义的元数据字段包括矿区名称、报告类型普查/详查/勘探、编写年份、坐标系统西安80/北京54/CGCS2000、矿种、数据来源类型。这些字段在清洗阶段就要从文档内容或文件路径中抽取出来注入每个切片。抽取方式分两种规则抽取和模型抽取。规则抽取适合格式固定的数据比如从文件路径/data/XX矿区/2023/详查报告.docx里直接解析出矿区和年份。模型抽取适合从正文里识别矿种和坐标系统用一个轻量NER模型就能搞定不需要上大模型。3. TXT数据清洗从乱码到规整记录3.1 编码检测的坑与多级回退方案TXT文件在探矿业务里主要承载化探数据、钻孔编录简表、坐标点文件。这些文件的编码之混乱超出一般开发者的想象。我遇到过同一个目录下有的文件是UTF-8有的是GBK还有几个是GB2312和UTF-8混着来的——前半部分GBK后半部分UTF-8因为文件被不同工具先后编辑过。chardet库是起点但不能全信。它的检测准确率在短文本上很差一个只有几十行的坐标文件它可能给出三四种可能编码置信度都不到0.7。我的做法是多级回退import chardet def detect_and_read(filepath): with open(filepath, rb) as f: raw f.read() # 第一级chardet检测 result chardet.detect(raw) encoding result[encoding] confidence result[confidence] # 第二级置信度不足时按优先级尝试 if confidence 0.8 or encoding is None: for enc in [utf-8, gbk, gb2312, gb18030, latin-1]: try: text raw.decode(enc) # 第三级用中文字符占比验证 chinese_ratio sum(1 for c in text if \u4e00 c \u9fff) / max(len(text), 1) if chinese_ratio 0.1 or enc utf-8: return text, enc except UnicodeDecodeError: continue return raw.decode(encoding, errorsreplace), encoding这里的关键是第三级验证用中文字符占比来判断解码是否正确。GBK编码的中文文件用UTF-8解会出现大量乱码字符中文字符占比会异常低。反过来UTF-8文件用GBK解也会产生乱码。这个方法比单纯看chardet置信度可靠得多。实操心得gb18030是GBK的超集兼容性最好放在回退链里能兜住绝大多数国标编码文件。但不要把它放在第一位因为gb18030解码几乎不会报错会把真正的UTF-8文件也“成功”解码成乱码。3.2 分隔符统一与记录规整编码解决之后下一个问题是分隔符。化探数据常见的分隔符有逗号、制表符、空格、分号甚至还有用竖线的。更麻烦的是有些文件用多个空格对齐列有些用制表符有些混用。我的处理策略是先探测再统一。统计每行中各种候选分隔符的出现次数选择出现次数最稳定方差最小的那个作为主分隔符。然后按主分隔符切分对每列做去空格处理。def normalize_delimiter(lines): candidates [,, \t, ;, |, ] scores {} for sep in candidates: counts [line.count(sep) for line in lines if line.strip()] if not counts: continue # 用众数出现频率和方差评估稳定性 from statistics import mode, variance try: m mode(counts) v variance(counts) if len(counts) 1 else 0 scores[sep] (counts.count(m) / len(counts), -v) except: continue best_sep max(scores, keylambda k: scores[k]) if scores else , return best_sep选好分隔符后还要处理列数不一致的问题。探矿数据里经常出现某几行列数多一个或少一个原因是数据录入时多打了分隔符或者漏了字段。我的做法是以众数列数为准多出的列合并到最后一列缺少的列补空值同时在元数据里标记“该文件存在列数异常”供后续人工复核。3.3 坐标与数值数据的特殊处理探矿TXT里大量出现坐标和品位数值这些数据的清洗有特殊要求。坐标数据常见格式有度分秒113°4530、十进制113.7583、带带号的平面坐标38500000, 2850000。RAG检索时用户可能用任意一种格式查询所以清洗阶段要统一转成十进制并保留原始格式作为元数据。度分秒转十进制的公式是度 分/60 秒/3600注意西经和南纬要取负值。品位数值要注意单位和有效数字。有的报告用百分比0.85%有的用ppm8500有的用g/t。清洗时要统一转成ppm或百分比并在切片里同时保留原始表述。比如“铜品位0.85%”这个切片元数据里存cu_grade_ppm: 8500文本里保留“0.85%”这样无论用户怎么查都能命中。注意数值转换一定要做范围校验。我遇到过把“0.85”当成85%的bug原因是代码里漏了除以100。探矿数据的数值范围有行业常识铜品位超过10%就极其罕见超过这个阈值要触发告警而不是静默通过。4. Word文档清洗表格、公式与版本兼容4.1 python-docx的能力边界与补充方案python-docx是处理Word文档的首选库但它有几个明确的边界不能处理.doc老格式对文本框和艺术字支持有限对公式对象只能拿到OMML原始XML。探矿报告里这三种情况都很常见。.doc格式的解决方案是先用LibreOffice做批量转换命令行一行搞定libreoffice --headless --convert-to docx --outdir ./converted ./raw_docs/*.doc这个转换在Linux服务器上跑很稳但要注意中文字体问题。如果服务器没装中文字体转换出来的文档可能丢字。装一个fonts-noto-cjk包就能解决。文本框里的内容python-docx默认读不到需要遍历document.element.body下的所有w:txbxContent节点。这个操作稍微底层一些但能捞回不少被遗漏的文字——探矿报告里经常用文本框放图例说明和注意事项。4.2 表格抽取探矿报告的核心资产Word表格是探矿报告里信息密度最高的部分。钻孔编录表、样品分析结果表、储量估算表这些表格如果抽取不好整个RAG系统的价值就丢了一半。python-docx的表格API能拿到行列结构但合并单元格是最大的坑。一个跨三行的表头单元格在API里会在三行里各出现一次直接遍历会导致数据重复。我的处理方式是检测单元格的_tc元素是否有vMerge或gridSpan属性据此重建真实的表格结构。def extract_table(table): rows [] for row in table.rows: cells [] for cell in row.cells: # 检测合并单元格 tc cell._tc tcPr tc.tcPr is_merge False if tcPr is not None: if tcPr.find(qn(w:vMerge)) is not None: is_merge True if tcPr.find(qn(w:gridSpan)) is not None: is_merge True cells.append({ text: cell.text.strip(), is_merge: is_merge }) rows.append(cells) return rows抽取出来的表格我建议同时保留两种形式一种是Markdown表格供人阅读和向量化一种是JSON结构供精确查询。Markdown表格在切片时作为一个整体不要拆散否则“铜品位”和“0.85”可能被切到不同切片里检索时匹配不上。4.3 公式与特殊对象的处理探矿报告里的公式主要是品位计算公式、储量估算公式、坐标转换公式。Word里的公式对象是OMML格式python-docx只能拿到XML。直接把这个XML转成文本会得到一堆标签没有检索价值。我的做法是用XSLT把OMML转成LaTeX然后保留LaTeX文本。LibreOffice自带这个转换能力也可以用mathml2latex这类库。转换后的LaTeX文本虽然用户不会直接搜但可以作为元数据存储当用户搜索“储量计算公式”时能命中。实操心得公式转换不要追求100%准确能达到80%的可用性就够了。关键是不要因为公式转换失败而中断整个文档的清洗。用try-except包住失败就跳过记录日志后续人工处理。4.4 版本兼容与批量处理探矿单位的Word文档版本跨度极大从Word 97到Office 365都有。除了.doc转.docx还要注意.docx内部格式的差异。有些老版本生成的.docxpython-docx打开会报错原因是XML命名空间不标准。解决方案是用lxml做一次预处理修复命名空间声明。或者更简单粗暴用LibreOffice把所有文档统一转一遍不管原来是.doc还是.docx都转成标准.docx。这个转换会损失一些格式细节但对文本和表格内容没有影响反而能规避大量兼容性问题。批量处理的流水线我建议用concurrent.futures做并行但要注意LibreOffice的headless模式不能并发太多实例一般控制在CPU核数的一半左右。我试过开16个并发结果LibreOffice进程互相抢锁反而比串行还慢。5. PDF解析扫描件、双栏与表格的三重挑战5.1 文本层质量评估决定走哪条路PDF在探矿业务里分两类原生PDF和扫描PDF。原生PDF有文本层可以直接抽取扫描PDF是图片必须走OCR。但实际情况比这复杂——有些PDF是混合的前几页是原生文本后面附的图件是扫描的有些PDF有文本层但质量极差是OCR软件生成的错字连篇。所以第一步是文本层质量评估。我用PyMuPDF打开PDF抽取前几页的文本计算三个指标字符总数、中文字符占比、乱码字符占比。如果字符总数太少比如每页不到50个字符说明是扫描件如果乱码字符占比超过10%说明文本层质量差也要走OCR。import fitz def assess_pdf_quality(pdf_path, sample_pages5): doc fitz.open(pdf_path) total_chars 0 chinese_chars 0 garbage_chars 0 for i in range(min(sample_pages, len(doc))): text doc[i].get_text() total_chars len(text) chinese_chars sum(1 for c in text if \u4e00 c \u9fff) garbage_chars sum(1 for c in text if c in \ufffd\u25a1\u25a0) doc.close() if total_chars 50 * sample_pages: return scanned if garbage_chars / max(total_chars, 1) 0.1: return low_quality return native5.2 扫描件OCR精度与速度的平衡扫描件必须走OCR但OCR的精度和速度是一对矛盾。我试过Tesseract、PaddleOCR和几个商业API最终在探矿场景下选择了PaddleOCR原因是它对中文和表格的识别效果明显好于Tesseract而且可以本地部署数据不出内网。OCR之前要做图像预处理灰度化、二值化、去噪、纠偏。探矿报告的扫描件经常有倾斜不纠偏的话OCR精度会下降20%以上。PyMuPDF可以把PDF页面渲染成图片然后用OpenCV做预处理。import cv2 import numpy as np def preprocess_image(img): gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 自适应二值化 binary cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) # 去噪 denoised cv2.medianBlur(binary, 3) # 纠偏 coords np.column_stack(np.where(denoised 0)) angle cv2.minAreaRect(coords)[-1] if angle -45: angle 90 angle if abs(angle) 0.5: h, w denoised.shape M cv2.getRotationMatrix2D((w//2, h//2), angle, 1.0) denoised cv2.warpAffine(denoised, M, (w, h), flagscv2.INTER_CUBIC, borderModecv2.BORDER_REPLICATE) return denoisedOCR的精度还取决于分辨率。PyMuPDF渲染时用zoom3约216 DPI效果最好再高收益递减再低精度明显下降。这个参数是我对比了十几种组合后确定的可以直接用。5.3 双栏排版与阅读顺序还原地质报告大量采用双栏排版PyMuPDF默认按坐标顺序抽取文本会把左右两栏交错在一起读起来是“左栏第一行、右栏第一行、左栏第二行、右栏第二行”这种混乱顺序。解决方案是基于坐标的栏检测。先获取页面上所有文本块的边界框然后用聚类算法把文本块分成左右两组分别按纵坐标排序最后左栏全部在前、右栏全部在后拼接。def detect_columns(page): blocks page.get_text(blocks) if not blocks: return [] # 按x坐标聚类 x_coords [b[0] for b in blocks] mid_x page.rect.width / 2 left_blocks [b for b in blocks if b[0] mid_x] right_blocks [b for b in blocks if b[0] mid_x] # 如果右栏内容太少可能是单栏 if len(right_blocks) len(blocks) * 0.2: return sorted(blocks, keylambda b: (b[1], b[0])) left_sorted sorted(left_blocks, keylambda b: b[1]) right_sorted sorted(right_blocks, keylambda b: b[1]) return left_sorted right_sorted这个算法对标准双栏有效对三栏或不规则排版需要调整。探矿报告里偶尔有跨栏的表格或图件这些块的宽度会横跨中线需要单独识别并放在正确位置。5.4 PDF表格抽取从线条到结构PDF表格抽取比Word表格难得多因为PDF没有“表格”这个概念只有线条和文字。表格抽取的本质是从线条和文字位置反推表格结构。我用的方案是pdfplumber的表格抽取功能它基于线条检测。但探矿报告里很多表格没有完整线条只有横线没有竖线或者干脆没有线条只有对齐的文字。这种情况pdfplumber会失效。回退方案是基于文字对齐的表格重建。获取所有文字的坐标按y坐标分组同一行的文字y坐标接近然后在每组内按x坐标排序根据x坐标的间隔判断列边界。这个方法对规整的表格有效对合并单元格和错位表格需要额外处理。注意PDF表格抽取的结果一定要人工抽检。我见过把两列数据合并成一列的bug原因是两列之间的间距刚好和列内文字间距一样。抽检比例建议不低于5%重点检查数值列。6. 网页数据清洗从噪声中提取矿权信息6.1 正文抽取干掉导航栏和广告探矿业务需要的网页数据主要是矿权公示、地质资料目录、行业标准页面。这些页面的正文占比往往不到30%其余是导航栏、侧边栏、页脚、相关推荐。如果不清洗检索“XX矿权出让”会命中一堆导航链接。正文抽取算法我用过三种基于密度的如readability、基于文本比例的、基于机器学习的。在探矿场景下基于密度的readability-lxml效果最稳定因为它对中文页面的适配经过大量验证。from readability import Document import requests def extract_main_content(url): resp requests.get(url, timeout10) resp.encoding resp.apparent_encoding doc Document(resp.text) content doc.summary() # 进一步清洗HTML标签 from lxml.html import fromstring tree fromstring(content) text tree.text_content() return text.strip()readability的局限是它对表格的处理不好会把表格拍扁。矿权公示页面里的表格恰恰是核心信息所以需要先抽取表格再抽取正文两者合并。6.2 动态渲染页面的处理越来越多的矿权公示页面用JavaScript动态渲染requests拿到的HTML里没有数据。这种情况需要上无头浏览器。playwright是目前最稳的选择比selenium快API也更现代。from playwright.sync_api import sync_playwright def fetch_dynamic_page(url): with sync_playwright() as p: browser p.chromium.launch(headlessTrue) page browser.new_page() page.goto(url, wait_untilnetworkidle) # 等待表格加载 page.wait_for_selector(table, timeout10000) content page.content() browser.close() return contentwait_untilnetworkidle是关键参数它等待网络请求静默后再返回能兜住大多数异步加载。但有些页面有轮询请求永远不会networkidle这时候要改用wait_for_selector加超时。实操心得无头浏览器很吃内存批量抓取时一定要控制并发数并且每个页面处理完就关闭context。我试过不关context跑了几百个页面后内存爆了进程被系统杀掉。6.3 网页表格与结构化数据抽取矿权公示表格通常是标准HTML表格用pandas.read_html就能抽取。但要注意表头合并和多级表头的情况pandas默认会把多级表头拍成一行需要手动处理。import pandas as pd def extract_html_tables(html): tables pd.read_html(html) results [] for df in tables: # 清洗去掉全空行全空列 df df.dropna(howall).dropna(axis1, howall) if df.empty: continue # 转成Markdown md df.to_markdown(indexFalse) results.append(md) return results抽取出来的表格同样要保留Markdown和JSON两种形式。Markdown用于向量化JSON用于精确查询。矿权公示里的坐标、面积、矿种这些字段做成JSON后可以直接做结构化过滤。7. 切片策略与向量化让检索精度上一个台阶7.1 分源切片参数设计切片是RAG里最容易被忽视但影响最大的环节。通用方案建议512 token固定切片但在探矿业务里这个参数会让表格和章节结构全丢。我的分源切片策略如下表数据源切片方式切片大小重叠特殊处理TXT化探数据按记录块10-20行2行表头注入每个切片Word报告按章节300-500字50字表格独立成片PDF正文按段落400-600字80字跨页段落合并PDF表格整表不限无超长表格按行切网页正文按语义块300-500字50字标题注入切片关键原则是表格不拆散章节不跨级数值不孤立。一个包含“铜品位0.85%”的表格行必须和表头“样品编号、Cu(%)、品位”在同一个切片里否则检索时匹配不上。7.2 元数据注入与混合检索每个切片除了文本内容还要注入元数据。元数据的来源有三个文件路径解析、文档内容抽取、人工标注。我建议在清洗阶段就把元数据准备好切片时直接带上。检索时用混合检索先用元数据做过滤比如限定矿区为“XX矿区”、年份为“2020-2023”再做向量相似度检索最后用BM25做关键词补充。这个组合在探矿场景下的召回率比纯向量检索高30%以上。def hybrid_search(query, filters, top_k10): # 元数据过滤 candidate_ids metadata_filter(filters) # 向量检索 vector_results vector_search(query, candidate_ids, top_k * 2) # BM25补充 bm25_results bm25_search(query, candidate_ids, top_k * 2) # 融合排序 merged reciprocal_rank_fusion(vector_results, bm25_results) return merged[:top_k]7.3 常见检索失败模式与修复即使清洗做得再好检索也会出问题。我整理了探矿RAG里最常见的四种失败模式和对策。失败模式典型表现根因修复方案数值匹配不上搜“0.85%”找不到切片里是“0.85”没有百分号数值归一化多形式存储表格信息丢失搜“钻孔编号ZK001”无结果表格被拆散表格整片表头注入跨页段落断裂搜完整句子无结果段落被页边界切断跨页合并重叠切片同义词不匹配搜“铜矿”找不到“Cu矿床”向量模型对专业术语不敏感同义词词典查询扩展实操心得数值匹配问题最隐蔽因为开发时用文本查询测试发现不了。一定要用真实业务查询做回归测试比如“XX矿区铜品位大于0.5%的样品有哪些”这种查询会同时触发数值匹配和元数据过滤。8. 常见问题与排查技巧实录8.1 编码类问题速查编码问题是TXT清洗里最高频的故障。下面这张表是我从实际故障记录里整理出来的覆盖了90%以上的场景。现象可能原因排查方法解决中文全是问号用latin-1解码了GBK检查chardet结果改用gb18030部分中文乱码混合编码分段检测编码分段解码后拼接文件读取报错含BOM头检查文件头字节用utf-8-sig特殊符号丢失编码不支持对比原始字节用errorsreplaceBOM头是个经典坑。UTF-8 BOM是EF BB BF三个字节用utf-8解码会在文本开头多一个\ufeff字符。这个字符如果进了切片会影响检索匹配。解决方案是用utf-8-sig编码打开Python会自动去掉BOM。8.2 PDF解析类问题速查现象可能原因排查方法解决抽取文本为空扫描件无文本层检查字符数走OCR流程文字顺序混乱双栏排版检查文本块坐标栏检测重排序表格串列列间距过小可视化检查调整列检测阈值OCR错字多图像质量差检查渲染DPI提高zoom到3OCR错字在探矿场景下特别危险因为矿种名称和化学元素符号错一个字检索就完全失效。我的做法是建立矿种和元素的纠错词典OCR后做一次规则纠错。比如“铜”被识别成“同”“锌”被识别成“辛”这些高频错误直接替换。8.3 切片与检索类问题速查现象可能原因排查方法解决检索结果不相关切片过大检查切片长度缩小切片增加重叠同一内容重复召回切片重叠过多检查重叠比例降低重叠到10%表格检索不到表格被拆散检查切片边界表格独立成片元数据过滤失效元数据缺失检查切片元数据清洗阶段补全切片过大是新手最常犯的错误。512 token的切片在通用场景下没问题但在探矿报告里一个切片可能包含三个不同主题的段落向量化后语义被平均检索时哪个主题都匹配不好。我的经验是探矿类文本切片控制在300-400字比通用建议小一些。8.4 独家避坑清单最后分享几条从实际项目里用血泪换来的经验每一条都对应过真实故障。第一条永远不要相信文件扩展名。我遇到过.txt文件实际是PDF.docx文件实际是RTF。清洗流水线的第一步必须是魔数检测读文件头字节判断真实类型。第二条清洗日志比清洗结果更重要。每个文件的清洗过程都要记录编码检测结果、文本层质量评分、表格抽取数量、切片数量。出问题时日志是唯一的排查线索。第三条小批量验证再全量跑。清洗流水线写完先拿20个文件跑一遍人工检查结果。确认无误再全量跑。我见过全量跑了8小时才发现切片参数写错全部重来的惨剧。第四条保留原始文件。清洗后的数据可以重新生成原始文件丢了就真丢了。所有原始文件做只读备份清洗输出写到独立目录。第五条数值字段做范围校验。探矿数据的数值有行业常识范围超出范围的数值大概率是解析错误。铜品位0-10%、铅锌品位0-20%、坐标经纬度范围这些校验规则能拦住大部分解析bug。第六条表格抽取结果必须人工抽检。自动化表格抽取的准确率在80-90%之间剩下10-20%的错误如果进了知识库会持续产生错误检索结果。抽检比例不低于5%重点检查数值列和合并单元格。第七条向量模型选型要实测。通用中文向量模型在探矿专业术语上的表现差异很大。我实测下来在矿种、地层、构造这些术语上领域微调过的模型比通用模型召回率高15-20%。如果没有微调条件至少要用业务查询做一轮对比测试。第八条检索效果要持续监控。知识库上线不是终点。用户查询日志要定期分析找出零结果查询和高频误匹配反推清洗和切片的问题。我负责的一个项目上线三个月后通过日志分析发现“品位”和“品味”的混淆问题加了一个同义词映射后相关查询的召回率提升了12%。这套清洗方案在三个探矿知识库项目里验证过最大的一个库有40多万个切片覆盖2000多份报告和10万条化探数据。检索准确率从最初的不到50%提升到85%以上核心就是清洗环节做扎实了。RAG的上限由数据质量决定清洗就是拉高这个上限的唯一途径。