市图书馆的老周接到一个特殊任务:馆里要上线一个 AI 导读机器人,读者提问,它来回答。可机器人有个“怪癖”——它只认纯文本,摆在它面前的两类文档,它一个字都读不懂。第一类是 PDF,像一本本“照片册”,每页都是渲染好的画面,文字是烙在坐标上的字形;第二类是 Word,像“排好版的电子稿”,段落标题清清楚楚,却藏在压缩包里。老周的工作,就是把这两类文档“誊抄”成机器人能读的纯文本。这份誊抄的功夫,就叫文档解析,它是 RAG 系统里最不起眼、却决定成败的第一道工序。这篇文章会跟着老周一起,把 PDF 解析的挑战与方法、Word 解析的技巧、图文混排的处理、OCR 兜底方案、质量评估工具,以及解析库的选型,一一讲明白。

岗位关联

对 AI 从业者:文档解析是 RAG 系统的入口与质量上限,掌握它能显著提升检索效果,是企业知识库、文档问答类项目落地的必备基本功。

目录
  1. 可视化演示
  2. 解析作用与目标
  3. PDF解析挑战
  4. PDF解析方法
  5. Word文档解析
  6. 图文混排处理
  7. OCR兜底方案
  8. 质量评估方法
  9. 解析库对比选型
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

动态演示PDF与Word文档解析技术的核心机制,通过概览/流程/对比/趋势四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

PDF与Word文档解析技术:一PDF 一、PDF文档解析挑战与技术方 二、Word文档解析与结构化提 核心机制可视化 PDF与Word文档解析技术整体架构与关键组件

解析作用与目标

市图书馆的老周接到任务后,先弄明白了一个道理:机器人不是“看不懂 PDF”,而是 PDF 里根本没有它能直接读的“纯文本”。就像人眼能读照片上的字,但打印机只认字符编码。所以解析的第一目标,就是把 PDF、Word 这类非结构化文档,誊抄成一段段干净的文本。RAG 链路里,文档解析位于最前端,后面所有切块、向量化、检索,都建立在这份誊抄稿之上。

在 RAG 链路中,文档加载与处理是名副其实的“入口工序”。原始文档格式五花八门,但大模型的检索组件只吃“干净的文本”。解析要做两件事:一是把非结构化文档变成纯文本,二是尽量保留标题、表格等结构信息。解析质量直接决定后续检索质量的上限——这就是“垃圾进,垃圾出”的铁律。老周把这句话贴在工位上,时刻提醒自己别在入口偷懒。

为了判断誊抄是否合格,老周需要一个量化指标:可用信息率。它衡量“文档里真正被提取出来、能用于检索的文本比例”。比如一份 10 个段落的文件只提出 8 段,可用率就是 80%。低于阈值,机器人就会“失忆”,读者问到的内容恰好就在那丢失的 20% 里。

老周把“可用率”写进验收清单:解析不是走过场,而是给机器人喂饭的第一勺。翻不出来的段落,机器人永远回答不了。这一勺喂得好不好,决定了整条 RAG 链路的效果上限。

η可用率=N成功提取文本块N文档全部文本块×100%\displaystyle \eta_{\text{可用率}} = \frac{N_{\text{成功提取文本块}}}{N_{\text{文档全部文本块}}} \times 100\%
通俗说明:可用率就是“誊抄出来的内容占原文的比例”,数值越高,机器人能读到的东西越全。
  1. $N_{\text{成功提取文本块}}$成功提取的文本块数量。文本块可以是标题、段落、表格行等完整单元,是誊抄成果的盘点。
    N成功提取文本块N_{\text{成功提取文本块}}
  2. $N_{\text{文档全部文本块}}$文档实际包含的全部文本块总数,需要人工核对或抽样统计得到。
    N文档全部文本块N_{\text{文档全部文本块}}
  3. $\eta_{\text{可用率}}$两者的比值,代表解析的完整程度,是后续所有质量工作的基准。
    η可用率=N成功提取文本块N文档全部文本块\eta_{\text{可用率}} = \frac{N_{\text{成功提取文本块}}}{N_{\text{文档全部文本块}}}
  4. $\times 100\%$把比值换算成百分比,方便设定质量门槛,例如要求不低于 95%。
    ×100%\times 100\%

