核心摘要

生产级 RAG 评估必须指出系统在哪一层失败。检索、证据使用、答案正确性与相关性、噪声敏感度、拒答行为、引用质量、任务结果、安全与运营指标要分开测量。使用带业务切片的版本化测试集,以人工标签校准 LLM 裁判,并同时设置整体与关键切片门禁。高平均分不能掩盖证据缺失、无依据主张、不安全作答或跨租户检索。

为什么一个 RAG 分数不够

RAG 系统可能在模型收到上下文之前或之后失败。单一满意度只能说明“有问题”,却无法告诉团队应该修改检索器、索引、提示词还是生成模型。

flowchart LR A["问题"] --> B["查询处理"] B --> C["检索"] C --> D["重排与过滤"] D --> E["生成"] E --> F["答案与引用"] C --> G["检索指标"] E --> H["生成指标"] F --> I["任务与安全指标"]
层级 核心问题 指标示例
检索 是否找到并排好必要证据? recall@k、precision@k、MRR、nDCG、上下文召回率
证据使用 回答是否使用已有证据且没有复制噪声? 答案忠实度、噪声敏感度、引用支持度
答案 结果是否正确、相关并覆盖用户要求? 答案正确性、答案相关性、必要要点覆盖
可回答性 系统是否在正确时机回答或拒答? 正确拒答、错误拒答、越界作答
端到端 用户是否获得有用结果并完成任务? 任务成功、升级处理结果、用户纠错
安全 策略和数据边界是否成立? 越权检索、敏感信息泄露、不安全操作
运营 质量是否可负担且可靠? p95 延迟、成本、错误率、Token 与上下文预算

RAGAS 论文系统化描述了忠实度、答案相关性和上下文相关性等无需大量参考答案的评估方法。ARES 与 RAGChecker进一步说明裁判校准和主张级诊断的重要性。这些都是有用协议,但不能互换,也不能压缩成一个万能生产总分。

每项结果都应绑定完整的指标身份:

text
指标名称 + 实现版本 + 裁判模型与提示词
+ 主张或证据单元 + 参考来源与版本
+ 聚合方式 + 空样本策略

两个看板都写着「答案正确性」,也可能采用不同测量协议:一个按主张集合计算 Precision、Recall 或 F1,一个混合语义相似度与事实重合,另一个使用二元 Rubric。只有这些契约一致时,分数才可直接比较。

围绕失败切片构建测试集

有效测试集应代表业务决策和风险,而不是只随机抽问题。每条样本按所在切片记录必要证据与期望行为。

json
{
  "case_id": "returns-policy-014",
  "slice": ["policy", "multi_document", "must_cite"],
  "question": "已标记最终销售的破损商品能退货吗?",
  "expected_evidence": ["policy-v7#damaged-items", "policy-v7#final-sale"],
  "reference_answer": "破损商品即使标记最终销售,也应进入质量问题处理流程。",
  "expected_behavior": "answer",
  "forbidden_sources": ["tenant-b/*"]
}

测试集至少覆盖:

  • 按真实流量加权的常见问题;
  • 低频但代价高或受监管的问题;
  • 单跳、多跳、歧义和不可回答问题;
  • 最近更新、已经删除和无权访问的内容;
  • 错别字、短查询、长对话与多语言;
  • 已知事故与对抗性检索案例。

保留冻结核心集用于趋势比较,再维护轮换集吸收新流量。文档、分块、标签和评估提示词都要版本化,才能解释分数变化。

先评估检索,再评估生成

检索标签应回答:问题所需证据是否出现在排序结果中。

上下文召回率检查覆盖度,即是否找到全部必要证据;上下文精确率检查噪声与顺序,即相关证据是否排在无关分块之前。

症状 可能的检索问题 第一轮实验
必要来源缺失 召回不足 查询改写、混合搜索、过滤器、分块边界
正确来源排名靠后 精确率或排序不足 重排器、评分特征、去重
正确来源被权限层移除 授权与索引不一致 ACL 传播和策略测试
返回旧来源 新鲜度失败 版本过滤、删除和重建索引检查
重复分块过多 多样性失败 去重与单来源上限

