跳到主要内容

把资料变成文字(上) —— 文档、表格、数据库

这一章讲三件事: 「非结构化」到底是什么意思、四种最常见的资料各怎么变成文字、 以及表格为什么不能照文档那样处理

它在全书链条上的位置:第 02 章那条走查的第 0 步。 那条走查是从「一份干净的文本文件」开始的——而这一章讲的是那份文件从哪儿来。

1. 这一章讲什么

三句话:

  1. 你的资料是 Word、PDF、Excel、数据库,而这套系统从头到尾只处理文字;
  2. 每一种资料变成文字的路都不止一条,而多走一道工序换到的是具体的东西, 不是「更规范」这种空话;
  3. 表格是个例外:它有三条根本不同的路,选错了会得到一个「答得出来但答错」的系统。

2. 顶层全景:四种资料,四条路

Word ─┬─ 全部段落拼成一个长字符串 → 快,结构全丢
└─ 逐个元素带上类型和时间 → 慢,但买到了三样东西(第 4 节)

PDF ─┬─ 文字本来就是字符 → 直接抽出来 (第 5 节)
└─ 文字其实是像素 → 这条路必然失败 → 第 05 章

表格 ─┬─ 每行改写成一句话 → 只能查单条
├─ 整张表塞进提示 → 表必须小
└─ 让模型写查询语句 → 唯一能做汇总的 (第 6、7 节)

数据库 ─── 它在这套系统里身兼二职:装业务数据 + 兼职当向量库(第 8 节)

图说:注意表格那一栏的分界线不是"表有多大",是"用户问的是哪一类问题"。

3. 承重词:非结构化数据

这一章第一个承重词。一句话先给结论:非结构化数据就是「字在那儿, 但没有任何一处记着这行字是标题还是正文、这个数属于哪一列」的那种资料。

先看现象:两份都是「有字的文件」,差别在哪

A. 一张数据库表
employee_id | name | title | salary
10023 | Sarah | Data Engineer | 98000

每一格是什么,表自己记着。你可以问"salary 这一列的平均值是多少",一句话就查得到。

B. 一份 Word 会议纪要
"……Sarah 转岗到数据工程岗,新的职级从下季度生效……"

"Sarah"是谁?是人名。谁记着这件事?没有人记着。
你只能靠读,或者靠一个会读的东西。

图说:B 里的字一个不缺,缺的是"这个字是什么角色"这层信息。
这就是"非结构化"这三个字的全部意思。

书开篇给了一个数:「企业信息大约 80% 是非结构化的」,散在演示文稿、文档、邮件、 媒体文件里1

判断(我们的,不是书里的):这个 80% 不能当事实用。 书里没有给任何出处,而这个说法在业界流传了二十多年、来源一直可疑。 我们照实写:书里没交代它从哪儿来,我们也没能找到可靠的原始来源。 它在这一章里的作用只是「数量很大」这个定性判断,而这个定性判断不需要靠那个数才成立—— 你自己公司的共享盘打开看一眼就知道了。 如果错,会错在: 如果确实存在一份可靠的原始调查而我们没找到, 那这条批评就下重了;但即便如此,书没给出处这件事本身仍然成立。

为什么这件事对这套系统要紧

书把这一章的任务写成一句话:把各种格式变成一致的文本表示, 因为大多数检索器工作在文本上2

「一致」这两个字是重点。 检索器不认识 Word、不认识 PDF、不认识 Excel—— 它只认识「一段文字 + 一串数」。所以这一章干的活,本质上是把十几种格式压成同一种形状

4. Word:多走一道工序,能换到什么

两条路

书给了两条,而且它们的差距不在代码量上,在丢掉了什么3:

做法结果
简单路把所有段落拼成一个长字符串结构信息全丢:标题和正文变成同一种东西
结构路逐个元素取出,每个元素带上它的类型(标题 / 正文 / 列表项)、一个编号、和最后修改时间慢一点、代码多一点,但保住了三样能用的东西

那三样东西各买到什么(这是这一节的重心)

书没有停在「保住了结构」这种空话上,它逐条说了每样能换到什么4:

