很多人第一次接触 PDF 解析时,会觉得这个问题应该很简单:
不就是把 PDF 里的文字提取出来吗?
如果只是为了读取一份普通 PDF,确实很简单。
用 PyMuPDF、pdfplumber,几行代码就能把文本拿出来。
但如果需求变成:
- 解析企业内部几万份 PDF;
- 同时支持电子 PDF 和扫描件;
- 识别标题、正文、表格、图片;
- 保留章节层级和上下文;
- 最终还要进入 RAG 知识库;
- 并且要求解析结果尽可能稳定;
问题就完全不一样了。
真正的企业级 PDF 解析,本质上不是一个 Text Extraction 问题,而是一个 Document Understanding(文档理解) 问题。
一个相对完整的处理链路,大致可以拆成:
PDF 输入
│
▼
① PDF 基础解析
│
▼
② 文本提取 / OCR
│
▼
③ 页面布局理解
│
▼
④ 表格 / 图片理解
│
▼
⑤ 文档结构融合
│
▼
⑥ Chunk 切片
│
▼
RAG / 知识库
每一层解决的问题都不一样。
而真正影响最终效果的,往往并不是 OCR。
一、先理解一个事实:PDF 本质上不是“文档”
这是理解 PDF 解析最重要的一点。
我们平时看到的 Word 文档,本身具有明确的语义结构。
例如:
标题
正文
表格
图片
Word 知道这一段是标题,那一段是正文,某一块是表格。
但是 PDF 很多时候并不知道。
对于 PDF 来说,它保存的更接近:
在坐标 (100, 50) 绘制文字 A
在坐标 (200, 50) 绘制文字 B
在坐标 (100, 80) 画一条线
在坐标 (100, 120) 放一张图片
换句话说:
PDF 更接近一种页面绘制格式,而不是结构化文档格式。
这也是为什么一个 PDF 在人眼看来非常规整,但程序解析出来以后却可能完全乱掉。
因为人类看到的是:
第三章 系统设计
3. 1 数据库设计
系统数据库采用……
程序最开始看到的可能只是:
[
{
"text": "第三章",
"x": 100,
"y": 120,
"font_size": 22
},
{
"text": "系统设计",
"x": 180,
"y": 120,
"font_size": 22
}
]
至于它是不是标题、属于第几级标题、后面哪些内容属于它,这些都需要重新推断。
所以 PDF 智能解析的核心目标,其实可以总结成一句话:
把一个“页面绘制结果”,重新还原成一个“结构化文档”。
二、第一层:PDF 基础解析
整个流程首先需要解决的是:
PDF 文件本身能够提供哪些原始信息?
通常需要获取:
- 文本;
- 文本坐标;
- 字体;
- 字号;
- 图片;
- 页面尺寸;
- 页码;
- Drawing 信息;
- Block 信息。
Python 生态里比较常见的是 PyMuPDF(fitz)。
例如:
page.get_text("dict")
可以拿到类似:
{
"text": "系统架构",
"bbox": [100, 200, 300, 250],
"font": "宋体",
"size": 18
}
这里非常重要的一点是:
我们不应该只拿:
系统架构
而应该把:
文本 + 坐标 + 字体 + 字号 + 页码
一起保留下来。
因为这些信息后面都会参与页面结构判断。
另外还有一些常见工具:
pdfplumber
比较适合:
- 坐标分析;
- 文本提取;
- 简单表格分析。
Apache PDFBox
Java 项目中比较常见。
对于企业内部以 Java 为主的系统,PDFBox 也是一个很成熟的选择。
这一层本身技术难度并不算特别高。
真正的问题在于:
基础解析只能告诉你“页面上有什么”,不能告诉你“这些东西是什么意思”。
三、第二层:OCR——解决扫描件问题
不是所有 PDF 都真正包含文本。
企业内部经常会碰到:
- 扫描合同;
- 扫描发票;
- 扫描档案;
- 盖章文件;
- 历史材料。
这种 PDF 本质上可能只是:
PDF
└── 一张图片
你调用:
page.get_text()
可能什么都拿不到。
这个时候就必须走 OCR。
典型流程:
PDF Page
│
▼
页面转图片
│
▼
文本检测
│
▼
文本识别
│
▼
文本 + 坐标 + 置信度
最终得到类似:
{
"text": "合同编号",
"bbox": [100, 200, 300, 240],
"confidence": 0.98
}
开源方案中,中文场景经常使用 PaddleOCR。
商业环境下也可能直接接:
- Azure Document Intelligence;
- AWS Textract;
- 阿里云 OCR;
- 腾讯云 OCR;
- 企业内部 OCR 服务。
现在 OCR 对于正常印刷文字已经比较成熟。
真正容易出问题的是:
- 手写文字;
- 印章覆盖;
- 低清扫描;
- 倾斜;
- 强噪声;
- 模糊图片;
- 特殊字体。
所以 OCR 是一个重要模块,但它往往不是现代 PDF 解析系统里最难的部分。
因为 OCR 解决的只是:
这里写了什么。
它并没有解决:
这段文字在整个文档里面是什么。
四、第三层:Layout Analysis,真正的核心
如果让我选 PDF 智能解析里面最关键的一层,我会选:
Layout Analysis——页面布局理解。
假设我们已经拿到了:
第一章 系统概述
系统主要用于……
1. 1 建设目标
……
接下来需要判断:
- 哪一个是标题?
- 哪一个是正文?
- 哪一个是页眉?
- 哪一个是页脚?
- 哪一个是表格?
- 哪一个是图片?
- 哪几个 Block 属于同一段?
- 阅读顺序是什么?
也就是把:
坐标 + 文本
变成:
语义 Block
例如:
{
"type": "heading",
"level": 1,
"content": "第一章 系统概述"
}
或者:
{
"type": "paragraph",
"content": "系统主要用于……"
}
最简单的方案:规则
例如:
字号 > 18
→ 标题
页面顶部固定区域
→ 页眉
页面底部固定区域
→ 页脚
规则的优势非常明显:
- 快;
- 稳定;
- 容易解释;
- 成本低。
如果企业内部 PDF 模板高度统一,规则方案甚至可能非常好用。
问题在于:
泛化能力差。
一旦文件变成:
- 合同;
- 论文;
- 财报;
- 招标文件;
- 规章制度;
- 产品手册;
规则很快就会失效。
比如一个财报中的 18 号字体,可能只是表格标题。
而一个合同正文标题,可能只有 14 号字体。
因此工业系统一般不会完全依赖单一规则。
五、Layout 模型解决什么问题?
更进一步,可以引入文档理解模型。
例如 LayoutLM 系列的核心思想就是:
模型不只看:
“数据库设计”
还会看:
文字内容
+
文字所在位置
+
页面视觉信息
例如:
文字:数据库设计
位置:
x = 100
y = 200
字号:
18
视觉特征:
加粗、独占一行
模型最终判断:
Heading
类似的方向还有:
- LayoutParser;
- DocTR;
- 各类 Document AI 模型;
- 多模态文档模型。
这一层为什么难?
因为根本不存在一种统一的 PDF 排版方式。
论文的结构可能是:
Title
Abstract
1 Introduction
2 Related Work
合同可能是:
第一条
第二条
(一)
1.
招标文件又可能是:
第一章
1. 1
1. 1.1
一、
(一)
1.
财报还会夹杂大量:
- 表格;
- 图;
- 注释;
- 双栏排版。
所以 Layout Analysis 的本质其实是:
根据视觉信息、空间信息以及文本语义,重新推断文档的结构。
这也是 PDF 智能解析真正开始困难的地方。
六、第四层:表格解析,比 OCR 麻烦得多
表格通常是 PDF 解析里的第二个大坑。
一个人看到:
姓名年龄张三20
马上就知道:
张三的年龄 = 20
但 PDF 内部可能只是:
姓名
年龄
张三
20
然后每个文字分别有自己的坐标。
程序需要自己推断:
姓名 和 年龄
属于第一行
张三 和 20
属于第二行
张三 和 姓名
属于同一列
这就涉及:
- 行识别;
- 列识别;
- 单元格识别;
- 合并单元格;
- 表头识别;
- 跨页表格。
传统方案
例如 Camelot。
对于有明确线框的表格:
+--------+--------+
| 姓名 | 年龄 |
+--------+--------+
| 张三 | 20 |
+--------+--------+
处理效果通常不错。
Tabula 也是比较经典的方案。
AI 方案
例如 Table Transformer。
通常流程:
页面图片
│
▼
Table Detection
│
▼
Row / Column / Cell Detection
│
▼
结构还原
│
▼
HTML / Markdown / JSON
最终可能还原为:
<table>
<tr>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr>
<td>张三</td>
<td>20</td>
</tr>
</table>
但实际企业文件远没有这么理想。
最麻烦的是:
- 无边框表格;
- 合并单元格;
- 多级表头;
- 跨页表格;
- 单元格内部存在段落;
- 表格中嵌套图片;
- 同一页多个表格;
- 表格和正文混排。
特别是跨页表格。
例如:
Page 10
姓名 | 部门 | 金额
张三 | 技术部 | 100
李四 | 技术部 | 200
--- Page Break ---
王五 | 财务部 | 300
赵六 | 财务部 | 400
第二页可能根本没有表头。
系统还要判断:
第二页这一块是不是上一页表格的延续?
这已经不是单纯 OCR 可以解决的问题了。
七、第五层:图片、图表和架构图
现代企业文档里还有大量非文本信息:
- 系统架构图;
- 流程图;
- 柱状图;
- 折线图;
- 产品截图;
- 示意图;
- 网络拓扑图。
如果只是传统 PDF Parser,最多只能告诉你:
这里有一张图片。
但它不知道图片表达了什么。
因此可以考虑引入 Vision LLM。
例如让视觉模型分析:
PDF Page Screenshot
│
▼
Vision Model
│
▼
Image Description
比如一个系统架构图:
用户
↓
API Gateway
↓
订单服务
↓
数据库
视觉模型可能生成:
该架构采用网关统一接收请求,
下游包含订单服务,订单服务访问数据库。
这就可以进一步进入知识库。
但多模态模型也不是万能的。
简单截图通常问题不大。
真正困难的是类似:
30 个模块
+
100 条连接线
+
多种颜色
+
多个子系统
这类复杂架构图。
视觉模型非常容易:
- 漏掉节点;
- 看错箭头;
- 搞反调用关系;
- 合并不同模块;
- 编造不存在的连接。
所以这里仍然要区分:
“能描述图片”和“能精确还原图片中的结构关系”是两个完全不同的能力。
八、第六层:Document Object Model
到了这里,我们会发现一个新的问题:
前面拿到了很多东西:
Text
OCR
Heading
Paragraph
Table
Image
Page
BBox
接下来怎么统一?
这就是一个非常典型的工程问题。
一个比较合理的做法是建立自己的:
Document Object Model。
可以把它理解为 PDF 世界里的 HTML DOM。
例如:
{
"document_id": "xxx",
"pages": [
{
"page": 10,
"blocks": [
{
"id": "block_001",
"type": "heading",
"level": 1,
"content": "数据库设计"
},
{
"id": "block_002",
"type": "paragraph",
"content": "数据库采用……"
},
{
"id": "block_003",
"type": "table",
"content": {}
}
]
}
]
}
如果做得更完整,还可以保留:
parent_id
previous_id
next_id
page_number
bbox
source
confidence
heading_path
image_id
table_id
最终形成:
Document
├── Chapter
│ ├── Heading
│ ├── Paragraph
│ ├── Table
│ └── Image
└── Chapter
这一步的重要程度经常被低估。
因为如果没有统一的数据模型,前面的各个解析模块很容易变成:
OCR 输出一种 JSON
表格模型输出另一种 JSON
图片模型输出 Markdown
Layout 模型又输出一种格式
最后整个知识库系统会非常难维护。
所以企业级系统真正需要设计的不是一个:
PDF → Text
而是:
PDF → Structured Document
九、Chunk 并不是“500 Token 切一下”这么简单
PDF 终于解析完成之后,一般还要进入 RAG。
很多系统到了这里会直接:
500 Token 一个 Chunk
然后做 Embedding。
这当然可以跑。
但效果很容易出问题。
例如原始文档:
第三章 数据库设计
3. 1 数据库连接池
数据库最大连接数配置为 100。
如果切成:
Chunk A:
第三章 数据库设计
以及:
Chunk B:
数据库最大连接数配置为 100。
用户搜索:
数据库设计里的连接池最大连接数是多少?
Chunk B 虽然包含答案,但已经失去了:
第三章 数据库设计
3. 1 数据库连接池
这些上下文。
这也是为什么我一直认为:
RAG 的切片质量,本质上取决于前面的文档结构还原质量。
如果前面已经得到了:
第三章 数据库设计
└── 3.1 数据库连接池
└── 最大连接数配置为100
那么 Chunk 就可以保存:
{
"content": "数据库最大连接数配置为100。",
"heading_path": [
"第三章 数据库设计",
"3.1 数据库连接池"
]
}
最终进入 Embedding 的文本甚至可以是:
第三章 数据库设计
3. 1 数据库连接池
数据库最大连接数配置为100。
这样检索质量会明显更稳定。
所以相比单纯使用:
Fixed-size Chunking
我更倾向于:
Heading-aware Chunking
+
Semantic Chunking
+
Parent-Child Chunking
根据不同文档动态组合。
十、哪些环节最难?
如果按照企业实际落地时的效果保障难度,我个人会这样排序:
排名模块难度1Layout / 文档结构还原⭐⭐⭐⭐⭐2表格结构还原⭐⭐⭐⭐⭐3Chunk 与上下文保持⭐⭐⭐⭐4复杂图片 / 图表理解⭐⭐⭐⭐5Document Model 融合⭐⭐⭐6OCR⭐⭐7PDF 基础文本提取⭐
这里有一个比较反直觉的地方:
很多 PDF 解析项目一开始最关注 OCR。
但真正做到后面就会发现:
OCR 错几个字,影响的可能只是一句话。
而结构错了,影响的可能是一整篇文档。
比如:
标题识别错
→ 章节层级错
→ Chunk 切错
→ 上下文丢失
→ RAG 检索错
→ LLM 最终回答错
这是一个非常典型的误差传播链路。
十一、企业里真正现实的方案是什么?
如果真的要做一个企业级 PDF 解析系统,我并不认为应该一开始就追求:
任意 PDF
+
100% 自动解析
+
100% 准确
这个目标本身就不现实。
更务实的路线通常是:
PDF Parser
+
OCR
+
Layout Analysis
+
Table Parser
+
Vision Model
+
LLM Correction
+
Document Model
然后针对企业内部高频文档不断优化。
例如:
第一阶段
先覆盖 60%~70% 主流文件
↓
第二阶段
针对合同、制度、报告等高频模板优化
↓
第三阶段
补充特殊表格、跨页、图片等能力
↓
第四阶段
建立解析评估集持续回归测试
而不是:
为了一个非常特殊的 PDF
写 500 行特殊规则
企业知识库中的 PDF 类型往往非常杂。
今天是合同,明天是财报,后天又是 PPT 转出来的 PDF。
如果每一种文件都写一套特殊逻辑,最终系统一定会失控。
因此更加合理的思路应该是:
构建一套足够精细的底层文档模型和解析架构,再通过持续测试、模型升级和局部规则不断提高覆盖率。
十二、LLM 应该放在哪里?
现在还有一种很容易出现的思路:
既然大模型这么强,直接整页 PDF 截图丢给多模态模型不就好了?
小规模场景当然可以。
但是企业级系统很难全部这么做。
主要有几个原因:
1. 成本
几万、几十万份文件全部走视觉大模型,成本很高。
2. 稳定性
传统 Parser 对于:
文本
坐标
字体
这类信息是确定性的。
LLM 则存在概率性。
3. 可解释性
企业系统需要知道:
这一段内容来自第几页?
原始坐标在哪里?
解析置信度是多少?
不能只拿到模型生成的一段 Markdown。
4. 幻觉
特别是复杂表格和架构图,大模型可能生成原文不存在的信息。
所以我更倾向于把 LLM 放在:
传统解析解决 80%
+
LLM 处理复杂语义和修正
而不是:
LLM 替代整个 Document Parser
例如可以让 LLM 帮忙:
- 判断两个 Block 是否属于同一段;
- 修正异常阅读顺序;
- 判断标题层级;
- 判断跨页表格;
- 对图片生成描述;
- 修复 OCR 明显错误;
- 对解析结果进行一致性检查。
这样会更加符合企业系统的工程要求。
十三、最后:PDF 解析其实是一个“小型文档智能系统”
如果只是:
pdf_to_text()
PDF 解析非常简单。
但当目标变成:
“让 AI 真正理解企业 PDF,并把它变成可靠知识。”
整个问题就会从一个 Python 工具调用,逐渐演变成:
Document Parser
+
OCR
+
Computer Vision
+
Layout Understanding
+
Table Recognition
+
Multimodal LLM
+
Data Modeling
+
RAG
这也是为什么很多知识库系统:
Demo 看起来效果非常好。
但是一进入真实企业数据,马上就开始出现:
表格错乱
标题丢失
章节关系丢失
图片无法理解
跨页内容断裂
检索上下文不足
因为真正决定知识库效果的,往往不是最后用了哪个 Embedding 模型。
而是更早的一步:
你到底有没有把原始文档解析成“正确的知识结构”。
所以如果让我总结企业级 PDF 智能解析最重要的设计原则,我会归纳成三点:
第一,不要把 PDF 当成纯文本。
它首先是一个具有二维空间关系的页面。
第二,不要把 PDF 解析等同于 OCR。
OCR 解决的是“写了什么”,Document Understanding 解决的是“它是什么意思、它属于哪里”。
第三,不要只关注解析准确率,要关注结构是否能够一直传递到 RAG。
最终真正有价值的链路应该是:
PDF
↓
页面元素
↓
结构化 Document
↓
章节 / 段落 / 表格 / 图片关系
↓
语义 Chunk
↓
检索
↓
完整上下文
↓
LLM
当这条链路真正打通之后,PDF 才不再只是一份“可以搜索的文本”。
而真正变成了一份:
机器可以理解、检索、引用和推理的知识。