原始候选与过滤后结果要分别评估。离线检索器可能召回很好,但授权过滤器删除了唯一有用段落;也可能因为评估语料错误包含无权数据而看似准确。

根据证据评估生成

答案忠实度检查生成主张是否得到检索上下文支持。常见方法先提取原子主张,再逐条检查证据:

text
忠实度 = 有证据支持的回答主张数 / 回答主张总数

该分数不能证明事实正确。如果来源错误,忠实回答仍会复述错误;如果模型靠记忆答对但忽略已有证据,则可能正确却不忠实。

还应把四项属性分开测量:

属性 比较边界 典型失败
答案正确性 回答主张与可信参考或可执行判定器 回答忠实复述了错误来源
答案相关性 回答与用户问题及必要要点 内容正确但只回答一半问题,或加入大量无关信息
噪声敏感度 成对的干净/带噪上下文,或错误主张与噪声分块的归因关系 干扰内容诱发新的错误主张
拒答正确性 可回答性标签与实际回答/拒答行为 没有证据仍回答,或证据充分却拒答

引用溯源的支持度与覆盖率属于另一层。正确答案可能引用错误片段,证据充分的回答也可能遗漏必要事实。不要奖励冗长:主张越多,出现无依据细节的机会越大,也更容易用无关内容掩盖简洁正确的结论。

先判断可回答性,再给答案打分

每条测试用例都应声明:当前用户有权访问的语料是否包含充分证据。只有先固定这一标签,评估器才能区分:

  • 对可回答问题给出正确答案;
  • 证据充分却回答错误;
  • 证据不足时正确拒答;
  • 证据充分时错误拒答;
  • 证据不足却越界作答。

Trust-Align把 Over-responsiveness 与 Excessive Refusal 分开,UAEval4RAG则构造六类不可回答请求。论文数字只属于各自数据集与协议;可复用的工程结论是保留回答/拒答混淆矩阵的两侧,而不是只汇报一个拒答率。

校准 LLM 裁判

LLM 裁判是一种测量仪器,必须校准:

  1. 创建包含明确正例、反例和边界案例的人工标注集。
  2. 定义证据单元与允许不确定性的评分规则。
  3. 尽可能隐藏系统名称和候选顺序。
  4. 按切片比较裁判与人工标签,而不只看整体一致率。
  5. 审查分歧,先修改评分规则再调整阈值。
  6. 裁判模型或提示词变更后重新校准。

能用代码判断的属性应使用确定性检查:

go
package main

import (
	"errors"
	"fmt"
)

type Result struct {
	CaseID                string
	Answerable            bool
	Answered              bool
	AnswerCorrect         bool
	SchemaValid           bool
	CitationIDs           []string
	VisibleCitationIDs    map[string]bool
	UnauthorizedRetrieval int
}

func deterministicChecks(result Result) error {
	if result.CaseID == "" || !result.SchemaValid {
		return errors.New("invalid_result_contract")
	}
	if result.UnauthorizedRetrieval != 0 {
		return errors.New("unauthorized_retrieval")
	}
	for _, id := range result.CitationIDs {
		if !result.VisibleCitationIDs[id] {
			return fmt.Errorf("citation_not_visible:%s", id)
		}
	}
	if !result.Answerable && result.Answered {
		return errors.New("over_response")
	}
	if result.Answerable && !result.Answered {
		return errors.New("false_refusal")
	}
	if result.Answered && !result.AnswerCorrect {
		return errors.New("incorrect_answer")
	}
	return nil
}

func main() {
	result := Result{
		CaseID:             "returns-policy-014",
		Answerable:         true,
		Answered:           true,
		AnswerCorrect:      true,
		SchemaValid:        true,
		CitationIDs:        []string{"policy-v7#damaged-items"},
		VisibleCitationIDs: map[string]bool{"policy-v7#damaged-items": true},
	}
	if err := deterministicChecks(result); err != nil {
		panic(err)
	}
	fmt.Println("release_candidate=true")
}