① 保住"这是标题"
→ 检索时可以给标题更高的权重
→ 或者给标题单独造一份册子,先靠标题锁定相关的那一节,再只在那一节里搜
(书管这叫两段式检索,它说"当用户的查询能对应到具体的文档片段时,
这种做法胜过平铺的全文搜索")

② 保住"这是列表项"
→ 列表项常常本身就是一问一答的形状,可以直接抽成问答对

③ 保住"最后修改时间"
→ 检索时能按时间过滤,把过时的信息挡在外面

图说:三样各对应一种具体的检索动作。这就是"多走一道工序"的对价。

什么时候不值得

书给的反面判据同样具体5:

文档很短、或者内容很同质的时候,别这么干。 逐个元素处理的开销,只有在「不同元素需要不同处理」或者「检索确实能从结构过滤里获益」时才值回来。

翻译成可操作的一句: 如果你的资料是一堆没有标题、通篇一样长的段落, 那么保住结构什么也换不到——因为你没有标题可以拿来加权,也没有列表可以拿来抽问答对。

5. PDF:分水岭在「文字是字符还是像素」

这是这一章最该记住的一条判据

一份 PDF,你用鼠标去划它的正文:

能划中、能选中、能复制 → 文字是"字符" → 数字生成的 PDF,直接抽得出来
划不中、只能框出一块图 → 文字是"像素" → 扫描件,这条路必然失败

图说:三秒钟就能判完,而它决定了你要走的是这一章还是第 05 章。

书写得很直白:这条路只处理数字生成的 PDF——文字以可选中的字符形式存在的那种; 扫描件或者图片型文档里,文字是像素而不是字符,它必然失败6。 那种情况要走文字识别或者多模态模型,在第 05 章

抽出来的时候顺手多存一样:页码

书给了两个用处,而且第二个才是重点7:

用处长什么样
检索前先按日期、作者、章节过滤少算一些(要比对的块变少了),也提相关性
溯源用户能跳回原文档的那一页去核对

第二条为什么重要: 回到第 01 章第 6 节——这套做法真正的价值是「答案可以被核对」。 而「跳回第 14 页」和「跳回这份 200 页的 PDF」,是完全不同的可核对程度。

一处必须纠正的:书里用的那个库已经停止维护

全书的 PDF 代码都在用 PyPDF28

补充(不在书里):PyPDF2 已经停止维护。 它的最后一个版本是 3.0.1,发布于 2022 年 12 月 31 日; 项目页上写着「PyPDF2==3.0.X 将是 PyPDF2 的最后一个版本,开发将以 pypdf==3.1.0 继续」。 换句话说,今天该装的是 pypdf,不是 PyPDF2 来源:https://pypi.org/project/PyPDF2/(查阅于 2026-08-29)。

而书自己在第 1 章那张库清单里,把 PyPDF2pypdf 两个都列了—— 它知道有两个,但正文代码一路用的是停止维护的那个。照着书敲,你会装到一个三年多没更新的库。

6. 承重词:表格的三条路 —— 分界线是问题的类型

这一章第二个承重词其实是一整套判据。一句话先给结论:表格不能像文档那样切块了事, 它有三条根本不同的路,而选哪条取决于用户会问哪一类问题。

先看现象:同一张表,三个问题

一张员工表,15 列、48 000 行(这是书里那个跑例的真实规模)

问题甲:"Sarah 的职位是什么?" ← 查单独一条
问题乙:"这张表里谁的工资最高?" ← 要看过全表才答得出
问题丙:"工程师里多少比例年薪过 10 万?" ← 要做汇总计算

图说:三个问题看着都是"问这张表",实际上要三种完全不同的处理。

三条路

书把它们并列成一张表,而且每条都给了硬限制9:

做法适合硬限制
① 每行改写成一句话把列名当成语境标签,一行拼成一句自然语言中等规模(几百到几千行)的单条查找做不了汇总、百分比、跨行比较
② 整张表塞进提示把整张表写进那段交给模型的话里小表(100 行以内),而且需要全表语境受上下文窗口限;表一大就贵;大数据上复杂查询表现差
③ 让模型写查询语句表先进数据库;用户提问时,模型读表的结构 + 问题 → 写出一句查询语句 → 执行 → 再把结果解释成人话大数据集,要汇总、过滤、复杂分析要建数据库、要生成查询逻辑,实现复杂

第三条路有个名字叫 text-to-SQL ——直译是「从人话到查询语句」, 说的就是「让模型替你写那句 SQL」。这个名字你出门会撞见,记一下。

那条最容易被忽略的提醒

书专门用一句话点了这件事10:

每行改写成一句话适合查找;而汇总类的问题(百分比、平均、计数)必须走第三条路。

为什么必须——这一步是这一节的机制核心:

每行改写成一句话之后,库里是这样的:

块 1:"The candidate 39 years old is working in the Private sector…"
块 2:"The candidate 51 years old is working in the Self-emp-not-inc sector…"

块 48000

现在问"工程师里多少比例年薪过 10 万":
检索会取回最像的 3 块 —— 也就是 3 个人的资料。
3 个人,算不出 48 000 人里的比例。

图说:问题不在于检索取回来的块"不相关",而在于
这个问题的答案根本不在任何一块里面 —— 它在全部块的关系里。

这是「按意思搜」这套做法的一条结构性边界,而且它在第 15 章会以另一副面孔再出现一次。

7. 主走查:那张 15 列 48 000 行的表,三个问题走三条路

这是本章的主走查。 用的是书里那个真实跑例:一份人口普查收入数据集, 存成 Excel,15 列、48 000 行11

(15 列、48 000 行、100 行以内、以及那句改写模板的写法是书里的; 下面的具体答案、行数统计、token 数都是为演示编的,不是真实数值。)

第 1 步:看一眼原始数据

age | workclass | education | occupation | native-country | income | …共 15 列
39 | Private | Bachelors | Data Engineer | United-States | >50K
51 | Self-emp | Masters | Sales Manager | Germany | <=50K
…共 48 000 行

第 2 步:走第 ① 条路 —— 每行改写成一句话

书那个改写函数拼出来的句子长这样12:

"The candidate 39 years old is working in the Private sector.
The candidate was born in United-States, is Never-married
and has a Not-in-family relationship.
The candidate has a Bachelors degree and is working as a Data Engineer.
The income of the candidate is >50K."

注意它干了什么:列名变成了句子里的语境词。 occupation 这个列名没有出现,但「is working as a」这个说法把它的意思带进来了。 这一步之后,这 48 000 行就是 48 000 块普通文字,可以照第 02 章那条走查处理了。

问题甲:"Sarah 的职位是什么?"
→ 检索取回最像的 3 块
→ 其中一块是 Sarah 那一行改写成的句子
→ 模型读出 "is working as a Data Engineer"
→ 答:数据工程师 ✓

图说:这条路在单条查找上是成立的。

第 3 步:走第 ② 条路 —— 整张表塞进提示,当场撞上上限

问题乙:"这张表里谁的工资最高?"

这个问题必须看过全表才能答。于是把整张表写进那段话里:
48 000 行 × 15 列 ≈ 每行 40 个 token
48 000 × 40 ≈ 1 920 000 个 token

而书给这条路的适用范围是:100 行以内。
1 920 000 个 token —— 塞不进任何一个上下文窗口。

图说:这些 token 数是为演示编的。要看清的是形状——
这条路不是"慢一点",是"到某个规模之后直接不成立"。

如果这张表只有 80 行,这条路是三条里最好的:不用建库、不用切块, 模型看到的是完整的表,跨行比较对它来说很自然。上限卡在表的大小上,不在别处。

第 4 步:走第 ③ 条路 —— 让模型写查询语句

问题丙:"工程师里多少比例年薪过 10 万?"

① 先把这张表灌进数据库(这一步是离线做的,和第 02 章走查的入库线并列)

② 用户提问时,把表的结构 + 问题交给模型:
表 census 有这些列:age, workclass, education, occupation,
native-country, income, …
用户问:工程师里多少比例年薪过 10 万?

③ 模型写出一句查询语句:
SELECT
COUNT(*) FILTER (WHERE income = '>50K') * 100.0 / COUNT(*)
FROM census
WHERE occupation LIKE '%Engineer%'

④ 执行它,得到一个数: 37.4

⑤ 把这个数交回给模型,让它写成人话:
"在这份数据里,工程师中约有 37.4% 的人年薪超过 10 万。"

图说:37.4 这个数是为演示编的。要看清的是形状——
第 ④ 步的计算是数据库做的,不是模型做的。
模型只负责第 ③ 步(写出那句话)和第 ⑤ 步(把结果讲成人话)。

第 ④ 步那句「计算是数据库做的」是这条路成立的全部原因。 模型算术不可靠;而这条路根本没让它算——它只是把问题翻译成了一句能被精确执行的话。 (这个「模型只做决定、程序负责执行」的分工,在第 13 章会变成整套 agent 的内核。)

第 5 步:回头看这三条路

问题甲(查单条)问题乙(要全表语境)问题丙(要汇总)
① 改写成句子✗ 只看得到 3 个人✗ 同左
② 整表塞提示✓ 但浪费前提是表够小✓ 但模型算数不可靠
③ 让模型写查询✓ 但杀鸡用牛刀✓ 唯一可靠的一条

8. 生产里怎么办:按表的大小和问法混用

书没有让你三选一。它给的是一条按表分诊的规矩13:

什么样的表走哪条
小参考表(产品目录、员工名册)直接塞进提示
中等查找型(客户档案、交易日志)每行改写成一句话
大分析型(销售历史、传感器读数)必须让模型写查询语句

这条分诊规矩是这一节唯一要带走的东西。 它的形状和第 03 章第 6 节那条 「逐个流水线步骤地挑模型」一样:不是选一个最好的,是每一处各选各的。

9. 关系数据库在这套系统里身兼二职

这是第 09 章的入口,而它藏在第 3 章一个不起眼的地方。

书讲怎么连 PostgreSQL(一个常见的关系数据库)时,顺手说了一件后面要用到的事14:

同一个 PostgreSQL

职务一:装结构化的应用数据
会话记录、查询日志、用户反馈 —— 你的应用本来就要存的那些

职务二:靠一个扩展当向量库
存第 02 章那条走查里的那些"一串数",并且能搜

好处:省掉了"关系数据一套库、向量一套库"的两套运维

图说:这一条决定了第 09 章那道选择题的答案往往是"你已经在用的那个"。

它什么时候是对的选择,书给了很具体的三个例子15:

  • 按账户等级筛选客户的那些数;
  • 把产品描述和库存状态连起来;
  • 按关系表里的权限,把用户没资格看的文档排除掉。

反过来,它什么时候不该选:纯粹的相似度搜索、完全不需要和关系数据连接的时候—— 那时候专门的向量库性能更好、接口更简单16

(这条判断的完整版是第 09 章那条四步决策路径。)

10. 作者的判断与证据

说法它是什么
「企业信息约 80% 是非结构化的」书没给出处。 我们查不到可靠的原始来源。不许当事实用(见第 3 节的判断块)
「大多数检索器工作在文本上」是事实描述,和第 02 章那条走查一致
结构化加载换到的那三样是机制推导,不是经验:保住了标题就能加权,保住了时间就能过滤。这几条经得起推敲
「文档短或同质时别这么干」作者的经验判断,没有阈值,没有测量
PDF 的分水岭(字符 vs 像素)是事实,由文件格式本身决定
表格三条路的划分与各自的硬限制是机制推导。 尤其「汇总必须走第三条」那一条,理由在原理上成立(见第 6 节走查)
「小于 100 行」这个界作者给的经验数,书没说它是怎么定的
生产里按表分诊的那张表作者的经验做法。 三行都合理,但没有任何一行有数据支撑
数据库身兼二职是事实描述;「省掉两套运维」这条判断也站得住
「面对多种格式时,先全部转成 PDF」作者的一条有争议的工程主张,见下一节

11. 边界与局限

那条有争议的主张:先全部转成 PDF

书在第 3.1 节末尾放了一句很硬的建议17:

「大多数加载库(Unstructured、Docling)在 PDF 上尤其成熟。 面对多种格式时,先全部转成 PDF。」

它的道理是清楚的: 你不必为 Word、PPT、HTML、Excel 各养一条流水线, 只养一条 PDF 流水线,别的格式先转过来。运维成本从 N 条降到 1 条。

判断(我们的,不是书里的):这条建议成立的前提,书没有写出来—— 「转换本身不能把结构弄丢」。 而转成 PDF 恰恰是最容易丢结构的一种转换: Word 里那个「这是一级标题」的标记,转成 PDF 之后只剩下「这行字更大更粗」; Excel 里的行列关系,转成 PDF 之后只剩下一堆按位置排布的字。 也就是说,这条建议可能会把第 4 节那三样刚买到的东西又赔回去。 如果错,会错在: 如果你用的加载库在 PDF 上恢复结构的能力, 确实强过它在原格式上的能力(书说的正是这个),那么这笔账可能仍然划算。 判据是:拿你自己的十份文档,两条路各跑一遍,数一数标题识别对了几个。

书没覆盖的

  • 文档改了怎么办。 这一章讲的全是「第一次读进来」。 一份合同修订了、一行数据被删了,库里那些块怎么更新?全书没讲;
  • 一次读多少。 48 000 行的表要一次全灌进去吗?分批的话怎么保证不重不漏?没讲;
  • 抽出来的文字有多脏。 PDF 抽出来常常带页眉页脚、断词、栏序错乱, 这一章一个字没提(而它会直接污染后面每一步);
  • 表格三条路的成本对比。 书给了适用范围,但没给三条路各花多少钱—— 而第 ③ 条要多养一个数据库、每次提问多一次模型调用。

12. 可带走的

  1. 非结构化 = 字在那儿,但没有任何一处记着「这行是标题还是正文、这个数属于哪一列」。 这一章的活就是把十几种格式压成同一种形状:一段文字;
  2. 「企业信息 80% 是非结构化」这个数,书没给出处,别当事实用;
  3. Word 多走一道工序保住结构,换到的是三样具体的东西:标题能加权或单独造册、 列表项能抽成问答对、修改时间能挡住过时信息。文档短或同质时,这三样都换不到,就别走;
  4. PDF 的分水岭三秒钟能判:用鼠标划一下正文。 划得中是字符,直接抽;划不中是像素,这条路必然失败,走第 05 章;
  5. 抽 PDF 时顺手存页码。 它买到的是「跳回第 14 页核对」,而不是「跳回这份 200 页的文件」;
  6. 书里用的 PyPDF2 已停止维护,今天该用 pypdf(书自己那张库清单里两个都列了);
  7. 表格有三条根本不同的路,分界线是问题的类型: 查单条 → 每行改写成一句话;要全表语境 → 整表塞进提示(表须小于百行); 要汇总 → 只有「让模型写查询语句」这一条走得通;
  8. 为什么汇总只有那一条: 检索取回的是几块,而汇总的答案不在任何一块里面, 它在全部块的关系里;
  9. 生产里不是三选一,是按表分诊: 小参考表塞提示、中等查找型改写成句子、大分析型走查询语句;
  10. 关系数据库在这套系统里身兼二职:装应用数据 + 兼职当向量库,省掉两套运维—— 这是第 09 章那道选择题的一个常见答案;
  11. 「先全部转成 PDF」是一条有争议的建议: 它省下 N 条流水线, 但可能把第 3 条里刚买到的结构又赔回去

13. 原文地图

主题原书章原文位置
80% 非结构化(无出处)Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:4(搜「80% of enterprise information is unstructured」)
这一章的任务:变成一致的文本Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:8(搜「consistent text representation」)
对框架的表态(钉版本、隔离在适配层后面)Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:18(搜「frequent breaking changes」) · text/05-ch03-chapter-3-loading-data.txt:20(搜「pin dependency versions」)
Word 两条路Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:32(搜「Use python-docx for simple text extraction」)
结构化加载换到什么Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:96(搜「Titles can be assigned higher retrieval weights」) · text/05-ch03-chapter-3-loading-data.txt:98(搜「This two-stage approach outperforms flat text search」)
什么时候不值得Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:100(搜「Avoid this approach when documents are short」)
先全部转成 PDFChapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:104(搜「convert all to PDF first」)
PDF 用的那个库Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:120(搜「The PyPDF2 library」)
字符 vs 像素Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:164(搜「as pixels, not characters」)
页码的两个用处Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:166(搜「navigating to specific pages」)
表格三条路Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:187(搜「three approaches for handling tabular data」) · text/05-ch03-chapter-3-loading-data.txt:269(搜「What is Sarah’s job title?」)
数据集规模 15 列 48 000 行Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:197(搜「15 columns and 48,000 rows」)
每行改写成一句话的模板Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:211(搜「The candidate」)
汇总必须走第三条Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:230(搜「aggregate queries (percentages, averages, counts)」)
100 行以内那条界Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:275(搜「Small tables (< 100 rows)」)
按表分诊Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:287(搜「combine approaches based on table size」)
数据库身兼二职Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:343(搜「serves dual roles」)
什么时候该选它、什么时候不该Chapter 3. Loading Datatext/05-ch03-chapter-3-loading-data.txt:345(搜「joining structured filters with vector searches」) · text/05-ch03-chapter-3-loading-data.txt:347(搜「better performance for pure similarity search」)

Footnotes

  1. 出处:「Chapter 3. Loading Data」第 4 段(text/05-ch03-chapter-3-loading-data.txt:4,搜「80% of enterprise information is unstructured」)。原文没有给任何来源。

  2. 出处:同章第 8 段(text/05-ch03-chapter-3-loading-data.txt:8,搜「consistent text representation」)。

  3. 出处:同章第 32 段(text/05-ch03-chapter-3-loading-data.txt:32,搜「Use python-docx for simple text extraction」)。简单路的代码在第 52 到 58 段,结构路在第 68 段起(text/05-ch03-chapter-3-loading-data.txt:72,搜「partition_docx」);后者给每个元素带上类型(标题 / 正文 / 列表项)、编号和最后修改时间。

  4. 出处:同章第 96 段(text/05-ch03-chapter-3-loading-data.txt:96,搜「Titles can be assigned higher retrieval weights」)与第 98 段(text/05-ch03-chapter-3-loading-data.txt:98,搜「This two-stage approach outperforms flat text search」)。

  5. 出处:同章第 100 段(text/05-ch03-chapter-3-loading-data.txt:100,搜「Avoid this approach when documents are short」)。

  6. 出处:同章第 164 段(text/05-ch03-chapter-3-loading-data.txt:164,搜「as pixels, not characters」)。原文同时指出了两条替代路:文字识别引擎(3.6)和多模态模型(3.10),都在我们的第 05 章。

  7. 出处:同章第 166 段(text/05-ch03-chapter-3-loading-data.txt:166,搜「navigating to specific pages」)。

  8. 出处:同章第 120 段(text/05-ch03-chapter-3-loading-data.txt:120,搜「The PyPDF2 library」),代码见第 128 段(text/05-ch03-chapter-3-loading-data.txt:128,搜「import PyPDF2」)。补充(不在书里):PyPDF2 的最后一个版本是 3.0.1(2022-12-31),项目页写明后续开发转到 pypdf。来源:https://pypi.org/project/PyPDF2/(查阅于 2026-08-29)。书自己在第 1 章的库清单里两个都列了,见「Chapter 1. Getting Started with RAG」第 550 段(text/03-ch01-chapter-1-getting-started-with-rag.txt:550,搜「pypdf」)。

  9. 出处:同章第 187 段(text/05-ch03-chapter-3-loading-data.txt:187,搜「three approaches for handling tabular data」);三条路各自的适用与限制在表 3-1,见第 269 段(text/05-ch03-chapter-3-loading-data.txt:269,搜「What is Sarah’s job title?」)、第 275 段(text/05-ch03-chapter-3-loading-data.txt:275,搜「Small tables (< 100 rows)」)、第 281 段(text/05-ch03-chapter-3-loading-data.txt:281,搜「What percentage of engineers earn over $100k?」)。

  10. 出处:同章第 230 段(text/05-ch03-chapter-3-loading-data.txt:230,搜「aggregate queries (percentages, averages, counts)」)。

  11. 出处:同章第 197 段(text/05-ch03-chapter-3-loading-data.txt:197,搜「15 columns and 48,000 rows」)。用的是 Census Income 数据集。

  12. 出处:同章第 211 段(text/05-ch03-chapter-3-loading-data.txt:211,搜「The candidate」)。这是书那个改写函数拼出来的句子模板原文。

  13. 出处:同章第 287 段(text/05-ch03-chapter-3-loading-data.txt:287,搜「combine approaches based on table size」)。

  14. 出处:同章第 343 段(text/05-ch03-chapter-3-loading-data.txt:343,搜「serves dual roles」)。书说的那个扩展叫 pgvector,第 09 章会讲它具体长什么样。

  15. 出处:同章第 345 段(text/05-ch03-chapter-3-loading-data.txt:345,搜「joining structured filters with vector searches」)。

  16. 出处:同章第 347 段(text/05-ch03-chapter-3-loading-data.txt:347,搜「better performance for pure similarity search」)。

  17. 出处:同章第 104 段(text/05-ch03-chapter-3-loading-data.txt:104,搜「convert all to PDF first」)。顺带记一处口径: 这一章开头还有一段对框架的表态,是全书对框架最硬的一次——生产里若用编排框架,要钉死依赖版本、跟着升级指南、把框架相关代码隔离在小适配层后面;最稳的地基是成熟的通用库。见同章第 18 段(text/05-ch03-chapter-3-loading-data.txt:18,搜「frequent breaking changes」)与第 20 段(text/05-ch03-chapter-3-loading-data.txt:20,搜「pin dependency versions」)。