跳转至

第2章 协作与通信

学习目标

  • 理解 Agent 间通信协议(A2A)的设计要素与消息格式
  • 掌握队列、共享内存、事件驱动三类通信机制及其适用场景
  • 区分同步、异步、广播三种通信模式并能够正确选型
  • 使用 Python 实现基于消息队列的 Agent 间通信
  • 识别通信瓶颈并设计可扩展的分布式 Agent 通信架构

问题导入

某物流平台部署了订单、库存、配送、风控四个 Agent。最初采用直接函数调用(同步)方式:订单 Agent 调用库存 Agent,库存 Agent 调用配送 Agent……结果一旦配送 Agent 响应慢,整个链路阻塞,订单超时严重。更糟的是,新增"营销 Agent"需要修改订单 Agent 的代码才能接入。这说明:Agent 间的通信方式不是"能跑就行"的小事,而是决定系统可扩展性与健壮性的根基。本章系统讲解多 Agent 通信机制,帮你设计解耦、可扩展的协作架构。

核心概念

1. A2A(Agent-to-Agent)通信协议

A2A 通信协议定义 Agent 间交换消息的格式与规则。一条 A2A 消息通常包含:

字段 说明 示例
sender 发送方 Agent ID agent://order-001
receiver 接收方 Agent ID 或 broadcast agent://inventory-002
msg_type 消息类型 request / response / event / handoff
content 消息体(JSON/文本) {"sku": "A1", "qty": 10}
trace_id 链路追踪 ID trace-abc123
timestamp 时间戳 2026-07-01T10:00:00Z
reply_to 回复目标(可选) agent://order-001

协议设计原则: - 自描述:消息含足够元数据,接收方无需额外上下文即可处理。 - 幂等:同一消息重复投递不应产生副作用(带 msg_id 去重)。 - 可追溯:每条消息带 trace_id,串联全链路日志。

2. 三类通信机制

直接调用(Direct Call):Agent A 直接调用 Agent B 的函数/方法。最简单,但强耦合、易阻塞、难扩展。适合单机、简单、同步场景。

共享内存(Shared Memory / Blackboard):所有 Agent 读写同一块共享状态空间(如 Redis、数据库)。Agent 间无直接耦合,通过"观察黑板"协同。适合需要共享大量上下文、Agent 数量适中的场景。

消息队列(Message Queue):Agent 间通过队列(如 RabbitMQ、Kafka、Redis Stream)异步传递消息。发送方不等待接收方,天然解耦、可削峰、可扩展。适合分布式、高吞吐、需容错的场景。

3. 三种通信模式

模式 描述 优点 缺点 典型场景
同步 A 调用 B 并等待返回 简单、易调试 阻塞、耦合高 简单流水线、单机
异步 A 发消息给 B 后继续工作,B 完成后回调/通知 解耦、高吞吐 流程复杂、需回调 分布式、长任务
广播 A 把消息发给所有相关 Agent 联动性强 消息放大、需过滤 事件通知、状态同步

4. 事件驱动通信

事件驱动是异步通信的高级形式:Agent 不直接"调用"对方,而是"发布事件",订阅了该事件的 Agent 自动响应。

  • 事件总线(Event Bus):中心化事件分发器,Agent 注册订阅、发布事件。
  • 发布订阅(Pub/Sub):解耦的经典模式,发布者不关心谁订阅。
  • 适用场景:库存变化通知营销暂停、订单完成触发风控审计、异常告警多 Agent 联动。

5. 通信模式选型决策

  • 任务线性、单机、低延迟要求 → 同步直接调用(CrewAI 顺序流程即此类)。
  • 任务可并行、需解耦、分布式 → 异步消息队列。
  • 一变多动、需多 Agent 联动 → 事件驱动广播。
  • 需共享大上下文、Agent 数适中 → 共享黑板。

原理图