这段程序不负责判断自然语言是否正确,而是演示如何用确定性外壳约束单独校准的语义标签。不要让裁判模型推测权限、来源身份、Schema 合法性或精确字符串约束。

设计发布门禁

发布门禁应组合硬性不变量、切片最低值和回归上限。

yaml
hard_fail:
  unauthorized_retrieval: 0
  invalid_citation_ids: 0
  critical_over_response: 0
  critical_cases_pass_rate: 1.0

slice_minimums:
  faithfulness:
    policy: 0.95
    general: 0.88
  context_recall:
    multi_hop: 0.85
  answer_correctness:
    policy: 0.95

regression_limits:
  task_success: -0.01
  false_refusal_rate: 0.01
  p95_latency_ms: 500
  cost_per_success: 0.05

这些数字只是示例,不是行业统一阈值。实际值应来自标注数据、测量方差、业务影响,以及错误放行和错误阻断的成本。

应在同一批案例上做配对比较。模型采样或裁判存在随机性时,报告置信区间或重复运行波动。如果单次波动可达 0.04,平均分变化 0.01 就没有决策价值。

在线评估与可观测性

离线测试控制输入,适合比较发布版本;在线评估负责发现流量漂移、新文档、厂商变化和长尾失败。

采样应按风险与新颖度,而不是平均抽样:

  • 敏感或高影响结果全部评估;
  • 提高新查询簇与变更来源的采样率;
  • 检查低置信检索和拒答案例;
  • 只保留复核所需的有界、脱敏证据;
  • 把用户纠错和事故转成回归样本。

评估与追踪要分开:Trace 解释发生了什么,Eval 判断是否满足契约。Agent 可观测性指南说明如何连接两者而不收集不必要的私有内容。

实施顺序

  1. 定义 RAG 系统允许做出的决策。
  2. 设计版本化样本结构,以及有意义的流量、风险、语言和可回答性切片。
  3. 在优化生成前标注期望证据。
  4. 建立关键词、向量或混合检索基线。
  5. 添加权限、引用和 Schema 的确定性检查。
  6. 校准正确性、忠实度、相关性、噪声与拒答裁判。
  7. 设置硬门禁与切片回归限制,同时覆盖错误拒答和越界作答。
  8. 在 CI、索引变更和模型变更前运行评估。
  9. 抽样生产结果,并把事故加入测试集。
  10. 定期审查指标是否被投机利用或发生漂移。

诊断后需要优化检索时,可参考混合搜索与重排指南;架构基础见 RAG 深度指南。

常见问题

检索与生成应使用同一测试集吗?

可以共享案例,但标签不同。检索需要证据相关性和排序标签;生成需要主张支持、参考结果和拒答标签。字段应分开保存。

上下文精确率和召回率哪个更重要?

没有统一答案。缺少必要证据会限制回答上限,噪声过多则增加成本并干扰生成。应在上下文预算和具体查询切片内联合优化。

用户点赞能替代评估标签吗?

不能。反馈受到选择偏差、展示方式、用户专业度和错误延迟发现影响,应作为在线信号之一,再结合证据调查。

是否应同步调用裁判评估每个生产回答?

普通低风险请求通常不需要。同步裁判会增加成本、延迟和依赖。优先内联确定性检查,再做按风险异步采样;只有高影响决策才值得同步评审。

指标互相冲突时怎么办?

检查每一层证据。检索好但忠实度低,问题偏向生成;忠实度高但正确性低,可能是来源或参考答案问题;整体平均良好但关键切片失败,仍应阻断发布。

拒答率越高越安全吗?

不是。系统可以通过拒绝有用的可回答请求来提高拒答率。应在声明可回答性的测试集中分别报告正确拒答、错误拒答、越界作答和正确回答,再根据业务影响为不同错误类型赋予代价。

参考资料