冲突、同步与失败恢复

本节目标

理解并发协作的主要故障:重复、乱序、冲突写、局部失败和失联;用资源所有权、版本、检查点与有限重试得到可解释结果。

先消除不必要的共享写

最可靠的冲突处理是避免冲突:

  • 为每个文件、记录或任务指定唯一写入所有者。
  • 并行专家只产出不可变建议,汇总者统一写最终产物。
  • 临时结果按 task_id 分区,不共用“latest.json”。
  • 输入做快照或记录版本,避免同一轮读取不同事实。

如果多个 Agent 必须写同一对象,可选:

  1. 乐观并发控制:读取版本 v,提交时要求当前仍为 v;冲突则重新读取和合并。
  2. 锁/租约:在有限时间内独占资源。租约必须过期,持有者还需心跳,避免进程崩溃后永久锁死。
  3. 串行化队列:所有变更经过一个写入者,牺牲吞吐换取简单一致性。
  4. 领域合并规则:例如标签集合可取并集,但金额、权限和发布状态不能自动合并。

不要默认“最后写入获胜”。它会静默丢失更早但可能更正确的结果。

幂等重复与结果冲突要分开

幂等键并不是“忽略所有同 key 消息”的许可证。接收器应在 durable record 中保存 key、带算法/版本的 intent/result digest、验收状态和外部 receipt 引用:同 key、同 digest 才复用结果;同 key、不同 digest 是证据冲突,必须冻结任务、停止尚未开始的下游有副作用调度并转 needs_review。人工决定应绑定当前 state_version、两个 digest、receipt 指纹和一次性 decision ID;不是让某个 Agent 选“看起来更合理”的版本。

已经启动或已经提交的下游不能被一次状态迁移“撤回”。冲突发生后还要隔离其发布/继续写入,标记已发生的 receipt 与影响范围,并由有权限的人决定查询、补偿、撤销或向外披露;补偿本身也是新的、受审计和可能需要审批的动作。

这与 工作流的幂等记录 和 环境型 Agent 的 receipt 冲突 是同一类问题:名称不同,核心都是“外部事实不再唯一,自动化不得继续写”。

失败分类决定恢复方式

失败类别例子是否重试
输入错误缺少字段、格式不符否,退回上游
策略拒绝Agent 无写权限否,不得换角色绕过
临时故障连接中断、服务繁忙有限退避重试
永久故障资源不存在、能力不支持否,改计划或人工处理
质量失败输出结构正确但证据不足带反馈返工,次数受限
预算耗尽步数/时间/费用达到上限否,保存进度并停止

“所有异常重试三次”会重复不可逆副作用。只有确认操作幂等,或能用幂等键识别已提交结果时,才能安全重放。

检查点与恢复

检查点保存的是可恢复状态,不是整段内部思维:

  • 任务与依赖状态;
  • 已接受的产物引用和哈希;
  • 当前预算、尝试次数;
  • 已执行副作用的幂等键;
  • 等待中的审批;
  • 最近可验证事件位置。

恢复流程:加载 → 校验 schema/version → 验证外部副作用是否已发生 → 将过期 running 任务转为可判定状态 → 只调度未完成任务。检查点应原子写入,例如先写临时文件、同步后替换目标文件。

补偿而非“撤销一切”

分布式副作用往往不能真正回滚。发送邮件后删除本地记录不会收回邮件。应为动作设计补偿:取消预订、发送更正、恢复上一版本,并明确补偿也可能失败。高风险动作更适合执行前审批。

死锁、活锁与重复劳动

  • 死锁:A 等 B,B 等 A;通过 DAG 环检测、锁顺序和超时预防。
  • 活锁:Agent 不断互相退让/返工,状态变化但无进展;设置最大返工次数和进度度量。
  • 重复劳动:两个 Agent 不知彼此正在做同一任务;用任务租约和唯一所有者。
  • 脑裂:两个经理都认为自己是领导者;生产系统需使用可靠的领导选举或单一调度服务。

实践

为“两个审查者同时提交建议”设计:不可变审查报告、唯一合并者、草稿版本号、冲突后的处理。再模拟消费者收到同一 task_result 两次,验证最终状态只更新一次。

自测

  1. 何时租约优于永久锁?
  2. 为何 policy_denied 不能通过换 Agent 重试?
  3. 检查点显示 running,但进程已不存在,应直接标记 succeeded 吗?
  4. 哪些副作用必须在重试前查询外部提交状态?

下一步

继续 预算、停止与人工介入。

参考资料