速成课 · No. 12

RAG 是你把事实交给语言模型、让它据此作答的方式,而不是指望它那模糊的记忆。它是严肃 LLM 应用里最重要的单一模式 —— 而当它出错时,模型几乎从不是元凶。真正的工作和质量都藏在检索里:你如何对要喂给模型的文本做分块、建索引、检索和排序。

只讲精髓 · 每个想法一个画面 · 检索才是关键

§ 01

模型不知道你的数据,也不知道它训练截止之后的任何事。RAG 是你把事实交给它、让它据此作答的方式 —— 所以在钻研机制之前,先弄懂它要解决的问题。

模型的记忆不是放你事实的地方

一位多年前读完了整座图书馆的杰出毕业生 —— 但没读过你公司的文件,也没读过他毕业之后印出的任何东西。

一个 LLM 只知道它训练数据里有的东西,那些东西冻结在某个截止日期。它不知道你的文档、你的产品、你客户的记录,也不知道昨天的新闻。你照样问它,它就会凭模糊的记忆作答,或编出一个听起来合理的东西。 RAG 存在就是为了修好这一点:别再依赖模型背下来的东西,而是在提问时把相关的事实交给它。

先检索,再作答

一场开卷考试 —— 你不靠记忆,而是先查到相关的几页,再根据眼前的内容写答案。

**Retrieval-Augmented Generation(检索增强生成)**分三步:拿到用户的问题,取出你文档里最相关的块,把它们放进上下文,再让模型据此作答。现在答案来自你真实的数据,而不是模型的回忆。这就是一个助手如何准确而及时地回答关于你的文档、你的政策、你的知识库的问题。

接地才是真正的奖赏

一名必须为每条断言注明出处的记者,编造出的内容远少于一名凭记忆写作的记者。

RAG 之所以重要,深层原因是接地(grounding):因为答案是从检索来的文本里得出的,你就可以要求引用,并核对每条断言是否真的有出处支撑。它不能彻底消除幻觉,但能大幅削减幻觉,并让漏网的那些变得可被抓到。一个你能追溯到某份文档的答案,才是你能信任 —— 也能为之辩护 —— 的答案。

它是一个末端接了模型的检索系统

一名研究助理的能力,取决于他所检索的归档系统 —— 给他一座很棒的图书馆配一个糟糕的索引,他照样递给你错的那个文件夹。

能帮你省下好几个月的心智模型:RAG 大体上是一个检索问题,末端接了一个语言模型来组织答案的措辞。模型是简单的、基本已解决的那部分。困难的、决定质量的那部分,是一切决定哪些文本能抵达模型的东西 —— 而那正是本课程余下的内容。

别问模型它知道什么。把相关的事实检索出来,让它据此作答 —— 接地、及时、可引用。

§ 02

在你能检索之前,要先把文档切成片。你怎么切,悄悄地决定了检索质量 —— 这是最不起眼的一步,也是最重要的步骤之一。

你检索的是块,所以块是质量的单位

一本工具书只有切成能查到的条目才有用 —— 一整卷没建索引的长卷,或一千张撕碎的纸片,拿来查东西都同样没用。

你检索的不是整篇文档;你检索的是 —— 几百个词长的段落。那个块就是检索所看到、模型所读取的单位,所以它的边界极其要紧。好的块是自成一体、围绕单一主题的。分块做错了,再聪明的检索或再聪明的模型都救不回来。

太大埋没答案;太小丢掉上下文

把一整章丢给某人去回答一行的问题,他得在噪声里跋涉;只给他一句没有任何上下文的话,他根本看不出这话什么意思。

块切得太,每个块就把相关的事实和一堆不相干的段落混在一起 —— 检索变模糊,模型的注意力被稀释。块切得太,你就切断了让那个事实有意义的上下文。最佳点是恰好容纳一个连贯的想法,外加足够多的周边内容好让它能独立成立。没有一个通用的数字;它取决于你的文档,你靠测量来调它。

按意义切,别按字符数切

靠数字符来切一本书,可能把一句话 —— 或一份食谱 —— 干净利落地劈成两半。按章节来切,则让每一片都保持完整。

幼稚的做法是每 N 个字符切一刀,这会把句子、表格和想法都切断。更好的做法是按文档的自然结构来切 —— 标题、章节、段落 —— 好让每个块都是一个完整的想法。相邻块之间留一点重叠,可以防止一个事实掉进两个块之间的缝隙里。尊重文档本身的形状,检索就会白白变得更容易。

给每个块附上元数据

一张图书馆卡片不只装着正文 —— 它记下作者、日期和章节,好让你在检索之前就能筛到恰好正确的那一排书架。

