📑 本页目录(点开跳转)
12b · 人批了、进程断了:动作到底做没做
⏱ 按故障实践 | 将 Harness 的状态变成可恢复记录
🎯 一句话
超时代表结果未知,不能直接等同于执行失败。 创建工单、退款、发消息这样的动作,恢复前先确认外部状态。
🔗 先读Harness。共同项目的 workflow.py 把草稿、独立审批、执行与对账放进可检查的状态机;测试实际注入提交前后故障。环境见17b。
🔬 先跑一次“工单已创建,但响应丢了”
python examples/knowledge-assistant/lab.py demo
python -m unittest discover -s examples/knowledge-assistant -p test_core.py -v
观察 demo 的恢复结果与工单数量。它先写入模拟外部系统,再故意抛出响应丢失异常;重试按同一业务操作号查询已有工单,最终只有一张。模拟外部系统是一张使用独立事务的表,方便复现“业务副作用与本地确认不能原子提交”的间隙;它不等于已联调真实工单 SaaS。
一、审批绑定的是哪一个动作
| 审批记录 | 为什么绑定 |
|---|---|
| tenant、请求人、业务对象 | 防止另一用户拿着 run_id 执行 |
| 动作类型与规范化参数指纹 | 防止批准 50 元后改成 5000 元 |
| 审批人身份与 scope | 模型不能自己返回一个 approved 就获权 |
| 到期时间、状态 | 旧审批不能无限期重复使用 |
| 稳定业务操作号 | 网络重试仍代表同一笔业务动作 |
金额用整数分表示,拒绝 bool、NaN、无穷大及非法范围。订单归属、已支付金额、已退款余额从业务库读取,不能相信模型参数。这个原则同样适用于草稿工单的归属与内容。
二、幂等不是一个 try/except
本地先持久化执行意图,调用方使用稳定操作号;模拟下游以唯一约束保证同操作只创建一条记录。相同操作号与相同内容可以回放,不同内容必须冲突。测试同时发起 12 次相同执行,检查只有一张工单。
| 下游能力 | 恢复策略 | 不能声称的保证 |
|---|---|---|
| 支持幂等键与结果查询 | 同键重发或先查询 | 幂等键超过保存期后仍永远去重 |
| 只支持业务号查询 | 先查,未知转对账或人工 | 查询暂未可见就一定没执行 |
| 既不支持幂等也无法查询 | 限制自动重试,人工核对 | 仅靠本地锁就能端到端 exactly-once |
跨系统无法用本地 SQLite 事务包住真实网络副作用。生产可用 outbox 可靠投递意图,用 inbox/下游唯一键去重,再靠状态查询收敛;这仍要求明确下游语义。网络断开、进程退出和超时都是“未知”的来源。
三、故障注入怎样算验收
| 注入点 | 必须观察的结果 | 本项目状态 |
|---|---|---|
| 未批准直接执行 | 拒绝,工单数为零 | 已有自动测试 |
| 模型冒充审批者 | 缺审批 scope,拒绝 | 已有自动测试 |
| 审批后修改参数 | 指纹不符,拒绝 | 已有自动测试 |
| 批准过期且未执行 | 不再创建工单 | 已有自动测试 |
| 写执行意图后、调用前失败 | 可用原操作号恢复 | 已有自动测试 |
| 下游创建后响应丢失 | 查出已有工单,无重复 | 已有自动测试 |
| 响应丢失后审批过期 | 可对账已有结果,不能发起新动作 | 已有自动测试 |
| 执行前用户取消 | 后续调用拒绝 | 已有自动测试 |
| 执行中用户取消 | 返回需对账,不能承诺撤销 | 代码返回明确状态;真实下游补撤销协议 |
网络错误只对允许重试的动作采用退避与抖动,遵守 Retry-After 和总预算。权限错误、参数错误、审批失效不应自动重试。对长任务增加租约、心跳和所有权 fencing,避免旧 worker 恢复后与新 worker 同时写;当前单机示例未实现分布式调度。
四、恢复状态不等于恢复授权
恢复时重新读取可信用户身份和业务权限。历史 checkpoint 里保存过角色或 token,不意味着它现在仍有效。不要把原始 bearer token 写入持久化状态;必要凭证从秘密管理系统重新获取,日志只记安全的凭证标识。
审计最少记录 run_id、操作号、状态前后、请求人、审批人、内容指纹、时间、错误类型和下游对象号。记录事实,不记录模型“我已经完成”的自述。按数据保留规则清理正文;删除业务内容与保留必要审计要分别设计。
🛑 现在可以停:能用故障注入解释动作发生了几次,再去接框架的恢复 API。
✅ 检查点
- 超时后为什么不能换一个操作号直接重试?
- 为什么 checkpoint 中有 approved=true 仍不能执行?
- 审批已经过期,是否应该拒绝查询丢失的执行结果?
展开答案
- 下游可能已经成功;新号会被视为另一笔动作,产生重复副作用。
- 还需要当前身份、资源权限、动作指纹和有效的服务端审批记录。
- 不应拒绝对账已有动作;但未发生的动作必须重新满足审批条件。
审批绑定内容与身份;操作号跨重试保持稳定;未知先对账;checkpoint 不能代替当前授权。
🔗 接下来去哪
➡️ 下一站:16d · LangGraph 实作与运行边界。