最近在做 RAG 相关的项目,踩了不少 PDF 处理的坑。真实业务里的 PDF,可能是合同、财报、技术手册、学术论文,甚至是扫描件,内容不只有文字,还有目录、表格、流程图、页眉页脚、多栏排版。直接粗暴抽文本,最后 RAG 检索出来的大概率是一堆断裂、重复、缺上下文的垃圾片段。下面从工程角度聊聊 PDF RAG 的四层架构,这是我在实践中总结出的一套方法论。
为什么 PDF 转文本是 demo 级答案
以一个合同问答系统为例。用户问违约金条款是多少,RAG 系统需要从合同 PDF 里找到答案。直接把 PDF 转成纯文本,表格里的赔偿标准会变成一堆乱码或断裂字符,章节关系完全丢失,不知道一句话属于第三章第二条还是第五章第一条,页码信息也随之消失,用户提到第 15 页的条款时系统完全无法定位。合同里的签名、盖章、流程图等非文本内容更是无从处理。最后 Agent 回答问题时看起来很自信,但根本追不回来源,也没法验证答案的准确性。PDF RAG 不是一个简单的转文本加向量化就能解决的问题,而是一个需要分层处理的系统工程。
四层架构
PDF RAG 的处理可以分成四层,每一层解决一个核心问题:
| 层级 | 核心职责 | 关键内容 |
|---|---|---|
| 第1层:解析层 | PDF 类型识别与文本提取 | 类型判断、文本提取、OCR、视觉理解 |
| 第2层:结构还原层 | 保留文档骨架信息 | 标题层级、章节关系、表格结构、页码映射 |
| 第3层:切片和索引层 | 语义切片与索引构建 | 语义切片、metadata 标注、向量+关键词索引 |
| 第4层:Agent 调用层 | 工具链按需调用 | 工具设计、按需检索、多轮对话 |
解析层 —— 先识别,再处理
处理 PDF 的第一步,不是急着抽文本,而是先判断这个 PDF 是什么类型。
三种 PDF 类型
| 类型 | 特点 | 处理方式 |
|---|---|---|
| 原生文本 PDF | 文字可选中、可复制 | 直接提取文本 |
| 扫描件 PDF | 本质是图片,文字不可选中 | OCR 识别 |
| 图文混排 PDF | 文字+表格+图表+流程图 | 多模态理解 |
可以用 PyMuPDF 或 pdfplumber 先做个简单判断:检查 PDF 里的文字层是否存在,如果每页提取的文字数量极少,大概率是扫描件。
不同类型的不同策略
原生文本 PDF 是最简单的情况,直接用 PyMuPDF、pdfplumber 这类库提取文本。但要注意,有些 PDF 虽然是原生文本,排版复杂(多栏、表格),提取出来的文本顺序可能是乱的。
扫描件 PDF 需要走 OCR 流程。Tesseract 是开源方案,但中文识别准确率一般。如果预算充足,可以考虑百度 OCR、阿里云 OCR 这类商业 API,准确率高很多。
图文混排 PDF 是最复杂的情况。表格需要结构化提取,流程图需要视觉理解,截图需要多模态模型分析。Anthropic 的 Claude PDF Support 就是这个思路,它不是只能读文字,还能理解 PDF 里的图片、图表和表格。
1 | # 简单的 PDF 类型判断逻辑 |
结构还原 —— 保留文档骨架
抽完文本之后,下一步是还原 PDF 的结构信息。这一步很多人忽略了,但它直接决定了后续切片的质量。
为什么要还原结构
从一份 50 页的财报里抽出 3 万字的纯文本,如果不知道哪段文字属于哪个章节、哪张表格,这些文本就是一盘散沙。用户问2023 年营收是多少,系统可能检索到 2022 年的数据,因为它们在文本上很相似,但没有年份的结构信息来区分。
需要保留的结构信息
- 标题层级:一级标题、二级标题、三级标题,形成树状结构
- 章节关系:每段文字属于哪个章节
- 页码映射:每段文字在第几页
- 表格结构:表格的行列关系、表头、单元格
- 图片说明:图片的标题、描述、所在位置
- 页眉页脚:文档的元信息
实现思路
具体实现上,可以用 pdfplumber 提取表格,用正则表达式识别标题层级,然后把这些结构信息和文本内容关联起来。
1 | # 结构化提取示例 |
切片和索引 —— 语义切片是核心
这一步是整个 PDF RAG 的核心。切片质量直接决定了检索质量。
固定字数切片的问题
很多入门教程会教你每 500 字切一个 chunk,这种方法在 PDF 场景下基本不可用。PDF 的内容是有结构的,一个章节可能有 2000 字,切成 4 个 chunk,每个 chunk 都不完整。一个表格可能只有 100 字,但把它和前后文混在一起,表格信息就被稀释了。
语义切片的原则
- 按结构切:标题下面的段落放一起,不要把一个章节切成多个 chunk
- 表格单独处理:表格是结构化数据,不能和纯文本混在一起
- 图片单独处理:图片需要生成摘要,不能直接丢弃
- 长表格按行列拆分:如果表格太长,可以按行列字段转成结构化文本
metadata 标注
每个 chunk 都要带 metadata,这是后续追溯的基础:
1 | chunk_metadata = { |
Contextual Retrieval:给 chunk 补上下文
Anthropic 提出了一个实用的方法:Contextual Retrieval。核心思路是给每个 chunk 补一段上下文说明,让模型知道这个片段在原文中属于哪一部分。
举个例子,原始 chunk 是:
违约方应支付合同总金额的 20% 作为违约金。
补完上下文后:
本文档是《2024年服务合同》,属于第三章违约责任中的第3.2条违约金计算方式。具体内容:违约方应支付合同总金额的 20% 作为违约金。
这样做的好处是,即使检索时只召回了这个 chunk,模型也能理解它的上下文,不会只见树木不见森林。
1 | # Contextual Retrieval 示例 |
索引策略
切完片之后,需要建立两种索引:
- 向量索引:用于语义相似度检索
- 关键词索引:用于精确匹配(如条款编号、表格编号)
两者结合使用,检索效果会更好。可以用 Hybrid Search 的方式,把向量检索和关键词检索的结果融合。
Agent 调用 —— 工具链按需调用
最后一层是 Agent 的调用策略。核心思路是把 PDF 能力封装成工具,而不是把整个 PDF 塞进上下文。
工具设计
1 | # PDF RAG 工具链 |
调用策略
不同类型的用户问题,走不同的调用路径:
- 简单事实问题(如违约金是多少)→ 直接向量召回
- 复杂对比问题(如 A 合同和 B 合同的违约金差异)→ 先检索多个章节,再让 Agent 规划阅读
- 表格问题(如2023 年各季度营收)→ 调用 extract_table
- 图表问题(如这个流程图说明了什么)→ 调用 analyze_chart
这种设计让 Agent 可以根据问题的复杂度,动态决定调用哪些工具,而不是一次性把所有信息都塞进上下文。
生产级细节:可追溯引用和评测
PDF RAG 系统要上生产,可追溯引用和评测体系是两个绕不开的环节。
可追溯引用
Agent 回答问题时,必须带上来源信息,包括答案来自第几页、属于哪个章节、原始文本是什么。这样用户可以验证答案的准确性,也可以追溯问题的根源。Anthropic 的 Citation 文档里也提到,PDF 可以按提取出的文本做引用,并返回页码范围,这是一个值得采纳的实践。
1 | # 带引用的回答示例 |
评测体系
评测不能只看最终答案对不对,还要看整个链路的质量:
| 评测维度 | 评测内容 |
|---|---|
| 检索质量 | 检索片段是否命中、排序是否合理 |
| 页码准确性 | 返回的页码是否正确 |
| 表格解析 | 表格是否正确提取、结构是否完整 |
| OCR 质量 | 是否漏字、错字 |
| 图表理解 | 图表信息是否被正确理解 |
| 答案准确性 | 最终答案是否正确 |
搭建一个覆盖不同类型 PDF 和问题的评测数据集,定期跑评测、监控系统质量,再配合可追溯引用形成闭环,系统才能真正从 demo 走向生产。
参考资源: