跳到正文
动手理解 · CONCEPT LAB

给一次外部写操作补齐意图、收据与恢复协议

把同一封邮件想成 operation mail-42:Checkpoint 只保存任务进度;操作账本还要保存 actor、参数摘要、幂等键、attempt、结果状态和远端收据。切换场景,看系统怎样从“准备执行”走到“可证明完成”。

已经发生 正在观察 接下来
01 / 05

状态传导图

MODEL 模型提案
SCHEMA Schema 校验
AUTH 当前授权
HITL 调用级审批
RECHECK 执行前重验
READY 准备 Dispatch

此时只保存 proposed / authorized 意图,尚未产生邮件副作用。

观察点 01

模型提案先变成一张可核验的调用单

Host 校验工具 schema、当前授权与预算;高风险动作的审批绑定 call ID、actor、收件人、附件摘要、state version 和有效期。真正 dispatch 前还要按最新状态重验。

调用绑定完整度参数 + 版本 + 有效期
副作用执行进度尚未调用工具
此刻要记住

人类批准的是这一次具体调用,不是给 Agent 一张永久通行证。

Agent 工程 · Reliable Execution 可靠执行

Agent 可靠执行:可恢复,不等于可安全重放

Checkpoint(检查点)能找回执行进度,却不知道一次远端写操作是否已经发生。可靠性来自模型之外的协议:显式状态、操作收据、调用级审批、幂等与对账,以及对崩溃窗口的主动测试。

最危险的不是“报错”,而是结果未知:远端可能已成功,本地却没有收据。恢复流程必须先判断事实,再决定重试、补写、补偿或转人工。
00 · 先抓住

可靠系统必须回答两句话:刚才发生了什么?下一步怎样才安全?

生活类比
Checkpoint 像项目交接笔记,能说明“做到第几步”;工具收据像快递签收记录,能证明“包裹是否真的寄出”。只看交接笔记,无法判断一封邮件、一次扣款或一次删除是否已经发生。
合同邮件
Agent 已让邮件服务发送合同,却在保存成功状态前崩溃。重启后看到 pending 只能推出“结果不明”,不能推出“发送失败”;盲目再发一次,才是重复副作用的来源。
恢复进度持久化工作流状态、下一节点、输入输出与代码 / schema 版本。
恢复事实持久化操作意图、attempt、状态、远端资源 ID、原始返回和收据。
恢复权限审批绑定具体调用;等待后恢复时重新校验当前授权、参数与业务状态。
恢复信心用故障注入验证“不会重复做、不会假装完成、无法判断时会停”。
01 · 执行账本

先把四份记录分开,再谈 planner、executor 或 evaluator

Planner / executor / evaluator 是可选的组织方式,不是可靠性的最小架构。无论用单 Agent、固定 workflow 还是多 Agent,系统都要把工作流进度、外部操作、审批凭证与可观测轨迹分开保存;聊天摘要不能替代其中任何一份。

记录最少字段回答的问题
Workflow state
工作流状态
run_id、step、version、next、输入 / 输出引用任务走到哪一步,恢复后从哪里继续?
Operation ledger
操作账本
actor、operation ID、intent hash、attempt、status、receipt这次外部动作是否发出、成功、失败或仍未知?
Approval record
审批记录
call ID、审批人、工具与参数、state version、有效期、决定谁批准了哪一次具体动作,恢复后是否仍有效?
Trace / eval
轨迹与评测
状态转换、可见输入输出、延迟、成本、错误与终态系统为何走到这里,结果是否真的满足验收条件?
伪代码

副作用边界必须在模型之外

conceptual execution contract
proposal = model.propose(workflow_state)
call = gate.validate_and_authorize(proposal, actor, current_state)

op_id = stable_operation_id(run_id, step_id)
intent = canonical_hash(call.tool, call.arguments, actor)

approval.require_if_needed(call_id, actor, arguments, state_version, expiry)
gate.revalidate_before_dispatch(call, current_state)
op, created = ledger.create_or_get(op_id, intent, initial_status=PENDING)
                                                    # 原子查找;新建时已持久化意图

if op.intent != intent: raise IdempotencyConflict
if op.status in TERMINAL: return op.result        # 已有终态:返回原结果
if not created: return wait_or_reconcile(op_id, op.status)
                                                    # pending / in_progress / unknown 禁止再次 dispatch

try:
  receipt = tool.execute(call, idempotency_key=op_id)
except TimeoutOrConnectionLoss:
  ledger.mark_unknown(op_id)                     # 超时 ≠ 失败
  return reconcile_or_pause(op_id)

ledger.save_receipt(op_id, receipt)
workflow_state.compare_and_set(expected_version, reduce(receipt))

