RAG 评测指标:别只看答案像不像

0阅读12分钟

从一个真实的评测翻车说起

我在做企业知识库问答系统时,曾经用 BLEU 和 ROUGE 跑了一轮评测,综合得分 0.72,看起来还不错。上线第一天,客服团队反馈:系统给了一个「看起来很像那么回事」的答案,但引用的内部制度文件根本不存在。

问题出在我把评测当成了「生成质量」问题。RAG 系统是一个多阶段流水线——查询改写、文档检索、上下文排序、答案生成——每一层都可能出问题,而最终答案的「语言流畅度」和「事实可靠性」是两个完全独立的维度。BLEU 只能衡量词汇重叠,它分不清「基于证据的正确回答」和「一本正经的胡说八道」。

这次翻车让我重新审视了整个评测体系。这篇文章记录了我在实践中逐步建立的一套 RAG 评测方法论,核心观点是:评测必须拆开链路看,每个环节有独立的指标,最后还需要端到端的综合判断

RAG 评测的理论框架

RAG 链路的分层评测思路

RAGAS 框架(Es et al., 2023)提出了一种「无需参考答案」(reference-free)的评测范式,将 RAG 系统的评测拆成两个独立维度:检索质量和生成质量。这个拆分很关键,因为它让定位问题变得可操作——如果检索得分低,去优化向量库和切分策略;如果生成得分低,去调 Prompt 或换模型。

Mistral AI 在工程博客中进一步提炼了「RAG 三元组」(RAG Triad)的概念(Mistral AI, 2025),将核心评测压缩到三个维度:

维度评测对象核心问题
Context Relevance检索器召回的文档和用户问题相关吗?
Groundedness生成器答案是否忠实于召回的文档?
Answer Relevance生成器答案是否回应了用户原始问题?

Patronus AI 在此基础上扩展为五个指标(Patronus AI, 2025),增加了 Context Sufficiency(上下文是否充足)和 Answer Hallucination(是否有幻觉),使评测更贴近生产环境需求。

组件级评测 vs 端到端评测

一个容易混淆的边界是:什么时候评组件,什么时候评整体?

对比维度组件级评测端到端评测
评测对象检索器、生成器独立打分整个流水线最终输出
优势精确定位故障层反映用户真实体验
劣势各层指标好不代表整体好分数低时不知道哪里出了问题
适用场景迭代调优、A/B 测试上线验收、定期巡检
典型指标Context Precision、FaithfulnessLLM-as-Judge 综合分、任务完成率

我实际的做法是:日常迭代看组件指标,发版前看端到端指标。两者缺一不可。

案例一:检索阶段就失败了,后面全白搭

场景

一个内部技术文档问答系统,用户问「Kubernetes 集群如何做滚动更新?」,系统返回了 5 个 chunk,其中 3 个是关于 Docker Compose 的部署文档,只有 2 个和 Kubernetes 相关——而且排在第 4、5 位。

翻车

用 BLEU 评分,答案看着还行,因为生成器很擅长从有限上下文里「凑」出一个像样的回答。但检索阶段就已经引入了大量噪声,答案的准确性完全依赖生成器的脑补能力。

修复:引入 Context Precision 和 Context Recall

Context Precision 衡量的是「相关文档是否排在前面」,它本质上是 Mean Average Precision(MAP)的 LLM-as-Judge 版本。Context Recall 衡量的是「该召回的文档是否都召回了」。

指标公式逻辑关注点
Context Precision相关 chunk 的 Precision@k 均值排序质量——好东西是否在顶部
Context Recallground_truth 中被召回上下文覆盖的比例覆盖度——该找的是否都找到了
Recall@KTop-K 结果中相关文档占全部相关文档的比例数量——召回了多少正确答案
Hit RateTop-K 中是否至少包含一个相关文档存在性——有没有找到一点相关的

修复后的评测代码:

# ✅ 好做法:同时评估检索排序质量和覆盖度
from ragas.llms import llm_factory
from ragas.metrics import ContextPrecision, ContextRecall
 
llm = llm_factory("gpt-4o-mini", client=client)
 
# Context Precision: 相关 chunk 是否排在前面
context_precision = ContextPrecision(llm=llm)
 
