跳到主要内容

Word 和 PDF 怎么进来 — 文档的结构本身就是信息

这一章讲三件事: 从一份文档里到底该带走什么(答案不只是「文字」); 为什么「这段是标题、那段是列表」这种信息值钱; 以及为什么这本书主张把所有格式先转成 PDF。 位置:这是入库线的第一步——读进来。读完接第 03 章(表格)和第 04 章(录音与图片)。

1. 先看现象:同一份文档,读法不同,后面差很多

设想你把一本三百页的员工手册喂进系统。用户问:「试用期请假怎么算?」

  • 读法 A: 只把三百页的文字连成一大串存进去。 系统搜回来一段讲请假的话,答对了——但你问它「这在第几页」,它答不上来; 而且它可能把「实习生请假」那一节的内容当成试用期的答上来;
  • 读法 B: 除了文字,还记下每段属于哪一章、在第几页、是标题还是正文。 同样答对,而且能告诉你「见第 4 章第 87 页」,还能先锁定「试用期」那一章再往下找。

读法 B 贵不了多少,但后面每一步都因为它变简单。

这一章讲的就是:读法 B 到底多带走了什么东西。

2. 顶层全景

一份文档

├─(读法 A)只抽纯文字 ────────────→ 一大串字

└─(读法 B)拆成一个个元素 ───────→ 每个元素带三样东西:
① 这段文字是什么
② 它是什么类型(标题/正文/列表项)
③ 它从哪儿来(哪个文件、第几页、什么时候改的)

图说:②和③就是这一章的全部内容。它们不参与「意思相近」的比对,但决定了检索能不能收窄、
答案能不能给出处。

先把③那个东西的名字说清楚:这类「关于内容的说明信息」叫元数据—— 文件名、页码、作者、创建日期、最后修改时间都算。 它本身不是内容,它是内容的标签。 这个词第 04、05、06 三章都会用到。

3. Word:两种读法,差别在「要不要类型」

读法 A:只要字

书里说,最流行的库是 python-docx1。它把文档当成一串段落, 你挨个取出每段的文字、拼成一整串,就完事了。

什么时候够用: 文档本身就是平铺直叙的散文,没有表格、没有列表、没有分章。

读法 B:连类型一起带走

如果你的系统需要区分标题、正文、列表项这些东西,书里推荐另一个库 unstructured。 它的作用是把文档拆成一个个带类型的结构元素1

「把一个文件读开、拆成机器能一件件处理的部件」这件事,行话叫解析。 上面这个库干的就是解析:同样一份 Word 文档,它吐出来的不是一大串连着的字, 而是一个个已经分好类的部件。后面每次说「解析」,说的都是这个动作。

拆出来的每个元素带着:

元素带的东西例子
文字本身「试用期内每月可请事假两天。」
类型Title(标题)、NarrativeText(正文)、ListItem(列表项)2
元数据文件路径、最后修改时间

书里的做法是:挨个走一遍这些元素,给每个元素攒一个字典 (程序里的「字典」不是查词用的那种词典,它是一张「一栏名字配一栏值」的小表—— 「页码」这一栏填 87,「类型」这一栏填「标题」),把你想留的项目填进去3

4. 为什么「类型」值钱:它让检索能分两步走

这一节是这一章真正的核心,别跳。

先看问题

第 01 章说过,检索是「把问题变成一串数,在库里找最近的几块」。 这是一次平铺的比对——三百页手册切出来的两千块,一视同仁地跟问题比一遍。

块越多,越容易撞上「看着像、其实不相干」的那几块。

书里给的解法

作者举了一个特别好懂的例子:一本各章之间毫不相干的书, 可以先找出最匹配的章标题,再在那一章里面找具体信息4

笨办法:两千块,一次性全比一遍 → 从政治新闻里翻出一段体育新闻的话,也不奇怪

两步走:先比二十个章标题,锁定一章 → 再在这一章的八十块里比
└─ 搜索范围从 2000 缩到 80,而且缩掉的那 1920 块「本来就不该被考虑」

图说:第二步比第一步准,不是因为算法变好了,是因为不相干的东西被提前踢掉了。