这是概念协议,不承诺全局 “exactly once(恰好一次)”。审批恢复后先重验当前状态,然后 create_or_get 原子创建 pending 记录;只有本次创建成功的调用方能进入 dispatch。已有终态返回存储的原结果,已有 pending / in_progress / unknown 则等待、查询或暂停,不能再发一次。若进程来不及捕获异常,恢复扫描也要把长期失联的 pending 当作 unknown。真正防重仍依赖下游幂等契约、可查询收据,或在无法判断时停止自动执行。

状态转换也要防并发

两个 worker 可能同时读取 version 7。租约过期不会杀死因 Garbage Collection(GC,垃圾回收)暂停或网络分区而落后的旧 worker;要给每次领取分配单调递增的 Fencing Token(隔离令牌),并用 Compare-and-Set(CAS,比较并写入)验证当前 token,使旧令牌无法提交状态。这保证的是“至多一个有效状态提交者”,不是物理上只有一个执行进程;外部调用的重叠还要靠稳定 operation ID、下游幂等或对账处理。

Trace 不是模型“真实思维”

轨迹只能证明系统可见的消息、工具调用、结果与状态转换。它适合审计和复盘,但不能把不可见的内部推理当作事实,也不能替代环境终态验收。

02 · 故障语义

一次工具调用有四种事实;“出错”不自动等于“没做”

调用外部 API(Application Programming Interface,应用程序接口)时,要按副作用证据分类,而不是只按异常类名分类。尤其是 timeout、连接中断和 worker 崩溃:客户端没收到响应,不代表服务端没执行。

已知事实典型场景安全动作
尚未发出本地 schema 校验、授权或预算门拒绝修参数、申请权限或停止;不要把拒绝包装成“工具失败”。
已知失败下游明确承诺未产生副作用,并返回 validation / auth 等终态错误同样输入通常不应重试;修正意图后创建新操作。
结果未知timeout、连接断开,或远端成功后 worker 在回报前崩溃按 operation ID 查询 / 对账;只有下游支持同键幂等时,才可同键有界重试。
已知成功拿到可验证收据或查询到本次操作创建的资源补写本地收据并推进状态;不要因本地仍是 pending 再做一次。
mail-42:手走一次崩溃窗口
① 本地保存操作 mail-42 = pending;② 邮件服务已发出;③ worker 在保存 success 前崩溃;④ 恢复后读到 pending。如果服务按 mail-42 提供幂等语义,重复请求应返回语义等价的原结果;如果没有这种契约,就查 sent items / 供应商回执,仍无法确认则转人工,不能把“没记到账”当成“没发生”。

Retry · 重试

再次请求同一意图。只适合明确的瞬时失败,或具有稳定幂等键的调用;必须有最大次数、总 deadline、指数退避与 jitter(随机抖动)。

Reconcile · 对账

查询外部真实状态,而不是再做一次。查询要能把资源与 actor、operation ID 和原意图关联起来;“查不到”是否等于“没发生”也取决于下游一致性与保留期。

Compensate · 补偿

发起一个新的业务反向动作,例如退款或撤销预订。它不是数据库回滚,可能无法恢复原状态、执行顺序不一定完全相反,而且补偿本身也会失败。

Outbox 的边界
Transactional Outbox(事务型发件箱)能把“本地数据库更新 + 待发布事件”放进同一事务,解决这一段 dual write(双写)问题;事件仍可能重复投递,消费者仍需幂等。它不会自动让任意邮件、支付或第三方 API 与本地数据库组成一个全球事务。
03 · 恢复协议

Checkpoint 保存进度;幂等、审批与版本协议保护副作用

Durable execution(持久执行)框架可以保存历史、恢复节点并管理重试,但它无法替第三方系统发明不存在的幂等语义。可靠设计要把每个写工具的调用、恢复和升级契约写清楚。

