如果你已经在搭 RAG 知识库,大概率已经踩过这样的坑:喂进去一堆 PDF,最后模型答不上来;或者明明文档里写了,检索却捞不到。问题往往不出在模型,而出现在数据导入那一步。RAG 的链路是“导入 → 分块 → 向量化 → 检索 → 生成”,这个系列第二篇就聚焦最让人头疼的图文和 PDF 解析,把 OCR、多模态大模型、九种 PDF 工具选型一次性讲透。这篇文章适合正在做知识库、文档问答、企业资料库的开发者,也适合刚接触 RAG、想搞明白“为什么我的知识库效果这么差”的人。看完之后,你至少能判断手头哪些 PDF 该走 OCR、哪些该走版面解析、哪些直接提取文本就行,并且能照着实操案例把一套解析流水线跑起来。
1. 为什么图文和 PDF 是 RAG 导入中最容易翻车的环节
1.1 解析质量直接决定检索上限
RAG 的效果瓶颈,十有八九不在生成阶段,而在检索阶段。检索靠的是文本向量与用户问题的语义匹配,如果解析出来的文本本身就缺字、乱序、把表格拍平成一行,那向量化之后的内容就是一团噪声。模型再强,也做不到“从没检索到的东西里找答案”。
我见过很多团队在 embedding 模型和向量数据库上反复调参,但打开知识库一看,源文件是扫描版 PDF,解析出来全是乱码。这种场景下再换什么向量模型、再调什么 chunk 大小都没用,因为源头数据就是坏的。解析这一步,决定了整个 RAG 系统的信息天花板。天花板有多高,答案就最多能好到哪。
另一个容易忽略的点是分块质量。分块往往依赖解析结果里的标题层级、段落边界、表格结构。如果 PDF 解析只给出一大坨连续文本,分块就只能硬切,把完整的上下文切断在 chunk 之间。等检索的时候,相关片段被拆成两半,保证都没法召回完整内容。
1.2 PDF 的“伪结构化”本质
很多人以为 PDF 是“文档格式”,所以应该能直接读出段落、标题和表格。其实 PDF 的内部结构根本不是按语义组织的,它只是一堆绘制指令:在坐标 (x1, y1) 处绘制字体大小为 14 的字符串“某某公司”,在 (x2, y2) 处画一条线段。段落之间的换行、缩进、页码关系,PDF 格式本身并不关心,解析器全靠猜测还原。
这也是为什么同一份 PDF,用不同解析库会得到不同结果。有的库保留了坐标,有的库按内容流顺序拼接,有的库遇到字体缺失就把整行丢掉。更麻烦的是,如果 PDF 本身是通过扫描生成的,那每一页本质上就是一张大图片,里面根本不存在可提取的文字对象。这时常规的文本提取几乎是无效的,必须借助 OCR 甚至多模态大模型来解决。
1.3 表格、多栏与图片三大痛点
PDF 解析里最典型的三类硬问题,几乎每个 RAG 项目都会碰到。表格问题排在第一位:表格在视觉上是一个二维结构,行列关系靠坐标对齐,但转成纯文本后就变成一串串数字和表头混杂的线性文本。模型要回答“去年第三季度销售额是多少”,如果表格没有还原成 Markdown 或结构化数据,检索根本找不准“第三季度”和“销售额”的对应关系。
多栏排版是第二个坑。很多技术手册、学术论文、政府文件都是双栏甚至三栏排版。解析器如果按页面内容流顺序读取,会先读完左边栏上半部分,再接右边栏上半部分,读出来像是把两篇文字交织在一起。人眼一眼看出问题,但向量化之后,语义已经错乱。
第三类是图片。PDF 里嵌入了流程图、产品照片、盖章扫描件、数据图表,这些内容在传统解析流程里会被直接丢弃,或者只保留一个图片对象。但很多时候,问题的答案恰恰藏在图片里。比如一张架构图、一组统计柱状图,如果 RAG 压根没把图片内容变成文本或描述,那这个知识库从一开始就少了一大块信息。
2. OCR 方案选型:从 Tesseract 到 PaddleOCR
2.1 什么场景必须走 OCR
先判断清楚一个原则:OCR 不是默认选择,而是兜底方案。如果 PDF 本身带文本层,用文本提取工具更快、更准、更便宜。需要走 OCR 的,基本是三类情况:一是扫描版 PDF,整个文档就是图片集合,完全没有文本层;二是拍照生成的图片型文件,比如手机拍的合同、白板、票据;三是文本层存在但质量极差,比如字体嵌入异常导致提取乱码,或者文档层叠覆盖导致文字粘连。
实际项目里,很多 PDF 是“混合型”的,前面几页是电子版生成的文本层,后面几页是扫描附件。所以第一步永远是抽检每一页是否包含文本层,不要整本文件一刀切。用 PyMuPDF 每页读一遍字符数,几行代码就能把“文本页”和“图片页”区分开,然后再决定哪些页走 OCR。
2.2 本地 OCR 与云端 OCR 怎么选
OCR 方案大致分本地开源和云端 API 两类。很多人上来就纠结准确率,其实先要想清楚自己的约束:文档是否涉密?处理量有多大?运行环境有没有 GPU?是否需要私有化部署?
本地方案最大的优势是数据不出内网,处理大批量文档没有按次计费的压力,做二次开发和流水线集成也方便。代价是部署和调优需要自己扛,尤其是中文识别、复杂版面、低分辨率扫描件,默认模型未必够用。云端方案的优势是开箱即用,模型经过大规模样本打磨,对印刷体中文、英文混排、表格识别通常更稳,劣势是成本随调用量线性上涨,而且数据要传到第三方服务,很多企业内部文档场景直接排除。
从实际效果来看,中文场景下 PaddleOCR 是本地方案里综合表现比较好的选择,识别速度、中文覆盖率、表格支持都做得不错。Tesseract 是历史最悠久的老牌开源 OCR,胜在轻量和生态成熟,但中文识别和复杂版面表现一般,适合场景简单、语言单一的场景。云端 API 里百度 OCR、腾讯 OCR、阿里云 OCR 都各有特色,如果要做合同关键字段抽取,这类服务还提供了模板匹配能力,但那是另一个话题了。
2.3 Tesseract:轻量但中文排版有硬伤
Tesseract 的优点是部署极简,安装引擎和语言包就能用,Python 里通过 pytesseract 调它也很方便。但我要先说它的短板:对中文的分页、横排竖排、繁体简体混排、表格线干扰都比较敏感。做做简单截图、扫描清晰的书页可以,遇到盖章遮挡、低对比度票据,准确率下降很厉害。
如果决定用 Tesseract,预处理环节不能省。原图直接丢进去和先做灰度、二值化、降噪再识别,效果差距可能超过二十分。通常建议至少保持 300 DPI 的扫描分辨率,再做一次轻度锐化和二值化。语言包方面,中文简体需要用chi_sim,识别前必须确认语言包已安装,否则默认英文模型会把中文全识别成乱码。
2.4 PaddleOCR:中文场景下的本地首选
PaddleOCR 是百度开源的 OCR 工具链,这几个版本迭代下来,中文识别能力已经非常能打,而且不只做文字识别,还能输出文本框坐标、置信度,支持方向分类、版面分析和表格结构识别。对于 RAG 场景,最有价值的一点是——它能导出每个文字框的坐标。这意味着我们可以按坐标重新组织阅读顺序,而不是接受引擎默认的拼接顺序。
安装和调用都很直接,Python 环境下pip install paddleocr,然后初始化一个管道:
from paddleocr import PaddleOCR ocr = PaddleOCR(use_angle_cls=True, lang='ch') result = ocr.ocr("scan_page.png", cls=True) for line in result[0]: box, (text, score) = line[0], line[1] print(box, text, score)输出里的 box 是四个角点的坐标,有了坐标就能做很多事:判断文字块属于左栏还是右栏、过滤掉页眉页脚、按从上到下从左到右的顺序重新排序文本。这些能力对多栏 PDF 尤其重要,也是真正能提升 RAG 检索质量的关键操作。
PaddleOCR 的模型文件首次运行时会自动下载,离线环境需要预先下载模型包再手动指定路径。另外它的表格识别能力可以把图片表格转成 HTML 结构,配合 RAG 的 Markdown 分块非常顺手。
2.5 OCR 注意事项:别把识别结果直接当检索文本
OCR 识别完之后,人们最容易犯的错是直接把识别文本塞进向量库。这里有两个坑。第一,OCR 会输出大量识别错误,比如“0”和“O”、“日”和“目”在高分辨率不足时很可能混淆,这些噪声会让向量语义偏移。第二,OCR 结果没有标题和段落层级,直接切 chunk 会把一个章节硬切成好几段。
正确做法是识别完成后,先做文本拼接和去噪,再结合关键词或规则补一遍校错。比如数字编号、邮箱、网址、金额这类模式明显的文本,可以用正则兜底修正。更进一步,把 OCR 坐标信息用于版面重建,恢复标题层级和阅读顺序,然后再进入分块流程。这些步骤看着琐碎,但对最终检索质量的提升是立竿见影的。
3. 多模态大模型:从“识别文字”到“理解版面”
3.1 大模型解决的是 OCR 解决不了的问题
OCR 解决的是“把图片里的字变成文本”,但 PDF 里很多问题是“能看见字,却不理解版面结构”。比如一份研究报告,标题在哪、摘要从哪开始、哪句话属于图表注释、表格跨页后怎么续接,这些都需要对版面有整体理解,单靠 OCR 提取坐标很难做对。
多模态大模型在这条路上补上了关键一环。它能直接输入整页 PDF 渲染出来的图片,输出结构化的 Markdown,标题、段落、表格、代码块、图片位置都能一次性还原。把多模态模型当作一个“版面理解引擎”来用,而不是一个超级 OCR,是正确姿势。典型的做法是把 PDF 页转成 PNG,然后交给多模态模型,让它按需求输出特定格式,比如:
请把这张 PDF 页面转换成规范的 Markdown 格式: 1. 保留原文标题层级 2. 表格输出为 Markdown 表格 3. 图片用描述占位符表示,说明图片内容 4. 保持阅读顺序实测下来,对于版式复杂或频繁变化的文档,这种方式的还原度远远超过传统文本提取和 OCR 拼接。尤其适合产品手册、年度报告、行业白皮书这类页面美观但结构松散的材料。
3.2 多模态模型在 RAG 中的三种用法
第一种用法叫整页转 Markdown,是最常见的。把 PDF 页面渲染成图片,交给多模态模型生成 Markdown,再进入常规分块、向量化流程。优点是效果稳定,缺点是慢、贵,大批量处理成本偏高。
第二种用法是只做表格和图表理解。用工具先把页面里的表格区域裁切出来,只把这些图片交给多模态模型,让它转成表格结构或文字描述。相比整页调用,成本能省掉一大截,因为普通文本页完全不需要走大模型。
第三种用法是图片描述入库。PDF 里的架构图、示意图、产品照片,要么用多模态模型生成一句描述文本,和图片一起入库;要么只存描述文本,检索时命中描述,再在回答阶段把图片作为附件补充。这样处理之后,图片信息不再游离于检索体系之外,而是真正可以被语义检索命中。
3.3 图片到底能不能存进 RAG 知识库
这个问题的答案是:能,但不是直接把图片塞进向量库。常见做法是图片先经过视觉模型生成一段结构化描述,然后以“图片描述”作为文本内容进行向量化。查询时召回的是这段描述,追根溯源到图片文件本身。也可以用多模态 embedding 模型直接把图片和文字映射到同一个向量空间,但这需要专门的图文向量模型,普通文本 embedding 做不到。
纯文本生成模型只能读取文本上下文,所以回答阶段再把图片喂给多模态大模型才有意义。也就是说,图片进 RAG 的关键不是“存”,而是“描述”和“召回后的再理解”。如果项目里图片很多,我建议至少给每张图生成一段包含物件、场景、文字要素、关键关系的描述,让检索能先找到它。
3.4 成本与质量平衡的实操策略
多模态大模型好用,但不能无脑全量调用。我在项目里通常做一个简单的版面判断:先抽检 PDF 页面,文本提取质量高、版面规整的走传统工具;文本提取乱码、表格复杂、图片占比高的页面才交给多模态模型。粗算一笔账,一百页文档里可能只有二十页真需要多模态,剩下的都可以用免费开源工具解决,成本能差出五倍以上。
另外可以搭配“解析结果缓存”。PDF 解析一次之后,把生成好的 Markdown 文本持久化保存,下次导入直接读缓存。尤其要做的,是给每个 PDF 名称加一个哈希指纹,源文件内容不变就用缓存,避免同一批文档反复解析产生不必要的费时费力。整套链路跑顺之后,再贵的模型也只是在关键节点上用,而不是全程烧钱。
4. 九种 PDF 工具横评与选型
4.1 一份按场景分类的九种工具对照表
市面上 PDF 解析工具很多,真正常用且值得在 RAG 流程里试的,我梳理下来大概九种。把它们分成四组:通用文本提取、表格专项、深度学习版面解析、框架内置加载器,最后补一组桌面端兜底方案。先看总表:
| 工具 | 类型 | 优势 | 明显短板 | 上手难度 |
|---|---|---|---|---|
| PyMuPDF (fitz) | 文本提取 | 速度快、同时支持文本和图片坐标 | 表格结构还原弱 | 低 |
| pdfminer.six | 文本提取 | 布局分析细致、坐标信息全 | 速度偏慢、API 较底层 | 中 |
| PyPDF2 / pypdf | 文本提取 | 轻量、PDF 元数据和页面操作方便 | 复杂版面提取质量一般 | 低 |
| pdfplumber | 表格专项 | 表格抽取能力突出,能按格子读取 | 大 PDF 内存占用较高 | 低 |
| Camelot | 表格专项 | 处理规则表格的精度很高 | 对无框线表格不稳定 | 中 |
| Unstructured | 版面解析 | 自动识别标题、段落、表格、图片 | 自定义细粒度控制较弱 | 中 |
| Marker | 深度解析 | 基于深度学习还原 Markdown 效果好 | GPU 依赖、模型较重 | 中高 |
| LangChain PyPDFLoader | 框架内置 | 与 LangChain 生态无缝对接 | 底层仍是 PyMuPDF 等 | 低 |
| LlamaIndex PDFReader | 框架内置 | 节点化输出方便分块 | 同样受上层解析库限制 | 低 |
商用桌面端如 Adobe Acrobat、福昕编辑器、ABBYY 也值得提一嘴。它们能处理一些开源工具搞不定的复杂文档,还提供了 OCR 语言包安装界面,遇到个别顽固 PDF 可以作为最后的兜底。只是这类软件不适合批量流水线,更适合手工处理少量高价值文件。
4.2 通用提取组:PyMuPDF、pdfminer.six、PyPDF2
PyMuPDF 是 RAG 文本提取的主力。它的优点是快,处理几百页 PDF 通常只需几秒,而且能拿到文本块的位置信息。对于电子版 PDF,直接提取文本块、按坐标排序,得到的基础文本质量已经不错。
import fitz doc = fitz.open("sample.pdf") for page_num, page in enumerate(doc): blocks = page.get_text("blocks") for b in blocks: # b: (x0, y0, x1, y1, text, block_no, block_type) print(page_num, b[0], b[1], b[4])这段代码可以完成两件事:获取每页的文本块坐标,并判断页面是否包含文本层。如果 page.get_text() 返回字符串长度接近零,基本可以判定是扫描页。项目里我习惯先用 PyMuPDF 做“页面体检”,然后决定后续流程走哪条路。
pdfminer.six 的优势是布局分析更细,能输出字符级坐标和段落边界,适合需要精细控制阅读顺序的场景。缺点是速度偏慢,API 也更底层。PyPDF2 / pypdf 主要用在使用粒度轻、只依赖一个库的场景,它能做合并拆分、元数据读取、简单文本提取,但不适合当作 RAG 解析的主力。三种工具不冲突,反而是配合关系:PyMuPDF 跑批量体检,pdfminer 做精细版面,pypdf 做文件操作。
4.3 表格专项组:pdfplumber 与 Camelot 的实测选择
表格是 PDF 解析的重灾区,所以我专门把 pdfplumber 和 Camelot 单独拎出来说。pdfplumber 的extract_table方法对绘制了明显表格线的文档非常有效,输出是行列表结构,可以很方便地转成 DataFrame 或 Markdown。
import pdfplumber with pdfplumber.open("table.pdf") as pdf: first_page = pdf.pages[0] table = first_page.extract_table() for row in table: print(row)pdfplumber 的优势在于表格区域自动检测和可视化调试,能画出表格框帮助你判断提取是否准确。小缺点是处理超大 PDF 时内存占用偏高,最好按页处理并释放句柄。Camelot 对规则矩阵型表格的提取精度更高,尤其是带复杂合并单元格的表格,但它对无边框表格非常依赖视觉猜测,准确率会下降。
这类表格提取完之后,建议统一转成 Markdown 表格再进入向量库。Markdown 表格在语义上保留了行和列的关系,检索时更容易命中“表头 + 关键字段”的组合,也方便生成模型直接引用。
4.4 深度学习解析组:Unstructured 与 Marker
Unstructured 是 RAG 生态里热度很高的解析库,它做得最出色的地方是把“文件类型差异”对上层隐藏,无论输入 PDF、Word、图片还是 HTML,都能输出统一风格的文档元素列表,元素类型包括标题、正文、表格、图片等。LangChain 社区很多文档加载器底层就在用 Unstructured 的解析逻辑。
from unstructured.partition.pdf import partition_pdf elements = partition_pdf("document.pdf", strategy="hi_res") for el in elements: print(el.category, el.text)strategy="hi_res"会调用较重的模型做高分辨率解析,适合复杂版面;简单文档用"auto"或"fast"就能满足要求。Marker 是另一条技术路线,它用深度学习模型把整页还原成 Markdown,包含标题层级、表格、图片描述和阅读顺序,输出质量相当高,但模型体积大、需要 GPU 才能跑得舒服。如果服务器资源充足,且文档类型集中、页面美观,Marker 的效果很惊艳。
4.5 框架内置加载器与桌面兜底
如果你已经在用 LangChain 或 LlamaIndex,不要重复造轮子。LangChain 的PyPDFLoader、UnstructuredPDFLoader和 LlamaIndex 的PDFReader都帮你省掉了调用底层的功夫,输出直接是文档对象列表,能衔接后续的分块器。
from langchain_community.document_loaders import PyPDFLoader loader = PyPDFLoader("sample.pdf") docs = loader.load() for doc in docs: print(doc.page_content)框架加载器的问题是适配性取决于底层解析库,复杂版面照样会崩,所以它们适合“快速搭原型”阶段。项目上线前,还是要根据文件特征定制解析链。桌面端兜底工具 Adobe Acrobat、福昕编辑器则适合在自动化之外,遇到个别无法搞定、但又不允许丢失内容的文件时人工导出一次。福昕现在很多版本自带中文 OCR 语言包,但要注意语言包安装后需要在设置里手动切换,否则默认还是英文识别。
5. 一个可复现的实操案例:把扫描版带表格的 PDF 变成 RAG 可用的数据
5.1 场景准备:先看文件再看工具
这个案例我们做一份“供应商资质审核手册”,一共 42 页。前 20 页是电子版生成的规范条款,后 22 页是扫描归档的审核表单、盖章附件和一张统计表。目标是把它导入 RAG,让用户能问“供应商需要提交哪些材料”“2023 年合格率为多少”这类问题。
第一步不是选工具,而是摸清文件构成。我用 PyMuPDF 跑了一个逐页检测,判断哪些页有文本层、哪些页是纯图片;同时对页码和标题做了定位。只有搞清楚“哪一页是什么问题”,后续选 OCR、选表格工具、选多模态模型才有依据。
5.2 检测文本层并区分页面类型
检测逻辑很简单:对每一页调用page.get_text(),如果返回的文本长度低于 20 个字符,基本判定为扫描页或图片页。文本页进入常规提取流程,图片页进入 OCR 流程。如果有页面文本存在但版面错乱,再进入多模态修复流程。
import fitz doc = fitz.open("vendor_manual.pdf") scan_pages = [] text_pages = [] for i, page in enumerate(doc): text = page.get_text().strip() if len(text) < 20: scan_pages.append(i + 1) else: text_pages.append(i + 1) print("文本页:", text_pages) print("扫描页:", scan_pages)实际检测下来,扫描页主要集中在第 21 至 42 页。这部分先不要着急整页 OCR,我观察了一下,扫描页里有一页是“合格率汇总表”,还带盖章和手写边缘;其他页基本都是打印体文字表单,OCR 能处理。表格页计划单独用表格工具处理。
5.3 扫描页 OCR:PaddleOCR 的坐标重组
对第 21 至 42 页,我写了一个轻量脚本调用 PaddleOCR。关键不是识别文字,而是拿到每个文本框的坐标,按从上到下、从左到右的顺序重组文本。因为扫描页里包含了表单,表单的多行条目如果按默认输出顺序拼接,很可能前后颠倒。
import os from paddleocr import PaddleOCR ocr = PaddleOCR(use_angle_cls=True, lang='ch') def ocr_page(page_img_path): result = ocr.ocr(page_img_path, cls=True) if not result or result[0] is None: return "" lines = [] for item in result[0]: box = item[0] text = item[1][0] y_center = sum(point[1] for point in box) / 4 x_center = sum(point[0] for point in box) / 4 lines.append((y_center, x_center, text)) lines.sort(key=lambda t: (t[0] // 10, t[1])) return "\n".join(line[2] for line in lines)这里的排序技巧是按 y 坐标先分行,再按 x 坐标从左到右排序。对于表单类页面,一行的内容必须保持同一水平线,// 10是为了容忍轻微歪斜,把相近的行高聚拢起来。没有这一步,OCR 输出的“供应商名称”“联系人”“电话”会被打散成完全无关的顺序。
5.4 表格页提取:pdfplumber 还原结构
合格率汇总表那页,虽然属于扫描页,但我没有继续用 PaddleOCR 的表格识别,而是把它单独切出来,用 pdfplumber 尝试过一次。pdfplumber 在扫描图上若没有文本层,直接extract_table()是拿不到内容的,所以实际流程是用 OCR 识别文字后重建表格,或者用 PaddleOCR 的表格识别能力转 HTML。
from paddleocr import PPStructure engine = PPStructure(lang='ch') result = engine("summary_table.png") for item in result: if item['type'] == 'table': html_code = item['res']['html'] print(html_code)PPStructure 返回的 HTML 表格,再经过一个简单脚本转成 Markdown,方便入库。这一步很多人会忽略,以为 OCR 拿到文字就完事了。其实表头、行、列的关系才是检索命中的关键,同样是“合格率 87%”,如果摘成了“合格率 8 7%”或“合格率 8.7%”,问题就大了。
5.5 组装版面并分块入库
所有页面的文本拿到之后,进入组装环节。我的做法是为每一页生成一段相对完整的 Markdown,页面间用标题层级串起来;扫描页 OCR 出的文字粘成一个段落块,表格页粘成表格块,图片页如果有架构图则用多模态模型生成图片描述,贴在图片占位符位置。
分块策略采用按标题 + 段落边界的混合方式。匹配到一级标题时开新 chunk,段落超过 800 字时强制切分,表格作为独立 chunk 保存。这样做的原因很简单:检索单元应该是一个完整的语义单元,而不是纯粹按字数硬切。硬切最容易切到表格中间,让“表头”和“数据行”分到两个向量里,查询时基本召回失败。
最后一步是向量化入库。我用常规的 embedding 模型把 chunk 文本向量化,写入向量数据库,并在元数据里标上来源页码、文档名、解析方式,方便排查问题。入库后用几个测试问题做召回验证,比如“提交材料清单包括哪些”,如果召回结果里有对应的清单页面,说明解析链路基本打通。
5.6 验证检索效果并沉淀数据资产
验证时不光看召回率,还要看“无效召回”比例。有个常见的问题是 OCR 把页眉页脚、页码也识别进去了,用户检索“第三季度报告”,结果召回的是每一页底部的“第 X 页”字样,这种噪声会大幅拉低回答质量。所以我在组装环节会加一层过滤:坐标落在页眉页脚区域的文本块直接丢弃,页码、页脚、水印也按规则剔除。
跑完整个流程后,输出的 Markdown 文件本身也是一份很有价值的中间产物。类似文档以后再次入知识库,可以直接复用这份文本,不必重复跑 OCR 和大模型。我把解析结果统一存到一个parsed_docs/目录里,每个源 PDF 对应一个同名 .md 文件,这就是后续 RAG 流水线的稳定入口。
6. 避坑清单与效果反思
6.1 常见问题速查表
这里整理一份我在实际项目中反复遇到的典型问题,按现象、原因、解决方案三列排列:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 扫描件提取出乱码 | PDF 无文本层,直接走了文本提取 | 先用 PyMuPDF 做页面体检,识别扫描页再走 OCR |
| 表格内容在检索中答不准 | 表格被拍平成线性文本 | 表格专项提取,转成 Markdown 或 DataFrame 再入库 |
| 双栏文档检索语义错乱 | 读取顺序未按阅读顺序重组 | 用坐标排序,按列分割后按左到右、上到下读取 |
| 多次导入反复消耗模型费用 | 缺少解析缓存 | 对 PDF 做哈希指纹,结果落盘复用 |
| AI 问答总是引用页眉页脚 | 没有过滤噪声文本块 | 在解析环节按坐标剔除页眉页脚和页码 |
| OCR 数字/字母混淆 | 原始扫描分辨率太低 | 尽量保持 300 DPI,识别前做二值化和锐化 |
| 图片信息完全检索不到 | 图片被丢弃或未生成描述 | 多模态模型生成图片描述,以文本形式入库 |
| 页码跨页的表格内容断裂 | 分块硬切切到了表格中间 | 表格独立成 chunk,按标题边界分块 |
这张表基本覆盖了 RAG 图文导入阶段的七成问题。剩下的三成,大多属于文档本身版式怪异,需要针对特定文件单独写处理规则,这是体力活,没有办法完全自动化。
6.2 如何做好解析质量验收
我强烈建议:正式入库前,人工抽查解析结果。不需要每个文件都看,抽 20% 的代表性页面即可,但要看三类内容:纯文本页、表格页、图片页。重点检查是否有乱码、顺序颠倒、表格错位、图片丢失描述。如果抽样都不过关,后面整个 RAG 效果都会受影响。
做一个简单的验收清单:文本是否有明显识别错误;标题是否保留了层级;表格是否转成了结构清晰的格式;图片是否有对应的描述说明;阅读顺序是否符合人的直觉;页眉页脚是否被剔除。每一条都直接影响最终问答体验。很多人把精力花在调 prompt 和向量模型上,忽略了源头质量,结果调来调去效果都上不去。
6.3 成本与延时优化心得
解析链路跑得越深,成本越高。文书类文档全量调用多模态模型做整页转换,效率感人但钱包也很感人。我的建议是分级处理:第一优先级用开源工具解决 80% 的常规页面;第二优先级只对复杂表格、复杂版面、图片密集页面调用多模态模型;第三优先级才考虑整本 PDF 全量走深度学习解析。
另外要为解析任务加延时控制。大批量导入时,单页多模态模型如果响应太慢,容易拖垮整条流水线。最好是做一个异步队列,把解析任务分层调度,普通页面并发跑本地 OCR,重任务单独排队调大模型。整套链路跑稳定之后,你从丢一个文件进去到能在知识库里检索到内容,中间过程应该是完全无人值守的。
6.4 从解析到检索的闭环反思
解析这件事做到及格,RAG 系统才真正具备可用性。回到最开始的问题,很多人总以为是模型不够聪明,其实更大概率是解析阶段就把信息丢得七七八八了。图文和 PDF 作为知识库最常见的两种载体,对应的解析方案并不复杂,核心就是“分类处理”四个字:有文本层走提取,无文本层走 OCR,复杂版面走多模态,表格走专项工具,图片走描述入库。
我个人的体会是,解析流程至少要跑通“原始文件 → 结构化 Markdown → 分块 → 向量化 → 验证召回”这几步,才算真正能用。如果你正在做一个 RAG 项目,先找个包含扫描页、表格页、图片页的测试文档跑一遍,把这一篇读完,按案例复现一次,再看你手头的知识库效果,大概率会比之前顺畅很多。解析是 RAG 里最脏最累的活,但它值得你认真对待,因为它是整条链路的地基。