跳到主要内容

当关系本身就是答案

这一章讲三件事: 哪一类问题按意思搜永远搜不到、 把资料改写成什么样才答得了它、以及这么改要付什么。

它在全书链条上的位置:第 02 章那条走查的第 2 步和第 4 步被换掉了。 「切成块 → 每块换成一串数 → 比相近程度」—— 这一章把「块」换成了「点和线」,把「比相近程度」换成了「顺着线走」。

1. 这一章讲什么

三句话:

  1. 前面十四章一路把「按意思搜」建到了极致,而这一章开头就说了它的一个死穴: 检索是把每一块孤立地取出来的,它不知道各块之间怎么连;
  2. 解法是在存文字之外,再显式地把实体和它们之间的关系存下来—— 哪条条款属于哪份合同、下一条是哪条、这家公司在哪个国家;
  3. 这一章的诚实之处在于:它自己把代价写在了显眼的地方—— 「图带来比基础做法更高的复杂度和成本,需要仔细的数据建模、结构化的入库、更多的前期工作」。

2. 顶层全景:一个具体的问题,和它为什么答不了

先看一句真实的业务问题(这一章通篇都在回答它)1:

「哪些高支出的供应商,合同里缺少解约条款?」

── 按意思搜怎么处理它 ──
把这句话换成 1 536 个数 → 去库里找最像的几块

可是"最像"的块会是什么?
大概是一段讲解约条款的合同正文,或者一段讲采购支出的说明。
而这个问题要的东西,**在任何一块文字里都不存在**:
它要的是"这家公司的支出" 和 "这份合同里有没有某类条款"
这两件事**同时**成立 —— 而这两件事分别写在两个地方,
甚至"没有某类条款"这件事根本没有对应的文字。

── 图怎么处理它 ──
公司 ──有──→ 合同 ──有──→ 条款 ──属于──→ 条款类型

└ 属性:2024 年支出 = 480 000 欧元

于是这句问话可以变成一条能执行的指令:
"找出支出大于 X 的公司,
再看它的合同下面,属于『解约』这一类的条款有几条,
把条数为 0 的挑出来。"

图说:注意这里没有任何"像不像"的判断,全是"连没连上"。
这就是"关系本身就是答案"的意思。

3. 先看现象:孤立的块丢掉了什么

书这一章的问题陈述写得很准2:

向量搜索把每一块当成孤立的单元,不知道各块在更大的叙述里是怎么连起来的。 信息分散在长文档的多个部分、或者需要跨多个来源时,这种做法会漏掉相关语境—— 依赖关系、交叉引用、跨章节的关系全丢了。

这一段其实在算一笔账:切块那一刀,除了切开文字,还切掉了三样东西。

被切掉的一个具体的例子
归属这段话属于哪份合同、哪家供应商——块存进去之后,这个信息就只剩一个标注了
顺序「本条所述期限」里的「本条」是上一块——上一块是谁,块自己不知道
同类二十份合同里各有一条解约条款,它们互相不认识

第 06 章那道闸(先按标注缩范围)补的是第一样,而且只补了一半: 标注能告诉你「这块出自哪份文件」,但它没法告诉你「这份文件属于哪家公司、那家公司花了多少钱」。

4. 承重词一:知识图谱 —— 点,和带名字的线

这一章第一个承重词。一句话先给结论:知识图谱就是把资料写成两样东西—— 「点」(一个具体的东西:一家公司、一份合同、一条条款) 和「带名字的线」(两个点之间的一种关系:属于、下一条是、位于)。

书那张图长什么样

书用服务级别协议当例子——那是服务方和客户之间约定 「服务要达到什么标准、各自的责任、达不到怎么赔」的一种合同3

点(五种):
Company 一家公司
Address 一个地址
SLA 一份服务级别协议
Clause 协议里的一条条款
ClauseType 条款的类别(可用性 / 支持 / 维护 / 数据保护 / 责任 / 解约)