机制必须定义的合同常见误区
Checkpointthread / run、保存边界、resume / replay 会重跑哪些代码、状态 schema 与代码版本“有 checkpoint,所以后续 API 不会再调用。”
Idempotency
幂等性
actor + key + 规范化意图 / 参数;无记录才新执行,已有终态返回原结果,进行中 / 未知则等待或对账,同键不同参数报冲突;明确保留期“只生成一个 Universally Unique Identifier(UUID,通用唯一标识符),就天然恰好一次。”
HITL
Human-in-the-loop 人类在环
审批绑定 call ID、主体、资源、关键参数、state version 与有效期;执行前重验当前权限“用户曾说可以,所以以后相似动作都能做。”
Retry policy可重试错误、最大 attempt、总 deadline、指数退避、jitter、重试预算与熔断 / 降级“所有 5xx / timeout 都立刻再试。”
Code evolution长任务绑定模型、prompt、工具 schema 与 workflow 版本;老任务走兼容路径或显式迁移“线上改了节点顺序,旧 checkpoint 仍能无条件恢复。”
LangGraph 的边界
官方 checkpointer 文档说明:checkpointer 按 thread 在每个执行 step 保存快照;从旧 checkpoint replay 时,之后的 Large Language Model(LLM,大语言模型)调用、API 请求和 interrupt 会重新触发。interrupt 恢复还会从节点开头重跑,因此副作用要幂等或拆到独立节点 / task。
Temporal 的边界
Temporal 用历史重放确定性的 Workflow;外部 API、数据库和模型调用应放到 Activity。官方文档明确:Activity 在平台历史里可被观察为完成一次,但实际执行可能多次,甚至部分完成多次,所以写操作仍应幂等。
审批等待也是版本问题
OpenAI Agents SDK 的当前 HITL 文档把默认批准限定到具体 tool call ID,并支持序列化 RunState 后恢复;文档还建议为长时间 pending 的任务保存 agent / SDK 版本标记。业务系统仍应额外保存参数摘要与资源版本,并在真正执行前重新校验。
04 · 多 Agent

多 Agent 的可靠性,在单一有效提交者与结构化交接

Supervisor、worker、reviewer 只是拓扑。增加 Agent 会同时增加重复领取、过期上下文、部分完成、取消失效和权限扩散等故障面。一个子任务必须携带 owner 代次、单调递增的 fencing token、版本和完成证据,不能靠多个角色共享一段不断膨胀的聊天来“默契协作”。

交接字段为什么必须有
task_id / version识别同一个子任务,拒绝旧版本结果覆盖新计划。
owner / lease_generation / fencing_token只有当前 token 能通过 CAS 提交状态。租约过期不会停掉旧 worker;接管前要把旧外部 attempt 视为结果未知,用同一 operation ID 对账,或依赖下游幂等后再推进。
input / constraints传递必要上下文、权限与完成条件,而不是复制全部历史。
status / evidence / receipt区分成功、已知失败、结果未知与取消,并让主状态机独立验收。
cancel / superseded_by标记旧任务已取消或被替代;旧 token 的迟到结果不得推进状态或触发后续副作用,但无法撤回已经发出的外部动作。

适合并行 worker

  • 子任务可独立读取、交付物可结构化验收。
  • 多个探索分支没有共享写状态,或写入由主 owner 归约。
  • 失败、取消和超时不会让其他分支留下孤儿副作用。

先别拆成多 Agent

  • 多个角色要争写同一资源,却没有租约或版本检查。
  • 所有 worker 都重复读取全量上下文、调用同一高风险工具。
  • 所谓 reviewer 只评价文案,没有环境终态或独立证据。
租约不是强制停机开关
旧 worker 可能在租约过期后继续运行,甚至已经向外部系统发出请求。Fencing token 只能拒绝它之后的旧状态提交;已发出的副作用必须用稳定操作 ID、下游幂等或对账收口。
先简单,再增加自主性
Anthropic 的《Building effective agents》把 workflow 与 agent 区分开,并建议从简单、可组合的模式开始,只在效果证明值得时增加复杂度。Orchestrator-workers 适合子任务无法预先固定的场景,但它不是可靠性捷径;状态、权限和验收合同仍由宿主系统承担。
05 · 可靠性评测

不要只跑 Happy Path;在每个持久化缝隙主动“拔电源”

正常任务成功率回答“Agent 会不会做”;故障注入回答“系统坏一半时会不会重复做、假装成功或永远卡住”。评测要同时读取环境终态、操作账本与轨迹,并对非确定性任务运行多次 trial(试验)。

注入点验收断言
工具发出前崩溃恢复后最多产生一次真实副作用;旧审批若过期则不执行。
远端成功、收据落盘前崩溃进入 unknown 并先对账;不会因 pending 再创建第二个资源。
收据落盘、状态归约前崩溃恢复后用原收据推进状态,不再次调用写工具。
可重试的 429 / 5xx / 长延迟重试有退避、jitter、总预算和停止条件,不形成重试风暴;超时后的副作用仍按结果未知处理。
审批等待期间发布新版本旧任务走兼容版本或拒绝恢复;参数、权限与资源版本会重新校验。
两个 worker 同时领取旧 fencing token 的状态提交被 CAS 拒绝;接管方在再次调用外部工具前,先把旧 attempt 标为 unknown 并对账。无法确认且下游不支持同 operation ID 幂等时,必须暂停而非重复 dispatch。

至少看六类指标

环境终态成功率、重复副作用率、自动恢复率、恢复时长、人工介入率,以及 retry / token / 延迟放大。涉及扣款、删除、发布等动作时,重复副作用还应有单独的零容忍或极低错误预算。

单次通过会掩盖不稳定