PDF解析挑战

老周第一次打开 PDF 时愣住了——它不像一本书,倒像一本“照片册”。每一页都是一张渲染好的画面,文字像印在照片上:电脑看到的不是句子和段落,而是“某个字形被画在了某个坐标上”。想直接选中文字?有时可以,有时复制出来是乱码,有时顺序完全颠倒。老周意识到,PDF 的“字”和 Word 的“字”根本不是一回事。

这源于 PDF 的本质:它是页面描述格式,核心是一套绘图指令(内容流),而不是文档结构。每个字符只是内容流里的一条“绘制命令”外加坐标。PDF 不承诺维护段落语义,也不承诺“先写的字就是先读的字”。字体还常被裁剪成子集,导致提取出来的字符映射错乱。老周把这些难题总结成四类:结构缺失、抽取顺序不等于阅读顺序、复杂版式、字体编码异常。

具体来说,结构缺失指 PDF 没有标题、段落标记,只有“画在哪”的坐标;抽取顺序错乱是因为内容流按绘图命令书写顺序排列,表格线、背景图、正文文字往往交错出现;复杂版式包括双栏、三栏、文字环绕图片;字体编码异常则出现在 CID 字体或子集化字体上,直接复制会得到乱码。这些挑战叠加起来,“把字符抠出来”往往远远不够。

老周明白了:照片册里没有“段落”,只有一个个字模的位置。要把照片册变成通顺的文章,光靠蛮力不行,得靠下面几招聪明办法。

S页面={(xi,yi,gi)∣i=1,2,…,n}\displaystyle S_{\text{页面}} = \{ (x_i, y_i, g_i) \mid i = 1, 2, \dots, n \}
通俗说明:PDF 的每一页,本质是一个“字形坐标集合”。它只告诉计算机每个字画在哪里,不告诉哪个字和哪个字是一句。
  1. $S_{\text{页面}}$把一页 PDF 看成一个集合,页面就是全部“字符绘制信息”的总和。
    S页面S_{\text{页面}}
  2. $(x_i, y_i)$第 $i$ 个字形在页面上的横纵坐标,PDF 只记录“画在哪”。
    (xi,yi)(x_i, y_i)
  3. $g_i$第 $i$ 个字形(glyph),可以理解为字符的轮廓或编码索引,代表“画的是什么”。
    gig_i
  4. $i = 1, 2, \dots, n$$n$ 是该页字形总数;集合本身没有顺序,这正是乱序问题的根源。
    i=1,2,…,ni = 1, 2, \dots, n

PDF解析方法

老周请教了有经验的同事,学到 PDF 誊抄其实分三档:第一档,如果 PDF 自带“文字层”(文本型 PDF),可以直接抽取;第二档,抽取后顺序乱,需要按版面重排;第三档,整页是扫描照片,只能请 OCR 打字员(后面专门讲)。老周先学会了前两档。

文本型 PDF 的抽取原理,是遍历内容流中的文本绘制操作(如 Tj、TJ 指令),把字符串按流的顺序拼出来。但流的顺序约等于代码书写顺序,经常和人的阅读顺序不一致。因此更稳的做法是版面分析:把页面切成文字块,根据坐标聚成行、段、栏,再按“先上后下、先左后右”重排。抽取和版面分析配合,才能得到通顺的段落。

还可以借助元数据:PDF 书签(Outline)能反推章节层级,配合目录页能给后续切块提供标题锚点。抽取 + 版面重排 + 元数据,三步组成完整方案。常用工具里,PyPDF2/pypdf 轻量快速,pdfplumber 版式细腻,PyMuPDF 性能均衡,复杂版面还可以上深度学习版面分析模型。老周把这些工具按“快、细、全”分好类,备选。

老周把“先抠字、再看版式、最后重排”记进小本本。誊抄出来的稿子终于像人话,而不是一团乱麻。他长舒一口气:照片册也不是那么可怕。

