Chapter 08 · 知识层:RAG / 检索

演进 · 知识层6 个测试动手 chapters/08/rag.ts

目标:让 agent 能用上它训练时不知道的私有知识(你的文档、数据库、最新资料),并能跨会话记住。这是 2026 主流 agent 栈里独立成层的"知识层",也是从 demo 走向能解决真实业务的关键一环。

Day 0 讲过模型的硬边界:知识冻结在训练截止。RAG 就是突破它的主流方式。


一、RAG 是什么

RAG(Retrieval-Augmented Generation,检索增强生成):回答前,先从你的知识库里检索出相关片段,塞进上下文,再让模型基于这些片段回答。

flowchart LR
  Q[问题]:::io --> R["检索相关片段(从你的知识库)"]:::sub
  R --> C["片段 + 问题一起给模型"]:::model
  C --> A[基于片段作答]:::io

为什么有效:模型不用"记住"你的私有知识,只要当场读到就行。改知识 = 改库,不用重训模型。

二、检索靠"语义相似度"

怎么找"相关片段"?不是关键词匹配,是语义:

  1. embedding:用模型把一段文本变成一个向量(一串数),语义相近的文本向量也相近。
  2. 相似度:用余弦相似度衡量两个向量有多"同向"(1=最像,0=无关,-1=相反)。
  3. 检索:把问题也变成向量,在库里找最近的前 k 个片段。

三步对应你要写的:chunk(切片)→ cosineSimilarity(度量)→ VectorStore.search(检索)。

embedding 由模型算(是另一种模型调用);本章由调用者传入 fake 向量,聚焦"切片/相似度/检索"这套不依赖网络的核心机制。真实 embedding 模型现查文档接入。

三、切片(chunking)是门手艺

文档太长,要切成可检索的片段。切得好不好直接影响检索质量:

  • 太大:一个片段混了多个主题,检索到一堆噪声。
  • 太小:切断了上下文,片段自己讲不清。
  • 一般按语义边界(段落/章节)切,再控制长度。chunk(Lab 8.1)做最基本的段落贪心打包。

深一层:切片不止"切",还要"补上下文"(Anthropic Contextual Retrieval,2024)。 一个片段单独拎出来,常常缺了它在文档里的位置感("它"指谁、属于哪一节、哪个产品),检索就容易错。做法:在 embedding 之前,先给每个片段前面补一小段说明(这块讲什么、位于文档哪部分),再去算向量。Anthropic 报告这一手把检索失败率降了约 35%;再叠加关键词检索(下一节)到 49%、加重排到 67%。本章 Lab 的贪心打包是基线,这是它的下一步。

四、agentic RAG:让 agent 自己决定检索

朴素 RAG 每次都检索。更强的是 agentic RAG:把"检索"做成一个工具(search_knowledge),交给 Day 1 的 loop——模型自己判断"这个问题要不要查、查什么、查几次"。

  • 简单问题不查(省成本);复杂问题多轮查、逐步聚焦。
  • 这就是把 Day 1 的工具契约 + 今天的向量检索组合起来:检索只是又一个工具。

五、长期记忆 = 同一套机器

"跨会话记住用户偏好/过去结论"——用的就是 RAG 的机器:把要记的事实写进向量库,以后按相似度取回

  • 和 Day 4 的区别:Day 4 是"完整、有序的事实日志(session)";这里是"按语义随取随用的记忆"。真实产品常两者都用:session 记完整过程,向量记忆记可复用的知识点。

六、检索质量:2026 怎么让 RAG 真的准

本章的 cosineSimilarity + top-k 是 RAG 的基线。生产里要更准,三个抓手:

  • 混合检索(hybrid:语义 + 关键词):纯向量抓语义,但会漏精确项——产品型号、人名、报错码、罕见术语,得字对字才对得上。经典做法是并上 BM25(关键词检索):embedding 抓意思、BM25 抓精确匹配,两者合起来明显强过任一个
  • 重排(rerank):先用便宜的检索多召回(如 top-20),再用一个更强的 reranker 模型精排出真正最相关的前几个才喂给模型。"广撒网 + 精挑",是性价比很高的一档提升(叠加上一节的 contextual 手法,失败率可降到基线的约三分之一)。
  • 长上下文没有杀死 RAG:窗口涨到 1M 后有人说"全塞进去就行"。但呼应 Day 5 的 context rot——塞太多本身就掉质量,还更贵、更难更新。2026 的共识是混合:用检索只挑出相关的那几万 token,再让长上下文在其上推理。检索没被淘汰,而是变成"喂对上下文"的前置。

判断力:RAG 的准,七分在检索(切得好、召得全、排得准),三分在生成。别一上来就怪模型答不好——先看检索回来的片段对不对

七、你要建的(练习)

打开 rag.ts:

Lab 关键
8.1 chunk 按段落贪心打包成不超长的片段
8.2 cosineSimilarity 点积 / 模长积;同向 1、正交 0、反向 -1
8.3 VectorStore.search 按余弦相似度取前 k、降序
npm run test:08   # 6 个测试:切片×3 / 相似度 / 检索 top-k / k 超库存

卡住按 定位→签名→伪代码→局部 找 tutor。


八、收尾

  • 讲回来(JOURNAL.md):RAG 为什么能突破"知识冻结"?为什么用语义相似度而不是关键词——可纯向量又会漏掉什么(hybrid / rerank / contextual chunking 各补哪一环)?agentic RAG(检索当工具)比朴素 RAG(每次都查)好在哪?长期记忆和 session 持久化(Day 4)有什么区别?长上下文为什么没取代 RAG(呼应 Day 5 的 context rot)?
  • 迁移题:见 TRANSFER.md——接真实 embedding 模型、把 search_knowledge 做成工具接进 Day 1 loop(真 agentic RAG)、超长段落的二次细切、混合检索(语义+关键词)。
  • 真检验:切一篇文档进库,用一个语义相近但用词不同的问题去检索,确认能取回正确片段(而关键词匹配取不到)。