RAG 质量评估自动化 · 运维手册
适用场景:RAG 系统要进生产,但每次改切分策略、换 embedding、调提示词都不确定有没有变差——需要一套可回归、可进 CI 的质量评估,而不是靠人工抽查。
1. 为什么 RAG 必须做自动化质量评估
RAG 的坏是静默坏。检索错一段,生成就顺着错的编;chunk 切坏了,faithfulness 悄悄塌,用户可能两周后才投诉「答案不对」。这种劣化,靠人肉看几句话发现不了,更不可能在每次改配置时都看一遍。
自动化评估的本质:把「检索准不准、生成靠不靠谱」翻译成可回归的数字。任何改动(换 embedding、改 reranker、调 prompt)一跑评估,指标掉就报警。这是 RAG 上线后能不能睡安稳觉的分水岭。
2. 评估拆解:检索侧与生成侧各看什么
RAG 是两半拼起来的,评估也得拆开看:
- 检索侧:该用的段落有没有被找回、有没有排到前面。指标是 recall@k、precision@k、MRR、nDCG。
- 生成侧:答案有没有忠于上下文、有没有答对所问。指标是 faithfulness、answer relevancy、citation accuracy。
只测一半会漏。检索满分但生成瞎编,照样生产事故;生成很顺但检索错,答案是对的也不可信。我习惯两半都测,端到端再加拒绝/权限类检查。
3. 四大核心指标
不需要参考答案也能跑的三个(生产最常用):
- faithfulness(忠实度):答案的每个断言是否都能在检索到的上下文里找到支撑。低说明在编。这是 RAG 的命门指标。
- answer relevancy(答案相关性):有没有正面回答用户问题,而不是答非所问。
- context precision / recall(上下文精准/召回):检索回来的段落里有多少是相关的(precision),该用的关键段落有没有被漏掉(recall)。
有标准答案时再加 answer correctness,直接比对。注意:faithfulness ≠ correctness——模型可以很忠实地复述一段错误文档,所以 correctness 必须配参考答案或专家复核。
4. 工具选型:别一上来全装
- Ragas:RAG 专用指标最全(faithfulness / answer relevancy / context precision+recall),离线评测首选。
- DeepEval:pytest 风格,把评估写成测试用例,最适合接 CI 做回归门禁。
- TruLens:RAG triad + 链路追踪,看「为什么错」很顺手。
- Phoenix:追踪 + 评估,可自托管、可完全离线,敏感语料友好。
我的搭配:离线诊断用 Ragas,回归门禁用 DeepEval,线上排错加 Phoenix。不是越多越好,三个够用。
5. 测试集怎么建:100–300 条真实问题
评估的质量上限等于测试集的质量。走过场的测试集 = 走过场的分数。
# 测试集最小结构:问题 + 预期答案 + 相关源文档
test_cases = [
{
"query": "退款多久到账?",
"reference_answer": "一般 3–5 个工作日原路退回。",
"retrieval_context": ["退款政策.pdf 第3条…", "客服手册.docx…"],
},
# ... 至少覆盖难例、答不出的边界 case
]
要点:① 用真实用户问题,不是编造的;② 必含答不出的难例(验证拒绝能力);③ 人肉抽检一批答案,把人判和自动分对齐,校准 judge 后再信任分数;④ 测试集版本化,跟着代码进仓库。
6. 本地 judge 与数据不出域
很多团队卡在「评估要不要把语料发给云端大模型」。答案是:可以不出域。
- 检索指标(recall@k/MRR/nDCG)根本不需要模型。
- judge 类指标:DeepEval 文档支持 Ollama 作评估模型;TruLens 列了 Ollama/LiteLLM/HF 作 judge provider;Ragas 有基于 Vectara HHEM 的 faithfulness 变体;Phoenix 声明可完全离线(air-gapped)。
涉及合规语料,我一律本地 judge。把 judge 模型和被测系统放在同一内网,评估流量不触外网。
7. 把评估接进 CI:回归门禁拦住劣化
这是让评估「有用」的关键一步——不进 CI,评估报告就只是个摆设。
# test_rag_eval.py —— 直接进 pytest / CI
from deepeval import assert_test
from deepeval.metrics import FaithfulnessMetric, AnswerRelevancyMetric
from deepeval.test_case import LLMTestCase
def test_rag_faithfulness():
test_case = LLMTestCase(
input="退款多久到账?",
actual_output=answer,
retrieval_context=context,
)
assert_test(test_case, [FaithfulnessMetric(threshold=0.85),
AnswerRelevancyMetric(threshold=0.80)])
给每个指标设阈值(如 faithfulness < 0.85 就 fail),改 chunk 策略、换 embedding、调 prompt 的 PR 一跑就知道劣没劣化。指标掉 = 构建红 = 拦住发布。
8. 一次真实事故复盘:chunk 切错导致 faithfulness 塌方
某次为了提召回,把 chunk_size 从 512 拉到 2048、overlap 归零。离线看 recall@k 涨了,就合了。上线三天后客服反馈「答案开始胡说」。一查评估:faithfulness 从 0.92 掉到 0.71。
根因:大 chunk 把无关内容塞进同一段,生成时模型被噪声带偏、开始编造。回滚 chunk 配置,改回 512/64,faithfulness 回升。教训:recall 涨不等于质量涨,faithfulness 才是 RAG 的兜底指标,任何检索侧改动都必须连带跑生成侧评估。
9. 上线后的评估纪律与巡检
我把 RAG 评估固化成日常纪律:
- 版本化测试集:随代码演进,新业务线补对应难例。
- 每次改动必跑评估:chunk / embedding / reranker / prompt 任一变更,CI 里跑全套,faithfulness 掉就拦。
- judge 校准:每季度抽一批答案做人机对齐,judge 漂移就重训或换本地模型。
- 线上采样回归:每周从真实流量采样 50 条,跑一遍评估,盯 faithfulness 长尾。
- 指标看板:faithfulness / answer relevancy / context recall 接监控,跌破阈值即 P1。
绝不靠「感觉这次改得不错」上线——RAG 的劣化是静默的,只有数字能拦住它。
10. 常见问题排查
问题 1:faithfulness 低但答案看着对
大概率是检索把无关段落塞进 context,模型被带偏。先查 context precision:相关段落有没有排到前面。降噪(更小 chunk、加 reranker)通常立竿见影。
问题 2:评估很慢跑不完 CI
用本地 judge + 抽样测试集(先跑 50 条做门禁,全量留夜间跑)。检索指标本就无需模型,先把它们前置。
问题 3:judge 分数和人判对不上
judge 没校准。抽 30–50 条人工打分,比对偏差,要么换更强的本地 judge,要么把评判标准写进 prompt 重新校准,别直接信原始分。
问题 4:测试集建一次就完,还是得一直维护
必须持续维护,把它当代码一样对待。业务线加了新功能、知识库换了文档,测试集不跟着补,评估就测不到新路径,劣化会漏检。我的做法:每个新栏目或新数据源至少补 10–20 条真实问题,难例和边界 case 优先;季度做一次整体复审,删掉过时问题、补进新场景。测试集的版本要和被测系统的版本对齐,否则「分数涨了」可能只是测试集变了。