#AI#AI Agent

多 Agent 间的通信设计

多个 Agent 协作,最难的不是各自能力,是它们之间怎么通信——共享状态还是消息传递、用什么协议、怎么处理异步和错误。本文讲清多 Agent 通信的几种模式(共享黑板/消息传递/handoff)、序列化与契约、常见架构(supervisor/pub-sub/对等),以及工程上的序列化/版本/容错。

本文要点

  • 主题聚焦:AI、AI Agent
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2025年11月3日·更新于 2026年7月23日

单个 Agent 写得好,顶多算一个能干的员工。多个 Agent 凑在一起,能不能干成事,看的不是谁更聪明,而是它们怎么对话。

这事反直觉。大多数人搭多 Agent 系统,90% 精力花在"让每个 Agent 更强"——调 prompt、加工具、换模型。等真跑起来才发现:Agent A 辛苦查的结果,Agent B 没看懂;Agent C 卡在一个慢 tool 上,整条链路跟着挂;某个 Agent 改了 state 一个字段,下游全崩。根因都是"它们之间没对齐怎么说话"。

通信才是多 Agent 系统的真正骨架。前面那篇讲 LangGraph 编排,说的是"谁调度谁";这篇讲"它们之间那条线到底是什么"——通信范式、契约、架构、工程上的坑。

一、通信到底难在哪

多 Agent 通信看似就是"传个消息",但它在四个维度上比单 Agent 难一个数量级。

异步。 单 Agent 是个同步循环:想一步、做一步。多 Agent 不行——你调的某个 Agent 可能要 30 秒才回,你不能干等。异步带出一堆问题:消息乱序、超时怎么定、部分失败怎么处理。用同步 RPC 的心智模型去搭多 Agent,跑 demo 没问题,上量就死。

状态同步。 多个 Agent 同时读写一份共享数据,经典并发难题全回来了:竞态、脏读、丢失更新。Agent A 刚把 status 设成 reviewing,Agent B 在同一个 tick 读到的还是 drafting,基于旧状态做了一堆决策。

错误传播。 单 Agent 出错,影响域就是它自己。多 Agent 里,上游一个 Agent 输出格式错的 JSON,下游解析崩,再下游基于空数据继续跑——错误像滚雪球,等你看到症状时,根因已经离现场八丈远。

契约。 这是最易被忽视、却最致命的一条。两个 Agent 间传的"消息",本质是两个独立程序的接口。没有 schema、没有版本、没有向后兼容承诺,改一个 Agent 的 prompt 就能悄悄改变它的输出结构,把下游全弄崩——而且只在生产环境触发。

把这四点想清楚再动手,比急着选框架重要十倍。

二、三种通信模式

多 Agent 之间怎么交换信息,本质上就三种范式。选错范式,后面全是别扭。

模式一:共享黑板(blackboard)

所有 Agent 读写同一份共享 state,彼此不直接通信,通过修改这块"黑板"间接协作——一个 Agent 写上去,其他 Agent 在下一轮读到。

text
         ┌─────────────────────┐
         │   共享 State(黑板)  │
         │  { task, drafts,    │
         │    messages, ... }  │
         └──┬───┬───┬───┬──────┘
            │   │   │   │
       ┌────┘   │   │   └────┐
       ▼        ▼   ▼        ▼
    [检索Agent][写作][审校][发布]

LangGraph 的 StateGraph 是这个范式的代表:state 是图里所有节点共享的内存,每个节点接收 state、返回增量更新。

优点: 简单直观,状态集中、易观测(看一眼黑板就知道全局进展)。缺点: state schema 是上帝对象,字段越加越多,Agent 通过"隐式读字段"耦合,改一个字段名能炸一片。适合任务边界清晰、Agent 数量可控(通常 < 6 个)、状态稳定的场景。

模式二:消息传递(message passing)

Agent 之间不共享状态,只互相发消息。每个 Agent 是个独立封装体,有自己的私有状态,对外只通过消息交互——经典的 actor 模型。

text
   [Agent A] ──msg(topic=search)──▶ [Agent B]
      ▲                                │
      └─────────msg(topic=done)────────┘

发方不关心谁消费、什么时候消费;收方按自己的节奏处理。天然解耦、天然异步。

优点: 强解耦,Agent 可独立部署升级;横向扩展容易;失败隔离好(一个挂了不影响别人)。缺点: 调试难,一次协作要追一串消息才能拼出全貌;消息顺序和一致性要自己管。适合Agent 跨进程/跨服务、需要弹性扩展、可用性要求高的场景。

模式三:Handoff(移交)

一个 Agent 干到自己能力的边界,把控制权连同当前上下文一起交给另一个 Agent。不是"我查完告诉你",而是"你来接管这个会话"。

