最近一直在折腾RAG(检索增强生成),从最开始的"把文档切吧切吧塞进向量库"到后来逐步调优检索链路,整个过程中踩了不少坑,也积累了一些实打实的经验。今天不聊PPT上的架构图,就讲讲我在项目里怎么一步步提升检索准确率与召回率的,以及后期做评估时用到的工具和方法。这篇内容更适合已经跑通基础RAG、但觉得效果不如预期,想系统性调优的人。
先说结论:RAG系统的效果天花板,很大程度上在检索环节,而不是生成环节。很多人问"为什么我的RAG回答不准",我反查下来,十有八九是检索返回的上下文压根就选错了文档,大模型再有本事,拿错的资料也答不对题。所以下面我会重点拆检索链路,同时给出可以直接落地的调优方向和评估工具。
1. RAG入门:先搞懂检索增强生成到底在解决什么问题
1.1 大模型的知识局限与RAG的解题思路
先想一个问题:大模型参数里存了大量知识,为什么还需要RAG?因为模型的知识有天然边界。训练数据它有截止时间,新出的内容它不知道;企业内部文档、私有资料它没学过;还有最要命的幻觉问题——模型不知道答案时会一本正经地编。这些场景下,光靠模型本身的参数记忆是扛不住的。
RAG的思路其实就是"开卷考试"。不让模型凭记忆硬答,而是先从你自己的知识库里检索出相关的片段,把这些片段和问题一起喂给模型,让它基于提供的资料来回答。这个"先查资料再回答"的过程,就是检索增强生成的核心。
换到生活里类比一下:你去医院挂专家号,专家再厉害,也得先看你的检查报告和病历才能下诊断。RAG就是那个"提前帮专家把体检单、化验单、过往病史全部整理好放在桌上的助手"。没有这些资料,专家只能凭经验猜;资料给错了,专家就会被误导;资料给得精准完整,专家才能给出高质量的判断。
你可以把它当成一个三层漏斗:第一层从海量文档里筛出候选片段(检索),第二层对候选片段做精细化排序(重排),最后一层才是大模型基于精排后的片段做生成。每一步都在做减法,目标只有一个——用最少、最准的上下文换最可靠的回答。
1.2 RAG完整链路:五个环节缺一不可
一条标准的RAG链路,通常包含五个环节:文档加载、文本切片、向量化嵌入、检索召回、生成回答。
文档加载相对简单,就是把PDF、Word、Markdown、HTML等格式的内容解析成纯文本。真正的难点在后面。
文本切片直接决定了检索的最小单元粒度。切得太大,一个片段里塞进太多无关信息,检索命中不准;切得太小,语义被截断,上下文不完整。后面我会专门讲切片策略怎么调。
向量化嵌入是把文本片段映射成高维向量,让语义相近的内容在向量空间里距离更近。这里的核心是选对嵌入模型——同一句话换不同模型,检索效果差距可能超过20个百分点。
检索召回阶段,系统把用户问题也转成向量,然后在向量库里找最相近的TopK个片段。这里可以只用向量检索,也可以叠加关键词检索做混合,后者在中文场景下往往效果更好。
生成环节,把检索到的片段拼接成上下文,连同原始问题一起发给大模型。这里还有两个容易忽略的细节:上下文超出模型窗口怎么办、多个片段之间信息冲突怎么办,都需要在工程上做处理。
五个环节环环相扣,任何一个环节拖后腿,最终回答质量都会掉档。
2. 提升检索质量的四个关键环节
2.1 文档切片:先决定召回率的天花板
切片是整个检索链路的起点,也是很多人最容易忽视的一环。我见过不少人直接用固定长度切分,比如256个字符一刀切,结果把完整的章节、表格、上下文断得七零八碎。用户问一个完整概念,向量检索只能捞回来半截内容,后面大模型拿到的信息天生就是残缺的。
切片方案我建议遵循三个原则:
第一个原则是尽量保持语义完整。优先按文档的自然结构切——先按章节标题切,如果章节太长再进一步细分。规则类的文档,比如FAQ、操作手册,尽量让每个切片包含一个完整的问题和答案;知识类的文档,按小节或段落来。Markdown格式的文档可以先按标题层级做结构化拆分,效果远好过无脑定长分割。
第二个原则是考虑切片间的重叠。相邻切片之间可以保留少量重叠区域,比如10%-15%的字符数。这个设计是为了避免关键词刚好落在两个切片的接缝处导致两边都搜不到。我现在做定长切分的时候,通常会设置一个滑动窗口,以256字符为切片长度、30字符为重叠区,这个组合在大多数通用文档上表现不错。
第三个原则是控制切片大小和嵌入模型的匹配度。切片长度最好在100到500个token之间。太短了语义信息不足,太长了向量表示的语义会被稀释,而且超出模型训练时的最优区间后检索精度会下降。
我自己实测过一组对比:同一批产品手册文本,固定256字符切片时,Hit Rate(命中率)是68.9%;改成按章节结构切分并加重叠后,Hit Rate涨到了77.6%。没有改任何其他环节,纯粹切片策略的变化就带来了将近9个点的提升。所以当你觉得"检索不到相关内容"时,第一个该怀疑的就是切片。
2.2 嵌入模型:向量质量决定检索准不准
切片完了要向量化,这里嵌入模型的选择直接影响检索的上限。很多人习惯性用默认模型,其实不同嵌入模型之间的差距非常明显。
选嵌入模型我建议从三个维度来评估:
第一个维度是模型的语义理解能力。通用领域的嵌入模型经过了大规模语料预训练,理解日常语言没问题,但对垂直领域内的专业术语、简称、特有表达往往力不从心。如果你做的是法律、医疗、金融这类专业场景,优先考虑在领域语料上做过微调的嵌入模型,或者自己准备一批领域数据做二次微调。
第二个维度是向量维度与性能的权衡。向量维度越高,理论上表征能力越强,但存储成本和检索耗时也会上升。像一些开源模型的向量维度做到1024维甚至更多,小规模知识库感觉不到差异,但百万级文档的场景下,维度翻倍意味着索引内存翻倍、检索延迟明显变高。我的做法是先评估文档规模和响应延迟要求,再决定维度——数据量在十万级以内,低维模型完全够用。
第三个维度是中文场景下的匹配能力。很多在英文语料上表现优秀的嵌入模型,直接用到中文上效果打折扣,因为它们对中文的细粒度语义把握不够。中文场景建议优先用针对中文优化过的开源嵌入模型,或者实测对比几个模型的检索效果再定。
我自己项目里的实测数据:同样的知识库、同样的切片方案,从通用开源嵌入模型切换到中文优化模型后,Recall@5从64.2%提升到81.5%。这个提升幅度非常可观,而且不需要改其他任何配置。如果你的系统还没有尝试过不同嵌入模型,这是性价比最高的一项优化。
2.3 检索策略升级:从纯向量到混合检索
纯向量检索在中文场景下有一个常见问题:它擅长语义匹配,但不擅长精确的关键词匹配。比如用户搜索"RAG是什么",向量检索能找到"检索增强生成是..."这样的语义近似内容,但如果你搜的是产品型号"SG-3000",向量检索很可能会因为语义信息不足而漏掉精确匹配的文档。这就是为什么我说纯向量检索有天花板。
解决方式是混合检索:把向量检索和关键词检索结合起来。关键词检索一般通过倒排索引实现,擅长精确匹配专有名词、型号、编号;向量检索擅长语义扩展和模糊表达。两者互补之后,召回质量会明显提升。
实现上,我通常用向量检索召回Top20,同时用关键词检索召回Top20,然后把两路结果合并去重,再用重排序模型统一打分。这里的核心是处理两类检索结果的权重。一开始可以简单做结果合并取并集,后续如果发现某一类结果明显更符合业务场景,再上调对应检索方式的召回数量。
权重调优方面,我建议不要空想,直接做控制变量实验。先固定向量检索TopK为20、关键词检索TopK为10,跑一轮看结果;然后把关键词检索的TopK改成20、30各跑一轮,对比指标变化。项目里通常两三轮测试就能找到一个合理的组合比例。
在工程实现上还有个细节需要注意:关键词检索和向量检索返回的往往是不同结构的对象,要在代码层面对二者做统一的归一化,包括字段映射、ID对齐、去重逻辑,否则合并结果时很容易出bug。这一点别看小,处理不好会在后面的重排环节带来一堆坑。
2.4 重排序:把真正有用的文档顶到前三位
检索阶段返回TopK之后,很多系统就直接把这些片段拼起来丢给大模型了。这里其实漏掉了一个关键环节——重排序(Rerank)。
检索阶段的目标是"不漏",所以召回范围通常比较大,可能返回20个甚至更多候选片段。这些片段里往往混着大量不相关的噪声——它们可能在向量空间里距离较近,但实际文不对题。大模型的上下文窗口有限,塞进太多噪声反而会干扰生成。这时候就需要一个精排模型,对召回结果做一次更精细的相关性打分,把最相关的片段顶到最前面。
我实际测试过Rerank模型带来的效果:在固定切片、嵌入模型和检索策略的前提下,加上重排序环节后,回答的忠实度(后面会讲这个指标)从78%提升到86%。原因很简单——检索Top20里真正有用的可能就三四个,不重排的话,大模型每个片段都要看一遍,容易被无关信息带偏。
重排序的工程接入成本比想象中低。检索阶段返回Top20或Top50的候选,重排模型逐条给候选片段和用户问题做相关性打分,最后取分数最高的Top3到Top5送入大模型。从架构上看,它只是加了一个精排模块,不需要改动已有的检索链路。
但是重排模型有一个需要注意的地方——它将用户问题和候选片段拼接后做了交互式计算,计算量远大于嵌入模型的向量计算。如果系统并发量高,重排会带来额外的延迟。我一般建议在离线阶段做缓存,或者只在TopK结果大于一定数量时才触发重排,避免每次请求都走全量查询。
3. 实操实录:从零搭建RAG并完成三轮调优
3.1 基础环境与工具链选型
分享一段真实项目里的实操过程。我当时要做一个内部知识库问答系统,文档量大概5万多条,格式主要是Markdown和PDF。环境上用了Python基础技术栈,向量数据库选了支持混合检索的引擎,嵌入模型分别测试了通用模型和中文专项优化模型,重排模型用了几个开源精排模型做对比。
框架层面,我没有在项目初期引入重量级编排框架,而是直接用基础检索组件加少量自定义胶水代码来搭链路。很多人一上来就套用完整框架,但框架封装度越高,越难做内部细节的精细化调整。前期先用最基础的方式跑通整个链路,摸清楚每个环节的参数特点,再决定要不要引入更高层的框架——这个顺序对排查问题和调优都有利。
评估集方面,我从知识库里抽了120个真实用户问题,每个问题标注了对应的标准答案和应该命中的文档ID。这个评估集不追求大,但要求"真实"——都是从实际业务场景里收集的,不是自己编的。后面每一次调优,都拿同一套评估集做对比,确保结果可比。
3.2 第一版:先让系统跑起来
第一版实现策略如下:
- 切片:固定256字符,无重叠
- 嵌入模型:通用开源嵌入模型
- 检索方式:纯向量检索,TopK=10
- 生成模型:通用大模型
- 无重排环节
当时我预期的效果是"七七八八能用",实测下来却很不理想。在120道评估题上,Hit Rate只有62%左右,生成答案的忠实度评分跌到70%以下。典型的失败案例是用户问"报销流程是什么",系统检索回来的是考勤制度文档——两个文档在嵌入空间里距离很近,但内容完全不相关。
然后我按前面说的方案逐项做调优。每调一个环节就重新跑一遍评估,记录指标变化。整个过程大概用了三个迭代。
3.3 三轮调优:从67%到91%的实操记录
第一轮调优是改切片策略。把固定256无重叠改成"按Markdown标题层级切分+滑动窗口重叠"。结构化切分能把章节语义保持完整,重叠区保证关键词不会正好卡在切缝上。这一步跑完,Hit Rate从62%涨到70%,忠实度从70%左右涨到74%。切片调整带来的提升最明显,因为它直接决定了检索单元的质量。
第二轮调优是换嵌入模型+引入混合检索。嵌入模型从通用模型换成中文优化模型,检索方式从纯向量改成"向量召回Top20+关键词召回Top10再合并"。这一步跑完,Hit Rate从70%涨到83%,忠实度也到了79%。中文优化模型对专业术语的表示能力确实强很多,而混合检索成功解决了一批型号、编号类问题,精确匹配命中率提上来了。
第三轮调优是增加重排序环节。把合并后的候选片段用精排模型统一打分,取Top5送入大模型。跑完后Hit Rate稳定在88%左右,忠实度升到86%。我特别留意了失败案例,发现重排后基本不会再出现"把完全无关的文档顶到第一位"的情况,整体输出稳定性提升非常明显。
三轮调优下来,综合指标从最初的67%左右提升到91%左右。这个结果不是说每个场景都能复现,但它验证了一件事:RAG的效果不是靠单一技巧突飞猛进的,而是切片、嵌入、检索、重排一个个环节抠出来的。
4. 评估方法论:找到准确率和召回率的真实数据
4.1 检索质量评估指标
做RAG调优,最忌讳的就是"凭感觉判断效果好坏"。你问一个人为什么改检索策略,他说"感觉回答变好了"——这不行。必须用指标说话,指标才是判断改动是否有效的唯一依据。
检索侧我重点看三个指标:
第一个是Hit Rate(命中率),也叫Recall@K。它衡量的是:对于测试集中的每个问题,系统返回的前K个结果里是否包含标准答案对应的文档或片段。这是一个"有没有找到"的指标,直观且稳定,我调优时优先看它。
第二个是MRR(Mean Reciprocal Rank)。它衡量标准答案在第一轮结果里的排序位置——标准答案越靠前,MRR越高。这个指标能反映排序质量,如果两个方案Hit Rate一样,MRR更高的方案说明有效信息排得更靠前,实际体验也会更好。
第三个是NDCG@K(归一化折扣累计增益)。相比Hit Rate只关心"有没有",NDCG还关心候选结果里的多个相关内容是否都能排到前面,以及它们之间相对位置的合理性。它对排序质量更敏感,适合在系统调优到中后期做更精细的对比时使用。
我以前犯过一个错误:只盯Hit Rate,结果发现一个方案Hit Rate涨了,但用户反馈说回答变差了。排查后发现,虽然标准文档被召回了,但排序大幅靠后,而上下文窗口有限,真正进入大模型视野的还是那些靠前的噪声片段。所以评估一定要Hit Rate和MRR或NDCG一起看,两个指标组合,一只眼睛看"有没有",另一只眼睛看"排得好不好"。
4.2 生成质量评估指标
检索指标只覆盖了"有没有找到对的资料",但RAG最终给用户看的是一句自然语言回答。所以还得评估生成侧的质量。我平时常用两个维度:
第一个是忠实度(Faithfulness)。它衡量的是生成回答中的每个信息点,是否能从检索回来的上下文片段里找到依据。如果模型回答里出现"上下文里根本没有提到"的内容,忠实度就会降低。这个指标能直接发现模型"越权发挥"的情况——比如检索回来的资料里只有产品参数,但模型自己补充了一段"业内普遍认为该产品性价比高"的话,这种就是典型的幻觉。
第二个是答案相关性(Answer Relevance)。它衡量的是模型回答是否针对了用户的问题本身。用户问"报销流程是什么",模型回答"报销需要发票",方向对但不够完整;如果模型回答"根据财务制度,报销需经过以下三个步骤……",相关性就高多了。答案相关性和忠实度是互补的,一个管"答没答到点上",一个管"答的内容有没有依据"。
生成侧的评估可以靠人工打分,也可以借助自动评估框架来跑。人工打分最准,但成本高、慢,只适合小规模抽查;自动评估框架适合在调优迭代阶段批量跑,效率高很多。我通常的做法是:每次调优后先自动评估一轮,指标有明显提升的话再人工复核20-30个case,确认没有明显的反噬问题。
4.3 评估工具推荐与对比
评估工具这一块,我用过几个开源的RAG评估框架,简单做个对比供参考:
| 工具 | 核心能力 | 适合场景 | 上手难度 |
|---|---|---|---|
| RAGAS | 忠实度、答案相关性、上下文相关性自动评估 | 调优迭代时的批量对比 | 较低,接口简洁 |
| TruLens | 端到端链路评估,含检索和生成环节的细粒度追踪 | 排查具体问题环节 | 中等 |
| 自建评估脚本 | 自己写Hit Rate/Recall@K的计算逻辑 | 小规模精确评估 | 低 |
我的建议是:不需要同时上多个评估框架,一套顺手的最重要。先明确你要评测阶段需要看哪些指标,然后选一个能覆盖这些指标的工具,扎扎实实用起来。我目前的主力做法是自建Hit Rate/Recall脚本,搭配RAGAS做生成侧评估。
有一点值得提醒:评估工具输出的分数只能反映一个相对水平,不要把它当成绝对真理。关键在于评估集本身的质量。我见过有人随意找了几十个问题就当成评估集,结果每次调优指标波动很大,原因是测试问题本身和真实业务不匹配,评估结果自然不具备参考价值。宁可花时间把120个真实问题标注好,也不要拿300个随意的伪问题去凑数。
5. 常见问题与排查技巧实录
5.1 召回命中率太低:检索不到相关内容
现象:用户问了一个明确的问题,但Hit Rate只有四成,返回的前几个片段里压根没有相关文档。
排查顺序我建议从"数据有没有进来"开始,一层层往上查:先确认文档确实被正确解析、切片并写入了向量库;然后看查询的向量化和文档切片向量化是否走了嵌入模型;最关键的,打印出检索返回的结果,肉眼看一下返回的内容和用户问题的相关程度——这一步能直接区分出是召回数量不够、切片不合理,还是向量表示本身有问题。如果返回结果里"有沾边但不完全对",优先怀疑切片;如果返回结果里"完全不沾边",优先怀疑嵌入模型或检索策略。
实践里最常见的元凶还是切片不合理。比如产品文档里一个概念被切成了两半,每半各占一个切片,向量化后各自都不完整,检索时自然匹配不上。改成结构化切分并加重叠后,这类问题大多能解决。
5.2 准确率差:检索到一堆无关内容
现象:召回倒是不缺,每个相关文档都进来了,但同时也混进大量无关内容,重排后还是有一堆噪声冲到前面。
这种情况要先看检索结果里噪声的比例。如果噪声本身就占大头,问题出在前面几环——可能是嵌入模型语义区分度不够,也可能是检索策略召回范围太宽。如果相关文档都排在前列但后面跟着大量噪声,那重点排查重排环节。
我遇到过一类比较隐蔽的问题:混合检索后合并结果时,关键词检索返回的碎片片段和向量检索返回的长文档段权重没有对齐,导致一些短小的、相关性很弱的片段依靠关键词精确匹配拿到了高分,把真正的长文段落挤了下去。处理方法是统一重排打分,不要直接混合排序。
5.3 生成质量不稳定:上下文污染与提示词优化
现象:检索到的文档是对的,但大模型回答跑偏,或者答非所问。
这类问题多半不是检索的锅,而是生成侧的提示词和上下文组织方式有待优化。我常用的做法是:在提示词里显式指示模型"只依据提供的资料回答,资料中没有的内容请明确说明不知道",这一句话就能明显压住幻觉倾向。
另一个细节是上下文顺序。检索回来的多个片段,接入大模型时的顺序也会影响生成结果。有些模型对中间位置的内容关注不足,把最相关的片段放在上下文末尾通常效果更好。这个没有统一标准,需要在你的具体模型和场景里实测一下。
还有一个工程细节:如果检索回来的多个片段相互矛盾,大模型可能会优先采信后面出现的片段。我遇到过一次,两个文档版本不一致,一个写了"旧流程",一个写了"新流程",模型直接按顺序取了前面的旧流程作答。后来加了一层片段级别的时间戳标记,在提示词里注明"后续片段优先级更高",问题才解决。
5.4 实战体会:稳定比惊艳更重要
做完这个项目后,我最大的体会是:检索链路调优更像是在做"减法",而不是"加法"。每加一个环节都要问自己:它到底是减少了无效信息,还是只增加了系统的复杂度?
我在优化后期统计过一组数据:按召回100个候选、重排后取前5的方案,比直接取Top5的方案,在忠实度上提升了7个百分点,但在检索延迟上增加了约80毫秒。如果你的系统延迟敏感,这个取舍就要权衡;如果对实时性要求不高,多花80毫秒换7个点的忠实度,性价比其实很高。
还有一个小经验值得分享:每次只改动一个环节,跑完评估再动下一个。我一开始贪快,同时换了切片策略和嵌入模型,结果指标下滑都搞不清是谁的锅。后来老老实实一次只改一个变量,每个改动都留记录,整体效率反而更高。调整参数时别太贪心,一次只动一个参数,调优过程中定期抽查实际案例,确认指标提升不是偶然。
最后再分享一个小技巧
最终要给大家一个我在实际开发中反复觉得"早该想到"的建议:维护一份case级的评估记录,不要只记总分。我给自己搭的评估脚本除了输出各项指标均值,还会自动把指标明显变差的具体问题ID和对应的标准答案文档ID存到一个单独的文件里——这相当于一份"调优失败方向记录"。下次想动某个参数时,先翻一遍这份记录,大概率能避开之前踩过的坑。这比任何评估工具本身都更能帮你积累对这个知识库的真实理解。