5.8 检索增强生成 RAG
1.大模型的两个老毛病:知识会过期,嘴硬说错话
说起来,前面5.1 节聊预训练的时候我们提过,大语言模型把海量的文本读进去,把世间的常识压进了权重里。这么一做,模型确实变得博学起来,问它什么都能说上几句。可这么做的代价也明摆着,模型脑子里的知识,是被训练数据困住的,训练完那一刻就停了。
我先讲第一种最常见的情况。你要是问它2026年7月13日的天气,或者问它上周公司刚发的内部通知写了什么,它多半答不上来。这两件事其实是同一类毛病,我们叫它知识截止日期。训练数据什么时候收尾,模型的世界就什么时候定格,之后发生的事它一概不知。
第二种毛病更要命,我们叫它幻觉。说白了就是模型会一本正经地讲一件根本不存在的事,语气还挺笃定。1.4 节讲概率信息论时我们说过,语言模型本质上是在估一个条件概率 ,意思是在已经看到前 个词 的条件下,下一个词 取各个值的概率,这里的 是词在句子里的位置编号。模型挑的永远是概率最高、或者采样下来比较顺的那个词,至于这几个词拼出来的句子在现实里成不成立,它本身是不知情的。这么一来,遇到它没把握的问题,模型也倾向于硬着头皮编一段,编得还挺像那么回事。
这两个毛病摆在一起,就逼出了一个很自然的想法,能不能让模型在回答之前,先去查点资料,再带着资料回答呢。这个想法就是RAG(检索增强生成)的来由。
2.RAG的核心思路:回答之前先查资料
RAG三个字母是Retrieval-Augmented Generation的缩写,翻过来就是检索增强生成。说穿了,它的核心思路特别朴素,就是让模型在生成答案之前,先去外部的知识库里检索一些相关资料,再把这些资料连同问题一起喂给模型,让它有据可依地生成答案。
这个做法我们其实天天都在用。我记得以前做开卷考试的时候,老师发一张卷子下来,你不必把所有知识点背得滚瓜烂熟,遇到不会的题,翻一翻课本找到对应那一段,照着写就行。RAG干的就是这件事,模型不需要把所有知识都装进自己的参数里,只要在需要的时候,从外部知识库里翻出对应的那几页,照着回答就好。
这么一对照,前面那两个毛病的解法也就清楚了。知识过期的问题,靠更新外部知识库就能解决,不用重新训练模型。幻觉的问题,因为模型是照着检索回来的资料作答,只要资料靠谱,它信口开河的空间就被大大压缩了。
下面我们就把这套流程一步一步拆开来看。
3.完整流程:从一篇文档到一个可问答的系统
搭一个RAG系统,大致可以分成两个阶段,一个叫建库(离线把资料准备好),一个叫查询(在线回答用户的问题)。我们一步一步说。
3.1 把文档切成小块
第一步是把原始文档切成一小段一小段,这一步叫分块(chunking)。为什么要切块呢,因为一篇长文档动辄几万字,整篇塞给模型既塞不下,检索时也很难定位到具体哪一段在讲问题相关的内容。所以我们要把它切成一小块一小块,每一块就是一个语义相对完整的片段,我们把它叫作一个chunk。
切块的颗粒度有讲究。切得太粗,一个chunk里混了好几个主题,检索时匹配不准。切得太细,上下文被切断,一段话的意思讲不明白。常用的做法是按固定长度切(比如每块大约 到 个token),同时让相邻的块之间留一点重叠,重叠的部分我们叫overlap,重叠量大概设 个token上下。这里的token是大模型处理文本的最小单位,大致可以理解为一个词或者一个汉字片段。这样切出来,相邻的chunk之间还能共享一小段上下文,意思不容易断。
中文这边还要多一道工序。英文天然用空格把词隔开,中文却是一串字连在一起,得先用jieba之类的分词工具把句子切成词,再做后续处理。我之前看过一篇讲古汉语的论文,里头说古人写文章没有标点,读书人第一件事就是断句,这件事和我们这里的分词其实有点像,都是先把连续的字流切成有意义的单元,才谈得上后面的理解。
3.2 用嵌入模型把每个小块变成向量
切完块之后,每个chunk还是一段文字,机器没法直接算。这一步要做的事,是把每一段文字变成一个向量(一串数)。完成这件事的模型我们叫嵌入模型(embedding model)。
嵌入模型做的事,是把一段文字映射到一个高维向量空间里去。我们把这个向量记作 ,其中 表示一个 维的实数向量空间, 是这个向量的维度, 就是这段文字对应的嵌入向量,比方说 或者 ,具体多少要看用的哪个模型。
嵌入模型有一个特别讨喜的性质,就是语义相近的句子,它们对应的向量在空间里离得也近,语义无关的句子,向量离得就远。怎么衡量这里的近和远呢,我们常用余弦相似度。两个向量 和 的余弦相似度记作 ,定义是两个向量的夹角的余弦值,取值范围是 ,其中 表示方向完全相反, 表示方向完全一致。两个向量方向越接近,这个值越靠近 ,我们就说它们越相似。
关于嵌入这件事,5.1 节讲预训练的时候我们提过,Transformer这类模型把上下文压成一串向量,已经学会了相当强的语义表示能力。把这种模型拿出来做嵌入,正是借了它这份本事。
3.3 把向量存进向量数据库
向量算出来之后,要找一个地方存起来,这个地方就是向量数据库。向量数据库和普通的数据库不一样,它专门擅长一件事,就是给定一个查询向量,从成千上万个存着的向量里,快速找出和它最相似的那几个。
为什么要专门的数据库呢,因为检索这件事,要是老老实实把库里每个向量都拿出来算一遍相似度,库里几百万条数据,每次查询都得算几百万次,根本不现实。向量数据库内部用的是近似最近邻(Approximate Nearest Neighbor,简称ANN)算法,通过建索引的方式,把搜索范围大大缩小,速度上去了,精度稍微让一点点,但在大多数应用里这点损失可以接受。
常用的向量数据库有好几个。Milvus是开源里头比较成熟的,适合大规模部署。Faiss是Meta开源的一个库,本身是个算法库,部署轻量,适合原型验证。Pinecone则是云服务形态的,省心但要收费。具体选哪一个,要看团队规模、数据量和对运维的接受程度。
3.4 用户提问:检索出最相关的几段
库建好之后,我们就能开始回答用户的问题了。用户提一个问题,比如公司今年年假怎么算,我们先把这个问题用同一个嵌入模型转成向量 ,这里的 就是问题对应的嵌入向量。然后把 拿到向量数据库里去检索,数据库会按相似度从高到低,把和 最接近的若干个chunk返回出来。返回几个呢,常见的设top-,意思是最相似的前 个, 一般取 到 之间。这个 取大了上下文会丰富一些,但也容易混进无关的内容、还费token,取小了则反过来,精准但可能漏掉关键信息,所以要在自己的任务上反复调一调。
3.5 拼提示,让大模型作答
最后一步是把检索回来的 个chunk,连同用户的问题,一起拼成一段提示,交给大语言模型作答。提示的格式大概长这样,先把检索回来的资料作为参考上下文摆在前面,再写一句请根据以下资料回答问题,最后附上用户的原始问题。大模型读着这段提示,照着资料生成答案,答完之后我们再把答案返回给用户。
这一步里有个细节值得留意,就是检索回来的资料未必都和问题相关,有时候还会混进完全无关的内容。大模型本身是有判断力的,它大体上能从一堆资料里挑出有用的部分来组织答案,但要是无关内容太多,模型的注意力被分散,答案质量也会跟着下降。所以前面那一步的检索质量很关键,检索准了,后面生成这步才轻松。
4.把检索做得更准:混合检索和重排序
基础的RAG流程讲完了,我们再往前走一步。光靠向量相似度做检索,有时候会踩一个坑,就是语义上相关、但关键词对不上的内容容易被漏掉。比如用户问怎么报销差旅费,向量检索可能把差旅制度相关的内容找出来,但恰好把那张差旅报销单的填写模板给漏了,因为模板里大多是表格和专有名词,语义向量的表达力反而弱。
为了补这个短板,工程上常做的是混合检索。就是把基于向量相似度的语义检索,和基于关键词匹配的检索(比如BM25这种经典的全文检索算法)结合起来,两路结果再做融合。语义检索擅长抓住意思相近的内容,关键词检索擅长抓住字面对得上的内容,两者一拼,覆盖面就齐了。这就好比查资料既要看目录里的大标题,也要翻到具体每一页去找关键词,两条路一起走,才不容易漏。
再进阶一点,还有个步骤叫重排序,英文是rerank。做法是在初步检索出几十个候选chunk之后,再用一个专门的rerank模型,对这批候选和问题之间的相关度重新打分,按新的分数从高到低重新排一遍,再取前 个。rerank模型一般比纯向量相似度更精细,它能同时看问题和chunk两边的内容做交叉判断,所以排得更准。代价是算得慢一些,所以我们只在初检索之后的小范围内用,算是用少量计算换更高的精度。
5.RAG带来的三个好处
讲到这儿,我们不妨把RAG带来的好处再拢一拢。
第一,知识能随时更新。模型本身不动,我们只要往知识库里加新文档、改旧文档,第二天系统就掌握了新内容。比起重新训练一遍大模型,这个成本是数量级地低。
第二,答案可以溯源。模型每生成一句话,我们都能知道它参考的是库里哪一段chunk,把这一段展示给用户就行。这一点在企业场景里特别重要,用户看到一个回答,心里难免嘀咕这话靠不靠谱,我们把出处一并给出来,可信度就立起来了。我有个朋友在医院信息科上班,他做的那套就诊指引系统就特别在意溯源,每条建议都得附上对应的诊疗指南章节,这种场景里,溯源已经算硬性要求了,容不得含糊。
第三,幻觉能被显著压低。因为模型是照着检索回来的资料作答,只要资料本身没问题,模型信口开河的余地就小得多。当然这里要说明白,RAG并不能把幻觉完全消灭,模型偶尔还是会偏离资料,但比起纯靠参数里的记忆硬答,幻觉率能降下来一大截。这和5.5 节讲RLHF时提到的对齐思路有点像,都是用外部约束去矫正模型的行为,只不过RAG用的是检索回来的事实,RLHF用的是人类反馈。
实际应用里,RAG最常见的两个场景是企业内部知识库问答和客服机器人。企业内部的各种规章、手册、技术文档,堆在那里很少有人翻得动,做成一个RAG问答系统,员工直接问、直接答,效率高得多。客服机器人这边,把产品说明、常见问题、售后政策灌进知识库,用户来问,机器人照着资料答,答得既快又有依据,比纯靠模型瞎聊靠谱得多。
小张前阵子在一家创业公司实习,他们的产品就是给律所做的合同问答助手,把历年的合同模板和法律意见书存进向量库,律师提问的时候系统把相关的条款和案例捞出来,再交给大模型组织成一个工整的答复。小张跟我说,他实习三个月最大的体会,就是RAG看上去简单,但每一处细节都在拷问工程能力,分块怎么切、嵌入模型选哪个、top- 设多少、要不要加rerank,每一处都得反复试。
6.收个尾
今天我们从大模型的两个老毛病出发,一路走完了RAG的完整流程,从切块、嵌入、存库,到检索、重排、生成,也聊了聊混合检索和rerank这两个常用进阶。下一章我们会聊到大模型智能体这个话题,到时候会把今天讲的检索能力作为一块基础积木再用上。
练习
Q1. RAG的分块(chunking)为什么要让相邻块之间留一点重叠(overlap)?
因为切块切得太细,上下文会被切断,一段话的意思讲不明白。相邻块之间留一点重叠(比如每块约300到500个token,重叠约50个token),相邻的chunk之间还能共享一小段上下文,原本横跨两块的关键信息就不会因为切分而丢失,意思不容易断。重叠量是个要权衡的参数,太大冗余多费token,太小又可能漏掉边界信息。
Q2. 向量数据库检索时为什么要用近似最近邻(ANN)算法,而不是老老实实把库里每个向量都算一遍余弦相似度?
因为库里动辄几百万条数据,每次查询都把每个向量拿出来算一遍相似度(暴力精确检索)是几百万次计算,根本不现实、慢得没法用。ANN算法通过建索引的方式把搜索范围大大缩小,速度上去了,精度只稍微让一点点(找的不一定是绝对最优但很接近),在大多数应用里这点损失可以接受。这是用少量精度换大量速度的工程妥协。
Q3. 易错点:RAG是不是只要接上检索,幻觉(hallucination)就彻底解决了?
不是彻底解决,只是显著压低。RAG让模型照着检索回来的资料作答,只要资料靠谱,信口开河的空间就被大大压缩。但模型偶尔还是会偏离资料自己编,特别是检索回来的无关内容太多、把模型注意力分散时。所以RAG并不能把幻觉完全消灭,检索质量本身也很关键——检索准了,后面生成这步才轻松。此外还能配合重排序(rerank)、明确约束("只用给定资料里的信息")进一步压低幻觉。
Q4.(面试题) 请完整描述RAG的离线建库和在线查询两个阶段,并说明混合检索和rerank分别解决什么问题。
离线建库阶段:第一步分块(chunking),把原始文档切成语义相对完整的chunk,相邻块留overlap;第二步用嵌入模型把每个chunk映射成向量(如768维);第三步把这些向量存进向量数据库(用ANN索引加速)。在线查询阶段:用户提问先用同一个嵌入模型转成查询向量 ,再到向量数据库里按余弦相似度检索top-(一般3到10)个最相关chunk,最后把这些chunk连同问题拼成提示("请根据以下资料回答问题")交给大模型作答。混合检索解决的是"语义相关但关键词对不上"的漏检问题:单靠向量相似度容易漏掉表格、专有名词这种语义表达力弱的内容,所以把基于向量的语义检索和基于关键词匹配的BM25检索结合起来两路融合,覆盖面才齐。rerank解决的是初检索不够精细的问题:先初步检索出几十个候选chunk,再用专门的rerank模型对这批候选和问题做交叉打分、重新排序、取前 个。rerank模型能同时看问题和chunk两边内容做交叉判断,比纯向量相似度排得更准,代价是算得慢,所以只在小范围内用,是"少量计算换更高精度"。