text
   用户 ──▶ [客服Agent] ──handoff(ctx)──▶ [技术专家Agent] ──▶ 用户

OpenAI 的 Swarm、很多客服系统走这种。移交的关键是上下文压缩与转交——不能把前一个 Agent 的全部消息历史原样塞给下一个(太贵、噪声多),要总结成精炼的 handoff payload:用户意图、已确认的事实、当前卡点、下一步建议。

优点: 轻量、无中心调度、用户感知是连贯的一个对话。缺点: 容易"踢皮球"(A 交给 B、B 觉得不归自己又交给 A),没有全局视图,trace 是断的。适合线性流转的会话型任务(客服分级、工单升级),不适合需要并行多 Agent 协作的复杂任务。

一个选型观点: 任务能在单进程里跑完、Agent 数量少,用黑板;Agent 要跨服务、要扩展,用消息传递;任务是会话式分级的,用 handoff。强行把所有场景塞进一种范式,是架构灾难。

三、契约与序列化:Agent 间的"接口定义"

无论哪种模式,Agent 之间传的都不是"自然语言文本",而应是带 schema 的结构化消息。这一条没做好,后面全是脆。

很多人让 Agent A 把一段自然语言塞进 state["result"],Agent B 再用 prompt 解析。这能跑,但极脆弱:A 的措辞稍微变一下、加个前缀"好的,结果是:",B 就崩。LLM 输出本就不可控,把它当接口契约,等于把可靠性建在沙子上。

正确做法:Agent 之间的每一次交接,都走结构化 schema。

ts
// Agent 之间传递的消息契约
interface SearchResult {
  query: string;
  hits: { title: string; url: string; snippet: string; score: number }[];
  source: "researcher";
  schema_version: 1;
}

Agent A 用 structured output / tool call 强制输出符合 schema 的对象;Agent B 拿到的是确定结构的数据,不需要再用 prompt 解析。三个直接好处:可校验(收到先过 validator,不对就拒)、可演进(靠 schema_version 做向后兼容)、可测试(契约就是接口,能写单测)。

工程上两个细节常被忽略。第一,schema 要带版本号。Agent A 升级了输出结构,下游的 Agent B 还没跟上,没版本号就是线上事故。带上 schema_version,消费端能拒绝不兼容版本,而不是用旧逻辑解析新格式静默出错。第二,校验失败要有兜底。LLM 偶尔输出不合规结构,别让整个 pipeline 崩——重试一次,还不行就把错误反馈给上游让它修,或降级到安全默认值。

一个观点: 把 Agent 间通信当微服务间的 RPC 对待——有 IDL、有版本、有校验、有降级。很多人对内部服务调用的健壮性要求极高,对 Agent 间通信却放任自流,这是双标。

四、常见架构:supervisor / 对等 / pub-sub

通信模式解决"怎么传",架构解决"怎么组织"。三种常见架构,各有适用面。

Supervisor(中心调度)。 一个主管 Agent 负责拆任务、分派、汇总,专员 Agent 只跟主管说话。最常用,可控、可观测、容易加人工介入。

text
            ┌──────────┐
       ┌───▶│ Supervisor│◀───┐
       │    └─────┬────┘    │
  ┌────┴──┐  ┌────┴──┐  ┌───┴───┐
  │Agent A│  │Agent B│  │Agent C│
  └───────┘  └───────┘  └───────┘

代价是 supervisor 是单点和瓶颈:它挂了全挂,它慢了全等。适合任务可清晰分解、需要全局协调的场景,覆盖 80% 的多 Agent 需求。

对等协作(peer-to-peer)。 没有中心主管,Agent 之间直接通信、自组织协作——每个 Agent 知道该找谁,不需要"调度员"。

text
  [Agent A]◀──────▶[Agent B]
      │                │
      │    ┌───────┐   │
      └───▶│Agent C│◀──┘
           └───────┘

灵活,没有单点;但 Agent 间要维护"我该找谁"的知识,容易死循环(A 找 B、B 找 A),trace 难追。适合Agent 各有专长、协作关系动态的研究型场景。

Pub-Sub(事件驱动)。 Agent 往一个总线/主题上发事件,订阅了该主题的 Agent 自动收到。发方不关心谁收,收方不关心谁发,解耦最彻底。

text
                    ┌─────────────┐
   [Agent A]──────▶│  Event Bus  │
                    │ (topic=*)   │
   [Agent B]◀──────│             │
                    └──────┬──────┘
                           │
                      ┌────▼────┐
                      │[Agent C]│
                      └─────────┘

新增一个 Agent 只要订阅主题即可,扩展性最强。但事件流调试是噩梦,乱序、重复、丢失都要单独处理。适合事件驱动的长流程、Agent 动态增减的场景(比如监控告警触发的多 Agent 处置流)。

