01 · 先拆开“成功”:答案、执行、业务结果不是一回事
继续上一章的研究报告案例。最终回答“报告已保存”,只能证明模型输出了这句话;publish 检查点存在,说明本地记录了回执;远端 receipts 中只有一条记录,才是本次实验中重复发布未发生的证据。评测要分别读取这些事实,而不是让 Agent 自报一个 success=true。
| 维度 | 可观察证据 | 不能推出什么 |
|---|---|---|
| 执行可靠性 | 认领、提交、重试、取消事件 | 任务走完不能证明回答正确 |
| 结果结构 | summary 非空,citations 为合法 ID 数组 | ID 合法不能证明论点被来源支持 |
| 业务结果 | 独立发布库中的操作键、内容摘要、回执 | 单次实验不能保证所有网络故障下恰好一次 |
| 语义质量 | 论点与证据片段逐项核对 | 模型评分器的高分不是人工真值 |
| 资源消耗 | 实际调用次数、计费 token、墙钟时间 | 离线替身的耗时不能代表模型服务延迟 |
Anthropic 的评测方法区分 task、trial、grader、trajectory 和 outcome,并建议结合代码、模型与人工评分。我们采用这一基本区分,但下面的样本、门槛、代码与结果均为本站独立设计。
Anthropic · Agent 评测:用于核对评测对象、轨迹与实际结果的区别,以及评分器类型。(核对:2026-09-25)
本次成绩的正确读法: 基础 18 项软件机制测试通过;第三版增加 18 项审批测试;没有进行真实模型能力排名、语义质量评分或线上吞吐测试。测试里包含模拟 HTTP,默认摘要为确定性替身。
02 · 把需求变成样本合同:输入、故障、期望和证据一起写
一个测试只有输入问题是不够的。还要记录前置状态、故障注入点、合法终态和禁止出现的业务影响。尤其是取消、权限不足与记忆失效:正确停止可能就是通过,不能统一要求所有任务 succeeded。
测试合同示例
{
"case_id": "effect-before-checkpoint/v1",
"input_version": "report/v1",
"provider": "fixture",
"precondition": "fresh task + empty publisher database",
"fault": "after_effect",
"resume": "advance virtual clock by 31 seconds; rerun without fault",
"expected": {
"status": "succeeded",
"checkpoint_count": 4,
"publisher_receipts": 1,
"publish_deduplicated": true
},
"evidence": [
"jobs",
"checkpoints",
"events",
"publisher.receipts"
]
}
| 已实现测试组 | 场景 | 核心断言 |
|---|---|---|
| 恢复与业务效果 | collect 后宕机;外部成功后宕机;重复投递 | 已提交步骤不再执行,最终回执唯一 |
| 并发与任务控制 | 两个进程认领;旧代次写回;取消;deadline | 只一个认领者;非法提交失败;到期停止 |
| 输入与报告 | 同 ID 不同输入;未知来源 ID;幂等键内容冲突 | 拒绝复用旧身份;失败任务不发布 |
| 重试 | 模拟 429 连续失败 | 2 秒、4 秒退避;最多 3 次认领 |
| 记忆 | 租户/用户/项目隔离;过期;未确认;更新;删除后恢复 | 不合格记录不入候选;旧快照失效 |
| 评测与适配器 | 事件变异;真实 CLI 退出;模拟 HTTP 成功/429 | 错误轨迹被拒绝;退出码 75;错误分类正确 |
测试组不是生产事故统计。比如隔离测试使用可信 scope 参数,只能验证过滤函数;它没有攻击真实认证网关,不能据此声称“企业权限隔离已验证”。把证据强度写进样本合同,能防止结果在汇报时被放大。
03 · 从事件和检查点评分,不读取模型隐藏推理
本站事件表记录提交、认领、步骤开始、步骤提交、重试和取消。它保留 job_id、generation、时间和详情,足够还原本例状态转换。日志由运行时写入,模型输出不能改写这些记录。生产系统还应限制数据库写权限,避免工作进程之外的操作者伪造审计。
事件形状示意;真实序号见 evidence.json
{
"seq": 9,
"job_id": "evidence-001",
"at": 1031,
"generation": 2,
"kind": "step_started",
"detail": "publish"
}
这里使用虚拟时钟测试租约,1000、1031 是测试时间坐标,不是线上时间戳。事件序号由数据库产生;在本数据库中可排序,但扩展到多个服务后不能仅用各机器时钟确定因果关系。应加入 span_id、parent_span_id、operation_id 与 attempt_id。
| 记录位置 | 可保存 | 避免直接保存 |
|---|---|---|
| 任务元数据 | 输入摘要、版本、授权引用、预算 | API Key、会话 cookie |
| 工具事件 | 工具名、状态、耗时、资源 ID、错误分类 | 整份账户流水或敏感文档明文 |
| 模型计量 | 请求 ID、input/output token、重试次数 | 未经评估的全部用户对话 |
| 证据库 | 受控文档引用、片段哈希、读取权限 | 把引用可访问性误认为每个用户都有权限 |
不要要求收集隐藏推理来做轨迹评测。工具调用、显式输出、检查点与业务回执足以验证多数执行不变量。若必须保存完整工具输出,应独立设置访问控制与保留期限,不要把它们和普通调试日志一起公开。
04 · 可运行评分器:哪些规则能自动判,哪些必须留空
evaluate.py · 实际评分规则
def grade(snapshot, expected_status='succeeded'):
events = snapshot['events']
commits = [e['detail'] for e in events if e['kind'] == 'step_committed']
cp = snapshot['checkpoints']
checks = {'expected_terminal_state': snapshot['status'] == expected_status,
'no_duplicate_checkpoint_commit': len(commits) == len(set(commits)),
'trace_matches_checkpoints': set(commits) == set(cp)}
if expected_status == 'succeeded':
checks['all_steps_present'] = set(cp) == set(STEPS)
checks['source_id_gate_passed'] = cp.get('verify', {}).get('schema_and_source_ids') is True
checks['receipt_present'] = bool(cp.get('publish', {}).get('receipt'))
return {'passed': all(checks.values()), 'checks': checks,
'semantic_support': 'not_scored', 'model_quality': 'not_scored'}
这段代码检查终态是否符合预期、同一步骤是否重复提交、事件与检查点集合是否一致。对于 succeeded,还检查四步齐全、来源 ID 门槛通过且回执存在。它只断言机械一致性,不声称日志不可篡改,也不会把 semantic_support 自动填成 true。
评测故意不强制每个工具只能调用一次。崩溃恢复时 publish 可以有两次 step_started,但只能有一次 step_committed,远端业务记录也只能有一条。如果简单把“工具调用次数大于一”判失败,会把正确的恢复机制误判为坏路径。
反过来,只看最终状态也会漏错。test_grader_rejects_trace_mutation 会复制一条提交事件,证明评分器能够拒绝重复提交轨迹。它针对明确不变量做变异验证,而不是写一个永远返回通过的示例。
对实际任务库运行评分
python3 evaluate.py --db lab.sqlite --job report-001
# 如果是在验证明确取消的任务:
python3 evaluate.py --db cancel.sqlite --job report-001 --expected-status cancelled
05 · 本次运行记录:先看证据,再看结论
| 观察量 | 宕机时 | 恢复后 |
|---|---|---|
| 任务状态 | running | succeeded |
| 认领代次 | 1 | 2 |
| 本地检查点数 | 3 | 4 |
| 模拟发布库回执数 | 1 | 1 |
| publish 去重标记 | 本地尚无 publish 结果 | true |
这组数据来自本次 after_effect 实验。复现使用两个真实 SQLite 文件与虚拟时钟;检查点提交之后人为注入进程中断语义,再推进时钟让租约到期。另一个独立测试以 CLI 子进程执行 os._exit(75),验证退出后数据库仍保留已经提交的 collect。
evidence.json:完整前后快照与评分 · validation.json:环境、测试名称与已知未验证项 · test_lab.py:断言源码
本次 unittest 实际输出节选;耗时依运行环境变化
Ran 18 tests in 0.304s
OK
0.304 秒是这次离线测试集合的执行时间,不是 Agent 响应时延,也不能用来宣传 QPS。两进程认领测试只检验一个竞争场景,不代表高负载下所有调度正确性都已证明。一个失败场景的成功恢复,也不能转换为“恢复成功率 100%”。
06 · 语义评测怎么补:把一句话拆成可核对的论点
模型常见的失误是“引用真的存在,但并不支持这句话”。例如 s1 只说明检查点存储,却被用来支撑“系统效率提升 30%”。当前来源 ID 校验会放行这一引用;必须增加论点级核对才能发现问题。
| 待核对论点 | 要求的证据 | 建议标签 |
|---|---|---|
| 恢复后跳过已提交 collect | 运行事件中 collect 提交一次,恢复后没有再次开始 | supported |
| 任何外部接口都恰好一次 | 本例没有这样的证据,且反例存在 | unsupported |
| 平均节省 30% token | 同任务、新旧方案的真实计费数据 | insufficient_evidence |
| 删除后所有副本已彻底抹除 | 主库、缓存、检查点、备份的完整删除记录 | unsupported |
落地时,为每份报告建 claim_id → source_id → 证据片段 → 标签 → 理由的映射。第一批保留 20 个来自业务的争议论点,由两位熟悉业务的人独立标注;分歧先讨论规则,再校准评分模型。20 是起步工作量建议,不是统计学充分样本量。
人工标注记录模板
{
"claim_id": "c1",
"claim": "恢复后 collect 未重复执行",
"source_id": "run-evidence-001",
"evidence": "generation=2 无 collect 的 step_started",
"label": "supported",
"reviewer": "human-label-required",
"rubric_version": "grounding/v1"
}
评分模型只能读取任务、候选答案和允许证据;不得调用发布工具。固定 rubric 版本,并保存它与人工标签的混淆矩阵。若模型经常把“证据不足”判为“支持”,应先修评分器,不要立刻拿它优化被测 Agent。当前实验包尚未实现这一语义评分层。
07 · 比较两个方案:用配对任务,别比较两次演示
假设你准备把 prompt-v1 换为 prompt-v2。先冻结材料版本、模型版本、工具环境、任务预算与评分器版本,对同一组任务配对运行。每个任务重复若干次,保存所有试验,不只选最好的一次。模型别名背后可能更新,能使用固定版本时优先记录固定版本。
| 指标 | 计算口径 | 解读限制 |
|---|---|---|
| 任务成功率 | 符合该任务验收合同的 trial 数 / 全部有效 trial 数 | 明确基础设施故障如何归类,不无声删样本 |
| 安全/业务硬失败 | 越权、未审批写入、重复业务记录分别计数 | 不能靠答案质量平均分抵消 |
| 引用支持率 | supported 论点数 / 需要证据的论点数 | 区分无证据与证据反驳 |
| 成功任务成本 | 所有试验成本(含失败)/ 成功任务数 | 只算成功调用费用会低估真实成本 |
| 端到端时延 | 提交到终态,含排队和退避 | 另报模型调用时延,避免混淆 |
例如一个方案要试三次才能成功,pass@3 会好看,但单次用户体验可能变差。业务只允许一次提交时,应优先比较单次成功与重试开销;每次都必须可靠的流程,还要看重复试验的一致性。不要把“至少一次成功”和“每次成功”混为同一个指标。
小样本报告建议直接给出分子/分母、每任务差异和失败列表。没有足够样本时写“探索性结果”,不宣称统计显著。若新旧方案用不同输入、不同模型或不同工具数据,无法把改善单独归因于提示词变化。
08 · 把评测接到研发流程:什么错误阻止发布
对 Java 老系统,先从现有集成测试与生产缺陷中选择样本,再引入模型判断。涉及数据库更新和外部接口的 Agent,优先用隔离沙箱与受控工具;评测账号权限应小于等于试点用户,禁止为了跑测试临时放大权限。
- 每次代码改动运行本实验的机械回归,失败则保留输出并停止流水线。
- 涉及提示词、模型或检索变更,运行冻结开发集;保存输入摘要、输出、轨迹与评分器版本。
- 在保留集上比较候选方案,硬失败逐条审查;不要反复看保留集并针对题目改提示词。
- 小范围接入只读业务,记录人工纠正和失败类型;将复现条件明确的事故转成回归样本。
离线回归门槛;第二条要求先按 README 生成正常任务
python3 -m unittest discover -s . -p test_lab.py -v
python3 evaluate.py --db lab.sqlite --job report-001
当前 18 项测试既有成功路径,也有拒绝路径:来源 ID 非法应 failed、取消应 cancelled。CI 通过不是所有任务都 succeeded,而是每个任务都符合预先定义的合法结果。
升级时真正该问的是:“哪些失败被修复?是否引入新的重复写入、越权或成本问题?证据是否可重放?”这三个问题都需要记录级证据,单张评分截图无法回答。
09 · 评测失败的定位顺序
| 失败表现 | 先区分 | 下一步动作 |
|---|---|---|
| 最终文本不错,任务却失败 | 硬门槛还是语言分数 | 按事件定位未知来源、deadline 或非法状态,不先改文案 |
| 输出里有引用,语义评分低 | 来源不存在还是不支持论点 | 逐条建立 claim → evidence 映射 |
| 机械评分通过,却出现重复报告 | 本地提交还是远端真实效果 | 查询独立业务库;修复幂等协议,不能只增加日志 |
| 换模型后成本明显升高 | 单次 token 还是重试变多 | 按 trial 汇总含失败成本,检查格式错误与超时 |
| 不同运行波动很大 | 模型随机性还是工具环境变化 | 固定数据快照,再做多次配对试验 |
| 评分模型与人经常冲突 | rubric 模糊还是证据不足 | 先校准评分器,保留 disputed 标签 |
评分器也要维护版本和测试。增加一条规则时,至少准备一个应通过的反例和一个应失败的反例;确认没有把合法路线误杀。对“答案要完整”“代码要优雅”这类主观描述,先给可操作定义,再引入自动评分。
同一套案例,三篇文章共用
Python 3.10+ · 标准库 · 默认离线 · 含源代码、36 项测试和运行记录
10 · 增加审批以后,验收不再只有 succeeded
| 期望终态 | 样本必须断言 | 常见误判 |
|---|---|---|
| waiting_approval | 已有草稿,发布记录为 0,重复 run 不重新生成 | 把等待当超时失败 |
| failed / rejected / expired | 未发出且发布记录为 0 | 只断言状态,漏掉先执行后拒绝 |
| succeeded | 有效批准先于派发,只有一条业务记录 | 最终成功掩盖未经批准的第一次调用 |
| reconciling | 没有新发送;保留未知状态和核对线索 | 找不到回执就当没发生 |
| effect_confirmed | 回执匹配原 payload_hash,fresh_write_performed=false | 把过去的业务效果当成新的授权 |
第三版新增 18 项审批测试,与原 18 项基础测试合计 36 项。新增案例包括两个进程同时批准/拒绝、重复回调、内容改写、角色和作用域拒绝、审批过期、取消后的回执核对及无回执不重发。身份测试只检查本地 Principal 夹具规则,没有验证真实认证系统。
运行完整回归;仅 test_lab.py 不包含审批扩展
python3 -m unittest discover -s . -p 'test_*.py' -v
基础 evaluate.py 主要检查原四步任务。审批场景的业务效果和授权顺序由 test_approval.py 直接断言;不要把基础 passed=true 当作审批合规证明。完整源码与结果见 test_approval.py、validation.json。
11 · 如何证明恢复没有偷偷再发一次
仅看报告表最终只有一行不够:幂等服务即使收到第二次写请求,也可能返回同一行。本次审批测试在恢复时将 publish 方法替换为“一旦调用就抛错”的替身,再运行 reconcile 路径;同时核对真实 SQLite 回执。这同时验证没有新派发和原业务结果仍可追踪。
| 证据 | 实际检查 | 证明范围 |
|---|---|---|
| test_crash_after_effect_and_expiry_reconciles_without_write | 批准过期后,调用 publish 即报错;恢复仍完成 | 本代码路径只做回执查询 |
| test_no_receipt_after_dispatch_stays_unknown | 查不到回执,连续恢复仍不调用 publish | 未知状态不自动重发 |
| test_cancel_after_effect_does_not_claim_rollback | 取消后仍查到一条原回执,保留取消意图 | 状态报告没有伪造撤销 |
| test_concurrent_approve_reject_one_decision_wins | 两个真实子进程竞争,同一请求只有一个决定 | 本地数据库事务下的单请求竞争 |
测试没有跨区域网络、真实第三方队列或远端数据库灾备。生产核对服务需要定义查询的一致性、回执可见延迟、幂等记录保留期和最终失效条件;这些契约不同,结案规则也不同。
来源与验证
官方资料用于核对具体机制;表结构、程序与实验为本站独立设计。