Agent 验收不能只看执行日志
执行链路全部成功,并不代表业务数据正确更新或完成声明真实。文章提供 12 个合成验收案例、JSONL 格式和 Python 检查器,通过独立业务回执核对实际结果。
AI 深度解读
Agent 的执行轨迹全绿,客户资料却可能改错了,异步任务也可能还没落库。原文要解决的,就是这种「工具调用成功,业务结果却不对」的验收漏洞:把执行过程、业务状态和对用户的回复分别验证,再决定一次任务能不能算成功。
对正在做 AI 编程助手或业务 Agent 的团队,这个区分很实用。作者给出了结果契约、合成测试思路和 Python 检查器,但方案也有缺口:提供的正文没有完整的 12 案例矩阵,示例代码也没有彻底落实「未知就阻断」。
为什么 HTTP 200 还不够证明任务完成
Trace(执行轨迹)能记录模型回复、工具调用、返回结果、耗时和状态。它适合回答「哪个工具执行了」「哪里报错了」,却不能自动回答「业务是否做对了」。
比如,HTTP 200 的响应体可能写着业务拒绝;数据库更新可能命中了错误客户;队列接收了任务,消费者却还没执行。即使写入正确,Agent 也可能把「草稿已创建」回复成「采购已批准」。
调用成功、状态正确、回复属实,是三个需要分别取证的结论。
作者因此建议保留 execution_ok、state_ok、claim_ok 和 evidence_complete。前三项分别记录执行、状态和表述是否正确,可取通过、失败或未知;最后一项表示证据是否足够。未知不能直接算通过,缺少日志也不能证明没有副作用,事件可能只是丢了。
原文引用的研究支持这种区分,但不能拿来估算生产故障率。2026 年 3 月 3 日提交的「Beyond Task Completion」讨论了结果评分掩盖流程违规的问题,其中 27–78% 的范围仅适用于论文评估的特定设置。Amazon Science 的另一份报告分析了五个 Agent 领域的 4,671 条轨迹,发现部分失败能从行为信号识别,但重试多只能提示异常,无法代替业务系统的真实状态。
2025 年 11 月 25 日的一则 LangChain 社区报告也很典型:Agent 告诉用户需要补充信息,却在同一请求里继续调用工具。它值得变成测试用例,但一个帖子不足以证明问题普遍存在。
把「完成」写成能验证的结果契约
作者提出的 outcome contract(结果契约),是提前写清楚:允许 Agent 做什么,执行后必须满足什么条件,由谁提供证据,以及每种状态下能对用户说什么。
契约应按业务动作建立,并配置版本和负责人。主要字段包括 contract_id、workflow_version、intent_id、entity_ref、precondition、allowed_action、expected_postcondition、authoritative_system、receipt_type、allowed_user_claims 和 failure_route。
以修改配送偏好为例,前置条件可以要求用户已登录、地址由用户选定、订单标识有效。工具只允许修改该订单的偏好字段。执行后,再从订单系统读取记录,检查目标订单、偏好值和记录版本。
只有回读确认提交,才能回复「已更新」。API 如果只返回 accepted,允许的表述应是「请求已提交,等待确认」。
这里的 authoritative system,就是拥有业务事实的权威系统。按钮点击成功、工具返回成功、模型描述成功,都不能替代它的提交回执。
执行记录和结果记录也不必塞进同一个 span。作者建议用 trace_id、run_id、operation_id 关联两者,另行保存 attempt、状态版本、commit_status、claim_class、evidence_status、checked_at 和 checker_version。重试可以共享业务操作 ID,但每次尝试需要独立的执行 span,才能分辨一次操作重试了两遍,还是用户要求执行两次。
状态摘要也有前提。任意 JSON 直接算哈希,会受字段顺序、空白和易变字段影响;需要先约定规范化表示。日志则应尽量使用 entity_ref_hash 等脱敏引用,避免顺手记录完整客户资料、访问令牌和支付信息。
12 个案例提供了分类思路,检查器还没守住全部边界
作者把测试包标为 2026 年 11 月 8 日设计的 12 个手工合成场景。它不是生产流量样本,也不是模型基准。十二这个数量,只是方便首次评审,不能证明覆盖率。
提供的正文没有列出完整矩阵,只解释了部分案例:案例 2 是业务拒绝,案例 5 是外部状态正确但遥测缺失,案例 7 是一致性问题,案例 10 要保留部分成功,案例 12 则区分「准备转交」和「转交已被接收」。
这些区分很有价值。同样一条「任务完成」,背后可能藏着不同的业务状态,验收不能只检查工具成功和 Agent 结束。
配套脚本使用 Python 标准库,逐行读取 JSONL。保存为 check_outcomes.py 后,运行 python check_outcomes.py outcomes.jsonl。下面这段负责检查成功措辞是否有提交状态支撑,代码保持原样:
state_ok = row["expected_state"] == row["observed_state"]
committed = row["commit_status"] == "committed"
success_claim = row["claim_class"] in SUCCESS_CLAIMS
if success_claim and not (state_ok and committed):
errors.append("success claim lacks matching committed state")
if row["execution_status"] == "fail" and success_claim:
errors.append("failed execution cannot carry a success claim")
if row["commit_status"] == "rejected" and state_ok and row["expected_state"] != row["observed_state"]:
errors.append("rejected operation unexpectedly matches target state")
if row["evidence_status"] != "complete" and success_claim:
errors.append("success claim requires complete evidence")
要看其中检查 rejected 的那一行:state_ok 已经表示两个状态相等,后面又要求它们不等,因此这个分支永远不会触发。
更直接的缺口是,脚本没有统一把未知判为失败。execution_status 为 unknown 的记录,只要没触发其他条件,仍可能输出 PASS;没有成功措辞的状态不匹配记录,也可能通过。这与正文宣称的「失败或未知都会返回非零退出码」并不完全一致。
SUCCESS_CLAIMS 仅列出 updated、booked、sent、approved、completed。脚本检查的是结构化 claim_class,并不理解自然语言回复。如果 Agent 实际说了「已经处理好了」,却没有被正确分类,检查器就看不到这项承诺。
我的判断
我认同这套方案最有用的部分:先定义业务完成条件,再决定需要哪些日志。它把「看起来顺利」变成了可以核对的断言。
但它仍是工程设计建议,尚无对照数据证明接入后能降低多少错误。NIST AI Risk Management Framework 提供的是自愿风险管理框架,也没有要求使用本文的字段或测试包。
落地成本取决于系统能否提供可靠回执。异步写入需要等待或对账机制;状态查询需要绑定同一笔操作,避免把旧结果误认为本次成功。示例脚本既不验证回执签名,也不连接数据库,它信任输入文件的来源。
相比给所有任务建设完整审计系统,我更赞成作者按风险分配控制深度的主张。低风险起草任务可以保留来源快照和人工审核;修改账户、发送消息、触发支付,则需要更强的动作边界证据。Trace、业务断言和人工审核各有用途,应该组合使用。
能拿来做什么
- 给前端业务助手梳理「处理中」「已提交」「已完成」的展示条件。异步接口返回
accepted时,界面和 Agent 回复都应保留待确认状态,直到拿到契约要求的提交证据。 - 在 Node 工具适配层分别输出业务操作 ID、目标实体、提交状态和结果版本,再让验收程序从权威系统读取观察值,避免只比较工具自己返回的成功描述。
- 从现有工作流挑出错误实体、业务拒绝、异步待定、重复尝试和部分成功等用例。期望值经过评审后保存,观察值由测试适配器采集,不要按 Agent 的回复手工补齐结果。
- 接入示例检查器前,先修正未知放行和不可达分支,再测试自由文本如何映射到
claim_class。对会修改外部记录的任务,缺少足够证据时应阻断完成判定,或明确回复「暂时无法确认」。