犀牛鸟 2026 研究笔记 estelledc.github.io

Ch39: 挑战深读②——文档解析天花板

Part 10 · 对应挑战 #2 · challenges.md §2 前置:Ch34 RAGFlow 源码 · 实验语料:parse-pitfalls.md

结论先行:解析天花板 = 格式无限 × 解析一致 impossible。GIGO(Garbage In, Garbage Out)在 RAG 里被放大——噪声进 chunk、进向量、进回答,Rerank 救不了被撕碎的表格。大师基线:Level 2 结构理解(版面+表格)为生产默认;Level 3 页级 VLM 按需;噪声必须在 parse 层最早过滤


一、日常类比

搬家收到一堆不同规格包装箱:有的标签清晰(结构化 docx),有的只写「杂物」(纯 PDF 文本流),有的外语标签(扫描件),有的箱中套箱(嵌套表格),有的糊了旧报纸(水印印章)。你要统一编目入库——编目质量完全取决于能从每个箱子提取多少真实信息。RAG 的 docreader/docparser 就是那套编目流水线;编错一次,后面检索再强也是搜错内容。


二、解析三层次(Level 1–3)

Level 1: 文本提取 —— 能不能把字读出来
  └─ 多数系统都能做;扫描 OCR 错误率 5–15%;倾斜/印章/低分辨率级联放大

Level 2: 结构理解 —— 标题/段落/表格/图注是否还原
  └─ RAGFlow DeepDoc、WeKnora IMA 的主战场
  └─ 双栏 PDF 文本流乱序、Word 嵌套表、PPT 绝对坐标 —— 本质是视觉推理

Level 3: 语义理解 —— 「这张表说明什么业务风险?」
  └─ 目前没有项目可靠做到;需 VLM,成本高
  └─ 净利率降 5% 是波动还是风险 —— 要领域知识,非 parse 能独力解决

大师选型:默认停在 L2;仅当 L2 输出仍无法满足 QA(扫描公式、复杂图)时对 单页 调 VLM。Dify/FastGPT/AnythingLLM 多数停在 L1,复杂排版靠 Unstructured/PyPDF2,质量参差。

项目 解析深度 典型代价
RAGFlow DeepDoc L2 极致,GPU 吃紧 100 页 PDF 数分钟
WeKnora IMA + docreader L2,偏中文实体抽取 Python docreader ~513K 行
Dify / FastGPT / AnythingLLM L1 为主 人工审核或外部 txt2vec

三、Defense in Depth 管线

解析不是单点函数,而是 纵深防御——每一层拦截一类失败,避免漏到 chunker:

① 格式路由     按 mime/扩展名选 parser(Excel≠PDF≠MHTML)
② 结构恢复     版面顺序、表格 rowspan/colspan、公式 LaTeX
③ 噪声过滤     IMAGE/DISPIMG、页眉页脚、水印 OCR 垃圾
④ chunk 契约   输出格式满足下游 protectedPatterns(MD 表 / HTML 边界)
⑤ 检索仍失败   按需 VLM 页级理解(贵,非默认)

原则:能在 ③ 过滤的绝不要期望 ⑤ Rerank 过滤;能在 ② 还原结构的绝不要在 ④ 用滑动窗口硬切表。实验语料 parse-pitfalls.md 把三条 WeKnora 真 issue 写成 可复现陷阱清单,贡献 docreader 时应先对照该文件写 pytest。


四、三类文档陷阱(challenges.md 浓缩)

PDF 文本流陷阱:双栏 PDF 左栏末行与右栏首行在文本流中相邻,不做版面分析则左右栏交叉,产出不可读字符串。

Word 嵌套表:docx XML 允许表套表,深度 20+;多数解析器 展平 内层表,行列语义可能全错。

扫描级联错误:图像差 → OCR 错字 → 分词错 → BM25 漏召 → 向量语义偏移 → 每一步放大上一步误差

PPT 的绝对坐标、手写体、印章遮挡等仍在 L2 边界外——贡献前先确认 issue 属于哪一层,避免用 VLM 修 L1 OCR 能解决的问题。


五、WeKnora 实战三联:1785 / 1725 / 1743

Case 层级 现象 修复要点
#1785 ③ 噪声 WPS Excel 嵌入图 → cell 纯文本 DISPIMG("ID_xxx",1) 进回答 excel_parser.py 最早阶段正则过滤 IMAGE 变体;data_only=True 对伪函数无效
#1725 ②→④ 契约 PaddleOCR-VL 输出 HTML 表;chunker 只保护 \| pipe \| html_table_normalizer.go:简单表→MD,复杂表剥 style;否则表超 ChunkSize 拦腰切断
#1743 ①② 跨语言 MHTML 内嵌图降采样/链接在 chunk 边界断裂 Python 状态机保留 MIME+分辨率;Go 侧 ![...]() 整段保护

1785 方法论:RAG 噪声过滤必须在 最早解析层,不是下游检索兜底——与 #1248 反馈归因同属「边界识别」问题。

1725 方法论:上游输出格式必须满足下游 保护条件;HTML token 约为 Markdown 同内容的 2–3 倍,style 属性纯浪费 context。

1743 方法论:改 Python docreader 必须 追踪到 Go chunker 假设;跨语言 PR 要 Python 输出 → Go 输入集成测试。


六、平台对照与贡献切口

RAGFlow 最深,适合学 DeepDoc 表格模型与 GPU 部署 trade-off(Ch34)。WeKnora 贡献切口清晰:

前沿:VLM 端到端页解析(RAGFlow 探索中)、Nougat PDF→MD(中文弱)。竞赛 PR 优先 L2 可量化改进 + fixture,而非直接上全库 VLM。


七、为什么检索层修不好「表被撕碎」

用户问「Q3 营收是多少?」——若表头在 chunk A、数据在 chunk B,向量可能只召回含数字的 B,LLM 看到 23.5 | 18.7 不知单位。Rerank 对单 chunk 打分,无法重建表头-数据组合;图谱路也无法从不完整字符串抽实体。唯一正解在 ingest:整表保留 + 表头同行(见 Ch40Ch27)。


章末自检

延伸阅读