Featured image of post RAG评测与 Inspect:如何知道问题出在检索、重排还是生成

RAG评测与 Inspect:如何知道问题出在检索、重排还是生成

很多 RAG 系统不是不能用,而是出了问题后不知道该怎么定位。本文讲清楚评测、Inspect 和可观测性该怎么做。

很多 RAG 项目最开始都能跑起来,但一到线上就会卡在一个非常现实的问题上:

用户说答案不对,可你根本不知道问题出在:

  1. 没召回到正确内容
  2. 召回到了,但排序不对
  3. 排好了,但没进最终 prompt
  4. 进了 prompt,但模型没用
  5. 模型用了,但引用展示错了

所以 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 能理解上下文,判断答案是否"基于证据"。

注意事项

  1. 评判模型要强:用 GPT-4 或 Claude,不要用小模型
  2. 避免自我评判:生成模型和评判模型要不同
  3. Few-shot 示例:给几个评分示例,提高一致性
  4. 多次评判取平均:减少随机性

评测指标的层次

生成层评测应该回答几个不同的问题:

问题指标怎么测
答案对吗?正确率LLM 评判 + 人工抽查
有幻觉吗?幻觉率检查答案是否超出证据范围
引用对吗?引用准确率检查引用的 chunk 是否支持答案
用户满意吗?满意度用户反馈

幻觉检测是最重要的,因为 RAG 的核心价值是"有据可查"。

幻觉检测的方法

方法 1:LLM 评判

Prompt:
"以下答案是否包含参考资料中没有的信息?"

答案:{answer}
参考资料:{context}

请指出答案中的每一句话,并判断是否有证据支持。

方法 2:NLI(自然语言推理)

用 NLI 模型判断答案和证据之间的关系:

前提(证据):"Token 有效期为 24 小时"
假设(答案):"Token 有效期为 48 小时"

NLI 输出:Contradiction(矛盾)

方法 3:关键词匹配

简单但粗糙:检查答案中的关键信息是否在证据中出现。

答案:"API 超时时间是 30 秒"
证据:"API 的默认超时时间为 30 秒"

匹配:✅("超时"、"30"、"秒"都在证据中)

五、离线评测 vs 在线评测

离线评测

目的:在上线前验证系统质量

方法

  1. 准备评测集(50-100 条)
  2. 定期跑评测
  3. 对比不同版本

评测集构建

评测集应该包含:
- 核心问题(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. 对比历史版本

在线评测

目的:在生产环境持续监控

方法

  1. 用户反馈:点赞/点踩、修改问题、人工标注
  2. 自动评分:用 LLM 对线上答案打分
  3. 行为分析:用户是否追问、是否放弃

采样策略

不是所有请求都需要详细评测(成本高)。建议:

  • 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 + 可视化

核心建议

  1. 先建评测集:没有评测集,优化都是盲人摸象
  2. 分层评测:检索和生成分开评,知道优化哪一层
  3. 保留 Trace:每次请求都记录中间结果
  4. 持续监控:建立指标大盘,设置告警

只有这样,才能从"会用 RAG"走到"会调 RAG"。

下一篇会讲生产环境最常见的坑,以及一条可执行的迭代路线。

使用 Hugo 构建
主题 StackJimmy 设计