Agent 状态机实战:让任务可暂停、可恢复、可重放

本文由 AI 辅助生成并经结构化整理,代码为教学示例。

Agent 若只靠 while 循环推进,遇到重启、审批或超时后,很难知道任务走到哪一步。生产级实现应把执行过程建模为显式状态机,并持久化每次变化。

1. 状态模型

CREATED -> PLANNING -> RUNNING -> WAITING_APPROVAL-> FAILED              -> SUCCEEDED

状态机带来三项能力:可恢复、可审计、可控。模型可以建议下一步,但只有系统能批准状态转换。

2. 声明合法转换

from enum import StrEnum
class State(StrEnum):CREATED="created"; PLANNING="planning"; RUNNING="running"WAITING="waiting_approval"; SUCCEEDED="succeeded"; FAILED="failed"TRANSITIONS = {State.CREATED: {State.PLANNING, State.FAILED},State.PLANNING: {State.RUNNING, State.FAILED},State.RUNNING: {State.WAITING, State.SUCCEEDED, State.FAILED},State.WAITING: {State.RUNNING, State.FAILED},
}

未声明的转换直接拒绝,终态不再接受后继状态。

3. 用乐观锁避免并发推进

UPDATE agent_runs
SET state=:next, version=version+1
WHERE id=:id AND state=:current AND version=:version;

影响行数为零,说明其他 Worker 已抢先推进,当前执行器应重新读取而不是覆盖。

4. 检查点存什么

{"run_id":"run_123","state":"running","step":4,"tool_results":["call_7","call_8"],"pending_action":null,"version":9
}

大对象放对象存储,数据库保存引用与校验值。恢复时加载最后检查点,再判断未完成工具调用能否安全重试。

5. 副作用必须幂等

idempotency_key = f"{run_id}:{step_id}:{tool_name}"

执行前写调用记录,执行后保存结果。恢复时如果同一键已成功,直接复用结果,避免重复发信或重复扣款。

6. 审批是一种正常状态

进入 WAITING_APPROVAL 后应释放 Worker,保存动作预览、风险等级、确认令牌和过期时间。用户确认产生新事件,再由调度器重新入队。

7. 失败与重试分离

技术性失败可以指数退避重试;业务拒绝不重试。保存稳定错误码、尝试次数和下次执行时间,避免无限循环。

验收清单

  • 杀进程后任务能继续;
  • 两个 Worker 不会重复推进;
  • 重放事件不会重复产生副作用;
  • 非法转换会被拒绝并记录;
  • 审批等待不占计算资源;
  • 每次转换能关联触发事件。

结语

状态机把 Agent 的不确定推理装进确定执行骨架。系统必须知道这一步是否允许、是否执行过,以及失败后从哪里恢复。