多智能体系统最常见的故障不是模型答错,而是两个 agent 并发写同一份共享状态——文件、数据库行、API 令牌、记忆 blob——其中一个的更新被悄悄覆盖,全程不抛异常。本文以丢失更新为核心,实测 LangGraph Store、AG2/AutoGen、CrewAI 统一记忆、Letta 共享块四套生产栈在并发写下的默认一致性模型、冲突策略与可见性延迟,并拆解单写多读、append-only 事件源、CAS 乐观锁、CRDT 合并四类补救范式,附工具层事务化(幂等键 + CAS + 作用域隔离 + 补偿)的落地代码。
2026 年 4 月的行业统计显示,57% 的企业已在生产环境运行 AI agent,且越来越多团队让五个以上 agent 并发操作同一份代码库或共享记忆。这种"再加一个 agent 并行处理"的一行改动,本质上是把开发者推成了分布式系统工程师:read-modify-write 的六十岁经典竞态,在 agent 栈里原样重生。失败伪装得很好——输出语法合法、流水线绿灯、无异常抛出——但客户被重复扣款、文件缺了一半内容、agent 自信地依据一个已被覆盖的数字行动。问题从不在 prompt,而在两个 agent 读写共享资源的毫秒级间隙。下面用四套主流栈对照实测。
| 维度 | LangGraph Store | AG2 / AutoGen | CrewAI 统一记忆 | Letta 共享块 |
|---|---|---|---|---|
| 默认一致性 | 继承后端(Postgres 强 / Redis 最终) | 事件流到达序串行 | 全局共享 + 读时作用域过滤 | 单服务器内强一致(Postgres 行锁) |
| 并发写策略 | 无内置协调,需自加版本列 | append-only 事件日志 | 共享即默认,原地改 blob | 三档操作分级 |
| 冲突解决 | 后端决定,开发者补 CAS | 日志序天然串行 | 无锁,最后写覆盖 | insert 安全 / replace=CAS / rethink=LWW |
| 跨 agent 可见性 | eventual(Redis)/ strong(Postgres) | 订阅即见(强) | 即时共享 | 强(同库) |
| 缺省审计 | checkpointer 历史 | 事件日志天然全程审计 | 弱 | 写归属可查 |
| 丢失更新风险 | 中(须显式版本控制) | 低(append-only) | 高(共享 blob 原地写) | 可控(选对操作即可) |
诊断任何共享状态 bug 之前,必须先看清丢失更新的机械原理,因为补救方式完全取决于它。Agent A 读到任务记录"五个子任务完成三个",Agent B 几乎同时读到同一份"完成三个";A 完成自己的子任务,把计数写成 4;B 从未看见 A 的写入,也把计数写成 4。最终记录是"完成四个",一个子任务的完成事实无声蒸发。没有错误、没有日志、记录结构完好——只是它错了。
这就是教科书级的 read-modify-write 竞态,之所以反复出现,是因为 agent 框架把状态暴露为一个普通可变对象:字典、JSON blob、一行被 SELECT 后再 UPDATE 的记录。读和写是两段分离操作,中间留有间隙,任何其他 agent 都能挤进这个间隙。框架给了你共享内存的便利,却没有强加同步访问的义务。同一模式还衍生脏读(B 读到 A 改到一半的状态,对着一个永远不会成为终态的值行动)和重复执行(worker 的工具调用成功、确认丢失、编排器重试、非幂等操作跑两遍)。后两种的真实世界后果更难挽回:扣款重复、邮件发了两次、工单开了两张——没有任何回滚能完全撤销。
最安全的模式是把"写"垄断在监督者手里,所有 worker 只读。监督者持有权威状态——计划、开放问题清单、综合简报——在 worker 回报时更新。worker 把它作为输入 prompt 的一部分读,但绝不直接写;产出通过 handoff 载荷回流,由监督者决定提交什么。因为不存在并发写,所以不存在一致性问题。
代价是监督者既是瓶颈又是上下文膨胀源:每次写都过它的推理环、每次写都付一次模型调用决定如何提交、它的对话历史随看见每个 worker 的产出而线性膨胀。前文"协调税随规模飙升"提到的 15× token 税,部分就源于此——监督者为所有子任务的并集买单,因为它是唯一写者。适用范围很窄:专家数 ≤5、监督者每步决策质量比并行写吞吐更重要、审计故事必须干净的场景。一旦变成上百个子 agent 并行吐结论、全部要过单个监督者的上下文窗口,就是对监督者 prompt 的 fork bomb。
打破监督者垄断后,两个或更多 agent 同时拥有对同一类型化槽位的读写权,并发控制就变成强制项。Letta 的共享记忆块把这层生产化:一个块创建一次,通过 block_ids 挂到多个 agent,每个挂载 agent 用三种安全属性不同的操作读写。这套操作表的非对称性本身就是设计课:
block = client.blocks.create(name="open_questions", memory=[])
agent_a.add_block(block); agent_b.add_block(block) # 两 agent 共写同一块
block.memory_insert(value="顾客流失是否与提价相关") # append-only,多写安全
block.memory_replace(old="X", new="Y") # match-then-replace,CAS,旧值变则失败
block.memory_rethink(memory=[...]) # 整块重写,LWW,争用下不安全
memory_insert 是不知道是否会有争用时该伸手的那一档;memory_replace 就是 Postgres 的 compare-and-swap、Git 的非快进推送拒绝;memory_rethink 是无协调的整块重写。由此自然长出分层访问策略:大多数 agent 只允许 memory_insert,一个指定的"所有者"(常常是 sleep-time 整合 agent)独占 memory_replace/memory_rethink,且只在无其他 agent 写入时运行——这正是 Kafka 单写者压缩多写者日志的模型,特化到共享工作记忆块上。对比之下 CrewAI 走了相反的默认:记忆默认对 crew 内所有 agent 共享,靠分层作用域(/project/alpha、/agent/researcher)切出私有视图,共享是默认、私有是 opt-in;Letta 则是私有默认、共享 opt-in。两种都站得住,一致性问题的发作阶段不同。
LangGraph 的 Store API 是 store 级共享记忆:基于 Postgres、Redis、MongoDB 或内存实现,暴露一个命名空间化的键值层,任何线程(任何 agent、任何会话)都能 put/get/search。Store 位于每线程 checkpointer 之下——checkpointer 状态对单线程私有,Store 状态跨所有线程共享。语义刻意做得很薄:put((namespace,), key, value) 写一行,search 在配了 embedding 的命名空间做语义检索。
关键陷阱在于:Store 不强制一致性,它继承后端的一致性。Postgres 后端按需给可串行化事务;Redis 后端默认最后写胜;内存 dev store 是 racy 的。同一个 API 调用,换后端一致性语义全变。生产部署都得自己补齐缺失件:在 Store 之上加通知机制(Redis pub/sub、Postgres LISTEN/NOTIFY)、为乐观并发加版本列、加一张捕获每次写入的审计表。心智模型该是"Store 是一个带键值接口、却没有调度器的共享黑板"——任何 agent 都能读写,命名空间是唯一作用域机制,没有任何东西在某 key 变化时唤醒关心的 agent。这就是小 API 表面的代价:协调留给 agent 和它之上的 harness。
# 工具层包一层带版本的 compare-and-set,弥补 Store 不内置 CAS
def cas_update(store, ns, key, mutate):
while True:
cur = store.get(ns, key)
ver = cur.value.get("__v", 0) if cur else 0
nxt = {**mutate(cur.value if cur else {}), "__v": ver + 1}
if store.cas(ns, key, nxt, expect_ver=ver): # 版本挪了就拒绝、重读重算
return nxt
经多支团队验证更耐久的架构是角色分离加消息传递协调,三层层级:Planner 持续探索代码库、分解任务、向消息总线发布工作项,从不写源文件;Worker 订阅工作项、隔离执行、完成后推送变更,彼此从不直接协调;Judge 评估完成的工作、判定是否过验收、向 Planner 层发继续或重排队信号。核心不变量是"任意两个 worker 绝不同时碰同一文件"——Planner 在建任务时跟踪文件所有权,拒绝把重叠文件集派给并发 worker。
AG2(AutoGen 的继任者)2025 年底围绕这一模型重做了内核,发布异步优先的事件驱动执行引擎和可插拔编排策略;LangGraph 1.0 走了相关路线:Pregel/BSP 执行模型把每次状态更新都当事件,每步后做图级 checkpoint。值得注意的是乐观锁的反噬:Cursor 的扩展实验中,低争用下 CAS 干净有效,但逼近 20 个并发 agent 时,agent 变得趋避风险——知道激进的多文件编辑更可能撞版本冲突重试,于是主动漂向更小、更不争用的任务。重试成本的统计压力在扭曲 agent 行为本身。当冲突是工作负载的结构属性而非例外时,乐观锁就不再是主协调机制,文件所有权登记表才是。
结构上最稳、实现最重的是事件溯源。agent 不再读写共享可变状态,每次状态变更都是一个追加到日志的不可变事件;任何资源的当前态由重放事件日志(或从近期快照重放)派生。ESAA(Event Sourcing for Autonomous Agents,2026 年 2 月 arXiv 论文)把它形式化给 LLM 软件工程:agent 不直接写文件,而是向 append-only 事件库发射结构化意图(描述想做什么的已校验 JSON);确定性编排器校验意图、持久化事件、应用写文件的效果、维护当前代码库物化视图。
orchestrator.submit(Intention(
agent="coder-A", file="auth.py",
change=Diff(hunk="..."), idempotency_key="auth-fix-42"))
# 事件库的 append-only 语义天然串行化并发:两个 agent 同时提交,
# 编排器按到达序逐个处理,无丢失更新;失败从最近 checkpoint 重放
事件库的 append-only 语义天然串行化并发活动——两个 agent 可同时提交意图,编排器按到达序逐个处理,无丢失更新可能。日志的时间序还提供完整可审计性:代码库如何演化、哪个 agent 做了哪处改动、什么次序,全可重放。agent 任务中途失败时恢复直截了当:从最后有效 checkpoint 重放。LangGraph 的 get_state_history(thread_id) 和 update_state(config, values, checkpoint_id) 在框架层暴露类似能力,支持从任意历史态分支、跨长跑工作流的容错恢复。代价是运维开销显著——除了 agent,还要管事件库、投影层和物化视图。纯吞吐、恢复需求简单的场景,角色消息传递往往更合身。
完整的分布式共享记忆模型,是带调度器的多写多读类型化 store。调度器根据 board 当前态激活 agent。2025 年 Hearsay-II 复兴(Lu & Sasaki 论文及后续信息发现论文,在数据科学发现任务上相对基线提升 13–57%)是当代参考,适用于 agent 数真正大(10+ 专家)、谁先跑由数据决定且无法预测、agent 需对其他 agent 的写入做反应而非只对用户或监督者反应的场景。
形状是:带 region(hypotheses / evidence / open_questions / conclusions)的类型化 board、写日志(每次写带时间戳、归属、版本)、扫描 board 并派发 preconditions 命中当前态的下一个 agent 的调度器、以及每个 region 上的显式冲突解决规则——hypotheses 只追加、conclusions 单写者、open_questions 多写加 CRDT 集合并集语义。
board = Blackboard(regions={
"hypotheses": Policy(append_only=True),
"open_questions":Policy(merge="crdt_set_union"), # 任意两并发写可确定合并
"conclusions": Policy(single_writer="judge"),
})
scheduler.dispatch(board) # board 本身就是协调面,agent 看板决定做什么
黑板是为"没有任何单个监督者的 prompt 宽到能路由整任务"而建的。监督者模式的瓶颈(lead agent 的上下文窗口随任务复杂度线性增长)由"让板本身成为协调面"解决——agent 看板决定动作,监督者(若有)只观察。代价是分布式共享记忆里一切微妙处现在都得设计而非回避。冲突策略从 LWW(最简单,但任何含获胜者没有的信息的写都会丢,不安全)、乐观并发 CAS(写时捕获冲突强制失败者重试,代价是重试,收益是无静默丢数)到 CRDT 合并(定义确定性合并算子永不阻塞,但数据类型必须为 CRDT 设计——集合、计数器、Y.js 序列——自由文本不适用)逐档递进。
以上四类范式都只解文件级、任务级的共享状态。还有一个更安静却同样严重的一致性问题压在任何用共享向量库做多 agent 记忆/检索的系统之下:向量库的并发写不像关系库的并发写。多数生产向量库不是为高并发写设计的,多 agent 同时更新 embedding 时,索引重建通常非原子——重建可能把检索冻结几秒到几分钟,期间读库的 agent 看到的是陈旧数据。单 agent 负载下 50ms 可接受的查询延迟,会随并发写量跃升到 5 秒以上。生产团队的缓解套路收敛为:写批处理(指定单写进程累积 embedding 更新批量应用)、读写副本分离(读路由到最终一致副本、写到主)、版本标签命名空间隔离(每个任务执行独占向量命名空间,任务结束再合并回规范库)。2025 VLDB 论文里的 GaussDB-Vector 这类为多 agent 负载新生的库,在向量索引层实现 MVCC,是正确的长期方向,但尚未普及。
而修复的真正位置不是 agent——协调必须住在工具层,在模型之下,能被原子强制。让 agent 调用的工具表现得像一个小数据库而非一堆便利函数:
seen = {} # 幂等键存储
@tool
def charge_customer(account, amount, idempotency_key, read_version):
if idempotency_key in seen:
return seen[idempotency_key] # 重试 → 安全 no-op
row = db.execute("SELECT balance,version FROM acct WHERE id=?", account)
if row.version != read_version:
raise StaleVersion("re-read and retry") # CAS 失败要响亮
db.execute("UPDATE acct SET balance=balance-?,version=version+1 "
"WHERE id=? AND version=?", amount, account, read_version)
seen[idempotency_key] = receipt(account, amount)
return seen[idempotency_key]
四条铁律:每个变更型工具带幂等键(最便宜的保险,最常被跳过)、状态更新一律 CAS 拒绝盲写、agent 资源默认作用域隔离共享需显式论证、多步动作半路失败要有补偿(saga 模式)。多数"多 agent 并发 bug"其实是"共享了本不必共享的东西"。
flowchart TD
A[并发写共享状态] --> B{两个 agent 会碰<br/>同一资源吗?}
B -- 否 --> C[隔离: 各自 worktree/命名空间/令牌<br/>共享从默认变opt-in]
B -- 是 --> D{写冲突频率}
D -- 罕见: 元数据/任务队列 --> E[乐观锁 CAS<br/>版本列+compare-and-swap]
D -- 结构性: 同文件图 --> F{需要审计/可重放?}
F -- 否 --> G[消息传递+角色分离<br/>Planner持文件所有权登记表]
F -- 是 --> H[事件溯源 ESAA<br/>append-only意图日志]
A --> I{数据类型可CRDT化?<br/>集合/计数器/序列}
I -- 是 --> J[CRDT合并<br/>永不阻塞,自由文本不行]
I -- 否 --> K[单写多读监督者<br/>≤5专家,最安全但瓶颈]
C --> Z[在工具层补:<br/>幂等键+CAS+补偿saga]
E --> Z
G --> Z
| 场景 | 推荐栈 / 范式 | 关键动作 |
|---|---|---|
| <3 agent,文件偶有重叠 | LangGraph Store + 乐观锁 | 共享元数据加版本列 CAS |
| 3–10 agent,结构化工作流 | AG2/AutoGen 角色分离 + 消息传递 | Planner 维护文件所有权登记表 |
| 10+ 专家,数据驱动调度 | 黑板模型 + region 级冲突策略 | hypotheses 追加 / conclusions 单写 / open_questions CRDT |
| 需审计 / 合规 / 可重放 | 事件溯源 ESAA + LangGraph checkpointing | append-only 意图日志,失败从 checkpoint 重放 |
| 共享向量记忆 + 高写负载 | 写批处理 + 命名空间隔离 | 单写进程批写,任务结束合并回规范库 |
| 多写共享 blob,无 schema | Letta 共享块 | 默认 memory_insert,所有者独占 replace/rethink |
| 非幂等副作用工具(扣款/发信) | 工具层事务化 | 幂等键 + CAS + 补偿 saga,绝不在 prompt 里教 agent 避免竞态 |
选型的真正杠杆不在模型推理速度,而在协调开销:跨所有范式的共同失败模式,是低估 Planner/编排者维护"哪些资源在途"实时一致视图的角色——Planner 对所有权判断错了,下游任何协调机制都拦不住冲突。Deloitte 2026 年的分析也印证:决定多智能体能否在规模上稳定交付的是协调架构,而非模型能力。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
