7.1 检索增强生成(RAG)
为解决大模型知识更新慢、成本高的问题,检索增强生成(RAG)架构应运而生。它通过外挂一个可随时更新的知识库,在生成答案前先进行“检索”,将相关信息注入提示词,从而让模型能够参考最新资料回答问题。该技术的核心在于利用Embedding模型进行语义向量化、对知识库内容分块(Chunking)以及通过向量数据库实现高效检索,有效缓解了模型幻觉,并提升了答案的时效性和准确性。
想像你是一名学生,现在要参加一场考试。
老师在考前提前告诉你,考试会考某些冷门知识,甚至给你划上了重点并且给了你往期相关的题目示例。
你能怎么办?只能提前死记硬背,把所有的资料都啃下来、记进脑子里。考试那天靠脑子里的知识应答,这就是微调。
也就是说,你要把知识硬生生地刻进模型的脑子里,考试时才会回答,如果脑子里没有这个知识,只能“瞎蒙”,心里想的是“搞不好就蒙对了”,诶~大模型也是这样想的,这就是模型幻觉。
你需要提前准备好相关的资料花时间去背会它。
如果下次考其他内容,你需要重新去找资料、背诵、理解。
微调就曾一度被视为模型“进修深造”的主要手段。想让一个通用模型变得更懂法律?那就喂它几千万条法规判例。想让它更懂金融?给它灌入整个金融体系的知识。
听起来合理,甚至科学,但一个很现实的情况是,模型微调很贵,昂贵的训练资源并不是谁都能负担得起的。而且微调之后一旦有新的内容数据出现要怎么办?继续花时间和精力用新的数据微调吗?很有可能当我们一批数据训练微调结束后,新的数据又来了,重新基于新的数据训练微调后,下一批新的数据又来了,实时性比较强的垂直领域就会这样一直循环往复下去,微调明显是不合适的。
7.1.1 什么是 RAG?
老师说这次考试是开卷考试!
你不需要提前把所有书背下来,只需要带一本标好重点的资料书进考场。
真正在考场上做题时,你需要根据题目考察的知识点,快速翻书找到对应的知识内容。
然后把答案结合你自己的理解写到试卷上。
如果把大模型比做大脑,那微调就像给知识刻进了“脑纹”,想改?只能重做一副大脑,极其不灵活。
那么有没有一种方法,不用改大脑结构,就能让模型随时学习新知识、回答新问题呢?
这就是 检索增强生成(Retrieval-Augmented Generation,RAG) 架构出现的根本原因。
RAG 就像是给大模型外挂了一个可以随时更新内容的知识库,当我们问大模型问题时,先基于问题在知识库检索找到相关的知识点,把这些知识点都塞到提示词也就是问题中,一起发送给大模型,这样大模型就可以根据相关的知识回答我们的问题。
当我们把相关知识一起发送给大模型时,其实就相当于给大模型发送了一个长文本提示。
看起来是不是很简单,没错,RAG 的核心思想就是这样,理论上只要可以检索到相关的知识内容,模型在回答时有了知识参考,就可以一定程度上规避幻觉问题。
其实听名字就可以有一定的理解,从技术角度宏观来讲,RAG只有三步操作:
- 检索(Retrieve): 模型先把你的问题通过一些方式去检索,匹配相似的内容(就像翻书查资料)
- 增强(Augment): 把这些查到的内容一起塞进提示词里
- 生成(Generate): 最后再由模型根据“问题和查到的资料”来作答
7.1.2 RAG 工作原理
想象一下你在考试现场,遇到了一道完全不会的题目。
你皱着眉头盯着题目,脑子飞快转着:“这个好像和那只又死又活的猫有点关系?”
你迅速翻开带进考场的资料书,找到量子力学某一章“薛定谔的猫”,大致扫一眼:
“哦,是那个量子叠加态的经典案例。”
接着你一边参考资料,一边写下:“根据薛定谔的思想实验,猫可能既死又活……”
这整个过程,其实就是RAG的核心思路。
RAG 的工作原理简单来说其实就是把“查找”和“作答”两个过程拆解开来,并让它们各司其职。
那么问题来了,RAG 究竟是如何判断用户的问题和知识库中的哪些问题相关呢?
这就需要引入一种新的模型,它叫做 嵌入(Embedding) 模型。
1. 嵌入(Embedding)
在传统的软件系统中,我们通常把文本存入关系型数据库(如 MySQL)或文档型数据库(如 MongoDB),检索时通过关键词或模糊匹配。
例如搜索 “苹果”,你可能会搜到:
- 苹果手机
- 苹果派
- 小明喜欢吃苹果
但如果你搜索“手机品牌”,系统只能匹配包含“手机”或“品牌”的句子,像“苹果、三星、小米”这些具体品牌名是匹配不到的,但在人类的常识中,“手机品牌”和“苹果、三星、小米”是强相关的。
这是因为传统数据库看的是“字面相似”,它并不理解这些词的含义,所以传统的相似度匹配方式解决不了问题。
Embedding 模型通常也是基于 Transformer 架构预训练的模型,使用 Embedding 模型时,它会理解语义并把文本压缩成机器能识别、能比较的格式,即 语义向量(semantic vector),而这个过程就叫嵌入。语义向量其实就是一串浮点型数值组成的数组,其中每一个数值都表示这段话在某个语义维度上的含义,不同的 Embedding 模型支持的维度从几百到上千不等。
比如当我们使用同一个 Embedding 将不同语句转为向量时(以下向量数值仅用作示意):
"夏天喝点什么解暑"
→ 被转成 [0.21, -0.34, 0.55, … , 0.43]
另一句话:
"适合高温天气的清凉饮品?"
→ 被转成 [0.22, -0.36, 0.56, … , 0.42]
虽然字不一样,但这两个语义向量会很接近,因为它们的意思差不多,都是想找点适合夏天喝的。
Embedding 技术的强大之处就在于,它能从语义层面理解文本相似性,而不是只看字长得像不像。
2. 分块(Chunking)
现在我们已经把用户的问题转换成了可以计算相似度的语义向量。
但这里出现了一个新的问题:我们如何处理大量的文本内容,比如一本书或整个知识库呢?
显然,我们不能把整本书的内容一次性转换成向量,这就像是试图把整本书的内容压缩到一张便利贴上一样不切实际。
我们都知道大模型在处理输入时有其局限性。虽然模型的上下文窗口长度在不断提升,但面对大量内容时,过长的提示词依然会超出模型的处理能力。
想象一下:如果我们把知识库中的每本书都向量化,当用户提出问题时,系统需要在整本书的层面上和用户问题匹配相似度。即使找到了相关的书籍,我们也不能把整本书的内容都提供给模型。这不仅会超出上下文限制,而且会让模型在大量无关内容中迷失重点,因为真正相关的知识内容可能只有几页。
所以我们需要一种方法,能尽可能的只匹配出和用户问题相关的知识。它就是 分块(Chunking) 技术。
理解起来其实很简单,我们可以把整个知识库的内容分割成很多的小片段,在给每个小块向量化,这样匹配时的粒度足够小,匹配到的内容就会越精准。
分块的方式有很多种。比如我们要给下面这段内容分块:
“周末去公园野餐,准备了三明治、水果和果汁。天气晴朗,孩子们在草地上玩耍,小狗追着飞盘跑。傍晚时突然下雨,大家匆忙收拾东西回家。”
分块最简单的方法是按固定字数切分。比如每10个字为一块:
块1:"周末去公园野餐,准备"
块2:"了三明治、水果和果汁"
块3:"。天气晴朗,孩子们在"
...
这种方法的问题在于会打断句子的完整性,导致语义割裂,影响相关性计算的准确度。
更好的方法是按照自然段落或完整句子来分块。这样可以保持语义的完整性,但仍需注意上下文关联的问题,因为一个概念的完整表达可能横跨多个句子。
为了提高检索精准度,现代分块技术主要采用语义切分方法:利用大模型进行智能断句,结合滑动窗口和去噪清洗等技术,确保每个分块既保持适当长度,又能完整表达语义。
分块完成之后,接下来要做的是对每一个 块(Chunk) 都进行 Embedding 操作,把他们变成长度一致的向量。
3. 向量数据库
现在整个知识库的内容被拆分成一个个 Chunk,每个 Chunk 也有它自己的向量,但是光有向量是不够的,我们需要把每个向量和原始文本片段的对应关系保存起来。
一般来说,保存数据我们会用到数据库。但传统传统数据库中擅长的检索只有两种:
- 精准匹配:比如查找 ID 为 “001” 的的记录
- 模糊匹配:比如查找内容中包含“苹果”的记录
现在我们要查的不是一个精确值,也不是无语义关系的模糊匹配,而是要查询哪个 Chunk 向量距离我们的问题对应的向量最近。显然,传统数据库是做不到这一点的。于是 RAG 架构中就有了一个专门为这类场景设计的东西 —— 向量数据库(Vector Database)。
向量数据库中的每一条数据都有一个与之关联的向量(如表 7-1,向量数值仅用作示意)。
Text | Vector |
---|---|
夏天喝点什么解暑 | [0.21, -0.34, 0.55, ..., 0.43] |
适合高温天气的清凉饮品? | [0.22, -0.36, 0.56, ..., 0.42] |
冬天需要注意保暖 | [-0.15, 0.42, -0.33, ..., 0.28] |
寒冷天气如何防寒 | [-0.14, 0.41, -0.35, ..., 0.27] |
表 7-1 向量数据库表示意
在查询时,当我们输入一个向量,数据库就会找到和输入向量距离最近或者说最相似的几条数据。
在向量数据检索语义相关的内容时,会涉及到一个关键检索参数 —— Top-K。
Top-K 指的是在向量相似度检索中,返回相似度排名前 K 个的 Chunk。也就是说,大模型不会使用整个知识库中的内容,而是从中“挑 K 个最相近的”作为回答参考。
向量数据库是怎么从成千上万个 Chunk 中,快速找到和用户问题向量最接近的 Chunk 呢?
这就好像是把每个 Chunk 都投影到了一个超大的“语义地图”里,而语义相近的句子,距离会更近。
拿最简单的 2 维向量来举例,假如我们有三组 2 维向量分别是:
- A:[0.21,0.56]
- B:[0.20,0.55]
- C:[-0.20,-0.50]
把它们投射到一个二维坐标上(如图 7-1)。
图 7-1 二维向量图示
可以看到,向量点 A 和点 B 的距离非常接近,我们就认为这两组向量所代表的句子语义是相似的,反之,A 和 C、B 和 C 的距离都很远,那对应的句子语义就是不相似的。
在真实的向量相似度计算中也是类似的,只不过真实场景中这个维度被拉升到了上百维、上千维,这时通常会用一种叫 余弦相似度(Cosine Similarity) 的算法去比较两段文本的语义距离,当然它只是一个常用的算法,并不是唯一的。
我们并不需要死记硬背它的计算公式,只需要理解一下它的意思即可,针对余弦相似度,我们可以简单把每个向量想象成一根“语义方向的箭头”,两个箭头的夹角越小,说明它们在“语义地图”上的方向越一致,相似度越高。
在实际应用中,如果你的问题语句向量和某段知识库文档的向量余弦相似度是 0.96,和另一段文档向量余弦相似度是 0.52,系统就知道,前者更相关,就把它排到前面返回给你。
至此,我们已经完成了知识库的三个关键步骤: 分块、编码和存储。所有文本片段及其对应的向量都已准备就绪,可以进行检索了。当用户提出问题时,系统会执行以下流程:
- 使用相同的 Embedding 模型将用户问题转换为向量
- 从向量数据库中检索出最相似的 K 个内容片段
- 将检索到的内容与用户问题一起发送给大模型生成答案
这就构成了一个完整的 RAG 架构(如图 7-2)。
图 7-2 RAG流程图
7.1.3 RAG 优缺点
RAG 架构最大的优势在于可以让模型随时“查阅资料”,而不必提前把所有知识刻进脑子。传统的大语言模型要掌握新知识,往往需要通过昂贵的微调过程来“重新学习”。而 RAG 采用“检索+生成”的架构,只需将知识添加或更新到外部数据库中,模型就可以实时访问,不需要重新训练。就像开卷考试时,只需带上一本新版本的资料书,无需通宵死记硬背。
模型幻觉问题在 RAG 中也得到了明显改善。大语言模型常常会一本正经地“胡说八道”,因为它们依靠的是内部语言模式预测,而不是事实依据。而RAG会先检索出与问题相关的资料,将它们嵌入提示词中,模型在生成答案时等于参考了“标准答案”,在一定程度上具备了“有出处”的生成能力。面对需要准确知识支撑的场景,比如法律、医疗、金融问答,RAG 更容易给出可溯源的、高质量的回答。
相比一口气将整本资料塞进模型输入,RAG 在效率上也占据明显优势。它按需检索、动态加载,只用处理与当前问题相关的内容,从而节省了大量的上下文资源。对于知识库庞大、覆盖面广的系统,这种策略不仅响应更快,而且生成更精准。RAG架构还具备很好的模块化扩展能力。知识库可以是纯文本,也可以是图文、结构化数据,甚至是音频或视频的摘要信息。生成模型与检索模块分离设计,使其能够灵活适配不同数据源与内容格式,是构建多模态AI系统的理想基础设施。
但任何一种技术都不是完美无缺的,RAG 同样有一些缺陷存在。
RAG 的表现高度依赖检索系统的质量。一旦检索不到关键资料,或者返回的内容不相关、过于冗长,生成模型就难以给出准确答案。就像考场上资料带错了章节,再努力也答不出题目。检索过程中的嵌入模型质量、文档分块策略、召回排序等环节都会影响最终输出的准确性。任何一个环节掉链子,都会让整套系统效果大打折扣。
虽然 RAG 采用检索机制缓解了上下文限制问题,但大模型的输入长度仍然是一个约束条件。如果某个问题需要依赖大量上下文内容,而这些内容超出了模型可处理的最大 token 数,就必须通过压缩、摘要或优先级筛选来进行裁剪。而压缩过程本身也可能丢失关键信息,从而影响回答的完整性与质量。
RAG 并不会判断知识内容的对错。它只是把检索到的资料交给模型生成答案,如果资料本身就存在误导、错误或偏见,那么模型就会“理直气壮地胡说八道”。知识库的质量决定了系统输出的可信度。脏数据、不规范的内容、旧版本文档、模糊表达等问题,都可能在最终回答中被“放大”出来。
从系统工程的角度看,RAG 并不是一个开箱即用的解决方案。它需要多个组件协同工作,包括嵌入模型、向量数据库、分块算法、检索系统、Prompt 模板等等,每个部分都可能影响整体表现。维护成本相对较高,开发者需要具备多方面的知识,既要懂自然语言处理,也要熟悉信息检索系统的构建与调优,才能保证系统长期稳定可用。
总体来看,RAG 更像是一个“聪明的助理”,它不需要什么都记住,但必须懂得如何快速找到、组合并表达出你需要的答案。很适合处理开放性问题、更新频繁的知识领域以及对可解释性有要求的场景。不过,想要用好它,需要在数据治理、工程架构、语义理解等多个方面下功夫,才能真正把“会查资料”变成“答得漂亮”。
Last updated on