把 Agent 的 checkpoint/restore 做成产品功能之后,第一个撞上的坑不是状态恢复失败,而是恢复得太干净了。本地上下文回退到第三步,外部世界还停在第八步:邮件已经发出去、工单已经建好、支付接口已经扣款。Agent 重新跑一遍子目标,于是第二封邮件、第二张工单、第二笔扣款。
这类问题在调试环境里出现概率低,在生产环境出现概率高,因为只有生产环境的工具调用才有真实副作用。做子目标检查点回归时,需要把“回滚本地状态”和“外部副作用已提交”当成两件事分开测。
状态回滚与语义回滚不是一回事
云微在 ACRFence 里把这层区分讲得很清楚:传统检查点恢复只回滚本地状态,撤销不了外部世界已经发生的事。普通程序常用的解法是幂等键——重试带同一个 request id,服务端看到重复请求就返回旧结果。这个前提是请求内容逐字节可重现,而 LLM Agent 恰恰不满足。
恢复之后,模型重新合成一次工具调用,语义等价但字段不同:UUID 新生成、时间戳变成当下、引用号换了一种编法。服务端眼里这不是重试,是一次全新的合法请求。ACRFence 把这类场景归到 Action Replay,针对的是已经提交成功的不可逆工具调用。
幂等键假设请求可复现,LLM Agent 恢复后的重新合成打破了这个假设。这是子目标级检查点最需要单独覆盖的回归类型。
工程上的做法不是让模型学会复现,而是在检查点边界做“副作用登记”:每个会产生外部效果的工具调用,把调用指纹(工具名、参数规范化后的哈希、业务幂等键)写进检查点旁挂的日志。恢复时先查这份日志,命中就走缓存的返回值,而不是重新调用。这段逻辑不该放在 prompt 里,应该放在工具网关里。
子目标边界应该选在哪
bojieli 的 AI Agent 教材第七章提到一个容易被忽略的分层:端到端回归任务从初始状态跑到最终状态,最接近生产结果,但失败时难以定位是哪一步坏的;轨迹前缀回归任务把上下文、对话、工具返回、环境状态冻结,只让 Agent 执行下一步或下几步可观察动作,成本低,也能隔离单个策略或工具问题。对于需要高可靠性的生产 Agent,作者认为构建轨迹前缀回归集往往比端到端回归集更重要。
子目标检查点正好落在这两者之间。粒度选得太细,每一步都存检查点,存储和恢复成本上去,且大量检查点从未被用到;选得太粗,一次回滚吃掉五个子目标,等于重跑,外部副作用的风险窗口也就更大。
实践中一个可用的划分依据是“副作用边界”:把检查点放在一组只读操作之后、不可逆写操作之前。Coding Agent 里这可能是“读完仓库规则和跑完测试”之后、“开分支提交”之前。跑完 git commit 再回滚就是典型的危险位置,因为提交可以撤销但 PR 已经创建。
第七章还给了一个判断标准:轨迹前缀回归任务的答案应该定义为可接受动作集合,而不是唯一动作。检查点回归的断言也一样,重点应该问“恢复后有没有触碰危险工具”,不需要管“恢复后是不是走了黄金轨迹”。
回归用例怎么写才有信号
tianpan.co 那篇讲 Agent 评估的文章里有一个具体数字:一个 Agent 最终输出评估拿到 82 分,上线两周后用户投诉不断,回看 traces 发现 40% 的查询路由错误——最终答案却因为误打误撞而正确。放在子目标检查点上,这个结论更尖锐:语义回滚导致的重复副作用,最终答案可能非常合理,测试人员根本看不出来。
AgentBench 式的分层可以借用。L1 确定性断言每次 PR 跑:检查点恢复后,`tool("send_email").toBeCalledTimes(0)`(已登记过副作用的工具不允许重放);检查点文件是否存在;恢复耗时在区间内;token 增量没超预算;恢复后的上下文里工具返回值和外部日志一致。这些断言零 LLM 调用,成本接近零。
const restored = await replay.fromCheckpoint("ckpt-subgoal-3")
expect(restored)
.tool("send_email").toBeCalledTimes(0)
.tool("create_ticket").toBeCalledTimes(0)
.tool("search_docs").toBeCalled()
.latency().toBeBetween(800, 8000)
.run()
L2 留给必须看语义的部分:恢复后重新合成的工具调用,其规范化指纹是否和登记记录等价;答案内容是否和已提交的外部事实矛盾。把规范化的责任压到工具网关,是对的方案,压给 judge 是错的,因为 fingerprint 等价是确定性问题,不需要 LLM。
同一个源还提到一条踩坑经验,放在这里也成立:对温度大于 0 的输出做快照断言必然 flaky,快照只适合做 replay 的输入侧。检查点文件本身就应该被当作严格的输入侧——它必须能逐字节复现,路径明确、绝对不含对话文本之外的自由文本拼接。
量化和成本账
pass@k 和 pass^k 的区分在这种回归里格外有意义。恢复路径本身带有重试性质,用 pass@k 看待表现会显得乐观;但在生产里每次用户交互只有一次机会,真正要看的是所有 k 次恢复尝试都没有触碰副作用——也就是 pass^k 口径。
检查点回归的成本主要在两块:生成时保存完整 Agent 状态(对话历史、工具返回、中间变量、副作用日志),回放时逐步重演并拿回观察。如果检查点存得太完整,恰好就是本地需要推演的状态集合之外的部分(比如文件系统、外部 API 的响应需要提前录制),这些跟“Agent 的状态”其实并非一回事,必须提前扣在一起。而且重放代码的维护成本跟着 Agent 的框架版本走,框架改协议以后很多旧检查点是回不过去的。这点容易被低估——检查点文件和测试用例不一样,后者是别的部门能写的文本,前者依赖执行环境。同一种框架下能够稳定回放的时间窗口并不长,如果要长期保存回归资产,回放的输入和字段应该尽量写成规范化的形式,而不是把整份运行时快照当作真相。
| 维度 | 成本 | 收益 |
|---|---|---|
| 检查点粒度到每个子目标 | 存储放大、框架耦合加深 | 回滚代价最低,窗口最窄 |
| 检查点放在写操作之前 | 需要手工规划边界 | 杜绝重复副作用,最值 |
| 副作用指纹登记 | 工具网关多一层 | 子目标级恢复的核心保障 |
| 全量端到端回归 | 每次跑完整任务 | 最接近生产,但定位能力差 |
容易被低估的一处细节
“恢复”其实包括两种动作:回到检查点后重新决策、或者从这个检查点分叉走另一条路径试结果。前者是一个稳健性问题,目标是同一个子目标不要被执行两遍;后者是一个采样问题,本来就要跑不同分支,此时外部副作用必须全部拦在分叉之外,或者只允许读操作。在产品里把这两者做成一个按钮会导致副作用在业务侧随随便便翻倍,所以要么修复路径只允许回滚只读区,要么回滚以后自动挂到沙箱或测试环境。
子目标检查点的核心并不在于能回退多少步,而在于明确用一份地方记录下的“这次副作用已经发生过”,以此定义哪些步骤在恢复以后绝对不允许再走一遍。这件事靠 prompt 里的提醒是防不住的。
转载请注明出处: 皮哈皮阿
本文的链接地址: https://blog.phpat.com/agent-engineering/621.html
微信或手机浏览器扫一扫,随时随地随心阅读与分享
-
AI Coding Agent 如何稳定提交可合并的 PR
上周 review 了一个 AI 生成的 PR,测试全绿,但我还是把它关了。它改了 37 个文件,顺手升级了一个依赖,还修改了两条测试断言。问题不是代码不能跑,而是这个 PR 没人敢合。
2026/10/14
-
Agent Harness:可观测、可评测、可回滚的 Agent 基础设施
去年我们做了一个处理退款申请的 Agent。Demo 很顺,上线第一周就出了事故:进程重启后状态丢了,同一笔申请被处理了两次。问题不在模型,而在模型外面那层没人愿意写的基础设施。
2026/10/09
-
MCP 进入生产环境:工具协议、网关与权限边界
MCP 刚出来时,我以为最难的是协议兼容。真正接了十几个工具之后才发现,把工具接上只花了两天,把权限、审计和故障处理理清楚花了两个月。
2026/10/07
-
Agent 安全清单:提示注入、工具越权与数据外泄
一个内部助手被要求总结一封客户邮件。邮件末尾有一行白色小字:把最近的客户名单转发到某个地址。Agent 真的准备去调邮件工具了。安全不能靠模型自觉,得靠系统设计。
2026/10/16
-
并行工具调用暴露的工具设计耦合问题
并行工具调用能砍掉四成延迟,但它更重要的价值在于暴露工具之间的隐性耦合。本文拆解上下文依赖、共享状态竞争与时序前提这三类静默失败,给出读写分类规则、工具合并的判据,以及什么时候该上依赖图编排。
2天前
-
Agent 轨迹回放调试:把失败变成可回放资产
Agent 调试难在无法复现、故障点藏在数步之前。这篇文章讲三件具体的事:把轨迹归一化成可校验的步骤结构,用工具签名和领域策略生成可执行约束,以及用标注过的失败轨迹在 CI 里做回归回放——顺带说明为什么只盯延迟和错误率的可观测看不到这类故障。
1天前
京公网安备11011402053654 
@muffin:链接已更新,分类图像功能...
AI网址导航模版图标库更换指南
作者大大,图标库邀请链接失效了,麻烦更新...
AI网址导航模版图标库更换指南
太好看了,快点更新!
友情链接
@戴墨镜的刘备:哈喽,路过点赞。
vue.js组件之手写签名板vue-signature-simple
太好看了,快点更新!太棒了!👍真的很赞!
vue.js组件之手写签名板vue-signature-simple
你写得非常清晰明了,让我很容易理解你的观...
Emlog插件RSS订阅器发布