RAG 应用上线三个月后,最常见的问题不是模型不够强,而是"不知道它什么时候变差了":检索命中率悄悄掉了 8 个点、回答开始出现资料里没有的内容、往知识库加了一批新文档后噪声明显变多。这些靠人肉看案例根本发现不了。把 RAG 评测做成流水线,核心就三件事:一组可重复的黄金评测集、一套能自动打分的指标(忠实度/检索命中率/噪声敏感度)、一条挂在 CI 上的回归任务。下面是我在实际项目里的落地做法和踩过的坑。
一、指标层:别只盯着 RAGAS 的默认输出
RAGAS 是当前最常用的开源评测库,但很多人直接 evaluate() 一把梭,不看每个指标到底在算什么。四个核心指标先对齐定义:
| 指标 | 测什么 | 需要字段 | 典型用途 |
|---|---|---|---|
| faithfulness(忠实度) | 回答是否被检索资料支撑,有没有幻觉 | question / answer / contexts | 生成质量门禁 |
| context_precision(上下文精确率) | 检索回来的段落里有多少是真正有用的 | question / answer / contexts | 检索精排调优 |
| context_recall(上下文召回率) | 黄金答案需要的证据有没有被检索到 | question / answer / contexts / ground_truth | 检索召回调优 |
| answer_relevancy(答案相关性) | 回答是否答非所问 | question / answer | 兜底监控 |
关键认知:context_recall 高不代表端到端好。检索全召回但生成层不引用,faithfulness 照样崩;faithfulness 高但 recall 低,说明模型在"硬答"------资料没给全它也编得圆。所以四个指标要一起看,单独一个都会骗你。
跑批代码很简单:
from ragas import evaluate
from ragas.metrics import (
faithfulness, answer_relevancy,
context_precision, context_recall,
)
from datasets import Dataset
samples = [
{
"question": "退款一般多久到账?",
"answer": "审核通过后 1-3 个工作日原路退回。",
"contexts": ["退款在审核通过后 1-3 个工作日原路退回至原支付账户。"],
"ground_truth": "退款在审核通过后 1-3 个工作日原路退回。",
},
# ... 黄金集样本,见第二节
]
result = evaluate(
Dataset.from_list(samples),
metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
)
print(result) # {'faithfulness': 0.92, 'context_recall': 0.78, ...}
二、黄金评测集:评测的命根子
指标是尺子,评测集是刻度。刻度歪了,尺子再准也没用。我的经验是黄金集从 30~50 条起步,必须满足三个条件:
- 按 query 类型分层:事实型("退款多久到账")、流程型("怎么改绑手机号")、对比型("A 套餐和 B 套餐区别")各占三分之一,比例跟线上流量分布对齐,否则指标会被某一类 query 带偏。
- 每条都要有 ground_truth:context_recall 这类指标必须有标准答案才能算,别偷懒省掉。
- 必须掺负样本:至少 10% 的样本故意设计成"资料里根本没有答案"或"资料互相矛盾",专门用来暴露幻觉和检索乱召回。
评测集要进 git,版本化管理。每次改 prompt、换 embedding、调 chunk 策略,跑同一套集子,分数可对比,才有回归的意义。
三、噪声敏感度:专门测"检索变差时回答扛不扛得住"
线上检索不可能永远精准。知识库更新、embedding 模型换版、用户问法漂移,都会让检索结果混入噪声。噪声敏感度测试做的就是:在 context 里注入无关段落,看回答质量衰减多少。新版 ragas 提供了现成指标:
from ragas.metrics import noise_sensitivity_relevant, noise_sensitivity_irrelevant
noisy_samples = [
{
"question": "退款多久到账?",
"answer": "1-3 个工作日。",
"contexts": [
"退款在审核通过后 1-3 个工作日原路退回。", # 相关段落
"公司成立于 2015 年,主营企业级 SaaS,总部位于杭州。", # 无关噪声
],
"reference": "退款在审核通过后 1-3 个工作日原路退回。",
},
]
noise_sensitivity_irrelevant:噪声段落与问题完全无关,回答还稳得住吗noise_sensitivity_relevant:噪声段落看似相关(比如同主题但答非所问的段落),模型会不会被带偏
这个指标比 faithfulness 更早暴露问题:噪声敏感度升高往往意味着生成层开始"过度信任检索结果",等 faithfulness 掉下来再修就晚了。我一般把它当作预发告警指标。
四、踩坑记录
- Judge 模型一致性:所有指标底层都是 LLM-as-judge。judge 模型换版本(哪怕只是温度从 0 改到 0.2),分数就会漂移。评测流水线必须锁定 judge 模型版本,换 judge 等于重跑基线。跨版本对比分数没有意义。
- 小样本方差极大:20 条样本跑出来的分数,波动 ±5 个点是常态。别拿单次分数做发布门禁,要看 5 次以上的趋势线。我的做法是门禁用"与基线差值的滑动平均",而不是绝对分数。
- Judge 与人工要对齐:先人工标注 50 条,算 judge 和人工的 Cohen's kappa,低于 0.7 就调 judge prompt 或换 judge 模型。否则你优化的是一个和真实质量无关的代理指标。
- 成本别失控:一条样本跑 4 个指标 ≈ 5~8 次 LLM 调用。50 条黄金集一次全量跑批是几百次调用,CI 上每次 PR 都全量跑不现实。做法:PR 跑 15 条快速子集,合入主干跑全量。
- 评测集污染:黄金集样本如果被检索库收录(比如知识库同步了文档),指标会虚高。评测用的问题和文档要跟线上检索库隔离。
五、工具选型与落地建议
| 方案 | 优势 | 劣势 |
|---|---|---|
| RAGAS 全量指标 | 指标全、社区活跃、零门槛 | 自定义 judge 困难,黑盒 |
| TruLens | 有可视化面板,调试友好 | 指标可定制性一般,重 |
| 自研 judge prompt | 完全可控、可针对业务定制 | 要自己维护评测集和 prompt |
我的建议是混合:RAGAS 跑标准指标,自研 judge 跑业务专属指标(比如客服场景的"是否给出了可执行的下一步操作")。流水线挂在 CI 上长这样:
# .github/workflows/rag-eval.yml
on:
pull_request:
paths: ["rag/**", "prompts/**", "embeddings/**"]
jobs:
rag-eval:
runs-on: ubuntu-latest
steps:
- run: pip install ragas datasets
- run: python scripts/run_rag_eval.py \
--dataset gold/gold_v2.jsonl \
--subset 15 \
--gate faithfulness:0.85 context_recall:0.75
只有动了检索、prompt、embedding 相关代码才触发,跑一次几分钟,成本可控。
总结与进阶方向
评测流水线的本质是把"感觉变差了"变成"哪个指标从多少掉到多少、是哪条样本贡献的"。这一步做完,RAG 优化才从玄学变成工程。进阶方向有三条:一是 badcase 回流------线上被用户点踩的回答自动进评测集,让评测集跟着线上问题一起长;二是按 query 聚类分层看指标,别被平均数掩盖了某个类型的大幅劣化;三是把评测和 tracing 打通,用线上真实 trace 周期性采样评测,做漂移监控。
如果你也在做 AI 应用(RAG / Agent / LLM),不知道质量怎么测------我最近在给 AI 应用做免费质量体检,出一份可执行的测评报告(检索命中率、回答忠实度、噪声敏感度等维度),感兴趣可以直接私信我。