# Context Recall: 是否召回了所有需要的信息
context_recall = ContextRecall(llm=llm)
 
results = await evaluate(
    dataset=eval_dataset,
    metrics=[context_precision, context_recall],
)
print(f"Context Precision: {results['context_precision']}")
print(f"Context Recall: {results['context_recall']}")

对比只看最终答案相似度的做法:

# ❌ 坏做法:只看答案和标准答案的语义相似度
from sentence_transformers import SentenceTransformer, util
 
model = SentenceTransformer("all-MiniLM-L6-v2")
emb_pred = model.encode(predicted_answer)
emb_truth = model.encode(ground_truth_answer)
score = util.cos_sim(emb_pred, emb_truth).item()
# 问题:语义相似度高不代表事实正确,也不代表检索质量好
# 一个「流畅的错误答案」可能比「生硬但正确的答案」得分更高

这两段代码的核心差异在于:好做法把检索环节单独拉出来评测,精确定位到「是检索没找到」还是「找到了但排序不对」还是「找到了也排对了但生成器没用好」;坏做法把所有问题都混在一个分数里,出了问题完全不知道改哪里。

案例二:Faithfulness 揭露的幻觉问题

场景

一个金融合规知识库,用户问「反洗钱报告的时间要求是什么?」。系统给出了一个结构清晰的答案:「根据《金融机构大额交易和可疑交易报告管理办法》,可疑交易报告需在发现后 5 个工作日内提交。」

翻车

这个答案读起来很专业,但实际上「5 个工作日」这个数字在检索到的法规原文里并不存在。正确要求是「10 个工作日」。生成器在上下文不够充分时,选择了一个「看起来合理」的数字来填充。

如果用传统的 ROUGE-L 评测,因为答案和标准答案有大量词汇重叠,得分会很高。但事实上,答案中的关键数字是错的——在金融合规场景下,这种错误的后果不是「用户体验差」,而是「合规风险」。

修复:Faithfulness 指标

Faithfulness 的计算分三步(RAGAS, Es et al., 2023):

  1. 将生成的答案拆解为独立的事实声明(claims)
  2. 对每个声明,用 NLI(自然语言推理)判断它是否能从检索上下文中推导出来
  3. 计算比例:被上下文支持的声明数 / 总声明数
Faithfulness 分数含义行动建议
1.0所有声明都有据可查通过
0.7-0.9大部分有依据,少量超出检查是否需要在 Prompt 中强化引用约束
0.4-0.6大量声明缺乏上下文支持检索质量和 Prompt 都需要优化
< 0.4大部分内容是生成的,不是检索的系统基本不可用
# ✅ 好做法:用 Faithfulness 检测答案是否有据可查
from ragas.metrics import Faithfulness
 
faithfulness = Faithfulness(llm=llm)
 
result = await faithfulness.ascore(
    user_input="反洗钱报告的时间要求是什么?",
    response="可疑交易报告需在发现后5个工作日内提交。",
    retrieved_contexts=[
        "《金融机构大额交易和可疑交易报告管理办法》第十五条:金融机构应当在发现可疑交易之日起10个工作日内,向中国反洗钱监测分析中心提交可疑交易报告。"
    ]
)
# result.score ≈ 0.0 — "5个工作日" 无法从上下文中推导出来
# 这个信号明确告诉我们:生成器在编造信息
print(f"Faithfulness: {result.score}")

对比只看答案相似度的做法:

# ❌ 坏做法:用 ROUGE 评分,词汇重叠高但关键数字错了
import evaluate
 
rouge = evaluate.load("rouge")
score = rouge.compute(
    predictions=["可疑交易报告需在发现后5个工作日内提交"],
    references=["可疑交易报告需在发现后10个工作日内提交"]
)
# score["rouge_l"] 可能在 0.7 以上,因为大部分词汇重叠
# 但 "5" 和 "10" 的差异在合规场景下是不可接受的
# ROUGE 看不到这个语义层面的关键差异
print(f"ROUGE-L: {score['rouge_l']}")

