RAG 效果评估:别靠感觉用数据
作者:程序员马丁
Ragent AI —— 从 0 到 1 纯手工打造企业级 Agentic RAG,拒绝 Demo 玩具!AI 时代,助你拿个offer。
上一篇把意图识别和问题路由讲完之后,RAG 系统的各个环节已经全部串起来了:数据入库 → 向量化 → 检索 → 生成 → 工具调用 → 会话记忆 → Query 改写 → 意图识别。系统跑起来了,用户在用了,看着也挺好的。
但有个问题一直没回答——这个系统到底好不好?
你上周把 chunk size 从 512 改成了 1024,改完之后抽了 10 个问题问了一下,感觉回答质量还行。但还行到底有多好?有没有其他问题因为这次修改变差了?你不知道。
你前天换了一个新的 Embedding 模型,换完之后试了几个问题,检索结果看着不错。但看着不错和真的不错之间差着一个评估体系。
你昨天调了 Prompt,加了一条“请基于检索到的内容回答,不要编造信息”。加完之后幻觉是不是真的减少了?减少了多少?你还是不知道。
没有评估,优化就是盲人摸象。改了 Prompt 不知道效果变好还是变差,换了模型不知道值不值,上线新功能不知道有没有引入回归。这就是为什么需要一套系统化的评估方法——用数据说话,而不是靠感觉。
为什么需要系统化评估
1. 靠感觉优化的困境
假设你在做电商客 服 RAG 系统,上线一个月了,老板问你:“系统效果怎么样?”你说:“还行,用户反馈还可以。”老板追问:“具体多少准确率?”你答不上来。
这不是因为你不想量化,而是你没有一套可以量化的方法。日常优化全靠感觉:
- 你把 chunk size 从 512 改成了 1024,抽查了 10 个问题,8 个回答看着还行。但你没法确定这 10 个问题是否有代表性。也许有 50 个其他问题因为 chunk 变大了,关键信息被稀释了,检索效果反而变差了——你不知道,因为你没有测到那些问题。
- 你换了一个 Embedding 模型,试了退货政策和保修期两个问题,检索到了正确的 chunk。但运费承担、质量问题退换这些问题有没有变差?你没有测过。
- 你调了 Prompt,加了限定知识来源的指令,感觉幻觉少了。但到底少了多少?是从 30% 降到了 10%,还是从 30% 降到了 28%?你量化不出来。
10 个样本能说明什么?100 个问题里答对 60 个和答对 90 个,在 10 个样本上可能表现完全一样。
更麻烦的是回归问题:改了 A 环节的东西,B 环节可能受影响。你优化了检索参数让某类问题的召回率提高了,但另一类问题的召回率可能下降了。没有全面的评测,你根本发现不了这种按下葫芦浮起瓢的问题。
2. 分层评估的思路
RAG 系统是一个多环节的流水线,如果只看最终结果——用户的问题有没有被正确回答——你知道答错了,但不知道错在哪个环节。
打个比方,就像工厂的质检。一个产品从流水线下来不合格,你不能只说产品坏了就完事了。你得搞清楚是原材料有问题(检索到的 chunk 不对),还是加工工序出了问题(模型基于正确的 chunk 生成了错误的答案),还是设计本身就有缺陷(知识库里根本没有这个信息)。
RAG 系统的评估也一样,要分层:

三层评估的核心逻辑:
- 检索阶段:召回的 chunk 对不对?正确答案有没有在召回的 Top-K 里?排在第几位?——这层出了问题,后面全白搭,模型再厉害也没法基于错误的 chunk 生成正确的答案。
- 生成阶段:给了正确的 chunk,模型有没有忠实地基于 chunk 内容回答?有没有编出 chunk 里没有的信息(幻觉)?有没有答非所问?——这层出了问题,说明 Prompt 或模型需要调整。
- 端到端:不管中间过程,最终答案正确吗?用户满意吗?——这是最终的结果指标,但光看这个定位不了具体问题。
分层评估的好处是定位精准:检索指标好但生成指标差,说明问题在 Prompt 或模型上;检索指标差,那就先去优化检索环节,调 Prompt 没有用。
检索阶段的评估指标
检索阶段是 RAG 系统的基础,chunk 都没召回来,后面的生成再好也是空中楼阁。检索阶段的评估核心问题就一个:Top-K 个召回的 chunk 里,有没有包含正确答案对应的 chunk?
围绕这个问题,有四个常用指标。
1. 命中率(Hit Rate)
最简单的指标:Top-K 个召回的 chunk 里,有没有包含正确答案?有就是 1,没有就是 0。多个问题取平均值。
用电商客服的例子来算。假设评测集有 5 个问题,每个问题的 Top-3 召回结果:
| 问题 | Top-3 召回的 chunk | 正确 chunk 有没有在里面 | 命中 |
|---|---|---|---|
| iPhone 16 Pro 的退货政策? | chunk_12, chunk_05, chunk_33 | chunk_12 是正确答案 ✓ | 1 |
| AirPods Pro 的保修期? | chunk_21, chunk_07, chunk_44 | chunk_21 是正确答案 ✓ | 1 |
| 退货运费谁承担? | chunk_18, chunk_29, chunk_55 | 正确答案是 chunk_41,不在里面 ✗ | 0 |
| 跨境商品能退吗? | chunk_41, chunk_03, chunk_67 | chunk_03 是正确答案 ✓ | 1 |
| 质量问题怎么换货? | chunk_08, chunk_15, chunk_22 | chunk_08 是正确答案 ✓ | 1 |
Hit Rate = 命中次数 / 总问题数 = 4 / 5 = 0.8(80%)
命中率很好理解,但它有一个明显的缺点——不关心正确答案排在第几位。chunk_12 排在第 1 位和排在第 3 位,命中率上没有区别,都算命中。但实际使用中,排在第 1 位和排在第 3 位的差别很大——排在第 1 位意味着检索系统很自信地找到了正确答案,排在第 3 位可能只是凑巧混进来了。
2. MRR(Mean Reciprocal Rank,平均倒数排名)
MRR 在命中率的基础上,还关心正确答案排在第几位。
计算规则:如果正确答案排在第 1 位,得 1 分;排在第 2 位,得 1/2 = 0.5 分;排在第 3 位,得 1/3 ≈ 0.33 分……排在第 K 位,得 1/K 分。如果 Top-K 里没有正确答案,得 0 分。多个问题取平均。
还是用刚才的 5 个问题,这次多关注一下正确答案的排名位置:
| 问题 | 正确答案排在第几位 | 倒数排名(Reciprocal Rank) |
|---|---|---|
| iPhone 16 Pro 的退货政策? | 第 1 位 | 1/1 = 1.0 |
| AirPods Pro 的保修期? | 第 1 位 | 1/1 = 1.0 |
| 退货运费谁承担? | 没命中 | 0 |
| 跨境商品能退吗? | 第 2 位 | 1/2 = 0.5 |
| 质量问题怎么换货? | 第 1 位 | 1/1 = 1.0 |
MRR = (1.0 + 1.0 + 0 + 0.5 + 1.0) / 5 = 3.5 / 5 = 0.7
MRR 比 Hit Rate 更能反映检索质量。两个系统 Hit Rate 都是 80%,但一个系统的正确答案大部分排在第 1 位(MRR 接近 0.8),另一个系统的正确答案大部分排在第 3 位(MRR 可能只有 0.4)——后者的检索质量明显不如前者,因为排在第 3 位的 chunk 在实际使用中更容易被忽略或被不相关的 chunk 干扰模型生成。
MRR 的直觉理解:MRR = 0.7 意味着平均来看,正确答案大约排在第 1.4 位(1 / 0.7 ≈ 1.43)。MRR 越接近 1,说明正确答案越稳定地排在第 1 位。
3. 召回率与精确率(Recall & Precision)
命中率和 MRR 都是围绕一个正确答案来评估的。但现实中,一个问题的完整答案往往分散在多个 chunk 里。
比如用户问退货政策是什么,完整答案涉及三个 chunk:
- chunk_12:退货条件(7 天内、未拆封)
- chunk_13:退货流程(申请 → 审核 → 寄回 → 退款)
- chunk_14:退货运费(质量问题免运费,其他自付)
只命中其中一个,用户拿到的答案就是残缺的。这时候就需要召回率和精确率来衡量找全了没有和找准了没有。
继续用这个例子。系统 Top-5 召回了这些 chunk:
| 排名 | 召回的 chunk | 是否相关 |
|---|---|---|
| 1 | chunk_12(退货条件) | 相关 |
| 2 | chunk_05(会员等级说明) | 不相关 |
| 3 | chunk_13(退货流程) | 相关 |
| 4 | chunk_33(促销活动规则) | 不相关 |
| 5 | chunk_67(配送时效说明) | 不相关 |
召回率(Recall):该找的 chunk,找到了几个?分母是应该找到的总数,分子是实际命中的个数。
3 个相关 chunk 里命中了 2 个(chunk_12 和 chunk_13),漏掉了 chunk_14(退货运费)。用户问退货政策,结果运费相关的信息丢了。
精确率(Precision):找回来的 chunk 里,有几个是真正有用的?分母是召回的总数,分子是其中相关的个数。
召回了 5 个 chunk,但只有 2 个是相 关的,另外 3 个(会员等级、促销活动、配送时效)都是噪音。这些不相关的 chunk 会干扰模型生成,还浪费 Token。
这两个指标往往是跷跷板关系:
- 召回率高但精确率低——Top-K 设得很大,找了一大堆回来,正确 chunk 确实都包含了,但噪音也很多。多余的 chunk 会干扰模型生成,增加 Token 消耗。
- 精确率高但召回率低——Top-K 设得很小,找回来的每个 chunk 都很相关,但遗漏了部分正确 chunk。答案可能不完整。
RAG 场景通常更关注召回率——宁可多召回几个不太相关的 chunk(后面可以用 Reranker 过滤掉噪音),也不要漏掉正确答案。因为漏掉了就彻底没了,模型不可能基于没看到的信息回答正确。
实际项目中,Hit Rate 和 MRR 更常用,因为它们计算简单,评测集标注也简单——每个问题只需要标注正确答案对应哪个 chunk ID 就行。Recall 和 Precision 需要标注所有相关的 chunk,标注成本更高。如果你的评测集每个问题只标了一个正确 chunk ID,那就用 Hit Rate 和 MRR;如果标了多个相关 chunk ID,可以再看 Recall 和 Precision。严格来说,Hit Rate 和 MRR 也可以标多个正确 chunk ID(命中其中任意一个就算 hit),但它们的核心关注点始终是有没有命中这一个判断,不需要像 Recall 那样算命中了几个占总共几个,所以标注工作量确实小得多。
4. 检索指标对比
| 指标 | 定义 | 关注点 | 标注要求 | 推荐场景 | 参考阈值 |
|---|---|---|---|---|---|
| Hit Rate | Top-K 里有没有正确答案 | 有没有命中 | 每个问题标 1 个正确 chunk | 快速验证检索基本能力 | ≥ 0.85 |
| MRR | 正确答案排在第几位 | 命中位置 | 每个问题标 1 个正确 chunk | 关注排序质量 | ≥ 0.70 |
| Recall | 相关 chunk 被找到了多少 | 覆盖度 | 每个问题标所有相关 chunk | 答案涉及多个 chunk 的场景 | ≥ 0.75 |
| Precision | 找到的 chunk 里多少是相关的 | 噪音控制 | 每个问题标所有相关 chunk | 控制上下文质量和 Token 成本 | ≥ 0.50 |
参考阈值只是经验值,不是绝对标准。不同业务场景的容忍度不一样——医疗问答系统对召回率的要求远高于电商客服。先跑出一个基线数字,后续优化对比变化趋势比绝对值更重要。
生成阶段的评估指标
检索阶段找到了正确的 chunk,但模型有没有好好利用这些 chunk 来回答?这是生成阶段评估要回答的问题。
1. 忠实度(Faithfulness)
忠实度衡量的是:模型生成的答案是否忠实于检索到的 chunk 内容,有没有编出 chunk 里没有的信息?
看一个具体的例子。检索到的 chunk 内容是:
iPhone 16 Pro 保修期为 1 年,自购买之日起计算。保修范围包括硬件故障、制造 缺陷,不包括人为损坏、进水。
模型的回答:
iPhone 16 Pro 保修期为 2 年,自购买之日起计算。保修范围包括硬件故障、制造缺陷,不包括人为损坏、进水。如需延长保修,可购买 AppleCare+ 服务。
这个回答有两个忠实度问题:
- 2 年——chunk 里明明写的是 1 年,模型篡改了事实
- AppleCare+ 服务——chunk 里压根没提到,模型自己编的
忠实度关注的是答案有没有超出 chunk 内容,跟答案本身对不对是两件事。这里有一个容易混淆的点:
忠实度 ≠ 正确率
chunk 里写的内容可能本身就是错的(比如知识库没有及时更新,保修期已经从 1 年改成了 2 年),模型忠实地转述了过时的信息,忠实度是高的,但答案是错的。这种情况说明问题出在知识库而不是生成环节——这恰恰体现了分层评估的价值,帮你精确定位问题出在哪一层。
在实际项目中,团队通常还会在忠实度的基础上统计幻觉率作为系统级的红线指标。具体做法是:用 LLM 对每条回答的忠实度打分(1~5 分,5 分表示完全忠实,1 分表示严重编造,后面 LLM-as-Judge 自动评测章节会详细讲评分方法),然后统计忠实度 ≤ 2 分的回答占总回答数的比例,就是幻觉率。
忠实度是给每条回答打分(这条答案有多忠实),幻觉率是看整体比例(100 条回答里有多少条出现了明显幻觉)。RAG 的核心价值就是基于检索到的知识回答,如果幻觉率居高不下,那 RAG 就失去了意义。一般来说,幻觉率控制在 15% 以下是一个参考基线。
2. 答案相关性(Answer Relevancy)
答案相关性衡量的是:模型生成的答案是否回答了用户的问题?有没有答非所问?
几个例子:
| 用户问题 | 模型回答 | 相关性 |
|---|---|---|
| 退货运费谁承担? | 退货运费由买家承担,质量问题由卖家承担 | ✓ 高——直接回答了问题 |
| 退货运费谁承担? | 退货流程如下:1. 提交退货申请 2. 等待审核…… | △ 中——相关但没回答运费问题 |
| iPhone 16 Pro 的价格? | 我们的退货政策非常完善…… | ✗ 低——完全答非所问 |
答案相关性和忠实度是两个独立的维度,别搞混了。举个例子,用户问退货运费谁承担,系统检索到了一个关于退货流程的 chunk,模型忠实地转述了退货流程的每一步——忠实度很高,但压根没回答运费问题,答案相关性很低。
反过来,模型编了一句运费由卖家承担——答案相关性高(确实在回答运费问题),但忠实度为零(chunk 里没这个信息)。所以这两个指标要分开看,一个管有没有编,一个管有没有答对题。
3. 生成指标对比
| 指标 | 定义 | 评估方式 | 关注点 | 参考阈值 |
|---|---|---|---|---|
| 忠实度 | 答案是否忠实于 chunk 内容 | LLM 评分(1~5 分) | 有没有编造 | 平均分 ≥ 4.0,幻觉率(≤ 2 分占比)≤ 15% |
| 答案相关 性 | 答案是否回答了用户的问题 | LLM 评分(1~5 分) | 有没有答非所问 | 平均分 ≥ 4.0 |
端到端的评估指标
检索指标和生成指标分别看各自环节的效果,端到端指标则直接看最终结果——用户的问题有没有被正确回答。
1. 答案正确率
最直接的端到端指标:最终答案是否正确回答了用户的问题?
这个指标需要有标准答案作为对照。评判正确本身有一定主观性——模型回答的措辞跟标准答案不一样,但意思一样,算不算正确?通常采用语义匹配而不是字面匹配,让人工或 LLM 来判断模型答案的含义是否与标准答案一致。
2. 兜底率
系统回答“抱歉,找不到相关信息”的比例。在生成策略那篇里设计过兜底回答——当检索不到相关 chunk 或者模型判断无法回答时,返回兜底回复。
兜底率太高说明知识库覆盖不够,或检索效果差,大量问题找不到答案;太低也不正常,可能模型在强行回答不该回答的问题,编造答案。
参考范围:5%~15% 是比较合理的区间。低于 5% 要警惕是不是模型在硬答,高于 15% 要检查知识库覆盖度和检索配置。但这个范围跟业务场景强相关——如果你的知识库就是很垂直很小,只覆盖退货退款相关问题,那非退货问题触发兜底是完全正常的,兜底率高不代表系统差。反过来,如果你的知识库覆盖了所有业务场景,兜底率超过 15% 就要认真排查了。
3. 用户满意度
最终的北极星指标。前面所有指标都是技术指标,用户满意度才是业务指标。
可以通过两种方式收集:
- 显式反馈:在回答后面加点赞或点踩按钮,让用户主动评价。简单直接,但参与率低——大部分用户不会主动反馈,反馈的往往是特别满意或特别不满意的,有偏差。
- 隐式反馈:通过用户行为推断满意度。比如用户在得到回答后没有追问(可能满意了),或者用户重复问了同一个问题(说明对上次回答不满意),或者用户在对话后转了人工客服(说明 RAG 没解决问题)。
在实际项目中,用户满意度作为监控指标比作为评测指标更合适。因为它需要线上用户数据,没法在离线评测集上计算。离线评测主要看前面几个指标(Hit Rate、MRR、忠实度、正确率),上线后再关注用户满意度的变化趋势。
评测数据集的构建
评估指标再好,没有评测数据集也算不出来。评测数据集就是你的考试题库——一组预先准备好的问题和标准答案,用来系统性地检验 RAG 系统的效果。
1. 评测集的格式设计
一条评测数据需要包含以下字段:
{
"query": "iPhone 16 Pro 的退货政策是什么?",
"expectedAnswer": "iPhone 16 Pro 支持 7 天无理由退货,需保持商品完好、配件齐全、包装完整。退货运费由买家承担,质量问题除外。",
"relevantChunkIds": ["chunk_12", "chunk_13"],
"intent": "knowledge"
}
各字段的作用:
- query:用户问题,测试时输入给 RAG 系统
- expectedAnswer:标准答案,用来评估模型生成的答案是否正确
- relevantChunkIds:正确答案对应的 chunk ID 列表,用来评估检索阶段——系统检索到的 chunk 有没有包含这些 ID
- intent:意图类别,确保评测覆盖不同类型的问题
其中 relevantChunkIds 是评测数据集的关键。没有它,你只能评端到端的正确率,没法单独评检索阶段的效果。有了它,就可以算 Hit Rate、MRR——检索到的 Top-K 里有没有包含 relevantChunkIds 里的 chunk。