阶段 7 · 用起来

RAG:别指望模型记住
把资料递给它就行

上一章《向量嵌入与检索》把「意思相近」变成「距离很近」。 RAG 接住它:把原文递过去,模型什么都不用记。 想法简单到不像技术——但它解决的是三个最实际的问题。

1

它到底解决了什么

问题模型自身的困境RAG 怎么办
知识过时 训练完知识就冻结了。今天发布的产品,模型明年也不会知道 资料在你手里,随时改文档就行,不用重训模型
幻觉 不知道答案时,编一个像样的和说"不知道",没有天然的偏向——除非专门用拒答数据教过它 把原文递过去,让它对着材料回答,并且能给出处
私有数据 公司内部文档从来没出现在预训练语料里 数据不出内网,只把检索到的片段发出去
🎯 类比

微调像让学生把整本教材背下来再考试——背得慢、忘得快、改一个字要重背。
RAG像开卷考试:学生什么都不用背,只要会翻书就行。
代价也很像:开卷考试的分数,很大程度上取决于"你翻到的是不是那一章"。 这就是为什么 RAG 的成败几乎全在检索质量上——生成的部分反而不太容易出错。 这个类比管到"翻书"为止:翻到的资料本身可能是错的,模型也可能拿着对的材料答错—— 所以第 9 节要把"检索"和"生成"分开评测。

2

八站流水线,每一站都会失真

第一站「解析」就是把 PDF / 网页变成干净文本,表格和分栏最容易出错; 后面还有切块、嵌入、索引、检索、重排、拼上下文、生成。八站里最差的那一站,决定整个 RAG 的效果。

互动 · 点每一站看它会怎么坏

很多人以为 RAG 效果不好是"模型不够强"。实际上大多数失败发生在检索阶段: 文档没解析对、块切得不对、检索没召回、重排没排上——模型拿到的是一堆错材料, 它要么照着错材料胡说,要么凭自己的记忆编。 所以调试 RAG 的第一步永远是:把检索回来的原文打出来看一眼。

3

切块:听起来无害,实际最要命

文档太长了塞不进上下文,所以要切成块。听起来是个无害的操作—— 但切块会真的把语义切断。

互动 · 按不同参数切分,标出被切断的地方

切出多少块
—
没说完就断了的块
—
和标题分离的段落
—

⚠️ 为什么要 overlap(重叠)

把块之间留一段重叠,是为了让被切断的那句话在下一块里完整出现一次。 上面的图里,绿色部分是重叠区域。
重叠的代价是冗余:overlap 20% 就意味着索引体积和嵌入成本增加约 20%。 所以这是一个非常典型的"用存储换召回"的交换。
另一个更根本的问题:块的大小决定了它能装下多少上下文。 块太小 → 检索准但信息不全;块太大 → 一个块里有好几个主题, 整块过一遍编码器得到的那个向量,把几件事平均成了四不像,检索就不准。 这就是"父子块"策略出现的原因——用小块的向量去检索,但把大块的原文喂给模型。

⚠️ 按结构切,不按字数切

