大家好,我是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. 强耦合:
A2AClient
和 HTTP 通信方式焊死在了一起。如果有一天我们想换成 gRPC,或者别的什么协议,怎么办?得大改A2AClient
。2. 可靠性问题:如果目标 Agent 宕机了,HTTP 请求直接就失败了。没有重试、没有缓冲,消息就这么丢了。在一个需要高可靠的生产环境里,这可不太妙。
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. 可靠的信使 (Request-Reply):对于
tasks/get
这样的请求-响应调用,我们利用 Pulsar 的 RPC 模式。客户端把请求发到一个request-topic
,然后在一个专属的reply-topic
上等待回复。消息在 Pulsar 里是持久化的,就算目标 Agent 当时离线,等它上线后依然能收到任务并处理,保证了消息不丢失。**这里我使用了我之前贡献的使用pulsar-client实现的request-reply模型 pulsar-rpc-contrib2. 事件总线 (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
机器学习
物联网
云兴科技智慧城市 | 科拓停车 | 华为云 | 清华大学能源互联网创新研究院 | 涂鸦智能
通信
教育
推荐阅读
资料合集 | 实现原理 | BookKeeper储存架构解析 | Pulsar运维 | MQ设计精要 | Pulsar vs Kafka | 从RabbitMQ 到 Pulsar | 内存使用原理 | 从Kafka到Pulsar | 跨地域复制 | Spring + Pulsar | Doris + Pulsar | SpringBoot + Pulsar