线(五种,每条都有名字和方向):
Company ──LOCATED_AT──→ Address 这家公司在这个地址
Company ──HAS_SLA─────→ SLA 这家公司有这份协议
SLA ──HAS_CLAUSE──→ Clause 这份协议里有这条条款
Clause ──NEXT────────→ Clause 这条条款的下一条是它 ← 保住原文顺序
Clause ──OF_TYPE─────→ ClauseType 这条条款属于这一类 ← 把跨供应商的同主题条款归到一起

图说:注意最后两条线各补了第 3 节那张表里的一样东西 ——
NEXT 补"顺序",OF_TYPE 补"同类"。
这两样正是切块时被切掉的。

它凭什么值得建

书自己给了一句总结,值得原样记住4:

图建模之所以有效,是因为它抓住了嵌入抓不住的关系—— 比如哪条条款属于哪份合同、哪条条款排在下一条。 这让你能跨文档比较条款、按阅读顺序往下走、或者核查一份合同是不是缺了某条必需的条款。

最后那半句是最值钱的:「核查一份合同是不是缺了某条」。 「缺了什么」这件事在文字里没有对应的内容——它只在结构里存在。

建之前有一件事必须先做

书给了一条很实在的工程忠告5:建库之前先建唯一性约束—— 也就是让数据库强制「同一个名字的公司只能有一个点」。

理由书写得很直白:没有约束的话, 那个「有就用、没有就建」的写法在匹配逻辑含糊时会创建出重复的点。

为什么这条重要: 图里出现两个「Acme 公司」的点, 它的合同会挂在其中一个上,它的支出属性会写在另一个上—— 于是第 2 节那条查询会永远查不到它,而且不报错。

5. 建图的那一步,建在一条脆弱的规则上

这是我们要在这一章重点说破的一处。

书的做法是:把协议文档按标题切开,每一节成为一条条款; 然后给每条条款判一个类别——而判类别靠的是关键词匹配6:

正文里出现 availability 或 uptime → 归为 可用性
support / response time / incident → 归为 支持
maintenance → 归为 维护
data protection / gdpr / privacy → 归为 数据保护
liability → 归为 责任
termination → 归为 解约
一个都不匹配 → 打成 Other(其他)

这条规则决定了全章的检索质量——因为后面那些查询, 几乎每一条都建立在「条款属于哪一类」这条线上。

判断(我们的,不是书里的):这条关键词规则是这一整章最脆弱的一环, 而书从没讨论过它的失效率。 一条写成「任何一方可提前九十天以书面形式结束本协议」的条款, 里面一个 termination 都没有,会被打成「其他」。 于是第 2 节那条「缺少解约条款」的查询会把这家供应商报为「缺条款」—— 而它其实有,只是没被认出来。 书唯一沾边的一句是在另一节泛泛说的「如果你的分类体系不完整或不一致, 相关内容就会被漏掉」7——但它没有把这句话和自己这段代码联系起来。 如果错,会错在: 如果你的合同全部出自同一套模板、用词高度统一 (大企业的标准采购合同常常如此),那关键词匹配的命中率可以很高,这条担心就过虑了。 判据是:抽 50 条被打成「其他」的条款,人工看一遍有几条其实是有类别的。

6. 第二个维度:把业务实体也放进来

这一节讲的是这一章真正的分界线,而书自己把这句话写得很重8:

前一节只建了文档结构(协议、条款、条款类型)。 这一节加上业务实体和它们的属性作为第二个维度。 结果是一张把内容结构和业务语境结合起来的混合知识图谱—— 这正是企业级的做法与只有文档的图谱之间的区别。

加进来的是什么

书从四份表格数据里导入8:

导入什么具体字段
公司名称、国家、行业
地址地址信息
年度采购支出spend_2024、支出类别——写成公司这个点上的属性
协议元信息生效日期、服务名、适用法律

它换来的能力,两句问话就说清了

书给的两个例子8:

「哪些高支出的供应商缺少解约条款?」 「给我看德国医疗行业公司的数据保护条款。」

这两句的共同点:它们需要合同正文和实体属性出现在同一条查询里。 而在只有文档的图里,「支出」「行业」「国家」这些字根本不存在。