选型经验: 默认上 supervisor;只有当它真成为瓶颈或单点风险时,才考虑对等或 pub-sub。很多人一上来就要"去中心化的对等 Agent 网络",最后连一个稳定的任务流都跑不起来——复杂架构要为真实扩展性服务,不是为了炫技。

五、工程实战:异步、错误传播、跨 Agent trace

架构定了,下面几个工程决策决定它能不能上生产。

异步与超时。 调一个 Agent,永远别无限等。每次调用都要配超时,且要分层:单次 tool 调用(10 秒)、单个 Agent 一轮(60 秒)、整个任务(10 分钟)。超时后的策略要想清楚——重试、降级、还是 fail fast。规则就一句:凡是等 Agent 的地方就有超时,凡是超时就有兜底。

错误传播与降级。 多 Agent 系统里,错误传播比错误本身更危险。上游失败,把错误信息当"正常输出"塞给下游,下游基于错误数据继续跑,产出一堆看起来对、实际错的结果——最隐蔽的故障。

正确做法是错误必须显式、结构化地传播。用 Result<T, E> 模式,成功和失败走不同通道;失败时附带 error_code、failed_at、trace_id。下游拿到失败信号,要么跳过、要么走降级路径,绝不能拿错误内容当正常输入推理。

ts
type AgentResult<T> =
  | { ok: true; data: T; trace_id: string }
  | { ok: false; error: string; code: string; trace_id: string; retryable: boolean };

跨 Agent 的 trace。 一次请求穿过 supervisor → A → B → C,出问题时你要能拼出整条链路。这就需要一个贯穿全链路的 trace_id / conversation_id,每个 Agent 处理时都打到自己的日志和 trace 事件里。

text
trace_id: abc-123
├─ supervisor (span 1)
│   ├─ agent-A (span 2, parent=1)
│   │   └─ tool: web_search (span 3, parent=2)
│   └─ agent-B (span 4, parent=1)
│       └─ tool: code_exec (span 5, parent=4)

用 OpenTelemetry、LangSmith、Langfuse 都行,关键是 trace_id 要从入口注入、逐层透传。没有 trace 的多 Agent 系统是黑盒,黑盒没法运维。

状态写入要串行化。 黑板模式下多 Agent 并发写 state,一定要有串行化机制(乐观锁、版本号、单写入者)。LLM 调用本身慢,并发写看起来没问题,高 QPS 下竞态会把你逼疯。给 state 加个 version 字段,写入带条件检查,冲突就重试。

六、三个反模式

最后列几个我在生产里反复见到的反模式,踩中一个就够你忙一周。

反模式一:Agent 间传递巨型自然语言上下文。 "把前面所有 Agent 的对话原样塞给下一个"——上下文膨胀、token 爆炸、噪声淹没信号。每个 Agent 只该拿到它干活需要的最小上下文,且应是结构化摘要,不是原始聊天记录。token 不是免费的,注意力也不是无限的。

反模式二:无契约的隐式通信。 两个 Agent 靠"它应该能看懂我的意思"协作,没有 schema、没有校验。开发环境碰巧能跑,换个模型、改个 prompt、遇到边角 case 就崩。这种系统 demo 里最唬人,生产里最坑。

反模式三:循环互调(死锁式 handoff)。 A 觉得这事归 B、B 觉得归 A,来回 handoff 直到预算烧光;或对等架构里 A 调 B、B 调 A、无限递归。任何允许多 Agent 互相调用的系统,都必须有全局的最大跳数上限。 没有这个上限的多 Agent 系统,本质是个按 token 计费的死循环。

小结

  1. 多 Agent 的真正难点在通信,不在能力。 异步、状态同步、错误传播、契约——这四个维度的复杂度,是单 Agent 的数倍。
  2. 三种通信模式按场景选: 黑板(共享 state,单进程小团队)、消息传递(actor 模型,跨服务可扩展)、handoff(移交控制权,会话型分级流转)。
  3. Agent 间的每一次交接都要走带版本的 schema 契约。 把它当 RPC 接口对待,有 IDL、有校验、有降级、有版本号。无契约的通信就是定时炸弹。
  4. 架构默认选 supervisor,有真实扩展需求才上对等或 pub-sub。 复杂架构要为真实需求服务,不是为了炫技。
  5. 生产化靠四件事: 分层超时、显式结构化的错误传播、贯穿全链路的 trace_id、并发写状态的串行化。

单 Agent 拼的是模型能力,多 Agent 拼的是通信工程。Agent 之间的那条线怎么画,决定了它们是协作的梦之队,还是互相拆台的乌合之众。

Related

相关推荐