最差的切法就是"每 500 字切一刀"——它完全不理解文档结构。
更好的做法是沿着文档自己的边界切:
· 先按标题(Markdown 的 # / ##)切
· 再按段落切
· 再按句子切(句号、问号、换行)
· 实在不行才按字符硬切
而且切完之后,一定要把所属的标题路径拼到每个块前面—— 比如「第 3 章 > 配置 > 超时设置 > 正文……」。 这样即使块被单独取出来,它也还带着自己的上下文。这一招几乎零成本,效果很明显。

4

混合检索:两路为什么都要

向量检索擅长"意思",但它在精确匹配上有硬伤。回看阶段 3 的分词那一章—— 同一个编号、同一个型号、同一段代码,在向量空间里可能和完全无关的文本更近。

查询类型向量检索BM25(关键词)
换个说法问同一件事
「怎么把超时时间调长」 vs 文档写「timeout 参数配置」
✅ 强项❌ 一个词都不重合,完全召回不到
找精确的错误码 / 型号
「E5032」
❌ 在向量空间里"E5032"和"E5033"几乎一样 ✅ 强项,精确匹配
专有名词、内部代号 ❌ 模型没见过这个词,向量是随机的 ✅ 有就有,没有就没有
长句语义问题 ✅ 理解整体意图 部分有效
代码符号、公式 ❌ 分词之后全是碎片 ✅ 原样匹配

互动 · 三套检索方案的真实召回率对比

⚠️ 两路的分数不能直接相加

BM25 是任意实数、余弦是 −1~1,尺度完全不同,所以最简单的融合做法是 只用排名、不用分数——就是下面这个 RRF。
一个文档如果在两路里都排第 3,它的得分就比只在一路排第 1 的更高, 这就是"两路都认可的,大概率是真的相关"。
工程上两路各取 50 → 融合成一份候选 → 重排取前 5——每一路都得给足候选,单路漏了就补不回来。

5

重排为什么必须两阶段

两阶段检索

双塔(Bi-Encoder)把查询和文档分开编码,可以在线算一次、离线算全库,快但精度略低; 交叉编码器(Cross-Encoder)把查询和文档拼在一起过一遍模型,精度高得多,但每算一对就要跑一次完整前向。 100 万篇文档上跑交叉编码器 = 100 万次前向,不可用。 所以:两路各取 50 → 融合成一份候选 → 用交叉编码器在这份候选里精排出前 5 篇——前向次数从 100 万降到几十次,可以接受。 重排通常是 RAG 里性价比最高的一次改进:加一个重排模型,往往比换一个更强的生成模型效果提升更大。

⚠️ 但重排有个前提

重排只能重新排序,不能找回。
如果粗筛阶段没把正确文档放进这份候选(top-50),重排再强也没用——"重排的上限由召回决定"。
所以工程上的顺序是:先把召回率做上去(混合检索、调 chunk、多路查询),再上重排。 反过来的话,你会在一个本来就漏的漏斗上加一层精细筛子。

6

拼上下文:位置比你想象的更重要

互动 · 把最相关的材料放在不同位置(曲线是按论文趋势搭的示意,不是实测数据)

💡 本章的核心图:位置比内容更容易被忽略

研究发现了一个稳定的现象:把关键信息放在上下文的开头或结尾,模型用上它的概率远高于放在中间。 上面那条是示意图:放在第 1 个位置时几乎总能用上,放中间明显下降—— 论文(Lost in the Middle)里的真实数字见拓展阅读。

实践含义:检索回来多个片段时,别按分数降序直接拼。 把最相关的放开头和结尾,次相关的放中间。 只留一个位置时放开头——论文的多文档实验里,开头和结尾都明显好于中间,开头通常是最高的那一端。 这一条几乎不花任何成本,却经常能带来可观的提升。

7

RAG 的几代演化

点一下看每一代在修什么

代际做法修了什么
Naive RAG切块 → 向量检索 → 拼进上下文 最基础的版本。问题是召回不准就全盘皆输
Advanced RAG 查询改写、混合检索、重排、元数据过滤 在检索的每一环都加了修正。今天实际部署的基本都是这个
HyDE 先让模型"幻想"一段答案,用这段假答案去检索 解决"问题太短、和文档表述不像"的问题。 用一个假答案当查询,往往比原问题召回得更准
Self-RAG 让模型自己决定"要不要检索""检索结果够不够""答案有没有依据" 不再是"永远检索固定条数",检索变成模型主动调用的动作
GraphRAG 从文档里抽取实体和关系,建成知识图谱再检索 解决全局性问题:「这批反馈里最主要的主题是什么」—— 答案不在任何单独一段里,要先把整个文档集汇总一遍
Agentic RAG 把检索当成 Agent 的一个工具,可以多轮检索、改写、验证 检索不再是流水线上的一站,而是一个可以反复调用的动作(见下一章)
8

长上下文会取代 RAG 吗

这是这几年被问得最多的问题。模型上下文从 4K 涨到 1M, "那我直接把整个知识库塞进去不就行了?" 先看量级:1 个汉字约 1~2 个 token,几万字就是几万到十几万 token——放得下就先别上 RAG;要检索的规模到了几十万 token 以上,才轮到它。

论点实际情况
"上下文够长就不用检索" 就算塞得下,效果也不好。 Lost in the Middle 说明中间的信息会被忽略; 注意力在超长上下文窗口里也会稀释。 (第 6 节那根滑块演示的是位置效应,不是稀释——两者是长上下文的两类毛病。)
"长上下文更简单" 每次调用都要为全部输入付钱(未计 prompt caching:重复的前缀能打折)。 100 万 token 塞一遍,每问一次都要付这个成本;而 RAG 每次只付检索到的 2000 token
"100 万上下文装得下我的库" 企业文档库动辄几百万篇,一亿 token 起。永远装不下
"检索会丢信息" 这是真的、也是 RAG 的真实弱点。所以出现了 "先检索再长上下文"的组合:用检索把范围缩到 10 万 token, 再用长上下文模型精读

互动 · 每问一次要付多少 token:全塞 vs 只塞检索回来的

这条账是第 8 节「长上下文更简单」那句话的反面: 上下文越长,你为每一次提问付的钱越多,而 RAG 每次只付检索到的那一点点。

检索负责"从一亿个东西里选出 500 个",长上下文负责"把这 500 个读透"。 两者解决的是不同的问题:检索解决"规模",长上下文解决"深度"。 所以今天最好的系统是长上下文 + 好的检索,而不是二选一——那种"上下文够长就不用检索了"的说法, 混淆了"装得下"和"用得上"这两件事。

M

数学 · 检索就是三步算术

前面说「RAG 的成败几乎全在检索」。检索听起来像黑魔法,拆开只有三步算术: ① 文字变成一个点(嵌入)、② 比谁和问题夹角小(余弦)、 ③ 两路名次合成一路(RRF)。下面三张图用的是同一批 6 个块、同一个问题, 所以它们能互相验证——每一步的数字都是当场算出来的,没有一个写死。

第一步 · 谁和问题夹角最小:余弦(真的在算)

那一步最没画面:为什么要「除以长度」

第二步 · 关键词那一路:BM25 的全部数学

关键词那一路(BM25)算的是三个数:这个词在全库有多罕见(idf)、 在这个块里出现几次、这个块有多长。结果是任意实数、没有上界——所以它和余弦不能直接相加。

第三步 · 两路名次合成一路:RRF

🎬 自己验一遍这三张图

把查询切到「E5032 报错」。看第一步那张表:E5031 和 E5032 的余弦只差 0.005—— 向量根本分不出谁是谁。
再看第二步:同一个查询下,BM25 给 E5032 满分、给 E5031 0.746—— 因为 E5031 的文本里根本没有「E5032」这个词。
最后看第三步:RRF 把两路的名次一合,最终第一名就定了。 这就是第 4 节那句话的全部内容:两路的错法完全不同,所以必须同时跑。

9

RAG 最难的地方在评测

问题说明什么时候真的会痛 → 换什么
检索错了会放大幻觉 这是最危险的一条。模型拿到错误的材料后, 不但会答错,还会很有底气地引用它——因为它的任务就是"根据材料回答"。 没有 RAG 时它至少可能说"我不知道" 答案要担责、要能追责 → 把检索质量做上去,并让回答带引用、找不到就拒答
分块必然丢上下文 无论怎么切,都会出现"这个代词指代的是上一块里的东西"这种情况。 块切得越碎,检索越准,但块越没头没尾 文档里代词、指代密集 → 按结构切,或上父子块(小块检索、大块喂给模型)
多跳问题需要多次检索 「A 公司的 CTO 之前在哪个学校读书」——这个答案需要先找到 CTO 是谁, 再去找他的履历。一次检索解决不了 答案散在两处、要分几轮查 → 多轮 / 迭代检索(下一章 Agent)
评测要拆成两段 最终答案错了,可能是检索没召回,也可能是召回了但模型没用好。 必须分开测:先测召回率(有没有找到),再测生成质量(有没有用好)。 混在一起测,你永远不知道该修哪一段 上线后要定位错在哪一段 → 先测召回,再测生成,分开看指标
文档本身可能自相矛盾 公司文档里有两个版本的流程图、政策已经更新但旧文档没删—— 检索会把矛盾的证据一起递给模型,然后它随机挑一个 旧文档没删、政策多次改版 → 先治理知识库:去重、只留最新版
检索结果可能被投毒 如果知识库里有用户可写的内容,攻击者可以植入一段 "忽略之前所有指令"的文本——一旦被检索到就成了提示注入(见安全那章) 知识库有用户可写入口 → 权限控制,过滤指令型文本(见安全那章)

互动 · 最终正确率 = 召回率 × 生成正确率

只盯着最终那个数字,你分不清是检索漏了还是模型没用对—— 所以这张表最后一条才说「评测要拆成两段」。试一下:把两个滑块调成 70% / 85% 和 85% / 70%,终值一模一样。

10

小结

它对应哪条线 ⑤ 高维里的低维——而且 RAG 是全书把高维空间里的几何真正当成工程工具用的一章。
它的整个发明建立在一个很跳的假设上:「意思」可以变成高维空间里的一个点, 而「离得近」真的就等于「意思像」。 在低维里这句话是荒谬的——你没法说「猫」离「狗」0.31 个单位。 但把一段文字压成 1024 维向量之后,余弦距离真的能替你做一部分语义判断, 而且做得比你写关键词规则好。
反直觉之处在于它同时也必然不精确:高维空间里的「最近邻」没有低维里那种可靠感。 这就是第 4 节那三套方案存在的理由:向量一路、关键词一路,两者的错法完全不同, 所以必须同时跑。
一句话 这一章的做法,本质上是在把「模型该知道什么」从参数里挪到外面去—— 参数一个没改、一次重训没做,模型「知道」的东西却变了。
代价是:你换成了一个每次都要现场猜「哪几段跟这个问题有关」的系统, 而猜错的代价比原来的幻觉还高(第 9 节第一条)。
它牺牲了什么 牺牲了「确定性」。
参数里的知识是压缩过的、模糊的,但至少每次查同一个问题它给的是一回事。 RAG 换成了一发赌:排名前 k 的块里到底有没有那个答案,事前无法保证。 切块切坏了、或者那个答案恰好被切到两个块的交界处,它就永远召不回来。
而且丢掉的那些块不是随机丢的——恰好是「跟问题长得最不像」的那些。 有些问题(否定、比较、精确编号)恰恰就长这个样子。
🎬 自己验一遍

回到第 6 节那根 U 形曲线图。拖「关键材料放在第几个位置」这个滑块, 让高亮的材料从第一个慢慢移到正中间——盯住下面的百分比。

材料一个字没变、长度没变、答案也没变, 只是它从「第一个」挪到了「中间」,被用上的概率就掉了一大截。 你以为你在管理「放什么」,决定成败的却是「放在第几」。

再把滑块拖回 0 或最后一个位置,看它涨回去。然后是第 4 节: 那里三套方案的召回率差异,是同一个反直觉的另一面。

它在暗线上站在哪

这一章在「信息流动」和「参数账本」两条上最典型,第三条是它没意识到的假设。

暗线这一章的回答
A 信息流动 全站形状变化最多的一条链,有一处不可逆: PDF 页面 → 纯文本 → 块(约 180 字) → 向量(1024 维) → top-k 块 → 拼成 prompt → 答案。
块变成向量之后,后面的比较都只在这个 1024 维的点上进行——原文里没被这 1024 个数装下的东西,对向量这一路就不可见了(关键词那一路还留着原文)。
C 参数账本 「不用重训」这个好处标在四个数上:BM25 零参数、嵌入模型约 3.3 亿参数; 1 亿块 × 1024 维 × 4 字节(float32) ≈ 410 GB(float16 是 205 GB); 全扫一遍约 10¹¹ 次乘加;而把一条新政策写进权重要一次预训练 (10²⁵ FLOP,见 《预训练》),RAG 只要重新嵌入那一个块。
E 它假设了什么 三条隐藏假设都会破:「语义相关 = 向量距离近」在否定、比较、精确编号上系统性失效(→ 混合检索); 「答案就在某一个连续的块里」在多跳问题上不成立(→ 多轮 / 迭代检索); 「答案要汇总整个库」的全局问题上也不成立(→ GraphRAG)。 「检索到的材料是真的」,可写的知识库会被投毒(→ 知识库治理,见第 9 节)。
一句话带走 RAG

RAG = 开卷考试:一个参数都不改,靠把检索回来的原文递给模型,解决知识过时、幻觉和私有数据。 成败几乎全在检索——按「解析 → 切块 → 混合检索(向量 + BM25,RRF 融合)→ 重排 → 摆位置」排查; 单靠向量一定会漏精确匹配,混合检索不是可选优化,长上下文也没有取代它。

下一章《Agent 与工具调用》接住。

11

拓展阅读

上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。

📄 这一章的说法从哪来

💻 工业界怎么写

∑ 更严格的形式