它的代价:数据会过期

书给的代价很具体,而且是这一章唯一一条运维层面的9:

支出、地址、组织结构这些属性会随时间变化,必须和源系统保持同步。 数据陈旧会导致过滤错误——比如把一个其实不是低支出的供应商标成低支出。

注意这类错误的形状:它不报错,它静默地缩小搜索范围。 你会拿到一个看起来正常的结果集,只是里面少了几家公司。

书还给了跳过这一步的判据9: 如果你的问题纯粹是文档层面的(「Acme 的合同关于支持是怎么写的?」), 一次简单的条款查找就够了。业务数据只有在它真的改变了检索结果时才值得导入和维护; 如果这些属性很少影响挑选,就把它们留在图外面。

7. 承重词二:图查询语句 —— 三种检索姿势

这一章第二个承重词。一句话先给结论:图查询语句是一种专门用来「顺着线走」的写法, 它写出来的东西读起来像画图:(公司)-[有协议]->(协议)-[有条款]->(条款)

它长什么样

MATCH (c:Company)-[:HAS_SLA]->(s:SLA)
WHERE c.spend_2024 > $min_spend
OPTIONAL MATCH (s)-[:HAS_CLAUSE]->(cl:Clause)
-[:OF_TYPE]->(t:ClauseType {name: "Termination"})
WITH c, s, count(cl) AS num_termination
WHERE num_termination = 0
RETURN c.name AS company, c.spend_2024 AS spend_2024, s.id AS sla_id
ORDER BY spend_2024 DESC

一行行读:
MATCH 找出这样的连法:某家公司,连着它的某份协议
WHERE 只要 2024 年支出超过阈值的那些公司
OPTIONAL MATCH "试着"再往下连到解约类的条款 —— **连不上也不丢弃这一行**
count(cl) 数一数连上了几条
WHERE = 0 只留下一条都没连上的
RETURN 把公司名、支出、协议编号交出来

图说:整段里没有一处在比"意思像不像"。
而 OPTIONAL MATCH 那一行是它能回答"缺了什么"的全部秘密 ——
普通的 MATCH 连不上就整行丢弃,那样永远查不出"没有"。

这种写法有名字,叫 Cypher,是那个图数据库自带的查询语言—— 你在任何讲图数据库的材料里都会撞见它。

三种检索姿势,对应三种问题

书把这一节收在一段总结上10:

你已经知道什么怎么查用来干什么
知道具体是哪份文档 / 哪个实体直接从那个点出发,顺着线走到它的条款「Acme 的合同关于支持怎么说?」
想跨很多文档比较同类内容从「条款类型」这个点出发,把这一类的条款全收上来「各家供应商的解约条款都是怎么写的?」
业务规则优先先按属性把公司或合同筛一遍,再去看正文「德国医疗行业公司的数据保护条款」

它的长处和它的死穴

书说得很干脆10:

结构化查询又快又准,因为它只返回符合你的标签与过滤的节点。 它的局限是依赖图结构的质量。如果你的分类体系不完整或不一致,相关内容就会被漏掉。

这正是第 5 节那个判断块说的事——而书自己在这里承认了它,只是没有回头指自己那段代码。

8. 按意思搜和图查询是接力,不是替代

下一节就是来补上一节那个洞的。

做法三步

书的做法11:

① 给每条条款算出那 1 536 个数,写回图里 —— 存成条款这个点上的一个属性
② 在图数据库里建一个向量索引(维数 1536,按余弦相似度比)
③ 查的时候先按意思搜出候选,再顺着线走回去过滤

那条关键的查询,一行行读

CALL db.index.vector.queryNodes("clause_embeddings", $top_k, $embedding)
YIELD node, score
MATCH (node)<-[:HAS_CLAUSE]-(s:SLA)<-[:HAS_SLA]-(c:Company)
WHERE c.industry = $industry
RETURN c.name, s.id, node.title, node.text, score
ORDER BY score DESC

