知识库与检索:把「找得到」做成一门手艺
这一章讲三件事: 一份文档是怎么 变成「可被精准找回」的(分块→嵌入→登记成可查的结构→融合→重排(把初筛结果重新精排));什么时候扁平检索不够用、要上结构化的组织方式;以及检索 和记忆怎么合流(双层记忆架构)。主走查是一道能拿笔手算的 BM25。
1. 这一章讲什么
把「从外部知识库找出相关片段、拼进上下文再让模型作答」这套做法叫检索增强生成,缩写 RAG。它解决的是一个朴素矛盾:模型训练数据有截止日期,而你的知识库随时在更新。 它的结构只有两句话:检索器负责从知识库里找出相关片段,生成器(通常 就是 LLM)拿着这些片段生成答案1。
但「找出相关片段」这五个字,展开是整整一套工序。本章就是这套工序的图纸。
2. 顶层全景
【离线索引】 文档 ─→ 分块 ─→ (可选:生成上下文前缀) ─→ 嵌入 ─→ 建索引
│
【在线查询】 问题 ─┬─ 稠密检索(懂语义)──┐
└─ 稀疏检索(懂关键词)─┴→ 融合 → 重排序 → 前 k 片段 → 拼进上下文
图说:上半是入库,下半是取用;两头由「嵌入向量是否忠实」连着。
分块切掉的上下文,是全章最后才补上的那个洞。
3. 核心原理
3.1 分块:为什么必须切,以及切的代价
文档入库前要分块(把长文档切成适合独立检索的片段)。为什么必须切? 两个原因:嵌入模型对输入长度有限制;而且一整篇文档压成一个向量时,多个主题 混在一起,向量「什么都像、什么都不像」2。
三种切法,代价各不相同:
| 策略 | 做法 | 代价 |
|---|---|---|
| 固定大小 | 按固定 token 数切,块间留重叠 | 无视结构——段落、代码、表格可能被拦腰截断 |
| 递归(大块先切、超长再切小)/结构感知 | 沿章节、段落、句子递归着切,超长再降级 | 生产系统的默认选择 |
| 语义切分 | 在语义「断崖」处下刀 | 要额外的嵌入计算3 |
块大小是典型权衡:太小,单块语义模糊(「该公司收入增长了 3%」——哪家公司? 哪个季度?);太大,一个块混杂多主题,向量被稀释,命中后还带进无关内容。 常见起点:每块 256-1024 token、相邻块重叠 10%-20%4。
书里还埋了一个贯穿全章的伏笔,先原样放在这里:无论哪种策略,分块都会切断 片段与原始上下文的联系——「该公司」指谁、出自哪份报告,留在了块的外面。 这个洞要到 3.6 节的上下文感知检索才补上5。
3.2 稠密(意思近=数字近)嵌入:把「意思」变成「方向」
稠密嵌入是检索的地基。嵌入(把词或句子转化成一串数字——「向量」——并且让语义相近的内容, 数字串也相近)。把这些向量想象成一张巨大地图上的点:意思越近,距离越近6。
衡量「近」的指标叫余弦相似度(两个向量夹角的余弦值,越接近 1 方向越一致): 我们关心方向而非长度,因为内容相同但长短不同的两篇文档,长度不同、方向一致7。 书里给了一个 3 维手算例(数字是书里的):「如何养猫」→(0.9, 0.5, 0.1), 「猫咪饲养指南」→(0.8, 0.6, 0.1),「股票投资策略」→(0.1, 0.1, 0.9); 前两者余弦相似度约 0.99(非常相似),与第三者只有 0.2 量级(不相关)8。
嵌入模型有一代际差:早期的 Word2Vec 靠词汇共现,能算出「king − man + woman ≈ queen」这类规律,但同一个词只有一个向量——「river bank」和「investment bank」里的 bank 拿到完全相同的向量;现代模型(如 BERT(一个老牌的语言理解模型)与 BGE-M3)结合上下文生成 向量,同一个词在不同语境下向量不同9。
海量向量怎么快速找出「差不多最近的那一个」?书里的对比实验给出两种主流 ANN 工具的取舍——拿一点精度(找得准的程度)换巨大的 速度: ANNOY 基于树,构建快、内存低,但不支持增量更新;HNSW 基于图,查询精度 极高、支持增量,但内存较高——需要实时入库的动态场景选 HNSW,静态库用 ANNOY 更省10。
3.3 主走查:手算一道 BM25
全部数字来自原书实验 3-5 的运行日志(N=10 篇的小语料(示例文档集),k1=1.5、b=0.75)。
稠密嵌入懂语义但「读不懂字面」;稀疏(只认字面、不认意思)检索相反。稀疏嵌入把文档表示成只有稀稀落落非零位的长向量,理论基石是词袋模型(只数词、不管语序)11。
这一路的代表是 BM25 算法(一套给「包含查询词的文档」打分的公式),它的核心直觉先用一个例子立起来:知识库 100 篇文章, 搜「模型蒸馏」——「模型」在 60 篇里都出现(太常见,没区分度),「蒸馏」只在 3 篇出现(稀有,区分度高),好算法应该给「蒸馏」更高权重12。两个量: TF(词在一篇文档里出现的次数)越高越相关;IDF(由包含该词的文档数 算出的逆文档频率)越高越稀有越值钱。
现在照书里的日志手算一遍。查询「 模型蒸馏」,在 10 篇文档的小语料上:
第 1 步 查询分词:[模型, 蒸馏]
第 2 步 查倒排索引(词 → 哪些文档含它,像书后的术语索引页):
模型:命中 3 篇,df=3,IDF = ln((10−3+0.5)/(3+0.5)) = 0.76
蒸馏:更稀有,IDF 更高
第 3 步 对每篇命中文档算分。看 doc_1:
「蒸馏」:TF=3,文档短(200 词),BM25 贡献 = 2.15
「模型」:TF=5,贡献 = 1.52
第 4 步 合计:doc_1 总分 = 3.67,遥遥领先
注意第 3 步里那件反直觉的事:doc_1 里「蒸馏」词频(TF=3)低于「模型」 (TF=5),但因为「蒸馏」更稀有(IDF 更高),它对得分的贡献(2.15)反而 超过「模型」(1.52)。稀有词压倒高频词,这就是 BM25 的全部灵魂13。 (两个修 正项:k1 让词频「饱和」——出现 20 次不等于 10 次的两倍那么相关; b 按文档长度归一化——短文档命中一次比长文档命中一次更值钱。)
稀疏检索的长短由此清楚:精确匹配在人名、代码、型号上表现极佳,却读不懂 同义表达——搜「kitty」找不到只写「cat」的文档。这个短板正好由稠密检索(用语义远近找)补, 于是有了下一节。顺带一提,稀疏检索也已进入「学习型」阶段(SPLADE 一类模型 给词项打权重、能扩展同义词),BM25 被选来讲原理,因为它无需训练、透明可手算14。
3.4 混合检索:两路并进,再加一个面试官
两路各有盲区(两路各自的核心部件都是编码器——把文字压成向量的部件):稠密懂语义但可能漏关键词(搜「HTTP-403」可能返回泛泛讨论 「机器那边报错了」的文章),稀疏精确但不懂同义。混合检索的思路朴素——两个引擎都跑、 结果合并;难点在两路得分不可直接比较,书里给的常用解法是 RRF(按排名 倒数计分融合,不比分数只比名次)15。
融合之后通常还有第三级:神经重排序。书里的比方精确得很:检索阶段的 双编码器(把查询和文档各算成一个向量,再比距离) 像猎头——靠预先抽取的特征大规模初筛,查询和文档从不「见面」; 重排序的跨编码器像面试官——把查询和候选文档放进同一个模型「面对面」 逐字斟酌,能捕捉初筛根本看不见的细微关联。三阶段各司其职:并行检索 → 融合 → 精排,后两者不互相替代16。
质量怎么量?书里给了三个核心指标:recall@k(正确文档出现在前 k 名的 查询比例——「该找的找到了吗」,最贴 RAG 需求)、MRR(正确答案平均排多靠前 ——「够不够快」)、nDCG(整列排序质量)。工业报告里常见「检索失败率」, 本质就是 1 − recall@20——看数字先问口径17。
3.5 扁平文本不够用:两个翻车案例
分块+检索这套「扁平」做法,遇到有内在结构的知识会翻车。书里的两个案例都值得 原样记:
黑猫白猫的计数问题:100 个案例文档(90 黑 10 白),问总数。检索只取 top-k,截断就数不准;分数参差、跨文档聚合对不齐。解法:预生成摘要, 一次检索拿到「90 黑 10 白」的结论,而不是让模型现场数18。
Xfinity 优惠规则的错误推理:三个孤立案例——退伍军人 John 获优惠, 医生 Sarah 获折扣,教师 Mike 不符合。护士来问,检索器因「护士」与「医生」 语义相近优先召回了 Sarah 的案例,模型于是错误推断护士也可享;而说明护士 不符的案例 C 根本没被召回。检索器没召回的,模型就当不存在19。
两个案例指向同一句话:注意力是基于相似度的软检索,不是思考引擎——必须 在索引(把整理好的知识登记成可查的结构)阶段投入提炼,不能指望模型在上下文里自己整理20。
3.6 结构化索引:RAPTOR 与 GraphRAG
结构化索引的思路:先建索引(把整理好的知识登记成可查的结构),即用 LLM 把知识整理一遍——归纳、抽象、建关联, 多花算力换检索质量。两条路21:
- RAPTOR:自下而上的递归抽象。文本块做叶子节点,语义相近的聚类分组, LLM 为每组生成父节点摘要,层层向上。检索时可以「跨层穿梭」:先在高层的 「SIMD 指令集演进」定位宏观概念,再下钻到叶子看 SSE 细节——适合 「从概念钻进细节」的查询22;
- GraphRAG:把知识建模成实体-关系图,三元组表达知识((北京, 是首都, 中国))。两个不可替代的能力:多跳推理(「我的医生所在医院的地址」 = 沿「用户→医生→医院→地址」的关系链走)与实体消歧(区分现实中两个 同名的张医生——这与「bank」的词义消歧是两回事)23。
但知识图谱有固有局限:语义降级。「如果下周还下雨,我就取消去海边的计划, 改成去博物馆」含条件与时间依赖,拆成三元组后只剩两条孤立事实,条件逻辑丢了。 所以书里的推荐是分层互补:核心信息用完整自然语言保存,结构化元数据做索引; 只在多跳推理与精确消歧的垂直场景(医疗、法律、家族关系)把图谱当专项索引24。
什么时候才需要这些?书里的判断标准很实用:查询主要是「找到包含某信息的片段」 (退款政策是什么),混合检索就够;经常要跨文档综合、多层次导航,才上 结构化索引——代价是大量 LLM 调用25。GraphRAG 的完整工程形态,我们的 frontier 书架有一份源码级拆解可对照(补充(不在书里,依据我们的 frontier 书架): 依据: shelf=ai-frontier-reference/graphrag#02-graph-extraction.md 事实=图谱构建分实体抽取与关系抽取两步,实体-关系三元组是图的基本组成块,与书里 的描述一致)。
3.7 文件系统范式与治理
第三种知识组织哲学来自工程界:OpenViking(字节火山引擎开源)把一切上下文—— 记忆、资源(只读的资料)、技能——组织成虚拟文件系统里的目录与文件。
每个条目拥有唯一地址(行话叫 URI,形式像网址):Agent 通过该地址访问知识, 框架在背后决定从内存、磁盘还是远程加载。
核心是 L0/L1/L2 三层按需加载:L0 摘要约 100 token(判断相关性)、 L1 概览约 2,000 token(规划决策)、L2 全文——与第 04 章 Skills 的渐进式披露 如出一辙。底层故意选 Markdown(带轻量格式标记的纯文本格式):用户可直接读改、Git 可版本控制、 Agent 能用通用文件工具自主记录。但有个易被忽视的前提:文件之间必须建链接 与索引——横向关联没人建,知识就成一堆孤岛,不同模型主动建链的意愿不同, 提示词必须写明26。
治理三件事,书里各给了一条硬原则:知识过期→选可增量更新的索引(ANNOY 要重建、 HNSW 可增量),给分块加版本号、生效/失效时间,在检索阶段就过滤失效内容; 权限→过滤必须下推到检索层,等文档已进上下文再拦截就晚了——进过上下文的 东西没法保证不泄露;租户隔离防串味27。
3.8 智能体(自主决策的 Agent)化 RAG 与双层记忆
传统 RAG 是单向管道:查询→检索→生成,一次定终身。智能体化 RAG 把检索 封装成工具,由 Agent 用「思考→行动→观察」的循环主导:像研究员反复查阅、 调整策略、交叉验证,而不是图书馆里搜一次就写报告。书里的中文司法问答实验 给出清晰的分界:简单问题(「正当防卫怎么规定的」)一次直查更快,质量相当; 复杂量刑问题(「醉酒过失致人重伤且有盗窃前科如何量刑」)则是多轮迭代的天下 ——第一轮并行三路搜索,评估后发现缺「累犯如何考量」的关键信息,二次聚焦 查询,最后综合——牺牲响应速度,换复杂问题上的鲁棒(稳、不容易被问倒)程度28。
安全暗面照例要提:检索到的文档正是间接提示注入最典型的载体——恶意指令 藏进会被收录的网页,等检索命中拼进上下文引爆。防御与 05 章同源:来源标记+ 指令数据分离,并且不让检索内容直接触发高风险操作29。
最后是本章的汇合 点。把检索技术反转回用户记忆:把对话历史本身当知识库(每 20 轮 分块索引),配上上下文感知检索——在索引前用 LLM 给每个块生成前缀摘要 (「这段是妻子 Patricia 设立初始电汇」),把分块切掉的上下文补回来。Anthropic 的数据:该技术结合 BM25 把检索失败率(1 − recall@20)降低 49%,再加重排 序降幅 67%,索引成本靠 prompt caching 控制在每百万文档 token 约 1 美元30。 要与第 05 章的「上下文感知压缩」划清界限:一个在索引期做加法(补背景), 一个在运行期做减法(裁剪历史),名字相近,方向相反31。
双层记忆架构就此合流:Advanced JSON Cards 常驻(结构化关键事实=概览) 加 上下文感知检索按需(原始对话细节=细节)——第三层的主动服务(护照 过期预警)正是靠这个组合实现的:先看卡片里的护照过期日,关联推理发现与机票 日期接近,再检索原始对话确认细节,最后主动建议加急续签32。
3.9 补充:从数据集里「挖」知识
RAG 的前提是知识以文档形式存在。但司法领域真 正的「判决知识」写在成千上万份 判例的权衡模式里。书里的做法分两阶段:知识提取(LLM 把案情转成标准化 JSON)、 因子分析(识别哪些因素最影响结果并量化(给每个因素打上数值)权重)。实验里两个细节聪明:因子发现 自下而上(让 LLM 自由列出可能因素,不预设模式);分类字段用独立开关位 编码(盗窃=[1,0,0],不用 1/2/3——免得模型误以为「诈骗的严重程度是盗窃的 3 倍」)33。
4. 作者的判断与证据
书里给了证据的: BM25 手算日志、ANN 对比、多模态三策略、智能体化 vs 非智能体化的司法问答、上下文感知检索的 49%/67%(转引 Anthropic)、双层记忆 的完整实验链(3-10/3-11/3-12)。
是作者的判断: 「结构化索引只在跨文档综合时才值得」是成本裁决;OpenViking 的 Markdown 选择被作者称为「看似反直觉但深思熟虑」,它的成败系于「文件间建链」 这个前提——这条前提的可证伪条件:若检索始终按目录纵向遍历而不依赖横向链接, 纯文本与数据库的差距会缩小。
5. 边界与局限
- 全章的嵌入、索引、融合参数(块大小、k1/b、RRF 常数)都是经验起点,不是 最优解——换语料要重调;
- 知识图谱的三元组语义降级没有通用解,书里只能给「分层互补」的折中;
- 上下文感知检索的 49%/67% 来自 Anthropic 的文档场景;对话记忆场景(实验 3-12) 里矛盾指令用例仍暴露局限——检索补回了上下文,但「三条指令哪条最后生效」 这类时序判断仍需额外的结构化状态;
- 多模态三策略各有胜场,「没有万能答案」是书里原话。
6. 可带走的
- RAG = 检索器 + 生成器;检索器质量决定上限;
- 分块是必要之恶:切小才能检索准,但切断了上下文——上下文感知检索是补洞的;
- 嵌入把意思变成方向;余弦相似度比长度,不管文本长短;
- 一词多义选上下文感知模型;静态词向量(一个词永远一个向量)过时了;
- BM25 的灵魂:稀有词压倒高频词;它透明可手算,是调检索的基准线;
- 混合检索三阶段:并行、融合(RRF 按名次不比分数)、精排(面试官式跨编码器);
- 指标先问口径:recall@k 管「找到没」,MRR 管「靠前没」,失败率=1−recall@20;
- 跨文档综合才上 RAPTOR/GraphRAG;只是找片段,混合检索就够;
- 权限过滤必须下推到检索层——进过上下文的拦不住;
- 双层记忆:结构化卡片当概览常驻,检索取细节按需——主动服务靠这个组合。
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| RAG 定义与两部件 | 3 用户记忆和知识库 | text/05-ch03.txt:263(搜「检索增强生成」) · text/05-ch03.txt:265(搜「检索器」) |
| 分块三策略 | 3 用户记忆和知识库 | text/05-ch03.txt:306(搜「分块」) · text/05-ch03.txt:310(搜「固定大小切分」) · text/05-ch03.txt:312(搜「递归」) · text/05-ch03.txt:314(搜「语义切分」) |
| 块大小权衡 | 3 用户记忆和知识库 | text/05-ch03.txt:316(搜「256-1024」) |
| 分块切断上下文(伏笔) | 3 用户记忆和知识库 | text/05-ch03.txt:318(搜「切断」) |
| 嵌入与向量空间 | 3 用户记忆和知识库 | text/05-ch03.txt:322(搜「嵌入」) |
| 余弦相似度 | 3 用户记忆和知识库 | text/05-ch03.txt:324(搜「余弦相似度」) · text/05-ch03.txt:326(搜「夹角」) |
| 3 维手算例 | 3 用户记忆和知识库 | text/05-ch03.txt:330(搜「0.9」) · text/05-ch03.txt:332(搜「1.03」) |
| Word2Vec 与上下文感知 | 3 用户记忆和知识库 | text/05-ch03.txt:338(搜「king」) · text/05-ch03.txt:340(搜「一词多义」) |
| ANN 索引对比 | 3 用户记忆和知识库 | text/05-ch03.txt:344(搜「ANN」) · text/05-ch03.txt:348(搜「增量」) · text/05-ch03.txt:376(搜「同等重要」) |
| 稀疏嵌入与词袋 | 3 用户记忆和知识库 | text/05-ch03.txt:380(搜「词袋」) |
| 100 篇文章的直觉 | 3 用户记忆和知识库 | text/05-ch03.txt:386(搜「蒸馏」) |
| TF-IDF | 3 用户记忆和知识库 | text/05-ch03.txt:388(搜「TF-IDF」) |
| BM25 手算日志 | 3 用户记忆和知识库 | text/05-ch03.txt:394(搜「k1」) · text/05-ch03.txt:398(搜「0.76」) · text/05-ch03.txt:404(搜「2.15」) · text/05-ch03.txt:408(搜「3.67」) |
| 稀疏优劣与学习型 | 3 用户记忆和知识库 | text/05-ch03.txt:410(搜「同义表达」) · text/05-ch03.txt:412(搜「SPLADE」) |
| 混合检索与 RRF | 3 用户记忆和知识库 | text/05-ch03.txt:416(搜「HTTP-403」) · text/05-ch03.txt:420(搜「三个阶段」) |
| 猎头与面试官 | 3 用户记忆和知识库 | text/05-ch03.txt:422(搜「猎头」) · text/05-ch03.txt:422(搜「面对面」) |
| 三个指标 | 3 用户记忆和知识库 | text/05-ch03.txt:426(搜「度量指标」) · text/05-ch03.txt:434(搜「前 k 个」) · text/05-ch03.txt:442(搜「检索失败率」) |
| 黑猫白猫与 Xfinity | 3 用户记忆和知识库 | text/05-ch03.txt:488(搜「黑猫白猫」) · text/05-ch03.txt:490(搜「Xfinity」) |
| 注意力=软检索 | 3 用户记忆和知识库 | text/05-ch03.txt:492(搜「软」) |
| RAPTOR | 3 用户记忆和知识库 | text/05-ch03.txt:496(搜「RAPTOR」) · text/05-ch03.txt:500(搜「自下而上」) · text/05-ch03.txt:502(搜「SSE」) |
| GraphRAG 与三元组 | 3 用户记忆和知识库 | text/05-ch03.txt:506( 搜「三元组」) · text/05-ch03.txt:508(搜「多跳」) · text/05-ch03.txt:510(搜「消歧」) · text/05-ch03.txt:512(搜「社区发现」) |
| 语义降级与分层互补 | 3 用户记忆和知识库 | text/05-ch03.txt:514(搜「语义降级」) · text/05-ch03.txt:516(搜「分层互补」) |
| 何时结构化 | 3 用户记忆和知识库 | text/05-ch03.txt:524(搜「不同问题」) · text/05-ch03.txt:526(搜「判断标准」) |
| OpenViking | 3 用户记忆和知识库 | text/05-ch03.txt:530(搜「OpenViking」) · text/05-ch03.txt:540(搜「L0」) · text/05-ch03.txt:542(搜「Markdown」) · text/05-ch03.txt:544(搜「链接与索引」) |
| 治理三则 | 3 用户记忆和知识库 | text/05-ch03.txt:550(搜「增量」) · text/05-ch03.txt:552(搜「失效」) · text/05-ch03.txt:554(搜「权限」) |
| 智能体化 RAG | 3 用户记忆和知识库 | text/05-ch03.txt:558(搜「单向数据流」) · text/05-ch03.txt:562(搜「图书馆」) · text/05-ch03.txt:566(搜「思考」) |
| RAG 安全边界 | 3 用户记忆和知识库 | text/05-ch03.txt:572(搜「间接提示注入」) |
| 司法实验 | 3 用户记忆和知识库 | text/05-ch03.txt:580(搜「正当防卫」) · text/05-ch03.txt:582(搜「并行」) · text/05-ch03.txt:590(搜「解决问题」) |
| 对话历史当知识库 | 3 用户记忆和知识库 | text/05-ch03.txt:596(搜「对话历史本身视为」) · text/05-ch03.txt:600(搜「两辆车」) · text/05-ch03.txt:608(搜「Accord」) |
| 上下文感知检索 | 3 用户记忆和知识库 | text/05-ch03.txt:620(搜「前缀摘要」的替换词:包含核心上下文) · text/05-ch03.txt:622(搜「划清界限」) · text/05-ch03.txt:624(搜「同时增强」) |
| 49% 与 67% | 3 用户记忆和知识库 | text/05-ch03.txt:632(搜「49%」) |
| 双层记忆架构 | 3 用户记忆和知识库 | text/05-ch03.txt:642(搜「双层」) · text/05-ch03.txt:646(搜「护照过期」) · text/05-ch03.txt:652(搜「概览」) · text/05-ch03.txt:654(搜「汇合」) |
| 判例知识提取 | 3 用户记忆和知识库 | text/05-ch03.txt:660(搜「判例」) · text/05-ch03.txt:676(搜「自下而上」) · text/05-ch03.txt:678(搜「1,0,0」) · text/05-ch03.txt:682(搜「读懂」) |