🧠 核心问题

当一个 Agent 系统中有多个执行单元(主模型、子 Agent、Skill、Cron 任务)可能同时操作同一份数据时,如何避免数据冲突?这个问题在数据库领域叫"并发控制",在 Agent 编排中同样存在,只是形式不同。

Claude Code 用了一个巧妙的互斥机制来解决记忆写入冲突:主模型自己写了记忆,后台提取进程就自动跳过。这个看似简单的约束,实则解决了一个核心问题 —— 同一条信息被两个执行单元各写一遍,措辞不同、粒度不同,导致后续检索时信号矛盾。没记忆比有矛盾记忆更好。

核心洞察:Claude Code 解决的是"同一份记忆写两遍"的问题,而在 Agent Skill 编排中,问题更严重 ——"同一份数据被两个 Skill 同时改",后者直接导致数据不一致,甚至业务逻辑错误。

⚔️ 三类互斥场景

场景一:读写互斥(Lost Update)

这是最基础也最常见的场景。Skill A 调用 get_xxx 拿到数据,正在组装入参准备提交;与此同时 Skill B 也对同一条记录做了修改并已提交。A 用旧数据调用 update_xxx,直接覆盖了 B 的改动。

这和数据库的 Lost Update 问题完全同构。实际案例:用户在用 Skill A 修改个人信息附件的同时,Skill B(如自助服务)顺手改了另一个字段,两个 Skill 各自 get → 修改 → update,后提交的那个覆盖前一个的全部变更。

场景二:编排锁(多 Skill 串行化)

当多个 Skill 可能操作同一资源时,需要某种形式的串行化。目前的 OpenClaw 编排缺少这个机制 —— Skill 之间是并行的,互不知道对方的存在。

可借鉴 Claude Code 的游标思路:给关键数据加一个"处理中标记"。Skill 操作前标记"我在改",改完清掉。另一个 Skill 看到标记就等或跳过。轻量但有效。

场景三:主模型 vs 子 Agent 互斥

这是最容易被忽略、但破坏力最大的场景。如果主会话正在操作某条数据,Cron 触发的 isolated session 也在操作同一条记录,两者完全互不感知。这和 Claude Code 的主模型 vs 后台子 Agent 冲突是同一个问题,但在 OpenClaw 中更严重 —— 因为 isolated session 可能执行任何操作,不只是写记忆。

危险场景示例:主会话正在执行请假提交(get 余额 → 组装 → update),此时 Cron 任务触发了一个余额查询 Skill,两者操作同一条假期记录。如果 Cron 的 Skill 在主会话 update 之前 get 了余额,它拿到的是旧数据,但用户看到的却是"最新余额"。

🛠️ 可落地的设计模式

模式一:互斥跳过(Claude Code 原生模式)

核心思路:先到先得,后来者跳过。主模型写了记忆,后台提取就跳过。适用于"写操作是幂等的、跳过不影响正确性"的场景。

  • 适用:记忆写入、缓存更新、日志记录等幂等操作
  • 不适用:业务数据修改(跳过 = 丢变更)

模式二:乐观锁(CAS)

核心思路:get 时记录版本号,update 时检查版本号是否变化。如果变了,说明有别人改过,重新 get 再提交。这和数据库的乐观锁完全一样。

在 Agent 编排中,可以在 Skill 的 get_xxx 返回值里包含一个 version 或 last_modified 字段,update_xxx 时带上这个字段做校验。后端已有支持的话,只需 Skill 编排加上这个检查逻辑。

模式三:编排级锁(轻量互斥标记)

核心思路:在编排层面加一个"资源锁"概念。每个 Skill 声明自己要操作的资源 ID(如 employee_id、record_id),编排引擎在执行前检查是否有其他 Skill 正在操作同一资源。

  • 实现方式:Session 级别的内存锁,或文件锁(如 flock),或 Redis 锁(分布式场景)
  • 粒度选择:太粗(锁整个 Skill)→ 并发度低;太细(锁每个字段)→ 复杂度高。推荐锁到"资源 ID"级别

模式四:合并机制(最后写入者合并)

借鉴 Claude Code 的合并机制:如果上一次操作还在运行,新的操作请求被暂存,等当前操作完成后,再用最新上下文执行。适用于"操作之间有关联、需要合并处理"的场景。

典型场景:用户连续发了三条修改指令(改名字、改手机、改地址),如果每个指令都触发一个 Skill 执行,三次 get → 三次 update,最后一次覆盖前两次。合并机制可以把三条指令合并为一次 get → 一次 update(三个字段一起改)。


📊 模式选择决策树

操作是幂等的? → 是 → 互斥跳过(最简单) 操作是幂等的? → 否 → 多个 Skill 可能同时操作同一资源? → 是 → 编排级锁 + 乐观锁双重保障 → 否 → 用户可能连续发指令? → 是 → 合并机制(批量处理) → 否 → 乐观锁(CAS)兜底即可


🏗️ 对 OpenClaw 编排的实操建议

  1. 短期(立即可做):在 Skill 编排的 update 前加"最后时刻 re-get"铁律。已经在员工自助 Skill 中验证有效 —— 附件上传、字典查询等中间操作做完后,update 前必须重新 get 最新数据。这不是互斥,但能大幅降低 Lost Update 概率。
  2. 中期:引入资源级别的轻量锁。Session 维护一个 {resource_id: skill_name} 的锁表,Skill 操作前检查,冲突时等待或提示用户。
  3. 长期:如果 Skill 数量和并发场景继续增长,考虑引入编排级的 CAS 机制 —— get 返回 version,update 带 version 校验,失败自动重试。这需要后端 API 的配合。
总结:互斥机制的本质是在"自动化效率"和"数据一致性"之间找平衡。Claude Code 选择了最轻量的方案(互斥跳过),因为它的场景是幂等的记忆写入。Agent Skill 编排面对的是非幂等的业务数据修改,需要更严格的方案(乐观锁 + 编排锁)。但核心哲学一致 —— 让并发操作有序化,而不是指望它不发生。

逍遥云初 | 2026.04.17