一行行读:
第 1–2 行 按意思搜:拿问题那一串数,在条款里找出最像的 top_k 条
每一条带一个相近程度分数
第 3 行 **注意箭头是反的** ——
从命中的条款往回走:它属于哪份协议,那份协议属于哪家公司
第 4 行 只留下行业对得上的
第 5–6 行 连公司名、协议编号、条款标题、条款正文一起交出来,按分数排序

图说:这就是"接力"的样子 —— 先用意思找到落脚点,再顺着线走回去看它的出身。
第 10 章那道"先缩范围"的闸在这里换了个位置:
那里是搜之前先筛,这里是搜之后顺着关系筛。

什么时候用哪个

书给的分界12:

用这种接力查询里既有「意思」又有「业务约束」的时候
只用图查询当「条款类型」这类标签本身可靠时,一条纯图查询更简单也更快

代价12:你要同时维护那些数和图结构两套东西,而按意思搜本身也要花钱和时间。 换来的是「图和向量各自都办不到的那类查询」。

它和普通做法的差别,书给了一句很好的总结

这一句值得记13:

图这套做法返回的不是孤立的块。 它把条款连同它相连的业务语境一起返回,这让答案更精确、也更能解释得清。

「更能解释得清」这半句常被忽略,但它是实际使用里最值钱的: 模型拿到的不是一段悬空的条款文字,而是「Acme 公司(德国、医疗行业)的第 7 号协议里的第 3 条」。

9. 主走查:那句「哪些高支出的供应商缺少解约条款?」

这是本章的主走查,前面每个机制都在它上面占一步。

(图的结构、那条查询语句、关键词分类规则、两句业务问话、 「先按意思搜再顺着线走回去过滤」的写法,都是书里的; 下面的公司名、支出金额、条款条数是我们为演示编的。)

第 0 步:入库,建出这张图

三份服务级别协议文档,按标题切开:
协议 SLA-001(Acme GmbH) → 8 条条款
协议 SLA-002(Borex AG) → 6 条条款
协议 SLA-003(Cimex SE) → 7 条条款

每条条款建一个点,并且:
挂到它所属的协议上 HAS_CLAUSE
和上一条连起来 NEXT ← 第 3 条的上一条是第 2 条
按关键词判一个类别,连过去 OF_TYPE

分类结果(我们编的):
SLA-001:可用性 2 · 支持 2 · 维护 1 · 数据保护 1 · 解约 1 · 其他 1
SLA-002:可用性 2 · 支持 1 · 维护 1 · 数据保护 1 · 其他 1 ← 没有解约
SLA-003:可用性 2 · 支持 2 · 数据保护 1 · 解约 1 · 其他 1

第 1 步:第二个维度灌进来

从表格导入,写成公司这个点上的属性:

Acme GmbH 国家=德国 行业=医疗 spend_2024 = 480 000 欧元
Borex AG 国家=德国 行业=制造 spend_2024 = 910 000 欧元
Cimex SE 国家=法国 行业=医疗 spend_2024 = 62 000 欧元

同时把每家公司连到它的地址(LOCATED_AT)和它的协议(HAS_SLA)。

第 2 步:执行那条查询(阈值设 200 000 欧元)

MATCH (c:Company)-[:HAS_SLA]->(s:SLA)
WHERE c.spend_2024 > 200000

→ 剩下 Acme(480 000)和 Borex(910 000);Cimex 的 62 000 被筛掉

OPTIONAL MATCH (s)-[:HAS_CLAUSE]->(cl:Clause)-[:OF_TYPE]->(:ClauseType {name:"Termination"})
WITH c, s, count(cl) AS num_termination

→ Acme : num_termination = 1
Borex : num_termination = 0 ← 一条都没连上,但这一行没有被丢弃

WHERE num_termination = 0

→ 只剩 Borex

RETURN → | company: "Borex AG" | spend_2024: 910000 | sla_id: "SLA-002" |

第 3 步:换一句问话,让两条路接力

「给我看德国医疗行业公司的数据保护条款。」

