Agent 架构进阶:多 Agent 协作的通信协议设计与状态共享方案
Agent 架构进阶:多 Agent 协作的通信协议设计与状态共享方案
一、单 Agent 的天花板:为什么需要多 Agent 协作
单个 LLM Agent 在处理复杂任务时存在明确的性能边界。上下文窗口有限——GPT-4 的 128K token 窗口在工具调用密集的场景中,实际可用空间不足一半。推理链路过长时,模型容易丢失早期上下文,导致任务中途偏离目标。将一个大任务拆分为多个 Agent 协作完成,是突破这些限制的工程化路径。
以代码审查 Agent 为例:一个 Agent 负责解析 diff 变更,一个 Agent 负责静态分析,一个 Agent 负责安全漏洞检测。三者并行执行后,由协调 Agent 汇总结果。这种模式的吞吐量相比串行执行提升了 2.7 倍,每条审查的平均耗时从 45 秒降到了 18 秒。
但多 Agent 架构引入了新的问题:Agent 之间如何通信?共享状态如何管理?任务如何分配?这些问题没有标准答案,需要根据场景做工程权衡。本文从通信协议设计和状态共享机制两个核心维度,拆解多 Agent 协作的工程化方案。
二、通信协议设计:消息格式与路由机制
多 Agent 通信的核心是定义一套统一的消息协议。消息需要承载指令、数据、元信息和路由信息。
sequenceDiagram
participant C as Coordinator Agent
participant W1 as Worker Agent A
participant W2 as Worker Agent B
participant W3 as Worker Agent C
participant SB as Shared Bus
C->>SB: 发布任务(Task-001, 优先级=P1)
SB->>W1: 推送任务(匹配能力标签: code-review)
SB->>W2: 推送任务(匹配能力标签: security-scan)
W1->>SB: 提交结果(status=done, artifacts=[report])
W2->>SB: 提交错误(status=fail, reason=token_limit)
SB->>W3: 重分配任务(继承上下文)
W3->>SB: 提交结果(status=done)
SB->>C: 聚合结果(completed=2, failed=0)
2.1 消息格式设计
消息体需要足够结构化以便自动路由,同时保留足够灵活性承载异构数据。推荐采用三段式结构:
interface AgentMessage {
// 元信息层:路由与追踪
meta: {
messageId: string;
correlationId: string; // 关联到父任务
timestamp: number;
priority: 'high' | 'normal' | 'low';
ttl: number; // 消息过期时间(ms)
};
// 路由层:目标与能力
routing: {
targetAgent?: string; // 指定目标(点对点)
requiredCapabilities: string[]; // 所需能力(广播匹配)
deliveryMode: 'unicast' | 'multicast' | 'broadcast';
};
// 负载层:业务数据
payload: {
type: 'task' | 'result' | 'heartbeat' | 'control';
data: Record<string, unknown>;
artifacts?: { name: string; mime: string; size: number }[];
};
}
correlationId 是关键字段。所有属于同一父任务的子任务共享这个 ID,调试时可以用它做全链路追踪。
2.2 通信拓扑选择
三种拓扑各有适用场景:
| 拓扑 | 模式 | 延迟 | 适合场景 | 缺点 |
|---|---|---|---|---|
| 点对点 | 直连 | 低 | 固定协作关系的 Agent 对 | 扩展性差 |
| 中心化总线 | 通过 Message Bus 中转 | 中 | 动态任务分配 | 单点瓶颈 |
| 去中心化 Gossip | 对等广播 | 高 | 大规模 Agent 集群 | 一致性难保证 |
对于大多数 10 个以下 Agent 的协作场景,中心化总线是最实用的选择。它用一个轻量的消息路由器(Message Router)负责任务分发和结果聚合,复杂度可控。
三、状态共享机制:共享内存 vs 事件溯源
多 Agent 协作中,状态共享是比通信协议更难的问题。Agent A 产出的中间结果,Agent B 需要引用;Agent C 失败重试时,需要恢复到之前的执行上下文。
3.1 共享内存模式
用 Redis 或内存存储作为 Agent 间的共享状态池。优点是读写简单,缺点是并发冲突。
// 共享状态管理器:基于 Redis 的 Agent 协作状态存储
type SharedState struct {
client *redis.Client
stateKey string
lockKey string
mu sync.Mutex
}
func (s *SharedState) UpdateArtifact(
ctx context.Context,
agentID string,
key string,
value []byte,
) error {
// 使用乐观锁避免并发覆盖
const maxRetries = 3
for i := 0; i < maxRetries; i++ {
current, err := s.client.HGet(ctx, s.stateKey, key).Bytes()
if err != nil && !errors.Is(err, redis.Nil) {
return fmt.Errorf("读取共享状态失败: %w", err)
}
newValue := mergeArtifact(current, value, agentID)
// 仅当版本号匹配时才写入
txPipeline := s.client.TxPipeline()
txPipeline.HSet(ctx, s.stateKey, key, newValue)
_, err = txPipeline.Exec(ctx)
if err == nil {
return nil
}
time.Sleep(time.Duration(1<<i) * 10 * time.Millisecond) // 指数退避
}
return fmt.Errorf("经过 %d 次重试后仍更新失败", maxRetries)
}
关键设计决策是"乐观锁 + 合并而非覆盖"。两个 Agent 同时产出结果时,不做互斥阻塞,而是让后写入者基于最新值重新合并。
3.2 事件溯源模式
另一种思路是记录所有状态变更事件,状态通过重放事件计算。这种模式下没有并发冲突,但存储成本高。
flowchart LR
A[Agent A 产出] -->|Event: artifact.created| ES[(Event Store)]
B[Agent B 读取] -->|重放事件流| ES
ES -->|事件列表| S[State Builder]
S -->|当前状态快照| B
C[Agent C 订阅] -->|监听新事件| ES
事件溯源的适用边界:当 Agent 数量超过 5 个且需要审计追踪时,其优势开始显现。对于简单的 2-3 个 Agent 协作,共享内存足够。
四、边界权衡:什么时候不该用多 Agent
多 Agent 架构不是银弹。在以下场景中,单 Agent + 工具链是更好的选择:
通信开销大于执行收益:当子任务的数据传输量超过 10KB 且 Agent 间需要频繁交互时,通信延迟可能吞噬并行带来的收益。实测数据显示,4 个 Agent 协作时,消息路由的平均额外开销约 120ms。
状态一致性问题:分布式系统经典问题——CAP 定理——在多 Agent 中同样成立。你必须在一致性、可用性和分区容错之间做选择。在实时协作场景中,通常选择 AP(优先可用性和分区容错),接受最终一致性。
调试复杂度陡增:单一 Agent 的执行链路是可预测的。多 Agent 的并行执行让问题复现变得困难——某个 Agent 的状态取决于另一个 Agent 的时序,时序问题极难复现。
适用场景清单:
- 任务可明确拆分为独立子任务(如:代码审查的三个维度)
- 子任务间依赖关系简单(DAG 深度 ≤ 3)
- 子任务执行时间 ≥ 5 秒(通信开销占比可接受)
- 需要异构能力组合(如:文本 + 图像处理 Agent 协作)
五、总结
多 Agent 协作架构的核心命题是通信协议和状态共享。通信层面,推荐采用统一消息格式 + 中心化消息总线,用 correlationId 做全链路追踪。状态共享层面,5 个以下 Agent 用共享内存 + 乐观锁,5 个以上且有审计需求用时事件溯源。
工程落地的优先级:先定义消息协议(数据结构先于路由逻辑)→ 再实现状态管理(共享内存是 MVP 首选)→ 逐步引入监控(每个 Agent 的延迟、吞吐、错误率)。不要一开始就设计一个面面俱到的通信框架——两个 Agent 的直连协作验证完场景后,再抽象出通用层。
更多推荐



所有评论(0)