flowchart TB
    subgraph 同步调用
        S1[Agent A] -->|阻塞等待| S2[Agent B]
        S2 -->|返回| S1
    end

    subgraph 异步队列
        A1[Agent A] -->|发送消息| Q[(消息队列)]
        Q -->|投递| A2[Agent B]
        A2 -->|回调/事件| A1
    end

    subgraph 共享黑板
        B1[Agent X] -->|写| BB[(共享黑板)]
        B2[Agent Y] -->|读| BB
        B3[Agent Z] -->|读写| BB
    end

    subgraph 事件驱动广播
        E1[Agent A] -->|发布事件| EB((事件总线))
        EB -->|通知| E2[Agent B]
        EB -->|通知| E3[Agent C]
        EB -->|通知| E4[Agent D]
    end
图:四种多 Agent 通信机制对比

示例代码

使用 Redis 实现基于消息队列的异步 Agent 通信,演示订单 Agent 与库存 Agent 的解耦协作。

import json
import time
import uuid
import redis


class AgentMessage:
    """A2A 消息封装。"""

    def __init__(self, sender, receiver, msg_type, content, trace_id=None):
        self.msg_id = str(uuid.uuid4())
        self.sender = sender
        self.receiver = receiver
        self.msg_type = msg_type  # request / response / event
        self.content = content
        self.trace_id = trace_id or self.msg_id
        self.timestamp = time.time()

    def to_json(self):
        return json.dumps(self.__dict__, ensure_ascii=False)

    @classmethod
    def from_json(cls, s):
        d = json.loads(s)
        msg = cls(d["sender"], d["receiver"], d["msg_type"], d["content"], d.get("trace_id"))
        msg.msg_id = d["msg_id"]
        msg.timestamp = d.get("timestamp", time.time())
        return msg


class QueueAgent:
    """基于 Redis 队列的异步 Agent 基类。"""

    def __init__(self, agent_id, redis_client):
        self.agent_id = agent_id
        self.r = redis_client
        self.inbox = f"queue:{agent_id}"

    def send(self, receiver, msg_type, content, trace_id=None):
        """向其他 Agent 发送消息(异步,不阻塞)。"""
        msg = AgentMessage(self.agent_id, receiver, msg_type, content, trace_id)
        self.r.rpush(f"queue:{receiver}", msg.to_json())
        print(f"[{self.agent_id}] → [{receiver}] {msg_type}: {content}")
        return msg.msg_id

    def receive(self, timeout=5):
        """阻塞等待一条消息。"""
        result = self.r.blpop(self.inbox, timeout=timeout)
        if result is None:
            return None
        _, raw = result
        return AgentMessage.from_json(raw)

    def handle(self, msg):
        """子类重写:处理收到的消息。"""
        raise NotImplementedError


class OrderAgent(QueueAgent):
    def handle(self, msg):
        if msg.msg_type == "response":
            print(f"[{self.agent_id}] 收到库存回复: {msg.content}")
        elif msg.msg_type == "event":
            print(f"[{self.agent_id}] 收到事件: {msg.content}")


class InventoryAgent(QueueAgent):
    def handle(self, msg):
        if msg.msg_type == "request" and msg.content.get("action") == "check_stock":
            sku, qty = msg.content["sku"], msg.content["qty"]
            # 模拟库存检查
            available = qty <= 100
            self.send(msg.sender, "response",
                      {"sku": sku, "available": available}, msg.trace_id)


if __name__ == "__main__":
    r = redis.Redis(host="localhost", port=6379, decode_responses=True)
    # 清空测试队列
    r.flushdb()

    order = OrderAgent("order-001", r)
    inventory = InventoryAgent("inventory-002", r)

    # 订单 Agent 异步发送库存检查请求
    trace = order.send("inventory-002", "request",
                       {"action": "check_stock", "sku": "A1", "qty": 5})

    # 库存 Agent 接收并处理
    msg = inventory.receive(timeout=5)
    if msg:
        inventory.handle(msg)

    # 订单 Agent 接收回复
    reply = order.receive(timeout=5)
    if reply:
        order.handle(reply)

事件驱动广播示例(发布订阅):

import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)

# 订阅端:营销 Agent 监听库存事件
def marketing_listener():
    pubsub = r.pubsub()
    pubsub.subscribe("inventory.events")
    for msg in pubsub.listen():
        if msg["type"] == "message":
            print(f"[营销 Agent] 收到库存事件: {msg['data']}, 暂停推广")