── 只用图查询 ──
按国家=德国、行业=医疗筛 → 只剩 Acme
顺着 HAS_SLA → HAS_CLAUSE → OF_TYPE=数据保护
→ 拿到 SLA-001 里那 1 条数据保护条款

可是:如果 Acme 那条条款写的是"个人信息的处理与留存",
关键词里一个 data protection 都没有 → 它被打成"其他"
→ 这条路查不到它

── 接力(按意思搜 + 顺着线走回去过滤)──
把问句换成 1 536 个数 → 在全部 21 条条款里按意思搜,取前 5:

条款 相近程度 出自
"个人信息的处理与留存"(SLA-001 第 5 条) 0.72 Acme
"数据保护与合规"(SLA-003 第 4 条) 0.70 Cimex
"保密义务"(SLA-002 第 3 条) 0.61 Borex
"数据备份与恢复"(SLA-001 第 6 条) 0.58 Acme
"责任限制"(SLA-002 第 5 条) 0.44 Borex

再顺着线走回去:node ← HAS_CLAUSE ← SLA ← HAS_SLA ← Company
Acme 德国 · 医疗 ✓ 留
Cimex 法国 · 医疗 ✗ 国家不对,筛掉
Borex 德国 · 制造 ✗ 行业不对,筛掉

→ 剩下 Acme 的两条,其中第一条正是被分类规则漏掉的那一条

(相近程度这些数是为演示编的。)

图说:第 5 节那条脆弱的分类规则,在这里被按意思搜补上了 ——
这就是书说的"下一节补这个洞"的具体样子。

第 4 步:把两条路的分工收成一句

它靠什么找 它答不了什么
纯图查询 标签和连线 标签打错的那些
纯按意思搜 那 1 536 个数 "缺了什么""属于谁""下一条是哪条"
接力 先意思、后关系 ——

图说:注意中间那一行的第二格 —— 那正是这一整章存在的理由。

10. 三种优化,都是把工作从查询时挪到入库时

书给了三种,而它们有一个共同的形状14:

优化它解决什么瓶颈具体做法
给每条条款写一段摘要取回很多条款,但只有几条真的相关用一个便宜的模型给每条条款生成一段摘要,存成属性——让模型能便宜地扫过很多条款,再去读全文
把常算的数先算好老是在重复算同样的计数或标志例如「每家公司每种条款类型有几条」,直接写成公司点上的属性
给整份协议也建一份向量索引用户问的是整份合同,而不是单条条款不必逐条款搜,直接在协议这一层搜

共同的形状,书自己点破了14: 这三种都是把工作从查询时挪到入库时——入库慢一点、存得多一点,换查询快一点、便宜一点。

两条纪律,而且第二条是全书隔了六章的呼应

纪律一14:

别一上来就加这些优化。先从基线图开始,量它的表现。 如果查询够快、token 用量可接受、答案准确,更简单的模型往往就够了。

纪律二14:

最终答案永远要取回完整的条款正文。 摘要和缓存值是用来过滤和排序的,不是用来替代原始数据的。

第二条正是第 05 章那条「摘要负责被搜到、原件负责被读懂」—— 同一条纪律,书隔了六章各说了一遍,而且一次都没有点破它们是同一件事。

这两条纪律各自的代价书也写了14: 摘要、预先算好的值、那些数,都必须随着数据变化保持同步,而且增加存储和入库时间。 它们最值钱的时候,是它们消除了一个已经被证实的成本或性能问题。

11. 作者的判断与证据