(这张图里的 2000 块、二十个章标题、80 块、1920 块都是为演示编的,不是真实数值。 书里在这里只给了做法——先找最匹配的章标题、再在那一章里找——没有给任何数。)

作者的原话是:知道文档结构,可以让检索这一步做得更好4。这句话背后的道理值得单独记住:

结构信息不参与「意思像不像」的计算,它参与的是「哪些块压根不该进入比较」。

这是两件完全不同的事,而后者往往更管用——因为排除比排序容易得多

这条思路在第 06 章会变成一个通用做法

先记下这个名字:靠元数据把搜索范围提前收窄,叫元数据过滤。 这一节看到的是它最朴素的形态(拿章标题当过滤条件), 第 06 章会讲它一般化之后长什么样、以及它救的是哪一类错误。

5. 复杂文档怎么办:各个部件分头处理

一份真实的文档很少只有文字。作者的建议是: 对带列表、表格、图片的复杂文档,给每一类元素各建一条处理流水线5

  • 图片 → 让模型写一段话解释图里是什么;
  • 表格 → 让模型提炼出关键结论。

注意这句话的分量: 它意味着入库不是「读一遍」,而是一次分诊—— 先看清这份文档由哪几种东西组成,再把每种交给不同的处理办法。 具体怎么做在第 04 章。

6. PDF:为什么它成了这条链上的中心

现象先行:你手上的文件多半是 PDF

作者的观察很实在:PDF 是最常用的信息分享格式—— 不管原件是 Word 还是幻灯片,对外发之前通常都会转成 PDF。 结果就是大多数文档你拿到手时已经是 PDF 了6

读 PDF 时多带走的那样东西:页码

书里用 PyPDF2 这个库来读(这个库今天不该再用了,见第 8 节)。 它的做法是逐页读:打开文件 → 一页一页取出文字 → 把页码连同文字一起存下来7

每页存下来的小字典里装着:页面文字、文件名、生成这个 PDF 的软件、页码、页面上的图片8

为什么页码这么重要? 书里给了这一章最实用的一句话:

页面上的文字接下来会被切成小块、变成一串数, 而这里抽出来的元数据会跟着每一小块走—— 这让我们能给出正确而且详细的出处9

第 87 页的文字 ──切块──→ 块 A、块 B、块 C
│ │ │
└──────┴─────┴── 每块都贴着「员工手册.pdf,第 87 页」

图说:元数据是「跟着块走」的,不是「存在文件那一层」的。这是最容易做错的地方——
一旦在切块时把它丢了,后面再也补不回来。

判断(我们的,不是书里的): 这一步的价值被大多数教程低估了。 一个能给出「见第 87 页」的系统,和一个只会说「根据资料」的系统,可信度差一个量级—— 因为前者可以被当场查证,后者只能被相信。 而查证成本正是用户愿不愿意把这套东西用在正经工作上的分水岭。 如果错,会错在: 如果你的场景里用户根本不会去核对(比如闲聊式问答), 那这一层的收益就只剩「心理上更可信」,不值得为它把流水线搞得更繁琐。

那个反直觉的策略:把所有格式先转成 PDF

这是这一章最值得带走的工程判断,作者说了两遍。

第一遍在讲 Word 时:unstructured 这个库对 PDF 尤其强; 当你的加载流水线越来越复杂时,只把一种文档类型打磨好,会让日子好过很多—— 与其为不同类型各养一条流水线,不如只养一条专门伺候 PDF 的, 在它前面加一步:把所有其他格式先转成 PDF10

第二遍在讲 PDF 时:业界工具里处理 PDF 的功能往往是最成熟、最先进的, 所以把整个应用围绕 PDF 来建是合理的11

常见做法:Word → 流水线1 这条策略:Word ─┐
PPT → 流水线2 PPT ─┼→ 转成 PDF → 唯一一条流水线
HTML → 流水线3 HTML ─┘
… → 流水线N

图说:N 条各自半吊子的流水线,换成 1 条被打磨得很好的。代价见下。

代价,作者也写了: 转换过程中别把元数据、排版、图片、可交互元素弄丢了12