Anthropic 的 Agent eval 指南区分 pass@k 与 pass^k。若每次成功率是 95%,并近似独立,连续 10 次都成功只有 0.95^10 ≈ 59.9%;真实故障若相关,简单独立假设还会过于乐观。

终态优先,轨迹辅助
Agent 输出“邮件已发送”不是成功证据;环境里存在唯一邮件、operation ledger 有可验证收据、收件人与附件符合批准参数,才构成终态验收。轨迹用于解释失败发生在哪一步,不能反过来替代结果。
06 · 案例与证据边界

四份公开材料分别证明什么,也不证明什么

材料可以支持不能外推
AWS 幂等 API同一 caller / token 的已完成重复请求可返回语义等价结果;同 token 但参数改变应报 mismatch,并要定义 token 保留期。不是“客户端带了 UUID,任意第三方操作就恰好一次”。原子去重必须由被调用服务兑现;进行中 / 未知状态也不能伪装成已有结果。
LangGraph Persistence按 thread 保存 checkpoint、支持 pending writes、interrupt / resume 与 replay / fork。旧 checkpoint 后的 API / LLM 不会重跑,或外部副作用会自动防重。
Temporal Workflow / Activity确定性 Workflow 可由历史重放;Activity 适合封装外部、可失败操作并自动重试。Activity 实际只执行一次。官方明确说明 worker 回报前崩溃时可能再次执行。
SWE-agent 2024论文摘要在其 GPT-4 Turbo、提示、工具和环境设置下报告 SWE-bench pass@1 12.5%、HumanEvalFix 87.7%,说明 Agent-Computer Interface(ACI,Agent—计算机接口)会显著影响能力。这些历史 capability benchmark 不是崩溃恢复、重复副作用或生产可用性的证明,也不是当前排行榜。
07 · 工程清单

上线前,把每个写工具过一遍这 10 个问题

1 · 意图operation ID 是否稳定?是否另存 actor、工具、规范化参数与 intent hash?
2 · 幂等下游如何处理无记录、已有终态、进行中 / 未知、同键不同参数、并发重复和 token 过期?
3 · 未知结果timeout / 崩溃后,按什么 ID 查询真实状态;查不到时是否会停并转人工?
4 · 审批审批是否绑定 call ID、actor、资源、关键参数、state version 与有效期?
5 · 权限模型输出是否只是一份提案;执行前是否由服务端按最新状态重验授权?
6 · 重试哪些错误可重试;最大 attempt、deadline、退避、jitter 和重试预算是什么?
7 · 状态checkpoint、操作账本、审批记录与 trace 是否分开;状态推进是否用 CAS / 事务?
8 · 升级长任务绑定了哪版模型、prompt、工具 schema 和 workflow;老任务怎样迁移?
9 · 多 Agent每次领取是否有递增 fencing token,旧 token 能否被 CAS 拒绝;旧 worker 已发出的外部 attempt 怎样对账?
10 · 评测是否在副作用前后、落盘前后、审批等待和并发领取处做过故障注入?

一手来源与核查口径(截至 2026-07-14)

来源本页采用的边界
AWS · Making retries safe with idempotent APIscaller token、语义等价响应、晚到请求、同键不同意图与保留期。
AWS · Timeouts, retries and backoff with jittertimeout 不代表无副作用;重试会放大负载,需退避、jitter 与预算。
AWS · Leader election in distributed systems租约持有者可因 GC 暂停、网络与时钟问题落后;租约本身不能阻止过期工作继续影响系统。
AWS · Transactional outbox pattern本地数据库 + 事件发布的双写边界,以及重复消息仍需幂等消费者。
Azure · Compensating Transaction pattern补偿是业务特定的新动作,可能失败,不一定恢复原状态或按严格逆序执行。
LangGraph · Checkpointerscheckpoint、thread、pending writes、fault tolerance 与 replay 重执行语义。
LangGraph · Interrupts恢复从节点开头重跑,interrupt 前的副作用应幂等或拆到独立 task。
Temporal · Workflow DefinitionWorkflow 确定性、历史重放、外部调用进入 Activity 与代码版本约束。
Temporal · Activity IdempotencyActivity 可能实际执行多次,幂等键由被调用服务兑现。
OpenAI Agents SDK · Human-in-the-loop调用级批准、RunState 序列化 / 恢复与 pending 任务版本标记。
Anthropic · Building effective agentsworkflow / agent 区分、简单可组合模式与复杂度边界。
Anthropic · Demystifying evals for AI agentstask / trial / trajectory / outcome、多个 trial、pass@k 与 pass^k。
Anthropic · Effective harnesses for long-running agents跨 context session 用进度文件、Git 与结构化交接留下连续性证据。
SWE-agent · NeurIPS 2024只引用论文摘要中的原始历史 benchmark,并明确不是可靠性指标。