T最终=Rearrange(Extract(内容流), Layout(版面))\displaystyle T_{\text{最终}} = \mathrm{Rearrange}\big( \mathrm{Extract}(\text{内容流}),\ \mathrm{Layout}(\text{版面}) \big)
通俗说明:先把字符从绘图指令里“抠”出来,再照着版面的位置把它们“摆”回正确的句子顺序。
  1. $\mathrm{Extract}(\text{内容流})$从内容流中提取全部文本操作,得到原始的、未经排序的字符序列。
    Extract(内容流)\mathrm{Extract}(\text{内容流})
  2. $\mathrm{Layout}(\text{版面})$版面分析的结果,包括文字块边界框、行、栏等信息。
    Layout(版面)\mathrm{Layout}(\text{版面})
  3. $\mathrm{Rearrange}(\cdots)$以版面信息为指导,把字符序列重排成有意义的段落和阅读顺序。
    Rearrange(⋯ )\mathrm{Rearrange}(\cdots)
  4. $T_{\text{最终}}$重排后的最终纯文本,直接喂给下一道切块工序。
    T最终T_{\text{最终}}

Word文档解析

处理 Word 时老周松了口气——它不像照片册,更像“排好版的电子稿”。标题、列表、表格都有自己的标签,抄起来轻松。但难点不在“能不能提取”,而在“怎么把结构和文字一起留下来”。机器人需要的不是一堆字,而是“哪句是标题、哪里是表格”这些骨架信息。

DOCX 的本质是一个 ZIP 压缩包,里面装着若干 XML 文件,最核心的是 word/document.xml——正文就是一棵 XML 树。每个段落是一个段落节点,里面的文字片段是文本节点,样式信息(比如“这是一级标题”)记录在 styles.xml 中。用 python-docx 可以很方便地遍历段落、文本片段与表格,甚至直接读出段落样式名。老周发现,这就像把电子稿的排版标记原封不动地读了出来。

Word 解析的关键产出不只是纯文本,而是“带结构的文本”:段落样式(Heading 1/2)、列表层级、表格的行列关系。这些结构对 RAG 切块极其宝贵——按标题层级切块,可以让每个块语义自洽,检索命中率更高。解析时还可以把表格转成 Markdown 表格,保留行列语义。老周在工具链里给 python-docx 留了固定位置,因为它成熟、稳定、文档全。

老周感叹:Word 就像贴满标签的档案袋,抄得又快又全,还能顺手贴上“内容标签”,给后面的切块帮大忙。难怪同事都劝他,能拿到 Word 就别用 PDF。

DOCX=Zip{document.xml, styles.xml, media/, … }\displaystyle \mathrm{DOCX} = \mathrm{Zip}\{ \mathrm{document.xml},\ \mathrm{styles.xml},\ \mathrm{media}/,\ \dots \}
通俗说明:DOCX 就是一个压缩包:正文、样式、图片各是一个文件,解析就是解压后读 XML。
  1. $\mathrm{Zip}$DOCX 是 ZIP 压缩格式,可以像解普通压缩包一样解开。
    Zip\mathrm{Zip}
  2. $\mathrm{document.xml}$正文 XML 文件,段落、表格、列表都写在这里。
    document.xml\mathrm{document.xml}
  3. $\mathrm{styles.xml}$样式定义文件,说明“一级标题长什么样”。
    styles.xml\mathrm{styles.xml}
  4. $\mathrm{media}/$图片等媒体资源所在目录。
    media/\mathrm{media}/
  5. $\dots$其它辅助文件(文档属性、主题等),解析时通常忽略。
    …\dots

图文混排处理

很快老周碰到刁钻的“宣传册”PDF:左半页一段文字,右半页一张图,图下面还压着图注;更麻烦的是两栏、三栏排版。按内容流直接抽,文字会左右穿插,读起来像把两篇文章搅在一起。老周头都大了:这哪是誊抄,简直是拼图。

图文混排处理的核心是版面还原。第一步还是版面分析:把页面切成图块和文本块,检测分栏列数。第二步是阅读顺序排序:人的习惯是“先上后下、先左后右”,所以把文本块按纵坐标、再按横坐标排序,就能恢复阅读顺序。图片位置用 [图片] 占位符保留,图注要和所在图块绑定,防止图文分离。老周在脚本里加了一个排序函数,宣传册立刻读通了。