判断(我们的,不是书里的): 这条策略成立的前提是**「转换是无损的」,而这个前提经常不成立**。 一份 Word 文档里的标题层级(哪一级是章、哪一级是节)在转成 PDF 之后, 通常退化成「字号更大的一行字」——你刚在第 4 节费劲拿到的结构,又丢回去了。 所以这条策略真正适合的是:你的来源本来就以 PDF 为主,零星几个别的格式懒得单独处理。 反过来,如果你手上大部分是结构清楚的 Word,或者是 Markdown(一种用 #、星号这类符号 直接把排版写在纯文本里的写法,程序员写文档常用它),那转成 PDF 是净亏如果错,会错在: 如果你用的转换工具能把标题层级完整写进 PDF 的书签和标签结构里, 并且你用来解析的工具读得出来,那这条反驳就不成立。 判据是:转完之后再拆一遍,还分不分得出「章」和「节」。

7. 作者的判断与证据:哪些有依据,哪些是经验

这一章几乎全部是从业经验,不是实验结论。分清楚:

说法属于哪一类
PDF 是最常用的分享格式可观察的事实,但书里没给数据
处理 PDF 的工具最成熟作者的观察;方向上可信,没有证据
知道文档结构能让检索更准机制上成立(排除了不相干的块),书里没做对照实验
「把所有格式转成 PDF」作者的工程主张,他自己也标了前提(别丢细节)
unstructured 对 PDF 尤其强作者的经验,没有对比数据

这本书没有做过一次 A/B 对照。 十九个配方里没有任何一处给出 「用了这个做法,检索准确率从 X 涨到 Y」。读的时候要一直记着这一点。

8. 边界与局限:三件必须知道的事

第一,书里用的 PDF 库已经停更了

书里从头到尾用 PyPDF2这个库的最后一版停在 2022 年 12 月 31 日, 官方公告写得很清楚:PyPDF2 的 3.0.X 是最后一个版本,开发转移到改名之后的 pypdf13

怎么改: 把代码开头那句「我要用 PyPDF2」换成「我要用 pypdf」, 底下各个零件的名字基本没变。 这是全书最容易踩、后果也最轻的一处过时。 (全书四处过时用法的完整清单在第 07 章第 6 节,那一张表才是正本,这里只说和本章有关的这一处。)

第二,今天读 PDF 有比书里更好的选择

书里给的两个选项是「只抽文字的 PyPDF2」和「拆结构的 unstructured」。 这本书之后又出现了一类专门做文档解析的工具,比如 IBM 在 2024 年 8 月放出的 Docling—— 它用专门训练的模型分析版面布局、识别表格结构,把 PDF 转成有结构的格式, 而且能在普通机器上跑14

判断(我们的,不是书里的): 这一处不是「书写错了」,是这一年里工具换代了。 书里那条「围绕 PDF 建一条流水线」的策略反而因此更成立了—— 专门为 PDF 打磨的工具确实在变强,而其他格式没有等价物。 如果错,会错在: 如果这类新解析器在你的文档上(比如满是手写涂改的扫描件)效果不如 老办法加人工规则,那就该按实际效果选,而不是按新旧选。

第三,书里的代码有真实的错误

这一章涉及的两段示例代码,照抄都会崩。

先立一个后面还要用的词:程序里一个起了名字的格子,用来暂存东西,行话叫变量file_path 就是一个变量的名字,格子里装的是一串文件路径。

读 Word 那段: 它先建了一个空的文字容器,然后叫这个容器「往列表末尾添一项」—— 文字容器压根没有这个动作,程序当场报错15

读 PDF 那段: 那个变量叫 file_path,里面装的却是一个 .docx 结尾的 Word 文件路径,然后把它交给了 PDF 读取库16

全书九处代码错误逐条写清「错在哪、会不会当场报错」的清单在第 07 章第 5 节—— 那一张表是正本,这里只点名和本章有关的两处。 再说一遍:读它的思路,不要读它的字符。

9. 可带走的

  1. 从文档里带走的不该只有文字,还有「这段是什么类型」和「它从哪儿来」;
  2. 元数据 = 关于内容的说明(文件名、页码、作者、修改时间),它是内容的标签不是内容;
  3. 只要字用 python-docx 那一路;要结构用 unstructured 那一路,后者把文档拆成带类型的元素;
  4. 结构信息的真正用处不是排序,是排除——先锁定一章再往下找,搜索范围从两千块缩到八十块;
  5. 排除比排序容易得多,这是提高检索质量最省力的一招(第 06 章展开);
  6. 页码要跟着每一小块走,不是存在文件那一层——切块时丢了就永远补不回来;
  7. 能给出「见第 87 页」的系统,可信度高一个量级,因为它能被当场查证;
  8. 「把所有格式先转成 PDF、只养一条流水线」是一条真实可用的策略, 但前提是转换别把结构弄丢——手上以 Word/Markdown 为主的话,这么做是净亏;
  9. 复杂文档要分诊:图片交给看图模型写描述,表格交给模型提炼结论(第 04 章);
  10. PyPDF2 已停更,换 pypdf;今天还有 Docling 这类专门的文档解析器可选;
  11. 这本书没有做过一次对照实验——所有「这样更好」都是经验之谈。

10. 原文地图

主题原书章原文位置
两个 Word 库、拆成结构元素Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:61(搜「The most popular library is python-docx」) · text/04-ch01-chapter-1-loading-data.txt:61(搜「break down the document into its structured elements」)
元素带类型和文字Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:90(搜「each with a text attribute for the element」) · text/04-ch01-chapter-1-loading-data.txt:113(搜「The result is a list of elements」)
复杂文档分部件处理Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:117(搜「we can build customized processing pipelines for each element type」) · text/04-ch01-chapter-1-loading-data.txt:117(搜「For images, we might create summaries explaining the content using LLMs」)
结构能优化检索、先找章标题Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:119(搜「Knowing the document structure can also optimize the retrieval step」) · text/04-ch01-chapter-1-loading-data.txt:119(搜「first find the best matching chapter title」)
把所有格式转成 PDF(第一遍)Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:123(搜「convert all other document types into PDFs」) · text/04-ch01-chapter-1-loading-data.txt:123(搜「optimizing it for one type of document can make your life easier」)
逐页读、存页码Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:133(搜「store the exact page number along with the text」) · text/04-ch01-chapter-1-loading-data.txt:147(搜「the dictionary contains the page text, filename, producer, page number, and images」)
元数据跟着每一块走、给出准确出处Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:177(搜「give the correct and detailed references」)
PDF 是最常用的分享格式Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:181(搜「PDF is the most commonly used file format for sharing information」)
围绕 PDF 建应用(第二遍)与代价Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:183(搜「the most advanced and mature」) · text/04-ch01-chapter-1-loading-data.txt:184(搜「your whole RAG application around PDF files」) · text/04-ch01-chapter-1-loading-data.txt:185(搜「lose any important details like metadata」)
两处代码错误Chapter 1. Loading Datatext/04-ch01-chapter-1-loading-data.txt:88(搜「text.append(paragraph.text)」) · text/04-ch01-chapter-1-loading-data.txt:155(搜「pdf_files/2023_Jan_7_Feature_Engineering_Techniques.docx」)

Footnotes

  1. 出处:「Chapter 1. Loading Data」第 61 段(text/04-ch01-chapter-1-loading-data.txt:61,搜「The most popular library is python-docx」)与同段(text/04-ch01-chapter-1-loading-data.txt:61,搜「break down the document into its structured elements」)。原文的说法是:python-docx 适合创建和更新 Word 文件;要区分标题、段落、列表这些元素的话,去看 unstructured 2

  2. 出处:「Chapter 1. Loading Data」第 90 段(text/04-ch01-chapter-1-loading-data.txt:90,搜「each with a text attribute for the element」)。原文举的三个类型正是 TitleNarrativeTextListItem

  3. 出处:「Chapter 1. Loading Data」第 113 段(text/04-ch01-chapter-1-loading-data.txt:113,搜「The result is a list of elements」)。书里那段示例存的字段是元素编号、文件路径、类型、文字、最后修改时间。

  4. 出处:「Chapter 1. Loading Data」第 119 段(text/04-ch01-chapter-1-loading-data.txt:119,搜「Knowing the document structure can also optimize the retrieval step」)与同段(text/04-ch01-chapter-1-loading-data.txt:119,搜「first find the best matching chapter title」)。「排除比排序容易」这句话是我们的概括,不是原文。 2

  5. 出处:「Chapter 1. Loading Data」第 117 段(text/04-ch01-chapter-1-loading-data.txt:117,搜「we can build customized processing pipelines for each element type」)与同段(text/04-ch01-chapter-1-loading-data.txt:117,搜「For images, we might create summaries explaining the content using LLMs」)。

  6. 出处:「Chapter 1. Loading Data」第 181 段(text/04-ch01-chapter-1-loading-data.txt:181,搜「PDF is the most commonly used file format for sharing information」)。

  7. 出处:「Chapter 1. Loading Data」第 133 段(text/04-ch01-chapter-1-loading-data.txt:133,搜「Use the PyPDF2 library to load PDFs」)与同段(text/04-ch01-chapter-1-loading-data.txt:133,搜「store the exact page number along with the text」)。书里对这个库的介绍见第 141 段(text/04-ch01-chapter-1-loading-data.txt:141,搜「open-source library for splitting, merging, cropping」),并提到它底层靠 Pillow 抽图片(text/04-ch01-chapter-1-loading-data.txt:141,搜「It uses Pillow under the hood」)。

  8. 出处:「Chapter 1. Loading Data」第 147 段(text/04-ch01-chapter-1-loading-data.txt:147,搜「the dictionary contains the page text, filename, producer, page number, and images」)。顺带一提,书里第 145 段把这个库返回的 pages 说成「一个字典的列表」(text/04-ch01-chapter-1-loading-data.txt:145,搜「which is a list of dictionaries」),这是不准确的——它是页面对象的列表,不是字典。

  9. 出处:「Chapter 1. Loading Data」第 177 段(text/04-ch01-chapter-1-loading-data.txt:177,搜「give the correct and detailed references」)。

  10. 出处:「Chapter 1. Loading Data」第 123 段(text/04-ch01-chapter-1-loading-data.txt:123,搜「unstructured is especially powerful for PDFs」)与同段(text/04-ch01-chapter-1-loading-data.txt:123,搜「convert all other document types into PDFs」)。

  11. 出处:「Chapter 1. Loading Data」第 183 段(text/04-ch01-chapter-1-loading-data.txt:183,搜「the most advanced and mature」)与第 184 段(text/04-ch01-chapter-1-loading-data.txt:184,搜「your whole RAG application around PDF files」)。原书这一段在转码文本里被硬换行拆成了三行,所以「围绕 PDF 建应用」这句话跨了两段号。

  12. 出处:「Chapter 1. Loading Data」第 185 段(text/04-ch01-chapter-1-loading-data.txt:185,搜「lose any important details like metadata」)。

  13. 补充(不在书里):PyPDF2 的官方页面写明「PyPDF2==3.0.X 将是 PyPDF2 的最后一个版本,开发将以 pypdf==3.1.0 继续」,最后一版 3.0.1 发布于 2022 年 12 月 31 日。来源:PyPI「PyPDF2」https://pypi.org/project/PyPDF2/(查阅于 2026-08-25)。

  14. 补充(不在书里):Docling 由 IBM 团队发布,技术报告首次提交于 2024 年 8 月 19 日,作者为 Christoph Auer、Maksym Lysak 等;它是一个 MIT 许可的开源 PDF 转换工具,用 DocLayNet 做版面分析、TableFormer 做表格结构识别。来源:《Docling Technical Report》https://arxiv.org/abs/2408.09869(查阅于 2026-08-25)。

  15. 出处:「Chapter 1. Loading Data」第 88 段(text/04-ch01-chapter-1-loading-data.txt:88,搜「text.append(paragraph.text)」)。上一行把 text 设为空字符串,而字符串没有 append 方法;正确写法是用 += 拼接,或者先建一个列表再 join

  16. 出处:「Chapter 1. Loading Data」第 155 段(text/04-ch01-chapter-1-loading-data.txt:155,搜「pdf_files/2023_Jan_7_Feature_Engineering_Techniques.docx」)。路径落在 pdf_files 目录下,文件名却是 .docx