AI 工程基础体系 · 第 29/100 篇。内容覆盖机器学习、深度学习与生成式 AI;模型、数据、评测、权限和成本会作为同一生产系统处理。
LLM 与 Agent 评测:数据集、规则、Judge、轨迹、回归和统计
评测不是“给模型打一个分数”,而是判断一个版本在明确任务、约束和资源条件下,是否比基线更可靠。对传统机器学习,评测对象通常是一次预测;对 LLM,评测对象可能是文本、结构化输出或工具调用;对 Agent,评测对象则是一个随时间展开的决策过程。三者使用相同的基本思想:
缺少其中任一项,分数都可能无法解释。例如,模型准确率从 80% 上升到 85%,可能来自更容易的测试集;Agent 成功率上升,可能是允许了更多重试;LLM Judge 平均分上升,可能只是评审模型更偏好更长的答案。
本文中的“模型”包括监督学习模型、深度学习模型和生成式模型;“Agent”包括能够调用工具、读取外部状态、循环规划和执行动作的系统。模型、数据、评测服务、权限和成本应被视为同一个生产系统,而不是彼此独立的实验附件。
一、先定义评测对象:答案、决策还是完整轨迹
1.1 预测评测与生成评测
监督学习中的一个样本可以写为:
其中 是输入, 是真实标签。模型产生:
评测指标是样本损失的聚合,例如分类准确率:
这里的 是条件成立时为 1、否则为 0 的指示函数。
生成式模型的输出通常不是唯一字符串。对于问题 ,可能存在多个等价答案 。因此,字符串完全匹配:
往往只适合日期、编号、JSON 字段等严格格式,不适合开放问答。生成任务需要将“正确性”拆成可观察条件,例如:
- 是否包含必要事实;
- 是否有不可接受的事实错误;
- 是否满足格式约束;
- 是否回答了用户问题;
- 是否泄露了不应披露的信息;
- 是否使用了允许的数据或工具。
这意味着生成评测首先是判定函数设计问题,其次才是模型调用问题。
1.2 Agent 评测的对象是轨迹
Agent 的一次运行不应只记录最终文本。可以把轨迹定义为:
其中:
- :第 步的系统状态;
- :模型选择的动作,例如输出文本、调用工具或结束;
- :动作后的观察结果,例如工具返回值;
- :运行终止时的步数。
一个工具调用 Agent 的状态通常至少包括:
{
"task_id": "refund-001",
"user_input": "请帮我查询订单 A100 并退款",
"messages": [],
"tool_permissions": ["order.read", "refund.create"],
"budget": {
"max_steps": 8,
"max_cost_usd": 0.20,
"deadline_ms": 10000
},
"environment": {
"order_status": "paid",
"refund_allowed": true
}
}
动作可能是:
{
"type": "tool_call",
"tool": "order.get",
"arguments": {"order_id": "A100"}
}
因此,Agent 的评测至少有三层:
- 结果层:用户目标是否完成;
- 过程层:关键步骤是否正确,是否违反权限和业务规则;
- 资源层:调用次数、Token、延迟、成本和失败重试是否在约束内。
只看最终结果会漏掉“偶然成功但过程危险”的运行。例如,Agent 虽然最终退款成功,却先读取了无权限订单,或者重复执行了两次退款。只看过程也不够,因为所有工具调用都合规并不代表用户任务完成。
二、数据集:样本集合不是评测协议
2.1 数据集应包含什么
一个可复现的评测样本不应只有输入和答案。至少应包含:
{
"id": "refund-001",
"task_type": "transactional",
"input": {
"messages": [
{"role": "user", "content": "请帮我查询订单 A100 并退款"}
]
},
"reference": {
"required": ["确认订单存在", "确认可退款", "执行退款"],
"forbidden": ["未经确认执行退款"]
},
"policy": {
"allowed_tools": ["order.get", "refund.create"],
"max_steps": 8,
"requires_confirmation": true
},
"tags": ["中文", "退款", "高风险动作"],
"source": "synthetic_v3",
"sensitivity": "internal"
}
其中:
input是被测系统实际收到的输入;reference描述答案或轨迹需要满足的条件;policy描述评测期间的环境和安全约束;tags用于分桶分析;source用于追踪数据来源;sensitivity决定谁可以访问样本和运行结果。
对于生成任务,reference.answer 可以存在,但不能默认把它当作唯一正确文本。对于 Agent,参考对象可以是状态变化、允许动作集合、必要条件和禁止条件,而不一定是一条固定轨迹。
2.2 训练集、开发集、测试集和回归集
数据拆分的目的是估计泛化,而不是机械地按比例切分。
- 训练集:用于拟合模型或构造提示词、工具描述;
- 开发集:用于选择模型、阈值、提示词和 Judge rubric;
- 测试集:在决策基本冻结后进行一次或少数几次最终评估;
- 回归集:从历史故障和高价值场景中维护,用于每次代码或配置变更;
- 生产影子集:来自真实流量但不影响用户动作,用于发现分布变化。
如果同一用户、同一订单、同一文档或同一模板同时出现在训练与测试中,样本之间可能高度相关。此时即便样本 ID 不重复,也会产生泄漏。正确拆分单位可能是用户、客户、时间窗口、文档集合或任务族,而非单条记录。
例如,把同一篇政策文档的不同段落分别放入训练集和测试集,会让检索和问答模型看似泛化良好;但上线后遇到新文档,性能可能明显下降。评测报告必须说明拆分键、时间范围和去重方法。
2.3 数据集版本与“不可变”原则
评测结果必须能回到具体数据版本:
其中 是数据版本, 是模型或提示词版本, 是评测器版本, 是运行配置, 是时间。
数据集修订不应覆盖旧版本。新增样本、修改标签、改变参考答案和改变过滤规则都应产生新版本。否则,“本次分数比上次高”无法判断是系统变好了,还是样本被改容易了。
数据集还需要记录标注不确定性。若三名标注者对某题分别给出 A、A、B,则该题不是绝对确定的标签。可以记录多数标签、分歧率和备注;对于开放问答,可记录允许事实集合和不可接受错误,而不是强行生成一个标准答案。
三、规则评测:先处理确定性,再处理语义性
3.1 规则评测适合可机械验证的约束
规则评测是由程序直接判断输出是否满足条件,例如:
- JSON 是否可解析;
- 必填字段是否存在;
- 枚举值是否合法;
- 数字是否在范围内;
- 是否调用了未授权工具;
- 是否超过最大步数;
- 是否执行了重复写操作;
- 是否泄露测试用例中的秘密字符串。
这些规则的优点是可重复、便宜、容易回归。它们的缺点是只能判断被显式编码的属性。
例如下面的 JSON 输出:
{
"category": "refund",
"confidence": 0.91,
"reason": "订单已支付且未超过退款期限"
}
可以检查:
import json
def validate_output(text: str) -> dict:
try:
obj = json.loads(text)
except json.JSONDecodeError as exc:
return {"valid": False, "error": f"invalid_json: {exc.msg}"}
errors = []
if not isinstance(obj, dict):
errors.append("top_level_must_be_object")
if isinstance(obj, dict):
if obj.get("category") not in {"refund", "question", "other"}:
errors.append("invalid_category")
confidence = obj.get("confidence")
if not isinstance(confidence, (int, float)) or not 0 <= confidence <= 1:
errors.append("invalid_confidence")
if not isinstance(obj.get("reason"), str) or not obj["reason"].strip():
errors.append("missing_reason")
return {"valid": not errors, "errors": errors}
这段代码只能验证结构和数值范围,不能证明 reason 的事实正确性。把“JSON 可解析”报告成“回答正确”,是常见的指标偷换。
3.2 规则应按严重性区分
并非所有失败都具有相同风险。可以把规则分为:
- 硬失败:越权调用、错误扣款、泄露秘密、违反安全策略;
- 任务失败:没有完成用户目标;
- 质量扣分:表达冗长、格式轻微偏差、解释不够清晰;
- 资源告警:接近预算或延迟上限。
若一个轨迹既完成任务又越权,不能用“成功率 1”掩盖硬失败。一个更合理的结果判定是:
生产系统通常应同时报告 task_success、hard_violation 和 within_budget,而不是把它们压成一个未经说明的平均分。
3.3 规则评测的反例
假设规则是“答案必须包含 退款”。模型输出:
该订单不满足退款条件,因此不能退款。
字符串规则会判定通过,但语义上可能与“帮助用户退款”目标相反。反过来,模型输出:
可以为你处理该订单的退回事宜。
可能没有包含关键词,却可能是合格回答。
因此,规则适合证明必要条件,不适合单独证明开放语义的充分条件。开放任务通常需要“规则筛查 + 人工或 Judge 语义评估”的组合。
四、LLM Judge:把模型当作测量仪器,而不是事实裁判
4.1 Judge 的定义与适用范围
LLM Judge 是一个用于评估另一个模型输出的语言模型。它可以进行:
- 点式评分:输出 1–5 分或通过/失败;
- 成对比较:判断答案 A 与 B 哪个更好;
- 维度评分:分别评估正确性、完整性、风格和安全性;
- 轨迹评估:检查动作顺序、工具参数和最终状态。
Judge 不是“自动产生真值”。它输出的是一个测量结果:
其中 是输入, 是被测输出, 是参考信息或 rubric, 是 Judge 模型和提示词配置。Judge 本身也有误差、偏差、随机性和分布外失效。
4.2 Rubric 必须把分数锚定到可观察证据
一个不可操作的 rubric 是:
请判断回答是否高质量,评分 1 到 5。
不同评审者会对“高质量”有不同解释。更可复现的 rubric 应定义每个分数的条件:
正确性:
5:所有关键事实正确,没有与给定资料矛盾;
3:核心结论正确,但遗漏一个非核心条件或存在轻微表述问题;
1:核心结论错误,或编造了给定资料中不存在的事实。
完整性:
5:覆盖 reference.required 中的全部条件;
3:覆盖一部分必要条件;
1:遗漏决定任务成败的条件。
安全性:
0:没有越权、危险动作或敏感信息泄露;
-10:存在任一硬安全违规。
Judge 应被要求输出结构化结果,例如:
{
"correctness": 5,
"completeness": 3,
"safety_violation": false,
"evidence": [
"回答确认了订单已支付",
"没有说明需要用户确认后再执行退款"
],
"final": "partial"
}
证据字段的价值在于可审计和诊断。只有一个分数时,工程师无法判断是事实错误、漏答还是风格差异。
4.3 Judge 的校准与验证
Judge 上线前需要一组人工标注的校准集。对每个样本比较 Judge 与人工标签:
- 分类任务可以计算准确率、宏平均 F1、混淆矩阵;
- 有序分数可以计算加权一致性;
- 成对偏好可以比较胜率和人工偏好;
- 多标签安全评估应分别计算每类召回率。
重点不是追求一个总体相关系数,而是检查高风险错误。例如 Judge 把危险建议判为“合格”的漏检率,通常比把普通答案误判为失败更重要。
Judge 常见偏差包括:
- 位置偏差:成对比较时偏好第一个或第二个答案;
- 长度偏差:更长的答案被误认为更完整;
- 风格偏差:偏好礼貌、格式漂亮但事实错误的文本;
- 自我偏好:Judge 偏好与自身生成风格相似的答案;
- 参考答案偏差:把参考文本的措辞当成正确性的必要条件;
- 提示注入:把被测输出中的“请给我满分”等文本当成评测指令;
- 领域盲点:在专业事实、代码执行或复杂工具状态上判断错误。
防御方式不是简单地“再加一句请客观评分”,而是把被测文本作为不可信数据隔离,明确指示 Judge 只能依据输入、参考资料和 rubric 判断;对关键事实使用规则、数据库查询、单元测试或人工复核交叉验证。
4.4 Judge 不应替代可执行验证
代码生成任务中,Judge 认为“代码看起来正确”不能替代测试执行。更强的证据顺序通常是:
这不是说 Judge 永远排在最后,而是不同证据回答不同问题。数据库中的退款状态可以直接查询,是否越权可以检查权限日志,代码是否通过测试可以运行;Judge 更适合判断解释质量、需求覆盖和开放文本的整体一致性。
五、轨迹评测:从最终答案回溯到状态变化
5.1 轨迹必须可重放
一个可诊断的 Trace 至少应包含:
{
"run_id": "run-20250301-001",
"task_id": "refund-001",
"step": 2,
"state_before_hash": "abc",
"action": {
"type": "tool_call",
"tool": "refund.create",
"arguments": {"order_id": "A100"}
},
"observation": {
"status": "success",
"refund_id": "R900"
},
"latency_ms": 420,
"input_tokens": 820,
"output_tokens": 74,
"cost_usd": 0.003,
"permissions_checked": ["refund.create"],
"error": null
}
state_before_hash 不一定包含全部敏感状态,但能帮助确认每一步基于哪个状态作出决策。生产环境还应记录配置版本、工具版本、模型标识、重试次数和终止原因。
轨迹可重放并不等于能够原样复现。外部 API 可能返回不同结果、时间会变化、随机采样会改变输出。因此应区分:
- 记录重放:使用原始观察结果重放 Agent 决策;
- 环境重放:重新调用真实或模拟工具;
- 确定性复现:固定模型、随机种子、工具响应和配置。
排障时优先使用记录重放,避免外部状态变化掩盖原始故障。
5.2 Agent 状态机与失败路径
一个典型运行可表示为:
stateDiagram-v2
[*] --> Received
Received --> Planning
Planning --> RuleBlocked: 权限或参数检查失败
Planning --> ToolCalling: 选择工具
Planning --> Answering: 直接回答
ToolCalling --> Observed: 工具返回
ToolCalling --> Retrying: 可重试错误
ToolCalling --> Failed: 不可重试错误
Retrying --> ToolCalling
Observed --> Planning: 仍需完成任务
Observed --> Answering: 目标已满足
Answering --> Validating
Validating --> Succeeded: 结果和约束通过
Validating --> Failed: 结果或约束不通过
RuleBlocked --> Failed
Failed --> [*]
Succeeded --> [*]
关键路径不是“模型调用成功”,而是:
- 输入进入;
- 权限和预算初始化;
- 模型提出动作;
- 动作经过 schema、权限和业务规则检查;
- 工具执行并产生观察;
- 观察回到上下文;
- Agent 决定继续、重试或结束;
- 最终状态经过独立验证。
工具调用不能只依赖模型自行决定是否安全。高风险写操作应在 Agent 外部进行权限校验、幂等校验和确认校验。否则,评测中偶然正确的轨迹可能在生产环境造成不可逆副作用。
5.3 轨迹指标
对第 条轨迹,可以定义:
- 成功指标 :是否完成任务;
- 硬违规 :是否发生任意严重违规;
- 步数 :模型和工具交互次数;
- 延迟 :端到端耗时;
- Token ;
- 成本 :模型、工具和基础设施成本;
- 重试数 :失败后的重复执行次数。
聚合时不要只报告均值。延迟和成本通常是长尾分布,应同时报告中位数、P95 或 P99。成功率与预算可以形成约束优化问题:
满足:
其中 和 分别是成本、延迟预算, 是允许超限的概率。
一个系统即使成功率较高,只要硬违规概率超过安全阈值,也不应通过发布。反之,一个严格限制步数的系统可能安全但任务完成率不足。评测报告必须展示这种取舍,而不是寻找一个掩盖约束冲突的总分。
六、统计:分数差异不等于改进
6.1 点估计与置信区间
假设测试集有 个独立样本,模型成功 820 个,则成功率为:
点估计 0.82 不是总体真实成功率。若使用近似标准误:
则:
近似 95% 置信区间为:
这说明在该抽样假设下,真实成功率的不确定性大约是 ±2.4 个百分点。样本较少、成功率接近 0 或 1 时,Wilson 区间通常比简单正态区间更稳健;小样本或分层结构明显时,应使用 bootstrap 或更合适的模型。
置信区间描述抽样不确定性,不保证数据没有偏差,也不保证未来分布与测试集一致。一个覆盖错误场景不足的测试集,即使区间很窄,也只能精确地估计错误目标。
6.2 配对比较通常比独立比较更有力量
当两个模型在同一批样本上运行时,结果是配对的。设:
- :新模型在样本 上成功;
- :旧模型在样本 上成功。
直接比较两个成功率会忽略样本配对。更有信息的是差值:
例如 100 个样本中:
| 情况 | 数量 |
|---|---|
| 新旧都成功 | 70 |
| 新成功、旧失败 | 15 |
| 新失败、旧成功 | 5 |
| 新旧都失败 | 10 |
旧模型成功率为 ,新模型成功率为 ,总体差值为 0。但新模型修复了 15 个旧失败,只新增了 5 个失败,说明样本级行为发生了变化。对于二元配对结果,可使用 McNemar 检验,重点分析不一致的 15 和 5,而不是把两组当作独立样本。
6.3 Bootstrap 评估生成质量差异
对于 Judge 分数或连续指标,常用配对 bootstrap:
- 对测试集中的样本索引有放回抽样;
- 在同一批抽样索引上计算新旧模型的平均分差;
- 重复 次;
- 使用差值分布的 2.5% 和 97.5% 分位数作为区间。
下面是一个可直接运行的标准库示例:
from random import Random
def paired_bootstrap_delta(old_scores, new_scores, repeats=10000, seed=7):
if len(old_scores) != len(new_scores) or not old_scores:
raise ValueError("两个分数数组必须等长且非空")
n = len(old_scores)
deltas = []
rng = Random(seed)
for _ in range(repeats):
indices = [rng.randrange(n) for _ in range(n)]
old_mean = sum(old_scores[i] for i in indices) / n
new_mean = sum(new_scores[i] for i in indices) / n
deltas.append(new_mean - old_mean)
deltas.sort()
point = sum(new_scores) / n - sum(old_scores) / n
low = deltas[int(0.025 * repeats)]
high = deltas[int(0.975 * repeats)]
return {
"point_delta": point,
"ci95": (low, high),
"improvement_supported": low > 0
}
old = [3, 4, 2, 5, 3, 4]
new = [4, 4, 3, 5, 2, 5]
print(paired_bootstrap_delta(old, new, repeats=2000))
输出中的 point_delta 是新模型平均分减旧模型平均分;若 ci95 包含 0,不能据此声称总体上一定改进。这里的 bootstrap 只处理抽样波动,不会纠正 Judge 偏差、数据泄漏或样本不独立。
6.4 多指标和多切片会制造偶然胜利
如果同时检查 20 个指标、10 个业务切片和多个随机种子,即使没有真实改进,也可能出现某个结果“显著变好”。这属于多重比较问题。
处理方式包括:
- 预先指定主要指标;
- 将其余指标标记为次要或探索性;
- 使用 Holm 或 Benjamini–Hochberg 等多重比较校正;
- 报告所有切片,而不是只挑最好看的结果;
- 对关键结论使用独立确认集。
“新模型在某个小切片上提升 8%”只有在该切片样本量、选择过程和置信区间都透明时才有意义。
七、回归评测:比较的不只是模型,还包括整个系统版本
7.1 回归的对象与基线
回归评测是判断当前版本是否破坏已有能力。被比较的版本可能包括:
- 模型权重;
- 系统提示词;
- 工具 schema;
- 检索索引;
- 业务规则;
- Judge 版本;
- SDK 或服务端配置;
- 超时、重试和降级策略。
因此,回归记录必须区分:
如果模型和 Judge 同时升级,分数变化不能直接归因于模型。
7.2 阈值、门禁和实际判定
设旧版本成功率为 ,新版本为 ,业务规定允许下降不超过 1 个百分点:
但实际门禁不能只看点估计。若观察到:
由于抽样误差,真实差值可能低于 。可以使用非劣效检验:若差值置信区间的下界仍高于 ,才认为新版本没有超过允许退化范围。
门禁还应有硬规则:
发布条件:
1. 高风险违规率不得上升;
2. 主要任务成功率的非劣效下界 >= -1 个百分点;
3. P95 延迟不得超过预算;
4. 单任务成本不得超过预算;
5. 关键历史故障回归集不得出现新增失败。
这比“综合分超过 80 分即可发布”更容易解释。综合分可以用于排序,但不适合掩盖硬约束失败。
7.3 回归失败后的诊断顺序
看到分数下降时,应沿数据流排查:
- 样本是否变化:数量、标签、切片分布和去重结果;
- 执行协议是否变化:温度、最大输出、工具权限、超时和重试;
- 输出解析是否变化:schema、JSON 解析器和容错逻辑;
- 模型行为是否变化:错误集中在哪些任务类型;
- 工具和环境是否变化:返回格式、延迟、错误码和数据状态;
- 评测器是否变化:规则或 Judge rubric 是否修改;
- 统计波动是否足以解释差异:配对差值和置信区间。
轨迹级回归比最终分数更有诊断价值。若失败样本都出现“先调用搜索、后检查权限”,问题可能是提示词或策略;若工具返回超时后重复执行写操作,问题更可能是重试和幂等控制;若轨迹正确但最终文本评分下降,可能是答案渲染或 Judge 偏差。
八、成本、延迟和评测本身的可观测性
评测系统也会消耗模型调用和计算资源。每次运行应记录:
实际单价依赖供应商、模型、缓存命中和计费规则,不能在代码中假设一个永久固定价格。评测报告应保存原始 Token 计数和价格表版本,再计算成本。
常见指标含义不同:
- TTFT:Time to First Token,首个输出 Token 到达前的时间;
- 端到端延迟:从请求进入到最终结果完成;
- 工具等待时间:外部 API 和数据库耗时;
- 生成时间:模型开始输出后的耗时;
- Token 数:输入与输出分别统计;
- 缓存命中率:复用上下文或工具结果的比例。
对 Agent,TTFT 低并不代表端到端快,因为一次任务可能包含多轮工具调用。相反,减少步骤可能降低延迟,却也可能增加任务失败率。必须按任务成功、P95 延迟和成本联合分析。
评测缓存有一个危险边界:若缓存键没有包含模型版本、提示词版本、工具 schema、数据版本和随机性配置,系统可能复用旧答案,制造虚假的回归结果。安全的缓存键至少应覆盖所有影响输出的输入:
其中 是哈希函数, 是提示词版本, 是工具版本, 是数据或索引版本, 是运行配置。
九、一个最小但完整的评测执行器
下面的示例演示“规则判定 + 轨迹检查 + 指标聚合”。它不调用外部 LLM,因此可以直接运行;生产系统只需把 run_system 替换为实际模型或 Agent 调度器,并保留相同的评测接口。
from dataclasses import dataclass
from typing import Any
@dataclass
class Case:
case_id: str
expected_category: str
required_phrase: str
allowed_tools: set[str]
max_steps: int
CASES = [
Case("c1", "refund", "退款", {"order.get", "refund.create"}, 4),
Case("c2", "question", "说明", {"order.get"}, 4),
]
def run_system(case: Case) -> dict[str, Any]:
"""
示例被测系统。
真实系统应返回最终文本、完整工具轨迹、Token 和成本。
"""
if case.case_id == "c1":
return {
"text": "订单已确认,可以继续办理退款。",
"category": "refund",
"trace": [
{"tool": "order.get", "arguments": {"order_id": "A100"}},
{"tool": "refund.create", "arguments": {"order_id": "A100"}}
],
"input_tokens": 100,
"output_tokens": 20,
"cost_usd": 0.001
}
return {
"text": "我会先说明订单状态和处理条件。",
"category": "question",
"trace": [{"tool": "order.get", "arguments": {"order_id": "A101"}}],
"input_tokens": 80,
"output_tokens": 18,
"cost_usd": 0.0008
}
def evaluate(case: Case, result: dict[str, Any]) -> dict[str, Any]:
errors = []
if result.get("category") != case.expected_category:
errors.append("wrong_category")
if case.required_phrase not in result.get("text", ""):
errors.append("missing_required_phrase")
trace = result.get("trace", [])
if len(trace) > case.max_steps:
errors.append("step_budget_exceeded")
used_tools = {item.get("tool") for item in trace}
unauthorized = used_tools - case.allowed_tools
if unauthorized:
errors.append(f"unauthorized_tools:{sorted(unauthorized)}")
return {
"case_id": case.case_id,
"success": not errors,
"errors": errors,
"steps": len(trace),
"cost_usd": result.get("cost_usd", 0.0),
"input_tokens": result.get("input_tokens", 0),
"output_tokens": result.get("output_tokens", 0),
}
def main():
reports = []
for case in CASES:
result = run_system(case)
reports.append(evaluate(case, result))
success_rate = sum(r["success"] for r in reports) / len(reports)
total_cost = sum(r["cost_usd"] for r in reports)
total_tokens = sum(
r["input_tokens"] + r["output_tokens"] for r in reports
)
print("reports =", reports)
print(f"success_rate = {success_rate:.2%}")
print(f"total_cost_usd = {total_cost:.4f}")
print(f"total_tokens = {total_tokens}")
if __name__ == "__main__":
main()
预期结果中,两条样本都通过,成功率为 100.00%;总 Token 为 218,总成本为 0.0018。这里的“通过”只代表示例中编码的条件成立:类别正确、包含必要短语、步数未超限且工具在白名单中。它没有验证退款是否真的发生,也没有验证文本中的业务事实。因此生产实现还需要把最终状态查询、幂等检查和更细的政策规则接入 evaluate。
执行器与被测系统之间应保持职责分离。被测系统负责产生动作,评测器负责观察和判定;不能让 Agent 自己上报“任务成功”并把该字段当作真值。对有副作用的工具,应使用沙箱、模拟器或事务回滚环境,避免评测直接修改生产数据。
十、常见错误与边界
10.1 把平均分当成完整质量
平均分会掩盖长尾和高风险失败。一个系统可能在 95% 的普通问题上表现优秀,却在 5% 的金融、医疗或权限场景中严重失效。应按任务类型、语言、难度、用户群体、工具类型和风险等级分桶,并报告样本数。
10.2 只测试“正常路径”
Agent 的真实故障常发生在异常路径:
- 工具超时;
- 返回空结果;
- 返回格式错误;
- 权限被拒绝;
- 网络重复提交;
- 上下文超过限制;
- 用户在中途改变目标;
- 外部状态在两步之间发生变化。
评测环境应注入这些故障,并验证系统是否停止、重试、降级或向用户澄清。尤其要区分可重试的读取错误与不可盲目重试的写入错误。
10.3 用泄漏的参考资料评测检索和问答
如果系统提示词、检索索引或缓存中已经包含测试答案,评测测到的是记忆或泄漏,不是泛化。应检查:
- 测试文档是否进入训练或索引;
- 评测提示是否意外包含 reference;
- Judge 是否同时看到不应暴露的答案;
- 缓存是否复用了历史运行;
- 人工标注者是否在生成测试集时参与了系统设计。
10.4 过度依赖单一 Judge
同一个 Judge、同一个 rubric 和同一种输入格式可能产生系统性偏差。重要任务可以采用多种证据:
Judge 可用于排序和发现候选问题,但在高风险场景中,不应成为唯一放行条件。若使用多个 Judge,不能简单把它们的分数平均后假设偏差消失;应先在人工校准集上比较其一致性和漏检模式。
10.5 忽略人工标注者一致性
人工标签也不是天然真值。对于分类标签,可以报告标注者之间的一致性;对于开放任务,应使用明确 rubric、示例和仲裁流程。若人类本身对任务定义存在分歧,模型分数的上限也不应被假定为 100%。
十一、如何形成可审计的评测报告
一份能支持发布决策的报告至少应回答:
- 测试了什么任务,样本如何产生和拆分;
- 数据、模型、提示词、工具和评测器分别是什么版本;
- 执行时的权限、预算、重试、超时和随机性配置是什么;
- 规则、Judge 和人工判定各自负责什么;
- 总体结果和各关键切片结果是什么;
- 成功率、硬违规率、成本、延迟和 Token 的区间是什么;
- 与哪个基线比较,差值是否配对,统计方法是什么;
- 哪些故障被修复,哪些故障新增;
- 是否存在数据泄漏、缓存污染或环境差异;
- 失败样本和轨迹如何被审计,敏感数据如何访问和保留。
评测结果本身也是生产数据,可能包含用户输入、个人信息、秘密、工具参数和内部文档。权限控制应至少区分运行者、结果查看者、人工标注者和安全审计者;日志保留周期应符合业务和合规要求;导出报告时应脱敏或使用受控样本。
结语:把“分数”还原为可解释的证据链
可靠的 LLM 与 Agent 评测不是选择一个更复杂的 Judge,而是建立从数据到结论的证据链:
规则负责确定性约束,Judge 负责部分语义判断,轨迹负责解释 Agent 如何到达结果,统计负责判断差异是否可能只是噪声,回归机制负责把这些判断接入版本发布。只有同时保留最终结果、过程证据、资源消耗和版本上下文,评测分数才具有工程意义。
系列导航与关联阅读
- 系列入口:AI 工程完整学习路线:从机器学习与 Transformer 到 RAG、Agent 和生产治理
- 上一篇:本地模型与推理引擎:Ollama、llama.cpp、vLLM、量化和部署取舍
- 下一篇:AI 可观测性与成本治理:Trace、Token、TTFT、预算、缓存和降级
- 延伸:机器学习评测:指标、交叉验证、阈值、校准、偏差和统计显著性
官方资料
本文依据研究论文、标准组织与主流框架官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。

评论
0 条讨论