对于更复杂的版式(文字环绕图形、跨栏标题),规则算法容易失效。这时可以用基于深度学习的版面分析模型,检测出每个块的类别和边界框,再按模型输出或规则做排序。最终结果常转成带标题层级的 Markdown,既保留结构,又方便切块。老周把这条链路写成了“检测 → 排序 → 转 Markdown”三步,处理各种花式排版都不慌。

老周把这套“排序规则”写进了解析脚本,宣传册终于读得和真人一样顺,图还是图,字还是字。他忍不住感慨:原来“读”这件事,背后是一整套坐标逻辑。

ReadingOrder=sort(blocks, key=(ymin⁡,xmin⁡))\displaystyle \text{ReadingOrder} = \mathrm{sort}\big( \text{blocks},\ \text{key} = (y_{\min}, x_{\min}) \big)
通俗说明:把人阅读的顺序翻译成坐标规则——先找“最靠上”的块,同一水平区域里找“最靠左”的块。
  1. $\text{blocks}$版面分析得到的文本块集合,每个块有边界框。
    blocks\text{blocks}
  2. $y_{\min}$块的顶部纵坐标。排版上靠上的内容通常先被阅读。
    ymin⁡y_{\min}
  3. $x_{\min}$块的左边缘横坐标。同一水平区域里靠左的内容先被阅读。
    xmin⁡x_{\min}
  4. $\mathrm{sort}(\cdots, \text{key}=(y_{\min}, x_{\min}))$先按纵坐标、再按横坐标排序,得到的就是人眼的阅读顺序。
    sort(⋯ ,key=(ymin⁡,xmin⁡))\mathrm{sort}(\cdots, \text{key}=(y_{\min}, x_{\min}))

OCR兜底方案

老周又翻出一批“纯照片书”——扫描版 PDF,整页就是一张大图片,连文字层都没有。前面学的方法全部失效,只能请“打字员”:OCR(光学字符识别)。但打字员怕模糊和歪斜,所以老周先帮照片“擦干净”,再让打字员上阵。他给这套流程起名叫“OCR 兜底”,意思是只有走投无路时才用。

OCR 的完整流程包括四步:图像预处理(灰度化、二值化、去噪、倾斜校正)→ 文字检测(定位每一行文字)→ 字符识别(用深度学习模型输出候选字符)→ 后处理(用词典和语言模型纠错)。预处理做好,识别率能提升好几个百分点。老周实测发现,同样的扫描件,先做倾斜校正再识别,错误率几乎减半。

OCR 识别本质是“图像 + 语言”共同决策:图像模型负责“这个字形像什么”,语言模型负责“这句话像不像人话”。两者相乘取最大概率,就是最终识别结果。常见引擎里,Tesseract 免费离线,PaddleOCR 中文场景精度高,云 OCR 省运维但按量付费。老周特别记了一条原则:OCR 只作为扫描件的兜底方案,文本型 PDF 不要用——又慢又引入噪声。

老周把“打字员”安排在最后一道工序,只服务扫描件;而且他记住了:照片要擦干净,句子要读得通,两个条件缺一不可。兜底不是万能,但关键时刻真能救命。

P(文本∣图像)∝P(图像∣文本)⋅P(文本)\displaystyle P(\text{文本} \mid \text{图像}) \propto P(\text{图像} \mid \text{文本}) \cdot P(\text{文本})
通俗说明:OCR 的“最终答案” =“图像像不像”乘以“这句话通不通顺”,两个信号一起投票。
  1. $P(\text{文本} \mid \text{图像})$在给定图像后,识别结果是某个文本的概率。OCR 目标就是让这个概率最大的文本胜出。
    P(文本∣图像)P(\text{文本} \mid \text{图像})
  2. $P(\text{图像} \mid \text{文本})$图像模型打分,衡量“这段文本长在这张图上像不像”。
    P(图像∣文本)P(\text{图像} \mid \text{文本})
  3. $P(\text{文本})$语言先验,衡量“这段话在正常语言中通不通顺”,就是语言模型的功劳。
    P(文本)P(\text{文本})
  4. $\propto$成正比。这里省略了归一化分母,因为比较候选文本时它是常数。
    ∝\propto