说法它是什么
「向量搜索把每一块当成孤立的单元」是机制事实,而且是这一章的地基
检索四步(初检找锚点 → 图扩张 → 过滤排序 → 组装生成)是做法描述;「通常走一到两跳」是作者给的经验值,没有来源
「图带来更高的复杂度和成本」作者的坦白,写在提醒框里,难得
那张图的结构(五种点、五种线)是这个跑例的设计,不是通用方案
「先建唯一性约束,否则会产生重复节点」是数据库的实现事实,而且是很实在的工程忠告
条款分类靠关键词匹配是代码事实。书没有讨论它的失效率——见第 5 节那个判断块
「图抓住了嵌入抓不住的关系」是机制推导,成立
第二个维度是「企业级」与「只有文档」的分界作者的判断,没有数据。 但它给的两句问话足以说明问题
「数据陈旧会导致过滤错误」是机制事实,而且是这一章唯一一条运维层面的代价
三种检索姿势作者的归纳,清楚且可操作
「结构化查询的局限是依赖图结构的质量」书自己承认的局限,写得很准
「先按意思搜再顺着线走回去过滤」是代码事实
「标签可靠时纯图查询更简单也更快」作者的判断,合理
三种优化都是把工作挪到入库时是机制归纳,书自己点破的
「最终答案永远取回完整正文」作者的纪律,没有实验支撑,但它和第 05 章那条是同一条
全章的实测一处都没有。 建图要多久、图查询比按意思搜快多少、加了摘要省了多少 token,一个数都没有

12. 边界与局限

  • 全章零实测。 这是这一章最大的空白:建一次图要多久、一条图查询要几毫秒、 三种优化各省了多少——一个数字都没有,而第 09 章至少给了两组毫秒数;
  • 条款分类那条关键词规则没有被检验过。 全章的检索质量建在它上面, 而书唯一沾边的一句写在另一节、而且是泛泛而谈;
  • 建图这一步是人手工设计的。 五种点、五种线全是作者按这个场景画的, 书没有讲换一个领域该怎么设计,也没有讲能不能让模型自动抽实体和关系—— 而后者是这个领域近几年最主要的话题;
  • 没有讲图怎么更新。 合同改了一版、新增一家供应商,这张图怎么增量更新,全书没有;
  • 「通常走一到两跳」没有依据。 为什么不是三跳?走多了会怎样?书没说;
  • 没有讲什么时候该放弃这条路。 书说了「关系要紧时才值得」, 但没有给一个可操作的判据——比如「实体少于多少个就别建」;
  • 这一章和前面十四章几乎是并行的两条线。 图这一套怎么和第 10 到 12 章那些招数 (按字面搜、重排、补上下文)配合,书一句都没有

13. 可带走的

  1. 有一类问题按意思搜永远答不了:它问的不是「哪段话跟这个意思像」, 而是「这些东西之间怎么连」。 典型的一句是「哪些高支出的供应商缺少解约条款?」;
  2. 切块那一刀切掉了三样东西:归属、顺序、同类。 第 06 章那道闸只补了第一样的一半;
  3. 知识图谱 = 点(一个具体的东西)+ 带名字的线(两个点之间的一种关系)。 书那张图里,NEXT 补的是顺序,OF_TYPE 补的是同类;
  4. 它最值钱的能力是「核查缺了什么」——因为「缺了什么」在文字里没有对应内容, 只在结构里存在;
  5. 建库之前先建唯一性约束。 否则会出现两个同名的点, 合同挂在一个上、支出写在另一个上,查询永远查不到,而且不报错;
  6. 书那套条款分类靠关键词匹配,匹配不上就打成「其他」—— 全章的检索质量建在这条脆弱规则上,而书没有量过它的失效率;
  7. 第二个维度才是企业级的分界:把业务实体和它们的属性(支出、国家、行业)也放进来, 于是合同正文和实体属性能出现在同一条查询里;
  8. 它的代价是这些属性会过期,必须和源系统同步。数据陈旧导致的过滤错误不报错, 它只是静默地少给你几家公司;
  9. 图查询里 OPTIONAL MATCH 是能回答「缺了什么」的全部秘密—— 普通的匹配连不上就整行丢弃,那样永远查不出「没有」;
  10. 三种检索姿势: 知道是哪份文档就直接查 / 要跨文档比同类就从类型出发 / 业务规则优先就先按属性筛;
  11. 按意思搜和图查询是接力,不是替代: 先按意思搜出落脚点, 再顺着线走回去按业务属性过滤。而标签本身可靠时,一条纯图查询更简单也更快;
  12. 三种优化(写摘要、预先算好常用的数、给整份合同也建索引) 共同的形状是把工作从查询时挪到入库时;
  13. 两条纪律:别一上来就优化,先量基线; 最终答案永远取回完整正文——摘要只用来过滤和排序 (这和第 05 章那条是同一条,书隔了六章没点破)。

