资讯动态

构建能够读图的PDF RAG系统:图文统一检索与理解实践

发布时间:2026/10/18 12:07:28 来源:尧图企业网站定制
1. 整体设计与思路拆解先把这个项目到底要干什么说清楚。标题是“构建能够读图的基于PDF的RAG系统”拆开看三个关键词PDF、读图、RAG。很多人做RAG做到最后发现PDF里的文字全都能检索到但一遇到图表、截图、扫描件就傻了检索出来的上下文是残缺的大模型回答得莫名其妙。这个项目的核心目标就是让RAG系统不只“读字”还要“看图”。我需要先讲清楚一个很多人容易混淆的点什么是“能够读图”。在RAG链路里“读图”其实分成两个层次。第一个层次是提取也就是把PDF里的图像内容转换成系统能够索引和检索的形式第二个层次是理解也就是在生成回答时大模型能够基于图像内容给出正确推理。很多方案只做了第一层用OCR把图里的文字抠出来就算完事但遇到柱状图、折线图、流程图这种靠空间位置和形状传递信息的图像纯OCR根本无能为力。所以真正的读图RAG必须让图文信息同时进入检索和生成环节。这个项目适合谁来参考如果你正在做一个企业知识库、产品说明书问答系统、论文阅读助手或者任何以PDF为主要载体的垂直领域问答应用这篇内容应该能帮你省掉几周的试错时间。我会按照实际构建过程中踩过的坑、对比过的方案、最终跑通的配置顺序来讲尽量每一步都给到可以直接抄作业的细节。整体架构上我分成了四个模块PDF解析层、内容增强层、索引检索层、生成回答层。PDF解析层负责把PDF拆成文本块和图片块内容增强层负责给图片做OCR、做描述、做结构化处理索引检索层负责把图文向量统一存进向量库并提供混合检索能力生成回答层负责把检索结果组装成上下文交给大模型。这个分层不是一开始就定好的是做了三轮迭代之后自然长出来的后面我会说明每一轮为什么调整。安全方面先铺垫一句这个项目里所有数据处理和模型调用都在可控环境内完成不涉及任何外部敏感请求后面所有示例都基于虚构的“模拟项目X”来做说明。2. PDF解析文本和图像的分离策略2.1 技术选型对比不是所有PDF库都能干这活PDF解析是整个系统的地基。我试过不少方案先说结论文本型PDF用PyMuPDFfitz做主解析器扫描型PDF用OCR流水线兜底两套流程并行最后统一输出成结构化的块Chunk。为什么是PyMuPDF而不是pdfplumber或者PDFium因为PyMuPDF在“同时提取文本和图像”这件事上效率最高。pdfplumber对表格坐标的还原确实精细但它提取图像的能力很弱而且处理几百页的PDF时速度明显跟不上。PyMuPDF底层是MuPDFC库实现解析速度快还能直接拿到每个文本块和图像块的坐标信息这对后面做版面分析和块合并非常关键。这里有个必须注意的坑PyMuPDF的page.get_text()返回的文本是按PDF内部结构顺序排列的如果原文是分栏排版比如两栏的论文提取出来的文字会左右两栏交错在一起。我一开始没处理这个结果检索效果惨不忍睹——上下文被截断、语序颠倒模型回答前言不搭后语。解决办法是开启sortTrue参数让文本块按坐标排序同时用page.get_text(dict)拿块级信息按块坐标重新组织段落顺序。提取图像也远没有想象的那么简单。page.get_images(fullTrue)拿到的只是PDF内部的图片对象引用很多图片在页面里只出现一次但被平铺平铺显示还有些图片嵌在表单或者注释里面直接提取会漏掉。我后来是先用get_image_info()拿到每个图片在页面上的显示位置和尺寸再根据显示尺寸过滤掉那些小于20像素的小图标和装饰性元素只保留真正的内容图。import fitz def extract_pdf_blocks(pdf_path): doc fitz.open(pdf_path) blocks_all [] for page_no in range(len(doc)): page doc[page_no] # 提取文本块带坐标排序避免分栏错乱 text_dict page.get_text(dict, sortTrue) page_text_blocks [] for block in text_dict.get(blocks, []): if block[type] 0: # 文本块 text_content .join([span[text] for span in block[lines]]) bbox block[bbox] if len(text_content.strip()) 4: page_text_blocks.append({ type: text, content: text_content.strip(), bbox: list(bbox), page: page_no, }) # 提取图像块按显示区域筛选有效图片 image_info page.get_image_info(xrefsTrue) page_image_blocks [] for img in image_info: bbox img[bbox] width bbox[2] - bbox[0] height bbox[3] - bbox[1] if width 20 or height 20: continue page_image_blocks.append({ type: image, xref: img[xref], bbox: list(bbox), page: page_no, }) blocks_all.extend(page_text_blocks page_image_blocks) doc.close() return blocks_all2.2 扫描版PDF的兜底方案如果你的PDF是从纸质文档扫描出来的上面那套方案直接废掉因为PyMuPDF提取不到任何文本。这时候需要走OCR流水线。我选的是PaddleOCR OCRmyPDF的组合。OCRmyPDF的核心能力是把扫描版PDF先做图像矫正再嵌入一个OCR文本层这样PDF依然保持原有的页面版式但文本是可以选中、可以搜索的。做完这一步PyMuPDF再用常规方式提取文本就能拿到内容而且文本的坐标和原始图像是对齐的。PaddleOCR负责的是对提取出来的图片块做文字识别。这里有个细节不是所有图片都需要OCR。如果原图已经通过PDF内嵌文本层获得了对应文字比如图表下方的说明文字再做OCR就是重复劳动还会把检测到的零散文字块打碎反而污染检索效果。我的策略是文本型PDF里提取的图片只有在页面中显示尺寸大于某个阈值时才做OCR扫描型PDF则全量OCR。OCR流水线里最影响准确率的三个参数检测阈值det_db_thresh、识别方向分类use_angle_cls、版面分析use_layout_analyze。我第一次用默认参数跑发现一堆竖排文字和旋转90度的表头识别不全后来开启方向分类器并调低检测阈值才解决。# 第一步OCRmyPDF给扫描版PDF加文本层顺便做倾斜矫正 ocrmypdf input_scan.pdf output_searchable.pdf \ --deskew \ --rotate-pages \ --optimize 1 \ --language chi_simeng跑完OCRmyPDF之后再回到PyMuPDF提取文本和图像。如果扫描件质量太差比如有手写批注、印章遮挡、纸张底色很重我建议在提取之前先用OpenCV做一次预处理转灰度、做二值化、用形态学操作去掉噪点。这个步骤能显著提升识别率代价是要多花一点处理时间。3. 图像理解从“提取图片”到“读懂图片”3.1 三条技术路线的取舍图像提取出来了接下来面临的是整个项目最核心的决策如何让RAG系统理解图像我在实际项目中对比过三条路线各有优劣。第一条路线是OCR 文本拼接。也就是把图里的文字全部识别出来当成普通文本块加入知识库。这条路线实现最简单、成本最低但对图表、示意图几乎无效。举个例子一份产品说明书的架构图方框之间的连线关系、箭头指向这些语义信息OCR完全拿不到。检索时你只能找到“包含哪些组件”这种零散知识点但问“A组件和B组件之间是什么关系”系统就答不上来。第二条路线是图像描述生成。用多模态模型比如视觉-语言模型为每张图生成一段详细的文字描述然后把“图片 生成描述”一起存入向量库。检索时先搜到文字描述再带着原图去看。这条路线能覆盖相当大比例的图表理解需求成本比OCR高一些但比端到端多模态检索便宜得多而且描述文本可以直接复用现有的文本检索链路不用对架构做大改动。第三条路线是多模态向量检索。用一个多模态embedding模型把图像和文本映射到同一个向量空间用户查询的时候同时计算文本相似度和图像相似度。路线很优雅但工程复杂度高而且目前开源多模态embedding模型对中文图表的理解能力参差不齐调优成本不小。我最后的选择是第二条路线为主第一条路线兜底。也就是说每张提取出来的图片都做两件事——如果图里有清晰的文字区域先OCR提取文本不管有没有文字都调用多模态模型生成结构化描述。然后文本块、OCR文本、图像描述统一进入向量库。3.2 结构化描述模板让模型知道该看什么直接丢一张图给模型让它“描述一下”得到的往往是泛泛的“这是一张图表展示了数据变化趋势”这种废话。必须用结构化提示词约束输出格式让它分别描述图类型、坐标轴含义、数据要点、异常点、结论。我用的提示词模板大概长这样请分析这张图片严格按照以下结构输出 1. 图类型判断是柱状图/折线图/饼图/流程图/架构图/截图/照片/其他 2. 图表标题如果图中有标题原文输出 3. 坐标轴与图例说明X轴、Y轴代表的含义图例中的分类有哪些 4. 关键数据点列出图中标注的数值、极值、趋势转折点 5. 图中文字内容完整提取图中出现的所有文字包括标注、批注、按钮文字 6. 语义总结用一段话描述这张图传递的核心信息重点说明图中的关系、流程、因果加了这个模板之后检索质量提升非常明显。原因是模板强制模型输出“图中文字内容”和“语义总结”两类信息前者保证了关键词命中的可能性后者保证了语义检索能泛化到那些没有原文关键词的查询上。比如用户问“咱们这个系统处理异常订单的流程分几步”如果原图是流程图模型在语义总结里写了“步骤一……步骤二……”向量检索就能把这个描述块召回出来。3.3 OCR与视觉模型的分工这里我需要着重强调一个容易翻车的地方OCR和视觉模型的调用时机。有些图比如截图型图片核心信息全在文字里视觉模型强行看图写总结反而会编造内容。有些图比如趋势图只有坐标轴和少数标注文字OCR提取的内容非常稀疏必须靠视觉模型理解曲线和点的位置关系。我的做法是做一个轻量级的路由判断先看图片尺寸和像素占比如果大部分像素集中在文字区域优先OCR并降低视觉模型输出的权重如果图片是典型图表结构比如检测到坐标轴、网格线就跳过OCR直接调用视觉模型。实现上不需要很复杂用几个规则加OpenCV的轮廓检测就能做到七成以上的准确率。4. 检索与索引图文统一召回的技术细节4.1 向量化的关键决策文本和图像怎么进同一个库如果你决定用我推荐的方案也就是“图片生成描述文本再入库”那恭喜你向量化这一步不需要专门的视觉模型直接用文本embedding模型对描述文本向量化就行。但这里有个坑纯粹的文本描述向量化会丢失图像的视觉特征。什么意思呢比如用户上传一张和原图非常相似的图片想检索“有没有类似的图”纯文本向量就无能为力因为文本描述里不会包含像素级别的视觉信息。解决这个问题的办法是双通道索引文本通道照常走另外为每张图用多模态embedding模型生成一个图像向量存到独立的集合里。检索时文本查询同时走两个通道视觉通道用图搜图文本通道用语义搜图最后合并排序。我实际用的方案是文本向量用BGE或同类中文embedding模型图像向量用CLIP类模型。注意CLIP对中文的理解比较弱如果项目里绝大多数图是中文图表建议用面向中文优化的多模态模型或者干脆给图像加一版中文描述再走文本通道。视觉通道的效果是锦上添花不是保底。4.2 块大小、重叠率与元数据设计PDF的内容结构跟网页文本差异很大不能直接按字符数硬切。硬切的结果是把一个完整的表格或者一个流程图拆成两半两块都变得不可用。我的做法是按版面语义切块。处理文本时先识别标题和段落层级。比如“2.3 系统架构”是一个标题那么这一节下的所有文本直到下一个同级标题为止算作一个候选块。如果块太长超过1000字符再做二级拆分但拆分时尽量选择段落边界并保留前一个块末尾的150字符作为重叠区确保跨块信息的连贯性。这个重叠区是为了防止检索时两个块之间的边界上下文出现断层。处理图片时每张图单独成为一个块块内容由三部分组成图片的文本描述、OCR识别的文字、图片周边的上下文字段图片在PDF上一段落和下一段落中的标题与说明文字。周边文字很重要因为很多图表的核心结论写在正文里而不是画在图上。元数据是检索系统容易被忽视的一环。我给每个块都打上了这些标签来源PDF文件名、页码、块类型文本/图片/表格、版面坐标、所属章节路径、是否扫描件。元数据不只是为了显示来源更重要的是可以在检索时做过滤。比如说用户只关心第3章之后的内容或者只搜索图表这些条件都可以通过元数据快速圈定候选集合不用每次都全库扫向量。4.3 混合检索和重排序纯向量检索最大的问题是精确关键词匹配能力弱。PDF文档里大量存在人名、型号、编号、专业术语这类token在embedding空间里的语义邻居往往不是它自己。用户搜“S-2300型号”向量检索可能召回一堆“S系列产品”的描述但页面上真正写着“S-2300”的块反而排在了后面。所以我用了BM25向量检索的混合方案。BM25跑关键词匹配向量检索跑语义匹配两边各取Top N用RRFReciprocal Rank Fusion算法做分数融合把两个榜单里都排名靠前的结果抬上去。这个方案实测下来对包含精确型号的查询效果提升非常显著。拿到融合后的Top K结果后再加一层重排序模型。重排模型单独跑一遍相关性打分它能同时结合查询、标题、正文、元数据做精细判断效果比前面的粗排高一个档次。这里有个实际工程建议重排阶段只用几十个候选块不要太早让重排模型从头筛选全部块否则延迟和成本都扛不住。5. 生成回答让大模型真正“看到”图5.1 上下文组装的艺术检索结果拿到了接下来的问题是怎么把这些块组织成上下文喂给大模型。这块做不好前面所有工作全部白费。我发现一个常见的错误做法把检索回来的所有块按顺序拼接中间用分隔符隔开一股脑丢给模型。这样做的后果是模型面对一大坨信息分不清哪张图是主要证据哪段文字只是背景补充回答起来逻辑混乱甚至被无关块的噪声带偏。我的做法是按块类型分组并显式区分证据等级。组装上下文时先把检索结果分成三个组核心答案组直接命中查询关键词或高相关得分的块、补充背景组相关度稍低但与主题相关的块、图表证据组图片块及其描述。然后在提示词中明确告诉模型哪些内容是高置信度证据哪些是辅助参考哪些需要模型自行判断是否可信。举个例子用户问“去年第四季度营收为什么下降”检索结果里有这样几个块一个块是折线图的描述显示Q4确实出现明显下滑一个块是正文里供应链问题的分析段落还有一个块是某个产品线宣传文案里面提到了“营收创新高”。如果不分组直接拼在一起模型可能会被第三个块干扰生成自相矛盾的答案。分组并标注证据等级后模型能够识别出前两个块是主要证据第三个块是无关噪声。5.2 图文联合提示词设计上下文准备好之后提示词决定了模型能不能把“图里的信息”转化成“正确的回答”。我在这上面迭代了很多版最后稳定下来的模板包含这么几层逻辑第一层告诉模型当前对话场景明确回答范围只限于提供的上下文避免幻觉外溢。第二层要求模型先识别问题涉及的信息类型——是数值比较、趋势分析、流程描述还是定义解释——然后优先从对应类型的块中提取证据。第三层强制要求输出部分列出“依据来源”标明引用的是文本块还是图片描述块。这一步表面上是做引用追溯实际上能显著抑制模型胡编乱造因为一旦要求它给依据它编造时就会犹豫。特别要处理的是图文交叉验证的场景。很多时候表格里的数值和正文的叙述是相互印证的比如表格显示某月销售额120万正文写“该月销售创下新高”。用户如果只问“那个月卖了多少”模型可以从表格得到数字但用户如果问“业绩怎么样”模型就需要同时看两个块才能给出结论。所以提示词里我加了一条如果某个结论同时有文字和图表支撑优先采用图表中的数值作为事实依据文字用于解释原因和背景。5.3 多轮对话中的图像记忆RAG系统不能只在单轮问答里表现好多轮追问才是真实使用场景。比如用户先问“这个系统的主要模块有哪些”得到答案后接着问“第三个模块的具体实现原理”此时系统必须记住上一轮提到了哪张架构图才能在下一轮带着那张图的上下文进入检索。这块我做得比较朴素保留最近两轮检索到的块ID列表在新一轮检索时把它们作为上下文的一部分传给模型。代价是上下文会变长但换来的是对话连贯性明显提升。如果项目对延迟敏感可以把“上下文中是否包含图片块”作为一个缓存开关只有上一轮出现了图片块才做这个操作。6. 你会踩到的坑问题排查与调优实录6.1 图表太多导致检索结果被“图片描述”淹没我第一次跑通全流程时发现一个奇怪现象检索结果里前几名全是图片描述块正文文本块很难挤进前排。分析之后才发现图片描述块是经过多模态模型加工过的语言风格更规范、信息密度更高和查询的向量相似度天然偏高反而压倒了原汁原味的正文片段。解决思路是给不同块类型的向量做加权处理。我用的方案检索时对候选集按块类型分别设置分数权重文本块权重1.0图片描述块权重0.85OCR纯文本块权重0.9。这个权重的具体数值需要根据自己的知识库调但思路是明确的——不能让配图文字喧宾夺主正文里精确的表述应该优先于对图片的自然语言转述。6.2 扫描版PDF的OCR错别字污染语义检索OCR识别不是100%准确的尤其中文里形近字特别多。比如“干燥”被识别成“干澡”“温度”变成“湿圌度”。这些错误在关键词检索里问题不大因为BM25匹配的是字面错字会导致漏配但在向量检索里问题更隐蔽embedding模型会把错别字和上下文混在一起编码导致整个块的语义向错误方向漂移而且你很难从检索结果中觉察到是哪个字错了。我的应对方案是OCR置信度过滤 人工抽检。PaddleOCR每个识别结果都会返回置信度分数我把置信度低于0.9的结果单独存到一个“低置信度文本池”不参与向量索引但保留原文以便后续人工修正。另外每隔一段时间抽几页扫描件把OCR文本和原图对比检查积累一个常见易错词表在OCR后处理阶段做词表替换。6.3 图片块与正文块的坐标关联失效PDF的版面解析有个隐蔽问题图片在页面上的坐标和它在阅读顺序里的位置不一定对应。我遇到过的情况是某页PPT版的PDF图片先渲染在页面顶部但正文里提到这张图的段落却在下一页。如果只按坐标就近合并图片会和上一页的文字错误绑定。后来我改成文本引用匹配先提取全文所有“如下图”“如图X所示”“见下页图表”这类引用短语再根据图号把图片块和引用它的文本块关联起来。如果找不到引用短语才回退到坐标就近策略。这么做需要额外处理中英文两种引用模式但换来的图文关联准确率提升是非常值的。6.4 调用成本与延迟的平衡多模态模型不是免费的。每张图都调用一次高精度模型知识库里有五千张图跑一遍索引就要花不少钱而且用户问题只要触发图搜延迟也会高不少。我做了三层优化第一层图像描述生成改用成本更低的轻量模型只在重排阶段使用高精度模型。第二层把图像按哈希去重重复出现的插图在手册类PDF里很常见只做一次描述后续直接复用结果。第三层给检索链路加了一个“图像必要性判断”前置步骤先用文本通道跑一遍检索如果文本块的得分已经很高且答案置信度足够就不触发图像通道只有文本证据不足时才走图搜。实测下来超过40%的查询可以完全跳过图像通道成本降了差不多一半而最终答案质量几乎没有下降。7. 一点实践经验与扩展建议这个项目从最初只能检索文本的简陋原型到最终能理解图表、支持多轮追问的完整系统中间迭代了好几轮。我最大的体会是做读图RAG重点不在模型选得多先进而在解析和链路设计得够不够细。PDF版面解析的鲁棒性、图片理解的规范化输出、检索阶段的混合融合、生成阶段的证据组织这四个环节任何一个有短板系统整体效果都会被拉下来。模型只负责把前面喂给它的信息用好喂进去的是残缺信息再强的模型也输出不了正确答案。最后分享一个小技巧在做图文联合检索的评估时不要只看最终答案的对错要额外记录“检索结果中图片块的占比”这个指标。如果问答正确但检索结果根本没有图说明模型是在靠文本上下文猜对的下次换一个问法可能就露馅。这个指标能帮你提前发现图文关联失效的问题而不是等用户投诉了才去排查。如果你准备在现有RAG系统上做改造我的建议是从小步开始先选一个图比较多的PDF文档作为测试集把解析、向量化、检索、生成全部跑通再逐步扩展。密度高的文档比如产品手册、行业报告最能暴露问题也最适合用来验证系统能力边界。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