Faithfulness 的价值在于它直接回答了一个问题:答案里的每一句话,是否都能在检索到的文档里找到依据? 这是传统 N-gram 匹配完全做不到的。

评测流水线的整体流程

在实际工程中,我把 RAG 评测组织成下面这个流水线。每一步对应具体的指标,每一层的输出是下一层的输入:

流程图画布 · 115%
Mermaid 流程图加载中...

这张图的关键在于:检索指标和生成指标是并行计算的,最终汇聚到诊断报告。根据得分分布,可以精确定位问题出在哪一层,而不是对着一个总分猜来猜去。

案例三:系统该拒答的时候没拒答

场景

一个医疗设备售后知识库,用户问「XX 型号的心脏起搏器和 YY 型号的兼容性如何?」。这个问题涉及两个具体型号的配合使用,但知识库里只有各自的技术手册,没有交叉兼容性说明。

翻车

系统没有拒答,而是从两个型号的文档里各抽了一段信息,拼凑出一个「看起来合理」的答案:「XX 型号采用蓝牙 5.0 协议,YY 型号支持蓝牙 5.0,因此两者可以兼容。」这个推理过程在技术上是错误的——通信协议兼容不代表设备兼容,但在语言层面非常流畅。

在医疗设备场景下,这种「自信的胡说八道」可能导致严重后果。

修复:拒答评测 + Answer Relevancy 联合判断

拒答能力不是一个独立指标,而是多个指标的综合表现。当 Context Sufficiency 低(上下文不够回答问题)且 Faithfulness 低(答案缺乏上下文支持)时,系统就应该选择拒答或追问。

拒答场景Context SufficiencyFaithfulness系统行为
上下文充足且答案有据正常回答
上下文充足但答案偏离重新生成或标注不确定
上下文不足但强行回答应该拒答但没有
上下文不足且正确拒答N/A理想拒答行为
# ✅ 好做法:在 Prompt 中明确拒答条件,并用多维度指标检测
from ragas.metrics import Faithfulness, AnswerRelevancy
 
# Prompt 模板中明确约束
PROMPT_TEMPLATE = """基于以下上下文回答用户问题。
如果上下文中的信息不足以回答问题,请明确说明「当前资料不足以回答此问题」,不要推测。
 
上下文:
{context}
 
用户问题:{question}
"""
 
# 评测时联合检查多个信号
faithfulness = Faithfulness(llm=llm)
answer_relevancy = AnswerRelevancy(llm=llm)
 
faith_score = await faithfulness.ascore(
    user_input=question,
    response=response,
    retrieved_contexts=contexts
)
relevancy_score = await answer_relevancy.ascore(
    user_input=question,
    response=response
)
 
# 当 faithfulness 低于阈值时,说明系统在缺乏依据的情况下仍然给出了断言
# 这就是需要触发拒答的场景
if faith_score < 0.5:
    print("⚠️ 系统给出了缺乏依据的回答,应优化拒答逻辑")

对比不做拒答控制的做法:

# ❌ 坏做法:没有拒答机制,什么问题都给答案
async def generate_answer(question: str, contexts: list[str]):
    prompt = f"基于以下上下文回答问题。\n上下文:{contexts}\n问题:{question}"
    # 没有「信息不足时拒答」的指令
    # 模型会尽力从有限上下文中拼凑答案
    response = await llm.generate(prompt)
    return response
# 问题:模型天然倾向于「有问必答」
# 如果 Prompt 中没有拒答指令,模型会利用任何可用信息来构造答案
# 即使这些信息和问题没有直接关系

核心指标的实操对比

在理解了上面的案例之后,我把 RAG 评测的核心指标整理成一张完整的对照表:

指标评测对象输入是否需要 ground_truth计算核心
Faithfulness生成器answer + contexts答案声明能否从上下文推导
Context Precision检索器question + contexts + ground_truth相关 chunk 是否排在前面
Context Recall检索器contexts + ground_truthground_truth 信息是否被覆盖
Answer Relevancy生成器question + answer答案是否回应了原始问题
Answer Correctness生成器answer + ground_truth答案和标准答案的事实一致性
Context Relevance检索器question + contexts召回文档和问题的语义相关性

