做RAG的人应该都有过这种经历:你辛辛苦苦把一堆业务文档切好、向量化、灌进知识库,结果模型面对一个简单问题,依然答得“满脸自信但全是胡说”。这种挫败感我太熟了。RAG,也就是检索增强生成,本质上就是解决这个问题的:模型不知道的知识,我们帮它查,查完再把资料放到它眼前,让它照着答。
RAG 是目前大模型落地知识问答、企业助手、文档问答类应用最主流的技术路径,没有之一。它能做的是“让模型学会开卷考试”——把私有知识、最新资讯、业务规则这些模型没学过的东西,通过检索的方式临时补充给它,而不是靠重新训练让它背下来。这篇文章适合正在做知识类应用、打算从零搭一套RAG、或者已经在跑但效果一直不理想的人。我会从定义讲到架构,再到底层原理和方案选型,最后把实操中遇到的坑也一并列出来。
1. 为什么需要RAG:它到底解决了什么问题
1.1 大模型“记不住”的两类信息
大模型的知识有一个天然边界:它只记得训练时见过的东西。
第一类是“训练截止之后的事情”。很多模型的知识停留在某个时间点,之后的新闻、新政策、新版本特性,它一概不知。你问它最近发布的一个产品功能,它要么答不上来,要么一本正经地编一个。第二类是“私有知识”。企业内部的规章制度、某个项目的复盘文档、你自己写的研究笔记,这些内容从来没有出现在公开数据集里,模型根本不可能知道。这不是模型笨,而是信息压根没进过它的视野。
但用户的期望不是这样的。用户默认你做了一个“懂业务的东西”,它就应该知道你们公司的报销流程、产品参数、售后政策。所以纯粹靠模型本身做业务问答,基本都会翻车。
还有更隐蔽的一层问题:模型在做一个概率预测,它输出的每个词都是“看起来最合理”的下一个词。当它没有把握时,它不会像人一样承认不知道,而是倾向于编一个听起来像样的答案。这就是大家常说的“一本正经地胡说八道”,术语叫幻觉。RAG 的核心目的之一,就是把这种胡说八道的空间压缩到最小。
1.2 为什么不是把所有资料都塞进提示词里
很多人第一次想到的解法很简单:模型记不住,那我每次提问时把所有文档都拼进提示词里不就行了?在上下文窗口只有 4k、8k 的时代,这根本不现实,因为一份像样的文档就超了。后来上下文窗口扩大到 128k、200k,这个方案看起来似乎可行了,但我劝你别高兴太早。
首先是成本问题。上下文越长,每次请求消耗的 Token 越多,费用随长度线性增长,甚至更夸张。哪怕你自己部署模型,长上下文也意味着更长的计算时间,用户等一个回答可能要十几秒。其次是效果问题。研究表明,模型对长上下文的注意力并不是均匀分布的,中间位置的信息很容易被忽略。你塞了三十份文档进去,真正有用的那一段偏偏被模型“漏看”了,这种情况在超长上下文场景里非常常见。
RAG 的思路是“按需取用”。它不把所有资料倒在模型面前,而是先检索出与当前问题最相关的少量片段,只把这几个片段交给模型。就像你让一个实习生写方案,你不会让他把整个资料室搬空慢慢看,而是先让他去目录里查,找到对应的几份文件,然后只读那几份。这样既省钱,又减少干扰,答案质量反而更稳。
1.3 RAG的核心价值:开卷考而不是背书
微调和 RAG 常被拿来对比,其实它们解决的问题是两种完全不同的“学习方式”。
微调是让模型把知识“背下来”,相当于让一个学生反复朗读教材,然后闭卷考试。这种方式的优点是模型本身拥有了知识,回答自然流畅;缺点是每次知识更新都要重新训练,成本高、周期长,而且模型可能把旧知识和新知识搞混。RAG 则是给模型一份资料库让它现查现答,相当于开卷考试。知识更新时,你不需要动模型,只需要替换或补充资料库里的文档就行。
在实际应用里,RAG 还有两个微调很难替代的优势:一是来源可追溯,回答引用了哪些文档片段,你可以直接展示给用户看,出了问题能定位到原始资料;二是权限可控,不同用户问同一个问题时,你可以在检索阶段就过滤掉他无权访问的内容。这两个特性对企业应用来说几乎是刚需。
这就是 RAG 存在的根本原因:它不去改变模型的记忆,而是改变模型回答问题时“眼前能看到什么”。明白这一点,后续所有的架构设计、参数调整、方案取舍,都有了判断标准。
2. RAG整体架构:三个模块如何分工
一个标准的 RAG 系统可以拆成三个阶段:索引、检索、生成。很多人刚上手时把精力全放在最后一步生成上,天天调 prompt,效果却一直上不去。实际上,前面两段才是决定上限的地方。
2.1 索引阶段:从原始文档到可检索的知识库
索引阶段做的事情,是把一堆“人看的文档”变成“机器能查的知识库”。这个过程看着简单,但细节非常多。
第一步是文档加载。PDF、Word、Markdown、网页、扫描件,格式五花八门。PDF 尤其麻烦,有的文本层正常,复制出来是乱码;有的是扫描图片,必须走 OCR。我踩过不少次坑,在项目初期花了整整两天处理一个 PDF 表格,最后发现问题出在源文件本身。所以第一步不是急着写代码,而是先看数据。
第二步是清洗。这一步很多人会跳过,但我要说,清洗比后面选什么 Embedding 模型更重要。页眉页脚、水印、目录、脚注、作者简介这些噪声信息如果不去掉,它们会被切成小片段丢进向量库,检索时就会污染结果。还有乱码、半角全角混用、换行符把一句话切成两半,都会直接影响切片质量。数据不进干净,后面的环节全在给错误兜底。
第三步是切片,也就是把长文档切成固定大小的文本块。切片策略直接影响检索效果,我后面会专门展开讲。
第四步是向量化。把每个文本块用 Embedding 模型转成向量,这一步是 RAG 的底层能力。第五步是入库,把切好的文本块和向量一起写入向量数据库,并建立索引。到这里,知识库就算是建好了。
2.2 检索阶段:查得准才有好答案
检索阶段是 RAG 系统里最关键的环节。
当用户输入一个问题时,系统会做这几件事:把问题转成向量,在库里做相似度搜索,召回 Top-K 个相关片段,有时还会做一次重排序,最后把筛选出来的片段作为上下文交给生成阶段。
这里有个问题你可能没意识到:向量检索只在“语义空间”里找相似,它并不保证返回的结果真的能回答用户的提问。比如用户问“报销流程中发票丢失怎么办”,检索出来的片段可能讲的是“报销流程中的发票粘贴规范”,虽然都提到了发票和报销,但并不是同一个问题。所以单纯依赖向量检索,召回内容往往“相关但不对题”。
为了解决这个问题,生产级系统一般会再加一层重排序。检索阶段先粗召回一大批候选,这一步追求“别漏掉”,系统用更精细的模型对这些候选重新打分,这一步追求“更准确”。粗召回可能拉回五十条,重排序后只选前五条。前面的召回负责广度,后面的重排负责精度,两者各司其职。
2.3 生成阶段:让模型拿着资料回答
检索做完之后,系统会把问题、召回片段、指令模板拼成一段完整的 prompt,交给大模型生成答案。
生成阶段的讲究不比检索少。第一,指令要明确约束模型“只依据给定的资料回答”,如果资料里找不到答案,必须明确说不知道,不许自由发挥。这句话能显著降低幻觉出现的概率。第二,多个片段之间可能存在冲突,你需要决定是让模型综合判断,还是优先某个来源。第三,要让模型在回答中标注来源,比如“根据《员工手册》第 3 章”,这样用户能追溯,也便于后续排查。
我在生成阶段还调整过一个很实用的细节:把召回的片段做编号,让模型在引用时输出编号而不是整段复述。这样既减少 Token 消耗,又方便前端把来源高亮展示给用户。作为系统来说,生成阶段还要考虑缓存、限流、接口超时这些工程问题。检索还好,生成往往是整个链路里最慢的一环,不做好缓存,高并发场景下体验会很难看。
3. 底层原理:Embedding与向量相似度检索
RAG 之所以能工作,关键是它把“文字之间的语义关系”变成了“数字之间的距离关系”。理解清楚这一层,你调参时就不会像无头苍蝇一样乱试。
3.1 Embedding是什么,如何理解“语义相近”
Embedding 做的事情,是把一句自然语言映射成一个几百上千维的向量。比如某个模型会把“猫”映射成 1024 维向量,把“狗”也映射成一个 1024 维向量。这两个向量在空间里的距离很近,因为它们的语义相近。而“汽车”的向量离“猫”就远得多。你可以把向量想象成地图上的坐标点:语义越接近的词句,坐标就越靠近。
这个映射不是靠人工规则定义的,而是靠大量文本训练出来的。训练它的任务通常是:给定一个句子,让模型判断哪句话和它是语义相似的,哪句话不是。经过海量数据的训练,模型学会把语义相近的文本放到相近的位置。我们平时说的“这个 Embedding 模型好不好”,指的就是它对语义相似性的刻画是否准确。
这里有一个很常见的误解:Embedding 相似不代表“能回答这个问题”。一个检索片段和问题在字面上相关、主题上接近,很可能向量距离也近,但它不一定包含回答问题所需的关键信息。向量检索解决的是“找到相关内容”,而不是“找到正确答案”。很多 RAG 效果差,就差在这个地方。
3.2 向量相似度的几种算法
向量入库后,检索就变成了数学上的“找最近邻”。常用算法有三种。
第一种是余弦相似度。它计算两个向量之间的夹角余弦,只关注方向、不关心长度,结果是 -1 到 1 之间。这是 NLP 里最常用的方式,因为语义相似性主要看方向而不是看向量的模长。
第二种是内积。内积把方向也考虑上了,如果向量已经做了归一化处理,内积和余弦相似度的排序结果基本一致,但计算更快。
第三种是欧氏距离。它在原始特征空间里计算两点间的直线距离。在高维向量中,欧氏距离对向量长度很敏感,两个语义相同但“长度”不同的向量,距离可能非常远,所以一般不直接用在文本向量检索里。我在项目里实际测试过,同样的库用余弦相似度和用欧氏距离排序,结果差异比你想的大得多。
用生活类比解释就是:同样说“我想吃水果”,有的说法长、有的说法短,语义是同一个,余弦相似度能抓住这个语义,欧氏距离可能因为向量长度不同而误判。
3.3 向量数据库如何做到毫秒级搜索
向量数据库要解决的核心问题是:在一个可能包含几百万条向量的库里,给定一个查询向量,快速找出最接近的 Top-K 条。如果每次查询都跟全库挨个算一遍余弦相似度,数据量一大就完全跑不动。所以向量数据库普遍使用近似最近邻搜索算法,也就是 ANN。
业界经常用的算法思路大概分两类。一类是基于图的算法,把向量组织成一张图,查询时从某个节点开始沿着最接近的路径跳转,逐步逼近目标,代表思路是 HNSW。另一类是基于倒排索引的思路,先把空间分区,查询时只扫描少数几个区域,代表思路是 IVF。这些算法都有几个关键参数需要调,比如图的连接数、搜索时的扫描深度、分区数量。参数调得合理,是准确率和查询速度之间的取舍;调得不好,要么召回率暴跌,要么延迟高到没法用。
很多人一上来就选分布式向量数据库,觉得数据量大必须集群。实际上,绝大多数业务在初期只有几万到几十万条片段,单机内存索引完全能撑住,毫秒级响应也没有问题。分布式带来的网络开销和运维复杂度反而可能拖慢迭代速度。我的建议是:先单机跑通,把数据量和性能指标测出来,再决定要不要上分布式。
4. 实操要点:切片、Embedding、Rerank与检索策略
这部分是干货最密集的区域。很多 RAG 项目上线后效果差,不是架构选错了,而是几个小细节没做好。这一节讲的每一个点,我都建议你在业务里亲自做一组对比测试。
4.1 切片策略:不要无脑按字数切
切片是决定检索效果的第一个关键操作。最常见的做法是按固定 Token 数量切,比如每 300 个 Token 一段,但这会带来两个问题:一句话可能被从中间切开,语义断裂;一个完整的知识点被切到两个块里,检索时各得一半,模型自然拼不出正确答案。
我实践下来,更好的做法是按文档结构切。Markdown 按标题层级切,PDF 按章节标题或段落边界切,这样每个切片是一个相对完整的语义单元。如果原文档结构不明显,再退而求其次按固定长度切,但要加上重叠机制:相邻两个切片之间保留 10% 到 20% 的重叠文字,保证横跨边界的信息不会丢。
切片大小也需要做实验验证。切得太小,比如一段只有几十个字,信息量不够,模型拿不到完整上下文;切得太大,比如超过一两千字,检索出来的片段里大量内容与问题无关,反而干扰模型。中文业务文档我一般从 200 到 500 字起步做对比测试,看哪组命中率最高。不同文档类型之间的差异很大,最优参数不是通用的。
4.2 Embedding模型选择:中文场景别盲目选“最强”
Embedding 模型的选型,坑比想象中深。现在公开渠道有很多预训练 Embedding 模型,榜单分数一个比一个高,但换到你自己的业务场景里,分数高不代表好用。
选型时我建议分三步走。第一步,确定你的文档语言。中文业务文档优先选择在中文语料上训练或者做了中文适配的模型,这一步就筛掉很多英文模型。第二步,看模型对长文本的支持。有的模型输入上限只有 512 个 token,超过部分会被截断,这意味着你的切片不能太大,必须反过来调整切片参数,两者是配套的。第三步,用自己的业务数据做评测。不要相信别人的评测榜单,抽一百条真实问题,搭一个最小链路跑一遍,看每条问题能不能在知识库里检索到正确的片段。这一步最耗时,但也最能说明问题。
我在一个法律咨询项目里踩过这样的坑:换了一个榜单分数更高的 Embedding 模型,整体效果反而下降,原因是该模型对文言文和法条类文本的理解不如原来的领域模型。最后回退到旧模型并用 Rerank 补了一截,才算解决问题。这个经历让我彻底明白:Embedding 的“最强”是相对的,领域匹配才是王道。
4.3 Rerank重排序:性价比最高的一步
如果只能给 RAG 做一个优化,我会毫不犹豫选择加重排序。理由很简单:向量检索擅长“语义粗匹配”,但它的排序能力有限。Top 5 里可能确实有正确答案,但也可能混着两三条似是而非的内容。
重排序的常见做法是用一个专门的相关性模型,把用户问题和每一个候选片段拼成一对输入,模型直接输出“这段内容与问题是否相关、相关到什么程度”。这种做法比向量检索更精细,因为它能看到完整的上下文交互。代价是很耗时,所以一般只在粗召回后的几十条候选上做,而不是在全库上做。
在具体流程上,我现在做 RAG 的标准配置是:向量检索召回 Top 50,Rerank 之后取 Top 3 到 Top 5 进入生成环节。这个流程我在多个项目里验证过,效果提升非常明显,尤其是文档量到了几万条之后,有没有 Rerank 几乎是两套系统。
4.4 元数据过滤与混合检索:容易被忽略的精确手段
向量检索不是只能做“语义模糊匹配”。如果你的文档带有业务属性,比如部门、年份、文档类型、密级,那么这些信息也应该一起入库,并在检索之前作为过滤条件。
举个例子:用户问“今年的报销政策是什么”,如果你知道当前日期是 2025 年,就应该在元数据上过滤只保留 2025 年的政策文档。否则库里 2023 年和 2024 年的老政策也会参与排序,很可能抢占前面的位置。这个操作一句话就能实现,但对答案正确率的影响是决定性的。元数据过滤相当于给检索加了一个业务层面的约束,把“在全部知识里找”变成“在符合条件的子集里找”。
混合检索也是强烈推荐的手段。它把两种检索结果合并起来:一个是前文说的向量语义检索,另一个是传统的关键词匹配检索。传统关键词检索对专业名词、编号、型号、人名特别敏感,比如“A100-2024-07 型号说明”这种内容,向量很可能因为语义变化而匹配不到,关键词直接搜型号反而稳得很。两者做加权合并,能互补短板。我常用的合并方式是给各自打分排名后取加权和,或者用 RRF 这种简单有效的排名合并算法,不需要复杂的调参。
5. 主流方案对比:RAG、微调与超长上下文该怎么选
方案选型是每个做知识问答的人都绕不开的问题。我在不同项目里分别尝试过 RAG、微调和纯超长上下文方案,这里把个人体会整理成一份对比,你可以当参考。
5.1 RAG vs 微调:不是替代关系
RAG 和微调经常被拿来比较,但在我眼里它们更像是“不同的工具”。微调适合改变模型的表达风格、输出格式、学会使用 API 工具;RAG 适合补充事实性知识、业务规则、动态更新的信息。
比如你想要一个“语气更专业、输出更简短”的客服助手,微调很有用;但如果你要它回答“当前产品的全部型号与参数”,而且参数每月更新,RAG 明显更合适。还有一点,微调后的模型依然有幻觉风险,它只是把你不希望它说的话“背”得更好一些。企业里真正对答案准确性要求高的场景,我几乎没有见过纯微调能撑住的。
两者也可以结合。先用微调教会模型回答的格式和风格,再用 RAG 提供事实依据。不过要提醒一句:同时上两者会让系统复杂度翻倍,问题定位变难,建议先做一版 RAG 跑通,再考虑要不要微调。
| 维度 | RAG | 微调 |
|---|---|---|
| 知识更新 | 改文档即可,即时生效 | 需要重新训练,周期长 |
| 来源追溯 | 可展示引用的文档片段 | 无法追溯,知识沉淀在模型内部 |
| 成本 | 需要检索系统,成本在存储和接口调用 | 算力成本高,且每次更新都要花钱 |
| 适合场景 | 事实问答、业务知识库、权限控制 | 输出风格、格式、专业术语习惯的培养 |
5.2 RAG vs 超长上下文:窗口大不等于效果好
还有一种路线非常诱人:直接把所有相关文档一股脑塞进上下文,因为模型本身的窗口已经足够大了。在某些场景下这确实简单有效,尤其是单次会话中只需要处理一两份核心文档时。
但一旦文档规模上来,问题就很明显。首先是成本,每轮对话都全量传输,Token 消耗量大。其次是效果,我在前文提过,模型对超长输入会“顾头不顾尾”,中间部分经常被忽略,导致检索到了但模型没用上。更隐蔽的问题是知识库里如果有一百份文档,其中只有三份相关,超长上下文方案通常做不到先“筛掉那九十七份”——它会把所有内容全部塞进模型,相关信息和噪声混在一起,生成质量就会受到干扰。
RAG 的优势是“在进模型之前先做减法”。它只把最相关的少量片段交给模型,避免噪声。窗口再大,也只是给 RAG 兜了一个底:万一 RAG 漏召回了,长上下文还能起到补救作用。所以现在很多系统干脆把两者结合,先 RAG 召回候选片段,再把完整文档塞进上下文,兼顾准确和全面。
5.3 框架选择:开源轮子与自己写代码怎么平衡
现在市面上的 RAG 框架大致可以分成三类。第一类是编排型框架,它们给你提供了从加载文档、切片、向量化到检索和生成的完整脚手架,好处是上手快,适合快速验证;坏处是封装太厚,出了问题不好排查。第二类是检索增强型框架,它们更聚焦于检索和重排序环节,给检索管线的控制力更强,适合对精度要求高的场景。第三类是自己写的极简管线,代码量不大,但每一步都能完全掌控。
我的建议是:项目启动时别急着写底层代码,先用编排型框架快速跑一个端到端 Demo,验证数据和场景能跑通。等开始往生产环境走,再逐步把关键环节替换成自己控制的实现,尤其是切片、元数据过滤和 Rerank 这三处。框架的价值是让你快速试错,而最终效果的好坏,始终取决于数据和细节。完全受限于框架往往很难做深度优化。
6. 常见问题与排查技巧实录
最后这部分,我把项目里反复遇到、也经常在内部复盘时被人问起的问题整理成一份速查表。RAG 的效果问题,九成都能在这里找到方向。
6.1 检索结果和问题无关,先看日志再看切片
这是最典型的“效果差”现象。排查的第一步永远是把检索结果打印出来人工看一眼。不要先猜模型问题,先看中间输出。我经常遇到的情况是:用户问“发票丢失怎么处理”,系统召回的片段全是“发票开具流程”,主题相关但答案无关。这时问题出在检索,不是生成。
如果检索结果确实偏了,顺手检查切片大小。切片太小,比如 100 字不到,常常只能捕获表面词汇,无法覆盖完整知识点;切片太大,比如超过 1500 字,又会造成检索命中不精准。另外看看你的 Embedding 是否领域匹配,我之前提到过某个项目换通用模型后效果下跌,就是这个原因。
6.2 答案开始胡说八道,检讨 Prompt 和候选冲突
如果你的检索结果本身是对的,模型还是答错,那就要检查生成阶段的 Prompt 约束是否够强。我在生成阶段一定加这么一句话:当资料中没有足够信息时,请直接回答“资料中未找到相关内容”,不要推测。这句约束能把一部分幻觉挡在门外。但还不能完全挡住另一种情况:召回的多个片段之间互相矛盾,模型不知道该采信哪个。这时候我建议在 Prompt 里让模型优先采信最新时间的片段,或者把多个片段按时间排序后展示,并要求模型说明来源。事实性要求高的场景,还可以接一步独立的校验模型,让它检查答案中有哪些信息没有原文依据,再决定是否修正。
6.3 答案质量忽高忽低,检查 Top-K 和温度参数
同一个问题,上午回答很好,下午回答就拉胯,这常常是随机性导致的。大模型生成时带有概率采样,温度越高波动越大。知识问答场景我一般把温度调到 0 或 0.1,让输出尽量稳定。另一个因素是 Top-K 的取值不稳定:召回的候选集稍微变化,答案就跟着变。你把 Top-K 调大,召回覆盖面增加,但噪声也增加;调小,结果更聚焦,但可能漏掉正确答案。我的做法是在 Rerank 后采用较小的 Top-K,比如 3 到 5,配合温度 0,基本能让答案稳定下来。
6.4 响应太慢,先分清瓶颈在检索还是生成
用户抱怨“转圈圈”,你要先定位瓶颈。最简单的方法是在代码里给两个环节分别记录耗时日志。如果是检索慢,常见原因是向量库参数不合适,或者候选量太大导致 Rerank 排队;如果是生成慢,那就是大模型本身输出速度的问题,需要从模型部署、量化、流式输出这些方向解决。还有一个非常容易忽略的问题:知识库文档数量远超实际需求,比如库里已经有几百万条无关数据,还在被全量检索。加上元数据过滤之后,检索范围大幅缩小,速度通常会有明显改善。
最后说点实在的
根据我个人的项目体会,RAG 系统的效果瓶颈往往不在模型,而在数据清洗、切片策略和检索质量的打磨上。模型再强,喂给它错误的片段,产出的答案也站不住。如果你只打算记住一个建议,我建议先建一个一百条左右的业务测试集,每条问题标出正确答案应该出现在哪份文档里。每次改切片、换 Embedding、调参数,都用测试集跑一遍。否则你改来改去,只会分不清哪个改动是正优化还是负优化。磨测试集比磨模型花时间,但这才是最稳的捷径。