把每个块连同元数据一起存:来源文档、标题、日期、章节、权限。这让你能在语义检索之前、或与之并行地做筛选 —— 只看这个产品的文档、只看现行的政策、只看这个用户被允许看到的内容 —— 而且它还给了你引用所需的出处。元数据存起来很便宜,却能把一堆扁平的文本变成你能精准命中的东西。

你检索的是块,不是文档。按意义切,让每个块自成一体,并给它打标签 —— 分块质量为下游的一切设了上限。

§ 03

要找出与问题匹配的块,你靠的是按意义检索,而不是靠词面匹配。Embeddings 就是让「帮我找出像这样的东西」成为可能的那个诀窍。

Embeddings 把意义变成坐标

一张地图,每个想法都有一个位置,意思相近的东西彼此挨着 —— 「狗」靠近「小狗」,两者都离「报税表」很远。

一个 embedding 是一串数字,代表一段文本的意义,被放进一个高维空间,好让意思相近的落在彼此附近。你把每个块嵌入一次,把向量存下来。现在「意义」有了坐标,而「找出相关文本」就变成了「找出附近的点」—— 一个计算机能飞快解决的问题。

向量检索靠相似度找,而非关键词

一名靠书的内容来找书的图书管理员 —— 找「像这样的东西」—— 而不是只匹配你说出的那个确切书名。

提问时,你用同样的方式嵌入这个问题,再在库里检索向量与它最接近的块。这就是语义检索:哪怕用户问的是「把我的钱拿回来」,它也能找到一个关于「退款政策」的块,因为这两个意思挨得很近。关键词检索会漏掉这一点;向量检索就是为它而生的。

向量数据库是引擎

一座仓库,被设计成给定任何一件物品,就能立刻递给你最相似的一百件 —— 不是靠读遍每一排货架,而是靠它的组织方式。

一个向量数据库存着你的 embeddings,并能在毫秒内回答「离这个最近」的查询,哪怕面对数百万个块,靠的是一个近似索引而不是扫描全部。它是 RAG 底下的检索引擎。你不需要自己造它 —— 但你确实需要知道它是你的块存放的地方,以及它的检索有多快、多准。

相似不等于相关

两个段落可以是同一主题却回答不同的问题 —— 「怎么取消」和「人们为什么取消」挨得很近,但只有一个才是问的那个。

向量检索返回的是语义上接近的东西,通常相关,但并不总是。一个块可以是关于正确的主题,却仍然不包含答案。这道鸿沟 —— 意义上接近,与真正有用 —— 正是为什么原始向量检索是个强有力的第一遍、却不是定论,也正是为什么有下一节。

Embeddings 给意义安上坐标;向量检索靠相似度找块。它很强大 —— 而「意义上接近」并不完全等于「回答了问题」。

§ 04

这是能修好大多数坏掉的 RAG 系统的那一课:当答案很糟时,原因通常是检索。把对的块送进去,一个像样的模型就能搞定其余。

垃圾进,自信的垃圾出

给某人错的文件,让他做摘要 —— 他会给你一份对完全错误的东西所做的完美摘要,而且听起来笃定得很。

一个 RAG 答案的好坏,只取决于它拿到的块。检索到错的段落,模型就会忠实地根据错误的上下文作答,带着它一贯的自信。这个 bug 看起来像是「模型错了」,但真正的失败发生在上游、在检索里。所以当答案很糟时,先检查检索到了什么,再去碰提示词或模型。

把关键词检索和语义检索结合起来

一名搜寻者确切知道词是什么意思,另一名匹配确切的名称和代码 —— 合在一起,他们能抓到任何一方单独都会漏掉的东西。

向量检索懂意义,但在确切的术语上会失手 —— 产品代码、名称、罕见的行话。关键词检索能精准命中这些,却会漏掉换了说法的表达。**混合检索(hybrid search)**两者都跑,再合并结果,互相补上各自的盲区。对于大多数真实语料库,混合检索都胜过任何一方单独使用,因为真实的问题里既混着概念,也混着具体的名称。

对候选短名单重排序

一个招聘流程:便宜的第一遍拉出五十份看起来靠谱的简历,再由一位细心的评审仔细阅读、排出真正的前五名。

第一遍检索快,但粗。一个**重排序器(reranker)**拿出排在前面的、比方说五十个候选,更仔细地针对问题给每一个打分,把真正相关的块顶到最前。先广撒网地检索,再重排序到精确的少数。这种两阶段的形态 —— 先宽而便宜地召回,再尖锐地排序 —— 是把一个平庸 RAG 系统升级的最高杠杆之一。