这里面有一个实际选择时的关键区分:需要 ground_truth 的指标(Context Precision、Context Recall、Answer Correctness)评测更准确,但构建标注数据集的成本高;无需 ground_truth 的指标(Faithfulness、Answer Relevancy、Context Relevance)构建成本低,但依赖 LLM-as-Judge 的判断质量。

我的实践建议是:初期用无参考指标快速建立基线,随着数据积累逐步引入有参考指标做精确评测。

LLM-as-Judge 与传统指标的对比

RAGAS 的核心创新在于用 LLM 本身来做评测。这引发了一个值得讨论的问题:用 LLM 来评价 LLM 的输出,靠谱吗?

对比维度传统指标(BLEU/ROUGE)LLM-as-Judge(RAGAS)
评测方式词汇/子串重叠语义理解 + 逻辑推理
是否需要参考答案可选
能否检测幻觉是(Faithfulness)
能否评测拒答
计算成本低(本地模型推理)高(需调用 LLM API)
一致性高(确定性算法)中(受温度和模型版本影响)
适用场景翻译、摘要等短文本生成RAG、对话等长文本开放生成

Evidently AI 在其评测指南中给出了务实的建议(Evidently AI, 2025):「不要用传统的词汇匹配指标,用语义相似度或 LLM-as-Judge 框架来捕捉更细微的质量差异。」

不过 LLM-as-Judge 也有明显的局限。Reddit 社区讨论中有人指出(Reddit/r/LangChain, 2024),RAGAS 缺少正式的技术报告来验证各指标和人类判断的一致性。在实际使用中,我也观察到评估 LLM 自身版本升级会导致分数漂移——GPT-4o 做 Judge 和 GPT-4o-mini 做 Judge,同一个答案的 Faithfulness 得分可能差 0.1-0.2。

我的处理方式:固定评估 LLM 的版本,把 Judge 模型版本作为评测配置的一部分管理。

端到端评测的完整代码示例

把上面的指标组合起来,一个完整的评测流程是这样的:

# ✅ 好做法:多维度评测流水线
from ragas import evaluate
from ragas.metrics import (
    Faithfulness,
    AnswerRelevancy,
    ContextPrecision,
    ContextRecall,
)
from ragas.llms import llm_factory
 
llm = llm_factory("gpt-4o", client=client)
 
# 定义完整指标集
metrics = [
    # 检索层指标
    ContextPrecision(llm=llm),   # 检索排序质量
    ContextRecall(llm=llm),      # 检索覆盖度
 
    # 生成层指标
    Faithfulness(llm=llm),       # 答案忠实度(是否幻觉)
    AnswerRelevancy(llm=llm),    # 答案相关性(是否回应问题)
]
 
# 执行评测
results = await evaluate(
    dataset=eval_dataset,  # 包含 question, ground_truth, contexts, answer
    metrics=metrics,
)
 
# 分层输出诊断报告
print("=== 检索层 ===")
print(f"Context Precision: {results['context_precision']:.3f}")
print(f"Context Recall:    {results['context_recall']:.3f}")
print("=== 生成层 ===")
print(f"Faithfulness:      {results['faithfulness']:.3f}")
print(f"Answer Relevancy:  {results['answer_relevancy']:.3f}")

对比只关注单一指标的做法:

# ❌ 坏做法:只看一个综合分数
from sentence_transformers import SentenceTransformer, util
 
model = SentenceTransformer("all-MiniLM-L6-v2")
scores = []
for item in eval_data:
    emb_pred = model.encode(item["answer"])
    emb_truth = model.encode(item["ground_truth"])
    scores.append(util.cos_sim(emb_pred, emb_truth).item())
avg_score = sum(scores) / len(scores)
print(f"Average similarity: {avg_score:.3f}")
# 问题:
# 1. 无法区分检索失败和生成失败
# 2. 语义相似度高不等于事实正确
# 3. 无法发现幻觉问题
# 4. 无法评估拒答能力

构建评测数据集的要点

指标选对了,没有好的评测数据集也白搭。这方面踩过的坑比选指标时更多。

数据从哪里来