14. 原文地图

主题原书章原文位置
孤立的块丢掉了什么Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:6(搜「treats each chunk as an isolated unit」)
图这套做法怎么补Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:8(搜「extracts entities, forms explicit relationships」) · text/34-ch09-chapter-9-graph-rag.txt:10(搜「pool of isolated embedding vectors」)
检索四步Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:20(搜「usually through one or two hops」)
代价的坦白Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:34(搜「additional complexity and cost」)
那张图的结构Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:50(搜「At the center is a Company node」) · text/34-ch09-chapter-9-graph-rag.txt:50(搜「NEXT relationships to preserve document order」)
唯一性约束Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:83(搜「MERGE」)
条款分类的关键词规则Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:202(搜「clauses that don't match any keyword pattern」) · text/34-ch09-chapter-9-graph-rag.txt:219(搜「return "Other"」)
为什么值得建图Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:267(搜「captures relationships that embeddings can't」)
第二个维度与那两句问话Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:417(搜「Which high-spend suppliers lack a termination clause」) · text/34-ch09-chapter-9-graph-rag.txt:427(搜「what distinguishes enterprise-grade graph RAG」)
数据陈旧的代价与跳过判据Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:423(搜「Skip enrichment if your questions are purely document-centric」) · text/34-ch09-chapter-9-graph-rag.txt:425(搜「Stale data leads to wrong filters」)
那条「缺解约条款」的查询Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:482(搜「high_spend_missing_termination」)
三种检索姿势与它的局限Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:546(搜「If business rules matter first」) · text/34-ch09-chapter-9-graph-rag.txt:550(搜「depend on the quality of the graph structure」)
按意思搜与图查询接力Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:660(搜「hybrid_search_by_industry」) · text/34-ch09-chapter-9-graph-rag.txt:698(搜「a pure Cypher query is simpler and faster」)
返回的不是孤立的块Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:552(搜「combines structure and semantics」)
三种优化与两条纪律Chapter 9. Graph RAGtext/34-ch09-chapter-9-graph-rag.txt:784(搜「moving work to ingestion time」) · text/34-ch09-chapter-9-graph-rag.txt:788(搜「Don't add these optimizations up front」) · text/34-ch09-chapter-9-graph-rag.txt:792(搜「Always retrieve the full clause text」)

Footnotes

  1. 出处:「Chapter 9. Graph RAG」第 417 段(text/34-ch09-chapter-9-graph-rag.txt:417,搜「Which high-spend suppliers lack a termination clause」)。书用这句话说明「把业务语境和合同内容组合起来」为什么关键。

  2. 出处:同章第 6 段(text/34-ch09-chapter-9-graph-rag.txt:6,搜「treats each chunk as an isolated unit」)。补法见第 8 段(text/34-ch09-chapter-9-graph-rag.txt:8,搜「extracts entities, forms explicit relationships」);两种做法的对照见第 10 段(text/34-ch09-chapter-9-graph-rag.txt:10,搜「pool of isolated embedding vectors」)。「切掉三样东西」这个归类是我们做的,书没有这么分。

  3. 出处:同章第 50 段(text/34-ch09-chapter-9-graph-rag.txt:50,搜「At the center is a Company node」)与第 50 段(text/34-ch09-chapter-9-graph-rag.txt:50,搜「NEXT relationships to preserve document order」)。服务级别协议是什么,书在同一段里给了定义:服务方与客户之间约定性能标准、责任与补救办法的合同。检索四步见第 16 到 24 段(text/34-ch09-chapter-9-graph-rag.txt:20,搜「usually through one or two hops」)。

  4. 出处:同章第 267 段(text/34-ch09-chapter-9-graph-rag.txt:267,搜「captures relationships that embeddings can't」)。

  5. 出处:同章第 83 段(text/34-ch09-chapter-9-graph-rag.txt:83,搜「MERGE」)。原文说没有约束的话,那个「匹配不到就创建」的写法在匹配逻辑含糊时会创建重复节点。「会永远查不到而且不报错」这个后果是我们推的,书没有展开。

  6. 出处:同章第 202 段(text/34-ch09-chapter-9-graph-rag.txt:202,搜「clauses that don't match any keyword pattern」),代码里的兜底返回见第 219 段(text/34-ch09-chapter-9-graph-rag.txt:219,搜「return "Other"」)。切分方式是按 Markdown 的二级标题正则切开,每一节成为一条条款。

  7. 出处:同章第 550 段(text/34-ch09-chapter-9-graph-rag.txt:550,搜「depend on the quality of the graph structure」)。这句话写在讲图查询的那一节,而不是写在那段分类代码旁边。

  8. 出处:同章第 427 段(text/34-ch09-chapter-9-graph-rag.txt:427,搜「what distinguishes enterprise-grade graph RAG」);两句业务问话见第 421 段(text/34-ch09-chapter-9-graph-rag.txt:421,搜「Show me data protection clauses for German healthcare companies」);导入的四类数据见第 417 段前后的正文。 2 3

  9. 出处:同章第 425 段(text/34-ch09-chapter-9-graph-rag.txt:425,搜「Stale data leads to wrong filters」)与第 423 段(text/34-ch09-chapter-9-graph-rag.txt:423,搜「Skip enrichment if your questions are purely document-centric」)。 2

  10. 出处:同章第 546 段(text/34-ch09-chapter-9-graph-rag.txt:546,搜「If business rules matter first」)与第 550 段(text/34-ch09-chapter-9-graph-rag.txt:550,搜「depend on the quality of the graph structure」)。那条查询见第 482 段(text/34-ch09-chapter-9-graph-rag.txt:482,搜「high_spend_missing_termination」)。补充(不在书里,来自通用知识):这种「像画图一样写查询」的语言叫 Cypher,是 Neo4j 这个图数据库带的查询语言;OPTIONAL MATCH 与普通 MATCH 的区别是前者匹配不上时保留左边那一行、右边填空值,所以才数得出「零条」。 2

  11. 出处:同章第 660 段(text/34-ch09-chapter-9-graph-rag.txt:660,搜「hybrid_search_by_industry」)是那个函数的定义,整段查询语句在其后几行。建向量索引那一步见第 620 段一带(text/34-ch09-chapter-9-graph-rag.txt:624,搜「vector.dimensions」)。

  12. 出处:同章第 698 段(text/34-ch09-chapter-9-graph-rag.txt:698,搜「a pure Cypher query is simpler and faster」)。代价见第 700 段(text/34-ch09-chapter-9-graph-rag.txt:700,搜「You must maintain both embeddings and graph structure」)。 2

  13. 出处:同章第 552 段(text/34-ch09-chapter-9-graph-rag.txt:552,搜「combines structure and semantics」)。

  14. 出处:同章第 784 段(text/34-ch09-chapter-9-graph-rag.txt:784,搜「moving work to ingestion time」);别急着优化那一条见第 788 段(text/34-ch09-chapter-9-graph-rag.txt:788,搜「Don't add these optimizations up front」);「永远取回完整正文」见第 792 段(text/34-ch09-chapter-9-graph-rag.txt:792,搜「Always retrieve the full clause text」);同步与存储的代价见第 790 段(text/34-ch09-chapter-9-graph-rag.txt:790,搜「must stay in sync as data changes」)。补充(不在书里):书在延伸阅读里点名的《Essential GraphRAG》(Tomaž Bratanič、Oskar Hane,Manning),我们书架上已有完整拆解,在 docs/essential-graphrag/ —— 那一本讲的是从零手写整条 GraphRAG 流水线(含让模型自动抽实体建图、社区划分、全局与局部检索),正好补上这一章没讲的那几处。 2 3 4 5