任务状态机与可恢复执行
会话消息记录发生过什么,任务状态决定下一步允许做什么。生产 Agent 至少要区分待处理、执行中、等待工具、等待用户、已完成、失败和取消。状态转移必须有来源事件和版本号,避免两个 worker 同时推进同一任务,或把旧重试结果写回新状态。
第一个可运行实验:带版本的状态转移
保存为 state.py,用 Python 3.12 运行。
python
TRANSITIONS = {
'queued': {'running'},
'running': {'waiting_tool', 'waiting_user', 'completed', 'failed', 'cancelled'},
'waiting_tool': {'running', 'failed', 'cancelled'},
'waiting_user': {'running', 'cancelled'},
}
def transition(task, target, event, expected_version):
if task['version'] != expected_version:
raise RuntimeError('stale task version')
if target not in TRANSITIONS.get(task['status'], set()):
raise ValueError('invalid transition')
return {**task, 'status': target, 'version': task['version'] + 1,
'last_event': event}
task = {'status': 'queued', 'version': 0}
task = transition(task, 'running', 'worker_started', 0)
task = transition(task, 'waiting_tool', 'tool_requested', 1)
assert task['version'] == 2
try:
transition(task, 'completed', 'late_result', 1)
except RuntimeError:
print('versioned state passed')
else:
raise AssertionError('stale update accepted')状态版本是并发控制,不是模型记忆。事件日志应保存请求、工具、人工确认和取消,当前状态可以从日志重建或由事务更新。恢复时先检查最后一个未完成动作,再决定查询、补偿或重新执行;不能看到 waiting_tool 就盲目再发一次。
变体实验
尝试从 queued 直接转 completed,要求拒绝。再模拟两个 worker 使用同一版本,只有一个能成功提交,另一个必须重新读取状态。
练习
- 增加
waiting_approval状态,说明审批过期后的转移。 - 为每次转移记录 actor、时间、run ID 和原因。
- 设计服务重启后恢复一个“工具执行未知”任务的步骤。