KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
11. 进程重启后,Run 如何接着走? — keel 龙骨
如果执行状态只保存在 Python 变量里,进程在“外部提交成功、保存本地回执之前”崩溃,重启后就无法判断下一步。持久化执行的目标不是让进程永不失败,而是让失败后仍能根据历史恢复。
如果执行状态只保存在 Python 变量里,进程在“外部提交成功、保存本地回执之前”崩溃,重启后就无法判断下一步。持久化执行的目标不是让进程永不失败,而是让失败后仍能根据历史恢复。
事件历史是恢复依据
1 run_started
2 proposal_received
3 command_created
4 preview_created
5 approval_granted
6 attempt_started
7 outcome_unknown
8 reconciliation_started
9 action_succeeded
10 run_completed
每个事件包含 run_id、command_id、序号、时间、类型、因果 ID、主体和脱敏 payload。序号与唯一约束防止重复追加。
Workflow 和 Activity 分开
持久化工作流系统通常要求流程决策可重放,而把网络、时间、随机数和外部副作用放入 Activity:
Workflow: 根据已有事件决定下一状态
Activity: 调用配置服务、发送通知、查询状态
重放 Workflow 不应再次发送通知。Activity 的结果先写入历史,随后流程从历史读取结果。这是 Temporal 等 durable execution 系统的重要边界。
恢复时先检查事实,不猜下一步
flowchart TD
R[加载最后事件] --> S{最后稳定状态}
S -->|ready| Q[检查幂等记录后调度]
S -->|running 且 lease 有效| W[等待当前 worker]
S -->|running 且 lease 过期| C[查询外部状态]
S -->|unknown| C
S -->|pending| O[轮询 operation ID]
C --> N[对账后决定重试/完成/人工]
不能看到 attempt_started 没有 succeeded 就自动重试,因为崩溃点可能在外部提交之后。
Trace 要保留跨系统因果
至少关联:
trace_id -> run_id -> command_id -> attempt_id -> external_reference
关键指标包括成功率、unknown 比例、对账耗时、重复命中率、补偿率、人工接管率、P95/P99 延迟和每种 Adapter 的错误分布。
日志不能保存完整 prompt、凭据、cookie 或未脱敏的外部响应。可观测性本身也是数据泄露面。
生产映射
教学项目使用单进程事件列表和内存 Adapter 来展示状态语义。真实项目通常需要:
- 事务数据库保存 Command、幂等记录和事件;
- 工作流引擎或可靠队列调度 Activity;
- worker lease/fencing 和死信处理;
- 独立的审计存储与留存策略;
- 对账任务和人工操作台;
- 分环境、分租户的凭据和网络策略。
检查理解
- 为什么重放 Workflow 不能重放外部副作用?
attempt_started后进程崩溃,恢复时第一步是什么?- 为什么 trace 不能简单记录完整输入输出?