状态传导图
此时只保存 proposed / authorized 意图,尚未产生邮件副作用。
观察点 01
模型提案先变成一张可核验的调用单
Host 校验工具 schema、当前授权与预算;高风险动作的审批绑定 call ID、actor、收件人、附件摘要、state version 和有效期。真正 dispatch 前还要按最新状态重验。
- 调用绑定完整度参数 + 版本 + 有效期
- 副作用执行进度尚未调用工具
人类批准的是这一次具体调用,不是给 Agent 一张永久通行证。
把同一封邮件想成 operation mail-42:Checkpoint 只保存任务进度;操作账本还要保存 actor、参数摘要、幂等键、attempt、结果状态和远端收据。切换场景,看系统怎样从“准备执行”走到“可证明完成”。
状态传导图
此时只保存 proposed / authorized 意图,尚未产生邮件副作用。
观察点 01
Host 校验工具 schema、当前授权与预算;高风险动作的审批绑定 call ID、actor、收件人、附件摘要、state version 和有效期。真正 dispatch 前还要按最新状态重验。
人类批准的是这一次具体调用,不是给 Agent 一张永久通行证。
状态传导图
幂等保留期也属于 API 合同;过期 key 不能假定仍被下游识别。
观察点 02
操作账本以 actor + operation ID 原子查找或新建:只有无记录的 new 能执行;已有终态返回原结果;pending、in-progress 或 unknown 必须等待 / 对账;同键不同 intent 报 conflict。
同键同意图只能证明“是同一操作”;只有已有终态时才有可返回的原结果。
状态传导图
橙色节点是分布式系统的关键不确定窗口。
观察点 03
mail-42 已保存 pending,邮件服务可能已经发送,但 worker 在保存收据前崩溃。恢复后将操作标成 unknown,用同一 operation ID 查询供应商,而不是把 pending 解释成 failed。
“没有本地 success”与“远端没有副作用”是两件事。
状态传导图
补偿并非时间倒流,可能失败,也不保证恢复到最初状态。
观察点 04
查询若确认邮件已发出,就保存原收据并推进状态;若下游明确确认未发生且支持同键幂等,才有界重试;仍无法判断就停。补偿是新的业务动作,也要单独记账。
Reconcile 读取事实,Retry 重做同一意图,Compensate 发起新意图;三者不能混用。
状态传导图
旧 token 可以被拒绝写状态,但已发出的外部副作用仍要独立收口。
观察点 05
子任务携带 task ID、version、lease generation、fencing token、deadline 和完成条件。租约过期不会杀死旧 worker;CAS 只接受当前 token,旧外部 attempt 则用稳定 operation ID 幂等或对账。
协议保证的是至多一个有效状态提交者,不是物理上只运行一个 worker。
你现在应该能解释:协议保证的是至多一个有效状态提交者,不是物理上只运行一个 worker。
Checkpoint(检查点)能找回执行进度,却不知道一次远端写操作是否已经发生。可靠性来自模型之外的协议:显式状态、操作收据、调用级审批、幂等与对账,以及对崩溃窗口的主动测试。
pending 只能推出“结果不明”,不能推出“发送失败”;盲目再发一次,才是重复副作用的来源。| 恢复进度 | 持久化工作流状态、下一节点、输入输出与代码 / schema 版本。 |
| 恢复事实 | 持久化操作意图、attempt、状态、远端资源 ID、原始返回和收据。 |
| 恢复权限 | 审批绑定具体调用;等待后恢复时重新校验当前授权、参数与业务状态。 |
| 恢复信心 | 用故障注入验证“不会重复做、不会假装完成、无法判断时会停”。 |
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 轨迹与评测 | 状态转换、可见输入输出、延迟、成本、错误与终态 | 系统为何走到这里,结果是否真的满足验收条件? |
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、下游幂等或对账处理。
轨迹只能证明系统可见的消息、工具调用、结果与状态转换。它适合审计和复盘,但不能把不可见的内部推理当作事实,也不能替代环境终态验收。
调用外部 API(Application Programming Interface,应用程序接口)时,要按副作用证据分类,而不是只按异常类名分类。尤其是 timeout、连接中断和 worker 崩溃:客户端没收到响应,不代表服务端没执行。
| 已知事实 | 典型场景 | 安全动作 |
|---|---|---|
| 尚未发出 | 本地 schema 校验、授权或预算门拒绝 | 修参数、申请权限或停止;不要把拒绝包装成“工具失败”。 |
| 已知失败 | 下游明确承诺未产生副作用,并返回 validation / auth 等终态错误 | 同样输入通常不应重试;修正意图后创建新操作。 |
| 结果未知 | timeout、连接断开,或远端成功后 worker 在回报前崩溃 | 按 operation ID 查询 / 对账;只有下游支持同键幂等时,才可同键有界重试。 |
| 已知成功 | 拿到可验证收据或查询到本次操作创建的资源 | 补写本地收据并推进状态;不要因本地仍是 pending 再做一次。 |
mail-42 = pending;② 邮件服务已发出;③ worker 在保存 success 前崩溃;④ 恢复后读到 pending。如果服务按 mail-42 提供幂等语义,重复请求应返回语义等价的原结果;如果没有这种契约,就查 sent items / 供应商回执,仍无法确认则转人工,不能把“没记到账”当成“没发生”。再次请求同一意图。只适合明确的瞬时失败,或具有稳定幂等键的调用;必须有最大次数、总 deadline、指数退避与 jitter(随机抖动)。
查询外部真实状态,而不是再做一次。查询要能把资源与 actor、operation ID 和原意图关联起来;“查不到”是否等于“没发生”也取决于下游一致性与保留期。
发起一个新的业务反向动作,例如退款或撤销预订。它不是数据库回滚,可能无法恢复原状态、执行顺序不一定完全相反,而且补偿本身也会失败。
Durable execution(持久执行)框架可以保存历史、恢复节点并管理重试,但它无法替第三方系统发明不存在的幂等语义。可靠设计要把每个写工具的调用、恢复和升级契约写清楚。
| 机制 | 必须定义的合同 | 常见误区 |
|---|---|---|
| Checkpoint | thread / 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 仍能无条件恢复。” |
RunState 后恢复;文档还建议为长时间 pending 的任务保存 agent / SDK 版本标记。业务系统仍应额外保存参数摘要与资源版本,并在真正执行前重新校验。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 的迟到结果不得推进状态或触发后续副作用,但无法撤回已经发出的外部动作。 |
正常任务成功率回答“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%;真实故障若相关,简单独立假设还会过于乐观。
| 材料 | 可以支持 | 不能外推 |
|---|---|---|
| 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 不是崩溃恢复、重复副作用或生产可用性的证明,也不是当前排行榜。 |
| 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 · 评测 | 是否在副作用前后、落盘前后、审批等待和并发领取处做过故障注入? |
| 来源 | 本页采用的边界 |
|---|---|
| AWS · Making retries safe with idempotent APIs | caller token、语义等价响应、晚到请求、同键不同意图与保留期。 |
| AWS · Timeouts, retries and backoff with jitter | timeout 不代表无副作用;重试会放大负载,需退避、jitter 与预算。 |
| AWS · Leader election in distributed systems | 租约持有者可因 GC 暂停、网络与时钟问题落后;租约本身不能阻止过期工作继续影响系统。 |
| AWS · Transactional outbox pattern | 本地数据库 + 事件发布的双写边界,以及重复消息仍需幂等消费者。 |
| Azure · Compensating Transaction pattern | 补偿是业务特定的新动作,可能失败,不一定恢复原状态或按严格逆序执行。 |
| LangGraph · Checkpointers | checkpoint、thread、pending writes、fault tolerance 与 replay 重执行语义。 |
| LangGraph · Interrupts | 恢复从节点开头重跑,interrupt 前的副作用应幂等或拆到独立 task。 |
| Temporal · Workflow Definition | Workflow 确定性、历史重放、外部调用进入 Activity 与代码版本约束。 |
| Temporal · Activity Idempotency | Activity 可能实际执行多次,幂等键由被调用服务兑现。 |
| OpenAI Agents SDK · Human-in-the-loop | 调用级批准、RunState 序列化 / 恢复与 pending 任务版本标记。 |
| Anthropic · Building effective agents | workflow / agent 区分、简单可组合模式与复杂度边界。 |
| Anthropic · Demystifying evals for AI agents | task / trial / trajectory / outcome、多个 trial、pass@k 与 pass^k。 |
| Anthropic · Effective harnesses for long-running agents | 跨 context session 用进度文件、Git 与结构化交接留下连续性证据。 |
| SWE-agent · NeurIPS 2024 | 只引用论文摘要中的原始历史 benchmark,并明确不是可靠性指标。 |