质量评估方法

誊抄完一堆文档,老周怎么知道抄得对不对?他拿出一沓原文,让助手把结果和原文逐句比对:有没有抄错的字?有没有漏掉的段落?句子顺序有没有乱?这就是质量评估。老周意识到,没有验收的解析等于没做——你根本不知道机器人读到的稿子有多“失真”。

最常用的自动指标是 CER(字符错误率,Character Error Rate)。它把解析结果与参考文本做逐字符对齐,统计替换、删除、插入三类错误,再除以参考文本总字符数。CER 越低越好,一般目标定在 5% 以内。老周写了个小脚本,批量算完所有文档的 CER,一秒就知道哪批文件需要返工。

光看字符还不够,还要看段落完整度(成功提取段落数占总段落数的比例)和顺序正确性(抽样检查句子位置是否和原文一致)。实践中采取“自动指标 + 抽样人工评审”:机器测指标,人看典型案例,两者结合才能覆盖 OCR 和版面错乱等疑难问题。老周的验收流程变成了三步:先跑 CER,再查段落完整度,最后抽 5% 文档人工复核。

老周的“验收表”让誊抄工作不再凭感觉。每份文档解析完,先跑指标,再过人工抽检,质量稳稳的。他把这套流程固化成了脚本,以后每个新文档进来都自动过一遍。

CER=S+D+IN\displaystyle \mathrm{CER} = \frac{S + D + I}{N}
通俗说明:把解析结果和原文“找不同”:算一算每 100 个字里,抄错、抄漏、多抄的加起来有几个。
  1. $S$(替换数)原文有某个字,结果抄成了另一个字,比如“未来”抄成“末来”。
    SS
  2. $D$(删除数)原文有的字,结果里漏掉了,比如整行被吞。
    DD
  3. $I$(插入数)原文没有的字,结果里多出来了,比如页眉页脚混入正文。
    II
  4. $N$参考文本的总字符数,作为分母做归一化。
    NN
  5. $\frac{S + D + I}{N}$三类错误总和除以总字符数,得到字符错误率,越低越好。
    S+D+IN\frac{S + D + I}{N}

解析库对比选型

工具备齐后,老周把候选方案摆了满满一桌:有免费轻量的、有准确但慢的、有功能全面但重的、还有云端按量付费的。选库就像选“誊抄外包团队”:没有最好,只有最合适。老周画了一张对比表,把性能、成本、准确率全部列了上去。

PDF 库横向对比:PyPDF2/pypdf 轻量快速,适合简单文本型 PDF,但复杂版式容易乱序;pdfplumber 版式解析细腻、表格处理强,但速度较慢;PyMuPDF 性能均衡,能拿文本也能渲染图像,API 丰富;复杂版面可上 LayoutParser 等深度学习方案。Word 方面,python-docx 是事实标准,成熟稳定。老周把表格贴在墙上,每个库的优缺点一目了然。

选型建议用加权打分:准确率(用可用率或抽样评测打分)、速度(页/秒)、成本(硬件或 API 费用)按项目场景定权重。例如中文扫描合同:准确率权重 0.6、速度 0.2、成本 0.2;海量日志 PDF:速度权重调高。注意:无论打分多漂亮,都要拿真实样本试跑验证后再上线。老周在打分表底部用红笔写了一行字:“一切以实测为准”。

老周把打分表往墙上一贴,所有候选方案一目了然。他心里清楚:工具是死的,场景是活的,选型永远服务于“机器人读得好不好”这个根本目标。至此,他的文档解析工具箱总算搭建完毕。

Score库=w1A+w2V−w3C\displaystyle \mathrm{Score}_{\text{库}} = w_1 A + w_2 V - w_3 C
通俗说明:给每个候选库算总分:准确率加钱、速度加钱、成本扣钱,权重由业务场景决定。
  1. $A$准确率评分,用 CER 或可用率换算成 0-100 分。
    AA
  2. $V$速度评分,例如每秒解析的页数,归一化到 0-100。
    VV
  3. $C$成本评分,包含硬件、API 费用,越高越不利,所以前面是减号。
    CC
  4. $w_1, w_2, w_3$三个权重,反映业务优先级;比如扫描件项目把 $w_1$ 调大。
    w1,w2,w3w_1, w_2, w_3
  5. $\mathrm{Score}_{\text{库}}$候选库的总分,选最高的,但最终以真实数据试跑为准。
    Score库\mathrm{Score}_{\text{库}}

