技术文档 | 当 Agent 遇上 Pulsar:如何重构 A2A 协议,玩转事件驱动架构

大家好,我是Sinan Liu。Github ID: Denovo1998。

最近 AI Agent 的浪潮是一波接一波,从单个能用工具的智能体,到多个 Agent 协同工作的复杂系统,整个行业都在飞速进化。我们正从 Level 2: AI Agent 迈向 Level 3: Multi Agent

在多 Agent 的世界里,"对话"是关键。Agent 之间怎么沟通?A2A (Agent-to-Agent)(https://a2aproject.github.io/A2A/) 协议就是为此而生的,它像普通话一样,让来自不同框架、不同厂商的 Agent 能够互相理解、协同工作。

A2A 协议最初的设计是基于 HTTP 的,简单直接,大家都会用。但随着我们的系统越来越复杂,我们发现,只靠 HTTP 的“打电话”模式,在某些场景下有点力不从心了。

我们不禁开始思考:Agent 间的通信,能不能不只是打电话,而是像一个高效、可靠的“中央邮局”?于是,我们把目光投向了事件驱动架构(EDA),并选择了一个强大的“邮差”—— Apache Pulsar

今天,我就带大家深入幕后,聊聊我们是如何对 A2A 的通信架构进行一次初步的重构,并把 Pulsar 无缝集成进来的。

一、最初的模样:HTTP 直连的 A2A 通信

在最开始,我们的 A2AClient 是直接依赖一个具体的 HTTP 客户端的。代码里大概是这个样子:

// 重构前的 A2AClient
publicclassA2AClient {
    // 直接依赖一个具体的 HTTP Client 实现
    privatefinal A2AHttpClient httpClient;
    privatefinal String agentUrl;

    publicA2AClient(AgentCard agentCard) {
        this.agentCard = agentCard;
        this.agentUrl = agentCard.url();
        // 硬编码创建了一个 JdkA2AHttpClient
        this.httpClient = newJdkA2AHttpClient();
    }

    public SendMessageResponse sendMessage(...) {
        // ...直接使用 httpClient 发送 HTTP 请求...
        A2AHttpResponseresponse= httpClient.createPost()...post();
        // ...处理 HTTP 响应...
    }
}

这种方式简单明了,对于两个 Agent 之间的点对点通信,完全够用。

但问题来了,当成百上千的 Agent 组成一个复杂的协作网络时:

  1. 1. 强耦合A2AClient 和 HTTP 通信方式焊死在了一起。如果有一天我们想换成 gRPC,或者别的什么协议,怎么办?得大改 A2AClient

  2. 2. 可靠性问题:如果目标 Agent 宕机了,HTTP 请求直接就失败了。没有重试、没有缓冲,消息就这么丢了。在一个需要高可靠的生产环境里,这可不太妙。

  3. 3. “伪”异步:像 SSE (Server-Sent Events) 这种流式通信,本质上还是一个长连接的 HTTP 请求。如果客户端断线了,再想接上之前的进度就很难了。它更像是“在线等”,而不是真正的“有消息了再叫我”。

我们需要一个更健壮、更灵活的架构。

二、破局之道:引入“交通工具”抽象层

为了解决上面的问题,我们进行了一次关键的重构。核心思想很简单,就是“依赖倒置”。

说白了,就是 A2AClient 不应该关心消息是“怎么”被送出去的。它不该知道是用 HTTP 卡车,还是 Pulsar 高铁。它只需要把“信”交给一个叫 Transport(交通工具)的接口,由这个接口的具体实现去负责运输。

于是,我们定义了一个干净的 Transport 接口:

// 新定义的 Transport 接口
public interface Transport {
    // 用于请求-响应模式
    CompletableFuture<String> request(String destination, String body);

    // 用于流式/事件模式
    void stream(String destination, String body, Consumer<StreamingEventKind> onEvent, ...);
}

有了这个接口,A2AClient 的代码就变得清爽多了:

// 重构后的 A2AClient
publicclassA2AClient {
    // 不再关心具体实现,只认 Transport 接口
    privatefinal Transport transport;
    privatefinal String agentUrl; // 或者其他地址信息

    publicA2AClient(AgentCard agentCard, Transport transport) {
        this.agentCard = agentCard;
        this.agentUrl = agentCard.url(); // 这里的 url 可能是 HTTP 地址,也可能是 Pulsar 的 topic
        this.transport = transport; // 交通工具是传进来的!
    }

    public SendMessageResponse sendMessage(...) {
        // ...
        // 我只管发货,怎么送是 transport 的事
        StringresponseBody= transport.request(agentUrl, requestBody).get();
        // ...
    }
}

看,A2AClient 现在彻底解放了,它和具体的通信方式完全解耦。原来的 JdkA2AHttpClient 摇身一变,成了 JdkHttpTransport,作为 Transport 接口的一个实现。

这个小小的改动,却为我们打开了通往新世界的大门。

三、Pulsar 登场:事件驱动的“神经网络”

现在我们有了一个可以插入任何“交通工具”的插座,是时候把 Pulsar 这辆“跑车”开进来了。

我们创建了一个新的模块 transport-eda-pulsar,在里面实现了一个 PulsarTransport

在这个新架构里,Pulsar 扮演了几个关键角色:

  1. 1. 可靠的信使 (Request-Reply):对于 tasks/get 这样的请求-响应调用,我们利用 Pulsar 的 RPC 模式。客户端把请求发到一个 request-topic,然后在一个专属的 reply-topic 上等待回复。消息在 Pulsar 里是持久化的,就算目标 Agent 当时离线,等它上线后依然能收到任务并处理,保证了消息不丢失。**这里我使用了我之前贡献的使用pulsar-client实现的request-reply模型 pulsar-rpc-contrib

  2. 2. 事件总线 (Streaming):对于 message/stream 这样的流式通信,我们彻底抛弃了 SSE。客户端发起一个流式请求,这个请求消息里会带上一个临时的、唯一的“回信地址”(一个临时的 Pulsar topic)。服务端的 Agent 收到后,就会持续地把状态更新、结果片段等事件,像发快递一样,一个个地发到这个“回信地址”上。这种方式比 SSE 更健壮,即使客户端断线重连,只要订阅关系还在,就不会错过重要更新。这里暂时没办法使用 pulsar-rpc-contrib,或许后续我可以优化一下。

四、点睛之笔:用 AgentCard 实现动态切换

你可能会问,客户端怎么知道一个 Agent 是用 HTTP 还是用 Pulsar 通信呢?

答案就在 AgentCard(Agent 的名片)里。我们对 AgentCard 做了扩展,增加了一个 transport 字段。

以前的 AgentCard:

{
  "name": "天气查询 Agent",
  "url": "http://weather-agent.com/a2a"
  // ...
}

现在的 AgentCard:

{
  "name":"天气查询 Agent",
// "url" 字段可能还在,用于 HTTP 兼容
"url":"http://weather-agent.com/a2a",
// 新增的 transport 字段!
"transport":{
    "type":"pulsar",// 告诉大家,我支持 Pulsar!
    "serviceUrl":"pulsar://pulsar.cluster.local:6650",
    "requestTopic":"persistent://public/default/weather-agent-requests",
    "streamingTopic":"persistent://public/default/weather-agent-streaming"
}
// ...
}

客户端在和 Agent 通信前,先看它的“名片”。如果发现 transport 字段里写着 pulsar,就会实例化一个 PulsarTransport 来和它通信。如果没这个字段,就默认使用老的 HTTP 方式。

这个小小的设计,让整个系统变得极其灵活和可扩展。

总结

通过这次重构,我们成功地将 A2A 的通信底层从一个强绑定的 HTTP 实现,升级为了一个可插拔、事件驱动的架构。

Apache Pulsar 在这个新架构中,不仅仅是一个消息队列,它更像是多 Agent 系统的“中央神经网络”。它让 Agent 之间的通信变得:

  • • 更解耦:Agent 们不需要知道彼此的网络地址,只需跟 Pulsar 打交道。

  • • 更可靠:消息持久化,任务不丢失。

  • • 更具弹性:可以轻松地增加或减少处理任务的 Agent 实例,而无需改动客户端。

  • • 真正异步:完美支持长时间运行、客户端可能离线的任务场景。

AI Agent 的世界正在从“个体英雄”走向“团队协作”。而一个强大、灵活的通信基础设施,正是打造顶级 Agent 团队的基石。我们相信,EDA 和 Pulsar 的结合,将为构建下一代复杂、智能的多 Agent 系统提供无限可能。

由于我仅用了一天的时间,这个PR目前还很粗糙,如果你感兴趣请关注:

a2a-java-eda-pulsar (https://github.com/a2aproject/a2a-java/pull/164)

pulsar-rpc-contrib (https://github.com/apache/pulsar-java-contrib/pull/6)

希望这次的分享能给你带来一些启发。欢迎在评论区留下你的想法,我们一起交流!

速报

截至发稿时该 PR 已被关闭。

烫知识:按照项目当前规定,A2A 通信必须通过 HTTP(S) 进行

https://a2aproject.github.io/A2A/v0.2.5/specif-ication/#31-transport-protocal

考虑到 EDA 的灵活性、可扩展性、松耦合和可靠性,这种架构简直就是为AI Agent量身定制的!那它之所以被排除在外是因为技术适配挑战?还是生态成熟度问题?或者有更好的替代方案?

欢迎大家在评论区分享看法!

图片

Apache Pulsar作为一个高性能、分布式的发布-订阅消息系统,正在全球范围内获得越来越多的关注和应用。如果你对分布式系统、消息队列或流处理感兴趣,欢迎加入我们!

Github: 

https://github.com/apache/pulsar

图片

扫码添加pulsarbot,加入Pulsar社区交流群

最佳实践

互联网

腾讯BiFang | 腾讯云 | 微信 | 腾讯 | BIGO | 360 | 滴滴  | 腾讯互娱 | 腾讯游戏 | vivo 科大讯飞 | 新浪微博 | 金山云 | STICORP | 雅虎日本 | Nutanix Beam | 智联招聘 

金融/计费

腾讯计费 | 平安证券 | 拉卡拉 | Qraft | 甜橙金融 

电商

Flipkart | 谊品生鲜 | Narvar | Iterable 

机器学习

腾讯Angel PowerFL

物联网

云兴科技智慧城市 | 科拓停车 | 华为云 | 清华大学能源互联网创新研究院 | 涂鸦智能

通信

江苏移动 | 移动云 

教育

网易有道 | 传智教育

推荐阅读

资料合集 | 实现原理 | BookKeeper储存架构解析 | Pulsar运维 | MQ设计精要 | Pulsar vs Kafka | 从RabbitMQ 到 Pulsar | 内存使用原理 | 从Kafka到Pulsar | 跨地域复制 | Spring + Pulsar | Doris + Pulsar | SpringBoot + Pulsar 

图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值