# 发布端:库存 Agent 发布事件
r.publish("inventory.events", '{"sku":"A1","event":"low_stock","qty":2}')

企业案例

案例 1:分布式订单系统的异步改造

某电商平台原用同步调用串联订单、库存、配送 Agent,大促期间链路阻塞频发。改造为基于 Kafka 的异步消息队列:订单 Agent 发出"下单事件"后立即返回,库存、配送、风控 Agent 各自订阅处理。改造后订单接口 P99 延迟从 3.2s 降至 200ms,系统吞吐提升 8 倍。

案例 2:智能运营中心的共享黑板

某零售品牌部署售前、售后、库存、营销四个 Agent,通过 Redis 共享黑板交换状态。库存 Agent 把"即将断货"写入黑板,营销 Agent 轮询发现后自动暂停该商品推广,售前 Agent 读取后调整话术。黑板模式让 Agent 间无需直接调用即可联动,新增 Agent 只需读写黑板即可接入。

案例 3:风控审计的事件驱动联动

某金融平台部署风控 Agent 监听全平台"大额交易事件"。当事件发生,风控 Agent、合规 Agent、通知 Agent 同时响应:风控评估风险、合规记录留痕、通知推送人工复核。事件驱动让一次发布触发多 Agent 并行处理,避免串行调用造成的延迟。

常见问题

Q1:什么时候该用消息队列而不是直接调用? A:当出现以下任一情况:①Agent 分布在不同进程/机器;②需要解耦以便独立扩展;③调用链长、需削峰填谷;④需保证消息持久化与重试。否则简单任务用直接调用更高效。

Q2:共享黑板会不会有并发冲突? A:会。多 Agent 同时写黑板需用乐观锁(CAS)或分布式锁,避免覆盖。读多写少场景适合黑板,写冲突频繁的场景更适合队列。

Q3:广播会不会导致消息风暴? A:可能。需通过主题过滤(topic)、消息 TTL、订阅者按需订阅来控制。生产环境建议用结构化主题(如 inventory.low_stock.*)精确路由。

Q4:如何保证消息不丢失? A:使用支持持久化的队列(Kafka/RabbitMQ 持久化队列)、消费端确认机制(ack)、生产端重试与死信队列。关键业务消息需做幂等处理。

面试题

1. 对比同步、异步、广播三种 Agent 通信模式的优缺点与适用场景。 参考答案:同步简单易调试但阻塞耦合,适合单机简单流水线;异步解耦高吞吐但需回调与状态管理,适合分布式长任务;广播联动性强但消息放大,适合事件通知。生产环境多采用异步+事件驱动组合。

2. 设计一个支持水平扩展的多 Agent 通信架构。 答题要点:用消息队列(Kafka/Redis Stream)解耦;Agent 无状态化、可多副本;用 trace_id 串联链路;用服务发现动态注册 Agent;用负载均衡分发消息;关键消息持久化与重试。

3. A2A 消息协议应包含哪些关键字段?为什么? 参考答案:sender/receiver 标识收发方;msg_type 区分请求/响应/事件;content 携带业务数据;trace_id 用于全链路追踪;msg_id 用于幂等去重;timestamp 用于时序与超时判断。

练习题

  1. 通信机制实操:用 Redis Stream 实现三个 Agent(A→B→C)的异步流水线,每步处理耗时不同,观察总耗时与同步方式的差异。
  2. 事件驱动设计:为"电商异常监控"场景设计事件主题与订阅关系,画出事件流图,说明哪些 Agent 订阅哪些事件。
  3. 思考题:如果消息队列宕机,多 Agent 系统应如何降级以保证核心业务可用?

小结

多 Agent 通信是协作的基石。同步直接调用简单但耦合高;异步消息队列解耦可扩展,是分布式生产环境的首选;共享黑板适合共享上下文;事件驱动广播适合多 Agent 联动。A2A 协议的自描述、幂等、可追溯三原则是设计健壮通信的关键。下一章将在此基础上讲解如何进行任务分工与协调。延伸阅读:Redis Streams 文档、Kafka 设计文档、论文《Multi-Agent Communication Protocols》。