AI 应用场景

企业制度库问答助手

阶段:文档加载与处理(解析)

解决的问题:传统知识库靠人工把 PDF 制度文件复制到后台,费时且容易遗漏;扫描版制度用 OCR 自动转文本,文本型 PDF 用 PyMuPDF 抽取加版面重排,解析后自动入库,员工提问能秒回原文出处。

具体案例:某制造企业的 HR 制度库有 300 份 PDF,其中 40% 是扫描件。工程师用 PaddleOCR 处理扫描件、PyMuPDF 处理文本件,按标题切块后接入 RAG。员工问“年假怎么算”,系统直接引用制度原文并标注页码。

学术论文检索助手

阶段:文档加载与处理(图文混排解析)

解决的问题:论文 PDF 多为双栏排版,直接抽取会左右串行;通过版面分析识别栏位、图表与图注,按标题层级重组 Markdown,检索时才能精准定位章节而非整篇糊在一起。

具体案例:某高校知识服务公司批量解析 5 万篇论文,跨栏排序后按“标题-摘要-章节”切块。研究员问“有哪些工作用对比学习做文档排序”,系统返回的是对应小节原文,而不是整篇 PDF。

合同条款抽取与问答

阶段:文档加载与处理(Word 结构保留 + 质量评估)

解决的问题:Word 合同没有结构就无法定位条款;保留表格和标题样式后,按“第 X 条”切块,再交给信息抽取,才能回答“违约金比例上限”这类条款级问题。

具体案例:某法务平台每周解析上千份 Word 合同,python-docx 保留标题层级,CER 抽检低于 3%。用户问“违约金比例上限”,系统定位到对应条款并高亮原文,法务审核效率提升数倍。

Python 演示

