很多 RAG 项目最开始都能跑起来,但一到线上就会卡在一个非常现实的问题上:
用户说答案不对,可你根本不知道问题出在:
- 没召回到正确内容
- 召回到了,但排序不对
- 排好了,但没进最终 prompt
- 进了 prompt,但模型没用
- 模型用了,但引用展示错了
所以 RAG 一定不能只做"生成结果观测",还要做"阶段化 inspect"。
这篇文章会讲:
- 如何分层定位问题
- 检索层和生成层的评测指标
- LLM-as-a-Judge 的原理
- Inspect 系统的设计思路
一、为什么评测这么难
RAG 的评测比传统检索或传统 NLP 都复杂,因为它是多阶段的 pipeline。
用户问题 → 检索 → 重排 → 组装 → 生成 → 答案
最终答案错了,可能的原因:
❌ 检索层:正确内容根本没召回
❌ 重排层:正确内容召回但被排到后面
❌ 组装层:正确内容被截断或过滤掉了
❌ 生成层:模型无视证据自由发挥
如果不分阶段评测,你只能知道"答案错了",但不知道"为什么错"。
一个真实的问题定位案例
用户投诉:“问错误码 401 的意思,答案完全不对。”
定位过程:
第 1 步:检查检索层
- 用户问题:"错误码 401 是什么意思"
- 检索候选 top-10:
1. 常见 HTTP 错误码列表
2. 认证失败怎么办
3. 权限问题排查
...
8. 错误码详解:401 Unauthorized ← 正确答案
结论:正确答案在候选里,但排第 8
第 2 步:检查重排层
- Rerank 后:
1. 错误码详解:401 Unauthorized ← 正确答案排到第 1
2. 常见 HTTP 错误码列表
...
结论:重排正常
第 3 步:检查组装层
- 最终进入 prompt 的 chunk:
1. 错误码详解:401 Unauthorized ✅
2. 常见 HTTP 错误码列表 ✅
3. 认证失败怎么办 ✅
结论:组装正常
第 4 步:检查生成层
- Prompt 包含正确证据
- 模型输出:"错误码 401 表示服务器内部错误..."
结论:模型幻觉!401 是认证错误,不是服务器内部错误
最终诊断:
✅ 检索层:正常
✅ 重排层:正常
✅ 组装层:正常
❌ 生成层:模型无视证据,产生幻觉
解决方案:优化 prompt,强化"必须依据证据"的约束
这就是为什么需要分阶段评测——不同阶段的问题,解决方案完全不同。
二、问题分层框架
建议用下面这套分层框架,每层有明确的责任边界和常见问题:
┌─────────────────────────────────────────────────────────────────┐
│ 第 1 层:检索层(Retrieval) │
│ │
│ 职责:从知识库中召回候选内容 │
│ │
│ 常见问题: │
│ ❌ 正确 chunk 不在候选里 → chunking、embedding、检索策略 │
│ ❌ 正确 chunk 排名太低 → 混合检索、Rerank │
│ ❌ 候选噪声太多 → metadata 过滤、召回数量调整 │
│ │
│ 评测指标:Recall@K、MRR │
└────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 第 2 层:重排层(Rerank) │
│ │
│ 职责:对候选重新排序,提升相关性 │
│ │
│ 常见问题: │
│ ❌ 正确 chunk 被排到后面 → Rerank 模型问题 │
│ ❌ Rerank 效果不明显 → 模型选择、候选数量 │
│ │
│ 评测指标:排序提升比例、NDCG │
└────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 第 3 层:上下文组装层(Context Assembly) │
│ │
│ 职责:控制进入 prompt 的内容和数量 │
│ │
│ 常见问题: │
│ ❌ 正确 chunk 被截断 → 调整 token 预算 │
│ ❌ 正确 chunk 被过滤 → 检查过滤规则 │
│ ❌ 内容太长噪声多 → 优化组装策略 │
│ │
│ 评测指标:正确 chunk 进入 prompt 的比例 │
└────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 第 4 层:生成层(Generation) │
│ │
│ 职责:基于证据生成答案 │
│ │
│ 常见问题: │
│ ❌ 模型无视证据 → Prompt 优化 │
│ ❌ 产生幻觉 → 强化约束、Few-shot │
│ ❌ 引用错误 → 后处理校验 │
│ │
│ 评测指标:答案正确率、幻觉率、引用准确率 │
└─────────────────────────────────────────────────────────────────┘
分层的意义
每一层的问题,需要不同的解决方案:
| 问题层 | 典型问题 | 解决方向 |
|---|---|---|
| 检索层 | 召不回正确内容 | Chunking、混合检索 |
| 重排层 | 排序不对 | Rerank 模型 |
| 组装层 | 内容被过滤 | 组装策略、预算调整 |
| 生成层 | 模型幻觉 | Prompt 优化、模型选择 |
不分层的问题:你发现答案错了,只能"盲调"——换模型、调参数,但不知道调的是哪个环节。
分层的好处:知道问题在哪一层,定向优化。
三、检索层评测
检索层的核心问题是:正确的 chunk 召回了吗?排得够靠前吗?
Recall@K
最直观的指标:正确答案是否在前 K 个候选里。
Recall@K = 正确答案在前 K 的请求数 / 总请求数
示例:
评测集 100 个问题
每个问题有 1-3 个正确 chunk
Recall@5 = 82/100 = 82%(82 个问题的正确 chunk 在前 5)
Recall@10 = 91/100 = 91%(91 个问题的正确 chunk 在前 10)
解读:
- Recall@5 > 80%:检索质量良好
- Recall@5 < 60%:检索需要优化
- Recall@5 ≈ Recall@10:排序效果好(正确答案排在前面)
- Recall@5 « Recall@10:排序效果差(正确答案在后面)
MRR(Mean Reciprocal Rank)
Recall@K 只看"是否在前 K",MRR 还看"具体排第几"。
MRR = Σ (1 / 正确答案的排名) / 总请求数
示例:
问题 1:正确答案排第 1 → 1/1 = 1.0
问题 2:正确答案排第 3 → 1/3 = 0.33
问题 3:正确答案排第 5 → 1/5 = 0.20
问题 4:正确答案未召回 → 1/∞ = 0
MRR = (1.0 + 0.33 + 0.20 + 0) / 4 = 0.38
解读:
- MRR > 0.6:排序质量好
- MRR < 0.4:排序需要优化
NDCG(Normalized Discounted Cumulative Gain)
更精细的指标,考虑多个正确答案的情况。
DCG = Σ (2^rel_i - 1) / log2(i + 1)
其中:
- rel_i:第 i 个位置的相关性(1=相关,0=不相关)
- i:排名位置
NDCG = DCG / IDCG
IDCG 是理想情况下的 DCG(所有相关文档都排在最前面)
直觉:正确答案排得越靠前,分数越高;排在后面会被惩罚(除以 log)。
不同指标的选择
| 指标 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Recall@K | 只关心是否召回 | 直观、易解释 | 不关心具体排名 |
| MRR | 关心正确答案的排名 | 考虑排名 | 只考虑第一个正确答案 |
| NDCG | 有多个正确答案 | 全面 | 计算复杂 |
建议:日常用 Recall@5 + Recall@10,深入分析用 MRR 和 NDCG。
四、生成层评测
检索层评测相对客观,生成层评测更主观——“答案好不好"没有标准答案。
传统指标
1. BLEU / ROUGE
来自机器翻译和摘要领域,计算模型输出和参考答案的 n-gram 重叠度。
BLEU-1: 单词重叠
BLEU-2: 二元组重叠
...
ROUGE-L: 最长公共子序列
问题:RAG 答案不追求和参考答案"字面相似”,而是追求"语义正确"。
参考答案:"使用 Bearer Token 认证"
模型答案:"认证方式是 Bearer Token"
BLEU 分数不高,但语义完全正确。
2. BERTScore
用 BERT 编码后计算语义相似度。比 n-gram 更好,但对 RAG 仍不够精确。
LLM-as-a-Judge
让大模型来评判答案质量,是目前最实用的方法。
思路:
输入:
- 用户问题
- 参考答案(可选)
- 模型答案
- 证据内容(检索到的 chunk)
输出:
- 分数(1-5)
- 是否正确
- 是否有幻觉
- 具体问题
Prompt 设计:
你是一个答案质量评判专家。请评判以下答案的质量。
问题:{question}
参考资料:
{context}
参考答案:{expected_answer}
模型答案:{actual_answer}
请评判:
1. 正确性(1-5分)
2. 是否包含幻觉(编造参考资料中没有的信息)
3. 是否遗漏关键信息
4. 引用是否正确
以 JSON 格式输出:
{
"score": 1-5,
"is_correct": true/false,
"has_hallucination": true/false,
"issues": ["问题1", "问题2"],
"explanation": "评分理由"
}
为什么有效?
LLM 本身有很强的语义理解能力,能判断"语义相似"而非"字面相似"。而且 LLM 能理解上下文,判断答案是否"基于证据"。
注意事项:
- 评判模型要强:用 GPT-4 或 Claude,不要用小模型
- 避免自我评判:生成模型和评判模型要不同
- Few-shot 示例:给几个评分示例,提高一致性
- 多次评判取平均:减少随机性
评测指标的层次
生成层评测应该回答几个不同的问题:
| 问题 | 指标 | 怎么测 |
|---|---|---|
| 答案对吗? | 正确率 | LLM 评判 + 人工抽查 |
| 有幻觉吗? | 幻觉率 | 检查答案是否超出证据范围 |
| 引用对吗? | 引用准确率 | 检查引用的 chunk 是否支持答案 |
| 用户满意吗? | 满意度 | 用户反馈 |
幻觉检测是最重要的,因为 RAG 的核心价值是"有据可查"。
幻觉检测的方法
方法 1:LLM 评判
Prompt:
"以下答案是否包含参考资料中没有的信息?"
答案:{answer}
参考资料:{context}
请指出答案中的每一句话,并判断是否有证据支持。
方法 2:NLI(自然语言推理)
用 NLI 模型判断答案和证据之间的关系:
前提(证据):"Token 有效期为 24 小时"
假设(答案):"Token 有效期为 48 小时"
NLI 输出:Contradiction(矛盾)
方法 3:关键词匹配
简单但粗糙:检查答案中的关键信息是否在证据中出现。
答案:"API 超时时间是 30 秒"
证据:"API 的默认超时时间为 30 秒"
匹配:✅("超时"、"30"、"秒"都在证据中)
五、离线评测 vs 在线评测
离线评测
目的:在上线前验证系统质量
方法:
- 准备评测集(50-100 条)
- 定期跑评测
- 对比不同版本
评测集构建:
评测集应该包含:
- 核心问题(30%):业务最常见的问题
- 边界问题(30%):难度高、容易出错的
- 长尾问题(20%):覆盖更多场景
- 坏案例(20%):之前出过问题的
每条评测数据:
{
"id": "eval_001",
"question": "API 的认证方式是什么",
"expected_chunks": ["chunk_012"], // 期望召回的 chunk
"expected_answer": "Bearer Token", // 参考答案
"category": "技术问题",
"difficulty": "easy"
}
评测流程:
1. 运行 RAG 系统,获取结果
2. 计算检索指标(Recall@K、MRR)
3. 计算生成指标(正确率、幻觉率)
4. 汇总报告
5. 对比历史版本
在线评测
目的:在生产环境持续监控
方法:
- 用户反馈:点赞/点踩、修改问题、人工标注
- 自动评分:用 LLM 对线上答案打分
- 行为分析:用户是否追问、是否放弃
采样策略:
不是所有请求都需要详细评测(成本高)。建议:
- 100% 记录基本信息(延迟、召回数量)
- 10% 详细评分(LLM 评判)
- 1% 人工审核
评测陷阱
陷阱 1:评测集泄露
训练 Embedding 模型或 Rerank 模型时,不小心用了评测集里的内容。评测分数虚高,线上效果差。
解决:评测集严格隔离,不参与任何训练。
陷阱 2:评测集不代表真实
评测集都是"理想问题",线上用户的问题五花八门:口语化、错别字、表达不清。
解决:定期从线上采样真实问题,加入评测集。
陷阱 3:只看单一指标
只看 Recall@5,可能忽略了答案质量。检索到了,但答案仍然不好。
解决:多指标综合评估,建立指标大盘。
六、Inspect 系统:定位单次问题
评测解决"系统整体质量",Inspect 解决"某次请求为什么出错"。
Inspect 系统的核心能力
| 能力 | 回答的问题 | 需要的数据 |
|---|---|---|
| 检索可视化 | 召回了什么?排第几? | 候选列表、分数 |
| 重排对比 | Rerank 前后有什么变化? | 重排前后列表 |
| Prompt 快照 | 模型看到了什么? | 最终 prompt |
| 引用追溯 | 答案来自哪? | 引用的 chunk |
Trace 的数据结构
每次请求都应该保存完整的 Trace:
Trace 结构:
{
"trace_id": "唯一标识",
"timestamp": "请求时间",
"query": "用户问题",
"retrieval": {
"dense_results": [
{"chunk_id": "xxx", "score": 0.82, "rank": 1},
...
],
"bm25_results": [
{"chunk_id": "xxx", "score": 5.2, "rank": 1},
...
],
"merged_results": [...], // RRF 融合后
"reranked_results": [...] // Rerank 后
},
"context": {
"final_chunks": [...], // 最终进入 prompt 的
"prompt": "...", // 完整 prompt
"token_count": 3200
},
"generation": {
"answer": "模型回答",
"citations": [...],
"model": "gpt-4o-mini"
},
"latency": {
"total_ms": 450,
"retrieval_ms": 50,
"rerank_ms": 180,
"generation_ms": 220
}
}
Inspect 页面的设计
一个实用的 Inspect 页面应该包含:
1. 概览区
问题:错误码 401 是什么意思
答案:401 表示认证失败...
耗时:450ms
状态:❌ 答案错误(模型幻觉)
2. 检索区
Dense 检索结果:
#1 [0.82] 常见 HTTP 错误码
#2 [0.79] 认证失败怎么办
...
#8 [0.65] 错误码详解:401 ← 正确答案
BM25 检索结果:
#1 [5.2] 错误码详解:401 ← 正确答案
#2 [4.8] 常见 HTTP 错误码
...
Rerank 后:
#1 [0.92] 错误码详解:401 ← 正确答案排到第 1
...
3. Prompt 区
最终进入 Prompt 的 chunk:
[1] 错误码详解:401 Unauthorized
Token 无效或已过期...
[2] 常见 HTTP 错误码
...
完整 Prompt:
[显示完整 prompt 内容]
4. 评分区
检索是否正确:✅
答案是否正确:❌
是否有幻觉:✅(模型说"服务器内部错误")
备注:________________
Trace 的存储
| 方案 | 优点 | 缺点 |
|---|---|---|
| SQLite | 简单、本地 | 不适合大规模 |
| PostgreSQL | 功能强 | 需要运维 |
| Elasticsearch | 搜索方便 | 成本高 |
| 专用平台(LangSmith、Langfuse) | 功能全 | 可能有成本 |
建议:小规模用 SQLite/PostgreSQL,大规模用专用平台。
七、排障流程
当你发现某个问题回答不对时,按这个流程排查:
第 1 步:获取 Trace ID
从用户反馈或日志中获取
第 2 步:检查检索层
正确 chunk 是否在候选里?
❌ 不在候选:
→ 检查 chunking(是否被切碎)
→ 检查 embedding(是否理解正确)
→ 检索策略(是否需要混合检索)
✅ 在候选但排名靠后:
→ 继续检查重排层
第 3 步:检查重排层
Rerank 后排名是否提升?
❌ 排名下降或不变:
→ Rerank 模型可能不适合这个场景
→ 考虑换模型或调整权重
✅ 排名提升但仍不在 top-5:
→ 召回数量不够
→ 增加召回数量
第 4 步:检查组装层
正确 chunk 是否进入 prompt?
❌ 未进入:
→ 被截断:调整 token 预算
→ 被过滤:检查过滤规则
✅ 已进入:
→ 继续检查生成层
第 5 步:检查生成层
模型是否正确使用证据?
❌ 模型无视证据:
→ 强化 prompt 约束
→ 添加 Few-shot 示例
❌ 模型幻觉:
→ 明确"不确定时说明不确定"
→ 后处理校验
❌ 引用错误:
→ 优化引用格式要求
→ 后处理修正
八、本文小结
RAG 要做到可迭代,必须建立完整的评测和观测体系:
| 能力 | 解决的问题 | 核心方法 |
|---|---|---|
| 分层评测 | 定位问题在哪一层 | Recall@K、LLM-as-Judge |
| 离线评测 | 验证系统质量 | 评测集 + 定期跑分 |
| 在线评测 | 监控生产质量 | 采样 + 自动评分 |
| Inspect | 定位单次问题 | Trace + 可视化 |
核心建议:
- 先建评测集:没有评测集,优化都是盲人摸象
- 分层评测:检索和生成分开评,知道优化哪一层
- 保留 Trace:每次请求都记录中间结果
- 持续监控:建立指标大盘,设置告警
只有这样,才能从"会用 RAG"走到"会调 RAG"。
下一篇会讲生产环境最常见的坑,以及一条可执行的迭代路线。
