PDF 解析不准?90% 的人第一反应都是错的
“2 年 RAG 开发经验,主导过企业级知识库系统,处理过百万级 PDF 文档”
看到这份简历,技术面我打算聊聊多模态。毕竟 RAG 做到后面,绑不开一个问题:PDF 里的表格、图表、流程图怎么处理?
候选人说用 OCR 提取文本,再走正常的 RAG 流程。
这是大多数人的第一反应,也是最容易踩坑的地方。OCR 的错误会像多米诺骨牌一样逐级放大——ICCV 2025 有篇论文专门量化了这个现象:即便用上最好的 OCR 方案,RAG 效果仍然比真实文本低 14%。
那是不是直接丢给 GPT-4o 看图就行了?也不是。VLM 会幻觉、会漏信息,长文档更是灾难。
今天这场面试,从架构选型到生产落地,把多模态 RAG 的几个关键问题聊明白。
面试官:”你说做过 RAG 系统,那多模态 RAG 和纯文本 RAG 的区别是什么?不是多了个图片处理这么简单吧?”
候选人:”嗯…核心区别就是多了图片和表格的处理吧?把图片用 OCR 转成文本,表格也转成文本,然后流程和纯文本 RAG 一样。”
正解:
多模态 RAG 的核心区别不在多了图片处理,而在于整条 pipeline 从解析、embedding、检索到生成都要重新设计。用纯文本 RAG 的思路硬套多模态,效果会很差。
纯文本 RAG 的流程很清晰:文本分块 → embedding → 向量检索 → LLM 生成。但一份真实的 PDF 文档——比如财报、论文、产品手册——里面有表格、图表、公式、流程图,这些内容的语义不在文字里,在视觉结构里。一张柱状图的数值趋势、一个表格的行列关系,OCR 转成纯文本后信息丢失严重。
多模态 RAG 有三条主流架构路线,选错了比调参数影响大得多:
路线一:解析式 Pipeline(OCR + 布局识别)
文档先过版式分析模型(如 LayoutLMv3、DiT),识别出文本块、表格区域、图片区域,再分别处理:文本走 OCR,表格走结构化提取(输出 HTML/Markdown),图片走 VLM 生成描述。最后所有内容转成文本做 embedding。
优点是成熟稳定、成本可控;缺点是模块多、error 会级联——任何一个环节出错都会传递到下游。代表工具:MinerU、Marker、Unstructured。
路线二:VLM 摘要式
文档按页切分,每页直接丢给 GPT-4o 或 Qwen-VL 生成文字摘要。检索时用摘要的 embedding 匹配,命中后把原始页面图片和摘要一起送给生成模型。
优点是流程简单;缺点是摘要可能丢信息,VLM 对密集表格的准确率不高,且每页都要调 VLM,成本高——按 GPT-4o 价格算,处理 1000 页 PDF 的摘要生成费用约 $3-5。
路线三:端到端视觉检索(ColPali 系列)
完全跳过 OCR。把文档页面当作图片,用 VLM(如 PaliGemma、Qwen2.5-VL)直接生成视觉 embedding,检索时用 late interaction 机制匹配 query 和文档 patch。
这条路线是最近两年最大的突破。ColPali 在 ViDoRe 基准上大幅超越了传统 OCR pipeline,尤其在信息图、图表密集的文档上优势明显。缺点是模型较大(3B-8B 参数),存储开销高,部分复杂场景仍需要文本辅助。
生产环境的主流做法是混合路线:用 ColPali 做检索(准确率高),用 OCR 提取的文本辅助生成(减少幻觉)。
要点速记
– 三条路线:解析式 Pipeline / VLM 摘要式 / 端到端视觉检索– 解析式稳定但 error 级联,VLM 摘要简单但成本高(~$3-5/千页)– ColPali 在 ViDoRe 基准上大幅超越 OCR pipeline– 生产主流:ColPali 检索 + OCR 文本辅助生成的混合方案
面试官:”你处理过百万级 PDF,那表格解析用的什么方案?准确率多少?”
候选人:”用的 LlamaParse,表格识别效果还可以,具体准确率没测过…”
(面试官内心 OS):百万级文档,表格准确率没测过?
正解:
表格是多模态 RAG 最容易翻车的环节——不是因为 OCR 识别不出来,而是因为表格的语义在行列结构里。结构一乱,内容全错。
为什么表格这么难?PDF 里的表格本质上是一堆坐标点和线条,不像 HTML 有 <table> 标签。解析器需要:1)检测表格区域边界,2)识别行列结构(包括合并单元格),3)提取每个单元格的内容,4)保持行列对应关系。任何一步出问题,整张表就废了。
最典型的灾难场景:跨页表格。一张表格从第 3 页延续到第 4 页,表头在第 3 页,数据在第 4 页。大多数解析器会把两页的表格当成两张独立的表,第 4 页的数据丢失了表头映射关系,检索时答非所问。
主流工具实测对比(基于 OmniDocBench 和 FinTabNet 评测):
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
几个关键的工程细节:
1. 表格输出格式选 HTML,别用 Markdown。 Markdown 表格不支持合并单元格,复杂表格会丢信息。HTML 的 <table> 结构保留了 rowspan/colspan,LLM 理解起来也更准确。实测 LLM 解析 HTML 表格的准确率比 Markdown 高 10-15%。
2. 跨页表格的处理策略。 MinerU 2.0 和 Marker 的最新版本支持 LLM 辅助跨页合并:把相邻页的表格片段丢给 LLM,让它判断是否属于同一张表,如果是就合并。这个策略能挽救 60-70% 的跨页表格。剩下的只能靠规则兜底——比如检测第二页是否缺少表头,如果缺少就向前查找。
3. 表格的 embedding 策略。 表格不适合直接做 embedding(结构化数据转成一段文本后语义损失大)。推荐两层索引:
python
# 第一层:表格摘要 → embedding(用于检索)summary = vlm.describe(table_html) # "2024年Q1-Q4营收对比表,Q4营收同比增长23%"summary_embedding = embed(summary)# 第二层:原始 HTML → 存储(用于生成)store.save(table_id, raw_html=table_html, summary=summary)
检索时匹配摘要 embedding,命中后把原始 HTML 传给 LLM 生成回答。这样既保证了检索准确率,又保留了完整的表格结构给生成模型。
4. 实体链接问题。 正文里经常出现”如表 3 所示”、”见下图”这类引用。如果分块时把表格和引用它的段落切开了,检索到的 chunk 里只有”如表 3 所示”,没有表 3 本身。解决方案:在解析阶段建立文本-表格/图片的链接关系,分块时把引用段落和被引用的表格绑定为同一个 chunk。
排查流程:表格解析不准时,按这个顺序查——
-
先看表格检测:区域有没有框对?→ 换检测模型或调阈值 -
再看结构识别:行列有没有错位?→ 检查合并单元格处理 -
最后看内容提取:文字有没有乱?→ OCR 引擎问题,换用 Qwen2.5-VL 做 OCR
要点速记
– 表格难点:行列结构识别、跨页合并、实体链接– 输出选 HTML 而非 Markdown,LLM 准确率高 10-15%– 表格 embedding 用两层索引:摘要检索 + 原始 HTML 生成– 跨页表格用 LLM 辅助合并,能挽救 60-70%– 排查顺序:检测 → 结构 → 内容
面试官:”刚才你说 OCR 有误差,那我直接把 PDF 页面截图丢给 GPT-4o 看,不就绕开 OCR 了?”
候选人:”对啊,现在视觉大模型这么强,直接看图肯定比 OCR 转文本再处理更准。”
正解:
这是最常见的误区:觉得 VLM 能力强就可以替代 OCR。实际情况是,纯 OCR 和纯 VLM 都有硬伤,生产环境必须混合使用——用视觉检索提升召回率,用 OCR 文本保障生成质量。
先看数据。ICCV 2025 的论文 OCR Hinders RAG 做了系统性评测(OHR-Bench),7 个领域、8561 篇文档、8498 个 QA 对。结论很扎心:
OCR 的问题:
OCR 引入的噪声分两类——语义噪声(识别错字、漏字)和格式噪声(表格结构错乱、列表变段落)。这两类噪声在 RAG 中的影响不同:
-
语义噪声对检索和生成都有害,所有检索器和 LLM 都扛不住 -
格式噪声的影响取决于模型能力,强模型(如 GPT-4o)对格式噪声有一定鲁棒性,弱模型直接崩
评测结论:即便用最好的 OCR 方案(Qwen2.5-VL-72B 做 OCR),RAG 的 F1-score 仍比使用真实文本低约 14%。在金融和学术领域这种表格密集的场景,差距更大,达到 20%+。而且没有任何一个 OCR 方案能在所有场景下表现好——不同的文档类型需要不同的 OCR 策略。
VLM 看图的问题:
- 幻觉
。GPT-4o 直接看表格截图时,会出现数字错误(把 23.5% 读成 25.3%)、遗漏行列、凭空编造不存在的数据。实测在密集财务表格上,纯视觉提取的准确率只有 72-78%,而 OCR + 结构化解析能到 85%+。 - 长文档灾难
。一份 100 页的 PDF,你不可能把 100 张截图全丢给 VLM。即使模型支持长上下文,多页输入时效果会急剧下降——关键信息被稀释(Lost-in-the-middle 效应在多图场景更严重),证据定位困难。 - 成本和延迟
。GPT-4o 处理一张图片约 $0.003-0.01(取决于分辨率),1000 页文档光图片输入就要 $3-10。加上生成 token 的费用,成本比纯文本 RAG 高 5-10 倍。延迟也是问题:每个 query 要把检索到的页面图片送给 VLM,响应时间从 1-2 秒涨到 5-8 秒。
生产环境的正确做法:混合架构
NVIDIA 和 Mixedbread 的实测数据表明,最优方案是把视觉检索和 OCR 文本生成结合起来:
Query ↓[视觉检索] ColPali/ColQwen2 → 召回最相关的页面(NDCG@5 比纯文本检索高 15-20%) ↓[OCR 文本提取] 对召回的页面做 OCR → 得到结构化文本 ↓[混合输入] 页面图片 + OCR 文本 → 一起送给 VLM 生成答案 ↓Answer这样做的好处:视觉检索不会漏掉图表中的信息(OCR 丢失的信息靠视觉补回来),OCR 文本给生成模型一个锚点(减少幻觉,准确率提升高达 24.5%)。
关键配置:
-
检索阶段用 ColPali 或 ColQwen2,不用 CLIP(后者对文档检索太弱) -
生成阶段把 OCR 文本放在 prompt 前面,图片放后面,让模型优先参考文本,视觉作为补充 -
如果预算有限不用 VLM 生成,退回纯文本 RAG 也行——但检索阶段一定要用视觉模型
(来源:OCR Hinders RAG, ICCV 2025;NVIDIA NeMo Retriever 技术博客;Mixedbread 研究)
要点速记
– 最好的 OCR 方案仍比真实文本低 14% F1-score(OHR-Bench)– VLM 看图有三个硬伤:幻觉(表格准确率 72-78%)、长文档失效、成本高 5-10 倍– 混合架构:ColPali 视觉检索 + OCR 文本辅助生成,准确率提升 24.5%– 生成阶段同时输入 OCR 文本 + 页面图片,效果最优
面试官:”多模态检索你用的什么模型?CLIP?”
候选人:”对,CLIP 可以把图片和文本映射到同一个向量空间,用来做多模态检索挺合适的。”
正解:
CLIP 做通用的图文匹配可以,做文档检索完全不够用。文档检索需要的是理解页面布局、表格结构、文字位置,CLIP 对这些几乎是盲的。
CLIP 的训练数据是自然图片 + 短文本描述(”一只猫坐在沙发上”),它学到的是图片级别的全局语义。但文档页面不是自然图片——一页 PDF 里可能有标题、正文、三个表格、一个图表,信息密度极高。CLIP 把整页压成一个 768 维向量,细粒度信息全丢了。
ViDoRe 基准测试的数据很直观:CLIP 类模型(Jina-CLIP、Nomic-vision)在文档检索上 NDCG@5 普遍在 40-55%,而 ColPali 达到 81.3%。在图表密集的子集上差距更大,CLIP 类甚至不到 35%。
ColPali 为什么强这么多?关键在 late interaction。
ColPali 不是把整页压成一个向量,而是为页面上的每个 patch(通常 32×32 像素的区块)生成一个独立的向量。一页文档大约产生 1024 个 patch embedding。查询时,query 的每个 token 分别和文档的每个 patch 做相似度计算,取最大值后求和——这就是 ColBERT 风格的 late interaction。
这种方式的好处是:query 里提到”Q4 营收”,模型会精确匹配到页面上表格里”Q4″那一行所在的 patch,而不是模糊匹配整个页面的全局语义。
当前 SOTA 模型和选型建议(基于 ViDoRe V3 基准):
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
存储和延迟的工程考量:
multi-vector 模型的精度高,但存储开销是个问题。ColPali 一页文档约 1024 个 embedding,每个 128 维 FP16,单页约 256KB。100 万页文档 = 256GB 纯 embedding 存储。
优化方案:
- 维度投影
:把 embedding 从 3584 维投影到 512 维,存储减少 87.5%,精度保留 96%(来源:NVIDIA Nemotron 论文) - Token Pooling
:用聚类算法合并冗余 patch(比如白色背景区域),pool factor=3 时 embedding 数量减少 67%,精度损失仅 ~2%(来源:Answer.AI ColBERT Pooling 研究) - 预算有限用 dense 模型
:Nomic-Embed-Multimodal-7B 每页只生成 1 个向量,存储低 1000 倍,但 NDCG 低约 5 个点
选型决策树:
你的文档有多少视觉元素?├─ 很少(纯文本为主)→ 纯文本 embedding(bge-m3 等)就够├─ 中等(偶尔有表格图表)→ dense 多模态模型(Nomic 7B)└─ 很多(财报/论文/手册)→ multi-vector 模型(ColQwen2 或 ColNomic) ├─ 文档量 < 10 万页 → 直接上,存储可控 └─ 文档量 > 100 万页 → 加维度投影 + token pooling(来源:ViDoRe V3 Leaderboard;Nomic AI 技术博客;NVIDIA Nemotron-ColEmbed 论文)
要点速记
– CLIP 做文档检索 NDCG@5 约 40-55%,ColPali 达到 81.3%– 核心差异:CLIP 全局单向量 vs ColPali 每 patch 一个向量 + late interaction– SOTA:Nemotron-ColEmbed-8B NDCG@10 = 63.4(ViDoRe V3 第一)– 存储优化:512 维投影省 87.5% 空间,精度保留 96%– 预算有限选 dense 模型(Nomic 7B),NDCG 低 ~5 个点但存储低 1000 倍
面试官:”假设你要从零搭一个多模态 RAG 系统,处理企业内部的各种 PDF 文档——合同、财报、技术手册、产品说明书。你会怎么设计架构?”
候选人:”我会先用 MinerU 解析 PDF,提取出文本、表格、图片,然后分别做 embedding 存到向量数据库,查询时做混合检索,最后用 GPT-4o 生成回答。”
正解:
方向对,但细节决定成败。生产级系统要考虑文档多样性、模块容错、成本控制,不是一条 pipeline 能搞定的。
先画架构全景图。一个经过验证的生产级多模态 RAG 系统分四层:
第一层:文档解析(Parsing Layer)
不要只用一个解析器。不同类型的文档,最优解析器不同:
yaml
parser_routing: financial_reports: docling academic_papers: mineru scanned_documents: marker general_documents: mineru fallback: qwen2.5-vl-72b
解析输出标准化为统一的中间格式(参考 RAG-Anything 的做法):每个元素包含 type(text/table/image/formula)、content(原始内容)、metadata(页码、位置坐标、引用关系)。
第二层:索引构建(Indexing Layer)
分三路并行处理,不同模态走不同的索引策略:
文本块 → 语义分块(chunk_size=512, overlap=64)→ bge-m3 embedding → 文本向量库表格 → HTML 保留 + VLM 摘要 → 摘要 embedding + 原始 HTML 存储 → 表格索引页面图像 → ColQwen2 multi-vector embedding → 视觉向量库关键参数:
-
文本分块:512 tokens,overlap 10-15%,按语义边界切 -
表格:不分块,每张表一个索引单元 -
图像:每页一个索引单元,ColQwen2 生成 ~1024 个 patch embedding
第三层:混合检索(Retrieval Layer)
三路检索并行 + 融合排序:
python
def hybrid_retrieve(query, top_k=10): text_results = text_index.search(query, top_k=20) table_results = table_index.search(query, top_k=10) visual_results = visual_index.search(query, top_k=10) all_results = reciprocal_rank_fusion( text_results, table_results, visual_results, weights=[0.3, 0.3, 0.4] ) reranked = reranker.rank(query, all_results[:20]) return reranked[:top_k]
这里视觉检索权重设高一些(0.4),因为很多问题涉及表格和图表,视觉检索的召回覆盖面更广。文本和表格各 0.3。
实测数据:纯文本检索的 NDCG@10 约 45-55%,加上视觉检索后提升到 65-75%,增幅约 20 个点。
第四层:生成(Generation Layer)
根据检索结果的模态组合,选择不同的生成策略:
python
def generate_answer(query, retrieved_chunks): has_visual = any(c.type in ['image', 'table'] for c in retrieved_chunks) if has_visual and budget_allows_vlm: prompt = build_multimodal_prompt(query, retrieved_chunks) return vlm.generate(prompt) else: prompt = build_text_prompt(query, retrieved_chunks) return llm.generate(prompt)
成本控制的关键技巧:
- 检索阶段用开源模型
:ColQwen2(3B)本地部署,单卡 A100 可跑,检索成本几乎为零 - 生成阶段分级
:简单问题用 GPT-4o-mini($0.15/M tokens),复杂问题才上 GPT-4o($2.5/M tokens) - 缓存策略
:相同 query 的结果缓存 24 小时,重复率高的企业场景能省 30-50% 调用费
排查清单(上线后出问题按这个顺序查):
-
答案和文档对不上 → 先检查检索结果是否正确 -
检索没问题但回答有误 → 检查生成 prompt 里的上下文是否完整 -
表格数据不准 → 检查解析阶段的表格 HTML 是否正确 -
图表信息遗漏 → 视觉检索权重是否太低
要点速记
– 解析层:按文档类型路由不同解析器,财报用 Docling,论文用 MinerU– 索引层:三路并行——文本 embedding + 表格摘要 + 页面视觉 embedding– 检索层:三路融合 + RRF 排序,视觉权重 0.4,加视觉后 NDCG 提升 ~20 个点– 生成层:分级策略,简单问题用 mini 模型省钱,复杂问题上 VLM– 检索用开源(ColQwen2 本地部署),生成分级 + 缓存,控制成本
这场面试暴露了一个典型问题:做多模态 RAG 的人很多,真正理解 OCR 为什么不够用、视觉检索和文本检索怎么配合 的人很少。
多模态 RAG 不是纯文本 RAG 加个图片处理模块。从解析到检索到生成,每一层的设计逻辑都不一样。架构选错了,调参数也救不回来。
建议:
-
跑一遍 ColPali 的 demo(HuggingFace 上有 cookbook),亲眼看看视觉检索和纯文本检索的差距 -
用 OHR-Bench 测一下自己的 OCR pipeline,量化 OCR 噪声对 RAG 效果的影响 -
不要迷信单一方案——混合架构虽然复杂,但它是当下效果最好的路线
—
很多人做 RAG 习惯了纯文本的思维,觉得加个 OCR 就算多模态了。
真实世界的文档不是只有文字。表格里的行列关系、柱状图里没有标注的数值趋势——这些信息 OCR 转出来的纯文本里根本看不到。
能把多模态 RAG 讲清楚的人不多,但讲得清楚的,通常对整个 RAG 系统的理解也不会差。
夜雨聆风