python60 行
"""文档解析演示:DOCX 文本提取 + PDF 坐标重排 + CER 质量评估。 用法:    python demo_parse.py"""import zipfileimport xml.etree.ElementTree as ET W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"  def parse_docx(path):    """用标准库解析 Word 正文,无需安装第三方库。"""    with zipfile.ZipFile(path) as zf:        root = ET.fromstring(zf.read("word/document.xml"))    paragraphs = []    for para in root.iter("{%s}p" % W_NS):        runs = []        for node in para.iter("{%s}t" % W_NS):            runs.append(node.text or "")        text = "".join(runs).strip()        if text:            paragraphs.append(text)    return paragraphs  def extract_pdf_blocks(blocks):    """模拟文本型 PDF:内容流乱序 -> 按坐标重排阅读顺序。"""    ordered = sorted(blocks, key=lambda b: (b["y"], b["x"]))    return [" ".join(b["text"] for b in ordered)]  def cer(reference, hypothesis):    """字符错误率:编辑距离 / 参考文本字符数。"""    n, m = len(reference), len(hypothesis)    dp = [[0] * (m + 1) for _ in range(n + 1)]    for i in range(n + 1):        dp[i][0] = i    for j in range(m + 1):        dp[0][j] = j    for i in range(1, n + 1):        for j in range(1, m + 1):            cost = 0 if reference[i - 1] == hypothesis[j - 1] else 1            dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1,                           dp[i - 1][j - 1] + cost)    return dp[n][m] / max(n, 1)  if __name__ == "__main__":    blocks = [        {"x": 30, "y": 180, "text": "内容流最先写出这段,但它其实在页面下方。"},        {"x": 30, "y": 30, "text": "标题:文档解析实践指南"},        {"x": 30, "y": 90, "text": "正文第一段:解析是RAG的入口。"},    ]    extracted = extract_pdf_blocks(blocks)    print("\n".join(extracted))    reference = ("标题:文档解析实践指南 "                 "正文第一段:解析是RAG的入口。 "                 "内容流最先写出这段,但它其实在页面下方。")    print("CER = %.4f" % cer(reference, extracted[0]))
目的:用纯标准库演示三大核心能力——解析 DOCX 里的段落文本、模拟 PDF 按坐标重排阅读顺序、计算解析结果的字符错误率。
  1. zf.read("word/document.xml")解压 DOCX 拿到正文 XML,这是 Word 解析的入口。
  2. root.iter("{%s}p" % W_NS)遍历 XML 树中的所有段落节点,对应文档里的每个自然段。
  3. "".join(runs).strip()把段落内所有文本片段拼成一句,并过滤空段落。
  4. sorted(blocks, key=lambda b: (b["y"], b["x"]))按“先纵后横”排序文本块,还原人眼阅读顺序。
  5. dp[i][j] = min(...)编辑距离动态规划核心递推,用来计算 CER 的最小编辑代价。

常见误区

误区“我在 PDF 里能选中文字、能复制,那解析不就是把文字读出来吗?为什么还要做什么版面分析?”

正解能复制只代表这个 PDF 有文本层,不代表抽取顺序正确。内容流按绘图命令书写顺序排列,表格线、背景、正文交错出现,复制出来的文字经常前后颠倒。必须结合版面分析做阅读顺序重排,否则检索到的是“串行”内容,答非所问。

为何常见:因为屏幕上看到的是渲染好的“成品页面”,用户看不到背后的绘图指令顺序,误以为页面顺序就是文本顺序。

误区“扫描件用 OCR,文本型 PDF 也直接上 OCR,反正都是把字认出来,OCR 不是更通用吗?”

正解文本型 PDF 直接抽取又快又准,OCR 反而会引入识别错误且速度慢好几倍。OCR 只应该作为扫描件或图像型 PDF 的兜底方案。先用工具判断 PDF 是否含文本层(如 PyMuPDF 的 page.get_text() 是否为空),再决定走抽取还是 OCR。

为何常见:很多初学者只见过扫描版 PDF,误以为所有 PDF 都是图片,忽视了文本型 PDF 的大量存在。

误区“Word 用 python-docx 提取文本就完事了,反正机器人只要文字,结构无所谓。”

正解标题层级、表格结构、样式信息对 RAG 切块非常关键。丢掉结构,按标题切块的策略就失效了,切出来的块语义混乱,检索命中率大降。正确的做法是保留段落样式名、表格行列关系,输出带结构的文本(如 Markdown)。

为何常见:早期简单 demo 只要文字能显示就行,忽视了 RAG 对结构化切块的真实需求,等到检索质量差才回头补课。

误区“解析当然要 100% 准确,直接上最贵的云 OCR,每份文档都要完美提取。”

正解RAG 是迭代工程,解析只需达到任务所需的可用率即可。普通文本型 PDF 可用率 95% 足够;复杂扫描件才值得上深度学习模型或云 OCR。过度投入的边际收益很低,还会拖慢上线速度。

为何常见:追求完美的本能让人忽视成本收益,尤其是新手容易把“演示效果”当“生产标准”。

误区“CER 都到 1% 了,解析肯定没问题,不用再抽检了。”

正解CER 只衡量“字”层面的差异,句子顺序错乱时 CER 可能依然很小(字都在,只是位置不对)。必须同时检查段落完整度和阅读顺序,配合抽样人工评审,才能发现版面错乱、表格错位等 CER 看不出的问题。

为何常见:单一的量化指标容易自动化、容易比较,于是被当作唯一标准,忽略了质量的多维性。

面试题

Q1. 为什么 PDF 解析比 Word 解析更容易出现顺序错乱?

单选题 · easy

  • A.PDF 体积太大导致读取超时
  • B.PDF 按绘图指令存储字符坐标← 正确答案
  • C.PDF 格式不开放无法解读
  • D.Word 文件本身不含任何文字

答案:B。PDF 是页面描述格式,字符以“字形+坐标”的绘图指令存在,内容流顺序不等于阅读顺序。A 与体积无关;C 说反了,PDF 是开放标准;D 完全错误。

Q2. 文本型 PDF 与扫描型 PDF 的主要区别是?

单选题 · easy

  • A.前者体积定比后者小得多
  • B.前者含可选文字层可抽取← 正确答案
  • C.后者必须用 Word 才能打开
  • D.前者彩色而后者黑白灰度

答案:B。文本型 PDF 内含文字层,可以直接抽取;扫描型 PDF 是纯图像,需要 OCR。体积、颜色与类型无关,扫描型也不依赖 Word 打开。

Q3. 解析得到的文本块按 (y_min, x_min) 排序,主要目的是?

单选题 · mid

  • A.提高字符编码识别准确率
  • B.恢复人眼阅读的物理顺序← 正确答案
  • C.压缩文本块的存储开销
  • D.把图片替换成文字占位符

答案:B。先按纵坐标、再按横坐标排序就是“先上后下、先左后右”的阅读顺序,目的是恢复人眼阅读顺序。识别准确率、存储、图片占位都与排序无关。

Q4. 下列哪个属于 OCR 的常见预处理步骤?

单选题 · mid

  • A.对文本做词向量嵌入
  • B.灰度化与倾斜校正← 正确答案
  • C.用 BM25 做相关性打分
  • D.对段落做向量聚类

答案:B。OCR 预处理包括灰度化、二值化、去噪、倾斜校正,目的是让识别引擎看“干净”的图像。词向量、BM25、聚类都是检索或文本处理环节的事。

Q5. DOCX 文件的本质结构是?

单选题 · mid

  • A.纯二进制单文件格式
  • B.包含 XML 文件的 ZIP 包← 正确答案
  • C.与 PDF 相同的页面快照
  • D.仅有纯文本编码的文件

答案:B。DOCX 是 ZIP 压缩包,正文、样式、媒体各是独立 XML 或资源文件。它不是二进制单文件,也不是页面快照,更不是纯文本。

Q6. 从 PDF 内容流抽取文本后,仍可能出现乱序段落,最稳妥的处理是?

单选题 · hard

  • A.直接把抽取结果按行号反序
  • B.结合版面分析做阅读顺序重排← 正确答案
  • C.强制使用 OCR 重跑一遍
  • D.丢弃所有无标题的段落内容

答案:B。乱序的根因是内容流顺序不同于阅读顺序,正确做法是用版面分析得到文字块坐标,再按阅读顺序重排。反序是瞎猜,OCR 对文本型 PDF 是降级,丢段落更不可取。

Q7. 解析质量评估中,CER 指标衡量的主要是?

单选题 · easy

  • A.文档中图片数量的占比
  • B.字符层面差异的错误率← 正确答案
  • C.解析库占用内存的大小
  • D.向量检索延迟的高低

答案:B。CER 通过比对解析结果与参考文本的字符差异,衡量字符层面的错误率。它不关心图片数量、内存占用或检索延迟。

Q8. 下列哪些因素会导致 PDF 提取文本质量下降?

多选题 · mid

  • A.文档包含分栏排版← 正确答案
  • B.字体被内嵌为子集← 正确答案
  • C.页面包含页眉页脚← 正确答案
  • D.文件命名包含中文

答案:ABC。分栏会让抽取顺序左右穿插,字体子集化可能导致字符映射乱码,页眉页脚会混入正文;这三种都是经典的解析质量杀手。文件命名与文本提取质量无关。

Q9. 在 RAG 中,Word 解析保留标题样式信息的意义包括?

多选题 · extreme

  • A.方便按标题层级设计切块← 正确答案
  • B.帮助检索时识别段落主题← 正确答案
  • C.能直接跳过正文只索引目录
  • D.可减少向量化模型的参数

答案:AB。标题样式是天然的切块锚点,按“标题-正文”切块能让块语义自洽,也便于检索时判断段落主题。只索引目录会丢失内容,标题样式与向量模型参数无关。

Q10. 关于 OCR 兜底方案,下列说法正确的有?

多选题 · mid

  • A.扫描型 PDF 通常需要 OCR← 正确答案
  • B.图像预处理能提升识别率← 正确答案
  • C.语言模型可纠正识别结果← 正确答案
  • D.OCR 准确率永远高于文本抽取

答案:ABC。扫描型 PDF 没有文字层只能靠 OCR;灰度化、倾斜校正等预处理能显著提升识别率;语言模型可以对识别结果纠错。但 OCR 准确率通常低于文本抽取,D 说法错误。