精确率和召回率彼此较劲

一张大孔的网只捞得到大鱼,却放走了许多;一张细网什么都捞,连水草也捞上来。你按你需要的渔获来调网眼。

检索的块太少,你可能错过那个带着答案的块(低召回);检索的块太多,你就把答案埋进无关的文本里,稀释了模型,还耗 token(低精确)。块的正确数量在两者之间取得平衡,它因你的数据和问题类型而异。你不靠猜它 —— 你靠测它,那就是下一节。

大多数糟糕的 RAG 答案都是糟糕的检索。把关键词检索和语义检索结合,对短名单重排序,并在怪模型之前先核对那些块。

§ 05

一旦对的块进了上下文,任务就是让模型严格只根据它们作答 —— 并证明它确实做到了。这就是把检索变成一个可信答案的关键。

只根据检索到的文本作答

一名被指示只就亲眼所见作证的证人 —— 不就他所假定的、模糊记得的、或道听途说的作证。

指示模型只根据提供的块作答,并在答案不在其中时承认它不知道。这是接地的核心:检索到的文本是被允许的真相来源,而模型自己的记忆被排除在外。它不会完美 —— 模型仍可能跑偏 —— 但这条指示加上正确的上下文,已经做完了大部分工作。

引用让断言可被核对

一篇带脚注的研究论文,让任何读者都能把一条断言追回到它的出处去核实它 —— 或抓出它站不住脚的地方。

让模型注明它每部分答案出自哪个块。引用做两件事:它让用户(和你)能拿一条断言去对照其出处核对,而把每条陈述都接地到一个检索来的段落这一行为本身,会阻止模型偏离事实游走。一个出处可追溯的答案,是你能审计的;一个无出处的答案,只是一个自信的猜测。

教它说我不知道

你最信任的专家,是那个会说「这不在我手上的资料里」、而不是自信地用一个猜测把空缺填上的人。

最危险的 RAG 失败,是检索返回空了或跑偏了还照样作答。所以要让拒答成为一个合法的、被期待的输出:如果块里不含答案,模型就该这么说,而不是凭记忆即兴发挥。一个承认空缺的系统,远比一个把空缺糊弄过去的系统更可信 —— 而且它还会把你检索需要改进的地方暴露出来。

接地减少幻觉 —— 它不能终结幻觉

安全带大幅降低了死亡,但你照样小心开车。一个有力的保障,不是停止盯着路面的理由。

即便有完美的块,模型仍可能误读、过度概括,或把一个检索来的事实和它自己的记忆掺在一起。接地大幅削减幻觉并让它可被抓到;它不能消除幻觉。所以你仍要核验 —— 用用户能核对的引用,以及用衡量忠实度的评测。把接地当成一个强有力的控制手段,而非一项保证。

接地意味着答案只来自检索到的文本,并有引用来证明它 —— 以及当事实不在那里时,一句诚实的「我不知道」。

§ 06

RAG 有两个会以不同方式失败的阶段,所以你要分开衡量它们。把它们混作一团,你就会接连几周去调错的那一半。(《评测》那门课讲得更深;这里是 RAG 专属的形态。)

把检索和生成分开衡量

一道菜出问题的餐厅有两个可能的元凶 —— 送来的食材,或那位厨师。你先尝食材,否则你会去重新培训错的人。

一个 RAG 答案出错,可能是因为检索带来了错的块,也可能是因为生成根据对的块却答得很糟。这两者需要不同的修法,所以要分开衡量:对的块回来了吗?以及,给定那些块,答案是否忠实而完整?大多数「改不动自家 RAG」的团队,都只给最终答案打分,然后猜该怪哪一半。

检索:对的块回来了吗?

在评判作文之前,先检查这个学生是否真的拿到了对的参考页 —— 若没有,他写的任何东西都不可能是对的。

单独评测检索:对一组有已知相关文档的问题,检索是否在靠前的结果里把它们返回了?这能直接抓出糟糕的分块、薄弱的检索和缺失的重排序 —— 那些大多数糟糕答案的上游原因。先修检索,因为对提示词或模型的任何改进,都救不了一个由错误的块搭起来的答案。

生成:答案对块忠实吗?

一名事实核查员拿答案去对照所引的出处,标出任何一句出处其实并不支撑的话。

一旦检索做好了,就评测答案的忠实度:每条断言是否都有检索到的块支撑,没有任何编造、没有任何矛盾?也要核查它确实用上了检索到的内容、并回答了问题。这正是你抓住模型偏离扎实上下文游走的地方 —— 而它是可衡量的,常常用另一个模型来当评分员。