数据来源优势劣势适用阶段
线上真实 query 日志覆盖真实分布需要人工标注 ground_truth成熟期
专家手动构造质量高,覆盖边界场景成本高,数量少初期基线
LLM 合成 + 人工审核量大,成本低可能有偏差,必须人工审核扩展期
从文档自动生成 QA 对和知识库对齐容易产生简单问题检索层评测

Evidently AI 的建议很实在(Evidently AI, 2025):「从真实数据开始,审核自动生成的数据集,实验中坚持使用同一份测试集。」

容易忽视的数据质量问题

标注一致性:如果让多个人标注 ground_truth,需要先校准标注标准。我见过两个人对同一个问题写出完全不同但都正确的答案——这种情况下用字符串匹配算 Answer Correctness 就会出错。

难度分布:评测集里不能全是简单的事实查询。一个好的评测集应该包含:简单事实查询(30%)、需要跨文档推理的问题(30%)、知识库里不存在答案的问题(20%)、模糊或歧义问题(20%)。

时效性:知识库会更新,评测集也要同步。过期的评测集测出来的分数没有参考价值。

上线后的持续评测

评测不是一次性的事情。模型升级、知识库更新、Prompt 调整——任何一个环节变动都可能影响质量。

我把持续评测分为三个层次:

层次频率评测内容触发条件
冒烟测试每次部署50-100 条核心 query 的 Faithfulness + Answer RelevancyCI/CD 触发
回归测试每周完整评测集的所有指标定时或知识库更新
深度评测每月含边界场景、拒答测试、对抗测试模型升级或架构变更

Patronus AI 的建议是「定义明确的质量阈值,当指标下降超过阈值时自动触发告警」(Patronus AI, 2025)。我在实践中设置的阈值是:Faithfulness 下降超过 0.05 就触发排查,Context Recall 下降超过 0.08 就暂停发布。

上线前检查清单

数据准备阶段

  • 评测集覆盖至少 200 条 query,包含简单查询、推理查询、无答案查询、模糊查询
  • ground_truth 经过至少两人交叉审核
  • 评测集和训练集无重叠
  • 已标注每条 query 对应的 ground_truth 引用来源(context)

指标选择与实现阶段

  • 至少包含 Faithfulness、Context Precision、Answer Relevancy 三个核心指标
  • 已固定 Judge LLM 的版本和温度参数
  • 针对拒答场景定义了独立的评测逻辑
  • 检索层和生成层指标分开计算和展示
  • 已建立各指标的质量阈值和告警规则

持续监控阶段

  • CI/CD 流水线集成冒烟测试,每次部署自动运行
  • 评测结果持久化存储,支持趋势对比
  • 知识库更新后自动触发回归测试
  • 定期(每月)更新评测集,淘汰过期 query
  • 有明确的「分数下降 → 排查 → 修复」工作流

参考资料

  1. Es, S., James, J., Espinosa-Anke, L., & Scarlatos, S. (2023). RAGAS: Automated Evaluation of Retrieval Augmented Generation. arXiv:2309.15217. https://arxiv.org/abs/2309.15217

  2. Mistral AI. (2025). Evaluating RAG with LLM as a Judge. https://mistral.ai/news/llm-as-rag-judge/

  3. Patronus AI. (2025). RAG Evaluation Metrics: Best Practices for Evaluating RAG Systems. https://www.patronus.ai/llm-testing/rag-evaluation-metrics

  4. RAGAS Official Documentation. Available Metrics. https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/

  5. Evidently AI. (2025). A Complete Guide to RAG Evaluation: Metrics, Testing and Best Practices. https://www.evidentlyai.com/llm-guide/rag-evaluation

  6. IBM. (2024). Result Evaluation for RAG: Metrics & Best Practices. https://www.ibm.com/think/architectures/rag-cookbook/result-evaluation

  7. Microsoft. (2025). 开发 RAG 解决方案——大型语言模型端到端评估阶段. https://learn.microsoft.com/zh-cn/azure/architecture/ai-ml/guide/rag/rag-llm-evaluation-phase

  8. Databricks. (2023). Best Practices for LLM Evaluation of RAG Applications. https://www.databricks.com/blog/LLM-auto-eval-best-practices-RAG

评论 0

0 / 1000