用真实的问题来搭评测

你用真正会过桥的卡车来测一座桥,而不是用你手边方便找到的砝码。

你的评测集应当是用户会问的真实问题,包括那些杂乱的和超出范围的,每一个都配上应当被检索到的块和一个已知良好的答案。在每次改动上跑它,好让你看出一个新的分块策略或重排序器是否真的有帮助。没有这个,你就是在靠轶事来调 RAG —— 而轶事会骗人。

RAG 在两个地方失败。把检索和生成分开衡量,先修检索,并用真实的问题来搭评测。

§ 07

RAG 很强大,也常常被滥用过头。本事在于:在它配得上一席之地时才用它,让数据保持新鲜,而不为一个更简单的做法本可省掉的机器去买单。

能直接粘贴的,别上 RAG

你不会为桌上的三本书建一套图书馆目录 —— 你直接翻开它们就好。

如果相关的事实又少又固定 —— 几份文档、一条简短的政策 —— 就直接把它们放进提示词。 RAG 配得上它的复杂度,是当知识大到塞不进上下文、经常变动、或必须按用户筛选的时候。在三页内容上搭一条检索流水线,就是「为单次调用上一个智能体循环」那种过度工程的等价物。等数据逼着你时再去用它。

陈旧的数据就是错误的数据

一本电话簿只有重印了才有用 —— 上个十年的号码,自信满满却毫无用处地错着。

RAG 的承诺是当下的事实,这意味着你的索引必须**保持当下。**当来源文档变了,块和 embeddings 就必须更新,否则模型就会把答案接地在自信而过时的文本上。把重建索引当成系统的一部分来规划,而非事后才想起 —— 新鲜是一个你必须维护的功能,不是一个你一次性就得到的属性。

盯住成本和延迟

流水线里每多一步 —— 嵌入、检索、重排序、塞上下文、生成 —— 都给每一个问题加上一点时间和一点钱。

RAG 增加了阶段,每一个都耗延迟和 token。检索更多的块、重排序、更大的上下文 —— 在一定限度内都提升质量,并一路抬高账单。所以要调到能答好的最少块数,能缓存的地方就缓存,并记住:一个太慢或太贵的好答案,在生产里就不是一个好答案。

RAG 是一级台阶;模型仍是一个组件

一个好厨房有储藏室,但储藏室不是厨师 —— 它是给那个仍得把菜做出来的人的一个组织良好的输入。

RAG 在梯子上位于纯提示词之上、工具与智能体之下:当模型需要你的事实时去够它,并像对待任何依赖一样把它放在一个干净的接口之后。检索喂给模型;它不取代模型周围的判断。和到处一样的纪律 —— 用管用的最简单那一级,并让模型保持可替换。

在你上线一个 RAG 功能之前
  • 是否真的需要 RAG —— 还是说事实又少又稳定到足以直接粘进提示词? - 文档是怎么分块的 —— 按意义、自成一体、带元数据和一点重叠? - 检索是否做了混合并重排序,还是一个你从没测过的原始向量检索? - 模型是否 只根据检索到的文本作答、注明出处、并会说「我不知道」? - 检索的评测是什么, 与生成分开? - 索引如何保持新鲜,一次查询在时间和 token 上花多少?
判断问题出在检索的气味测试
  • 答案自信地错着,而你从没看过被检索到的那些块。 - 只用原始向量检索 —— 没有关键词兜底,没有重排序。 - 块按字符数切, 切穿了句子和表格。 - 你在调提示词和模型,去修一个其实是检索失误的问题。 - 自来源文档变动以来,索引就没重建过。
你把它做好了的迹象
  • 块是语义的、自成一体的,打了元数据标签用于筛选和引用。 - 检索是混合 + 重排序,调到了能答好的最少块数。 - 答案接地并注明出处, 当事实没被检索到时给出诚实的拒答。 - 你把检索和生成分开衡量,并先修了检索。 - 索引有一套新鲜度计划,而 RAG 坐在一个你能替换掉的接口之后。

RAG 是一个末端接了模型的检索系统。把块弄对,答案就跟着来 —— 模型很少是问题所在。

速成课结束 · 7 章 · 检索才是关键

接下来是实践:在你自己的少数几份文档上搭出最小的真实 RAG —— 按结构分块、嵌入、检索,并让模型带着引用和一句诚实的「我不知道」来作答。然后,在你调任何东西之前,写下十个真实的问题,看看检索回了什么。但有一个想法要置于其余之上:当答案错了,先看那些块。在 RAG 里,模型很少是问题所在 —— 是你的检索,而那正是你掌控的那一半。