图片
A2A不仅仅是一个技术协议,它代表了一种新的思维方式:不要试图创造一个无所不能的超级智能体,而是让专业的智能体各司其职,通过协作解决复杂问题。

引子:当AI智能体学会”打电话”

想象一下这样的场景:你在公司遇到一个棘手问题,需要财务部门查发票、法务部门查政策、物流部门查货运信息。你会怎么做?当然是分别联系这三个部门的同事,让他们各自提供专业信息,最后你汇总整理给出解决方案。

现在,如果把这些部门换成AI智能体呢?发票智能体、政策智能体、物流智能体……它们能像人类同事一样互相协作吗?

答案是:能!这就是我们今天要聊的A2A(Agent-to-Agent)技术——一个让AI智能体之间能够”打电话”、”发消息”、无缝协作的通信协议。

一、A2A是什么?为什么我们需要它?

1.1 从”单打独斗”到”团队作战”

早期的AI应用大多是单体架构:一个大模型试图解决所有问题。就像让一个人既当会计、又当律师、还要兼职快递员——听起来就不靠谱对吧?

随着AI技术的发展,我们意识到:专业的事应该交给专业的智能体。但问题来了:

  • 发票智能体只懂财务数据
  • 政策智能体只懂公司规章
  • 物流智能体只懂货运信息

它们怎么协作?总不能让开发者手动写一堆胶水代码,把A智能体的输出复制粘贴给B智能体吧?

1.2 A2A协议:智能体世界的”通用语言”

A2A(Agent-to-Agent)协议就是为了解决这个问题而生的。它是由Google主导、多家科技公司参与制定的开放标准,定义了智能体之间如何:

  • 发现彼此:就像通讯录,知道哪个智能体能干什么
  • 交换消息:标准化的消息格式,确保”鸡同鸭讲”不会发生
  • 管理会话:记住上下文,不用每次都重新自我介绍
  • 处理任务:支持长时间运行的复杂任务

用人话说:A2A就是智能体世界的”微信协议”——让不同的智能体能够互相加好友、发消息、协同工作。

二、Microsoft Agent Framework中的A2A实现

Microsoft Agent Framework是微软推出的企业级智能体开发框架,它对A2A协议提供了完整的C#实现。让我们看看它是如何工作的。

2.1 核心架构:三层设计

整个A2A实现可以分为三层:

图片图片

应用层负责业务逻辑,你只需要关心”我要调用哪个智能体”,而不用管底层通信细节。

协议层是核心,负责把Agent Framework的ChatMessage转换成A2A协议的AgentMessage,反之亦然。这就像翻译官,确保双方能听懂对方说什么。

传输层使用标准HTTP协议,支持普通请求和流式响应(SSE),让智能体可以实时”聊天”。

2.2 智能体发现:AgentCard机制

在A2A世界里,每个智能体都有一张”名片”——AgentCard。这张名片告诉别人:

var invoiceAgentCard = new AgentCard()
{
    Name = "InvoiceAgent",
    Description = "处理发票相关查询的专家",
    Version = "1.0.0",
    DefaultInputModes = ["text"],      // 我能接受文本输入
    DefaultOutputModes = ["text"],     // 我会返回文本输出
    Capabilities = new AgentCapabilities()
    {
        Streaming = false,              // 我不支持流式响应
        PushNotifications = false       // 我不会主动推送消息
    },
    Skills = [
        new AgentSkill()
        {
            Name = "InvoiceQuery",
            Description = "查询发票信息",
            Examples = ["列出Contoso公司的最新发票"]
        }
    ]
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

这张名片通过标准的.well-known/agent-card.json端点暴露出来,任何客户端都可以访问并了解这个智能体的能力。

2.3 消息转换:从ChatMessage到AgentMessage

Agent Framework内部使用ChatMessage表示对话消息,而A2A协议使用AgentMessage。框架提供了优雅的转换机制:

// 将多条ChatMessage合并转换为单个A2A消息
internal static AgentMessage ToA2AMessage(this IEnumerable<ChatMessage> messages)
{
    List<Part> allParts = [];
    
    foreach (var message in messages)
    {
        if (message.Contents.ToParts() is { Count: > 0 } ps)
        {
            allParts.AddRange(ps);
        }
    }
    
    return new AgentMessage
    {
        MessageId = Guid.NewGuid().ToString("N"),
        Role = MessageRole.User,
        Parts = allParts,
    };
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

这个设计很巧妙:它把多条内部消息”打包”成一个A2A消息,减少网络往返次数,提高效率。

2.4 会话管理:ContextId的妙用

A2A协议通过ContextId来维护会话上下文。框架中的A2AAgentThread专门管理这个ID:

public sealed class A2AAgentThread : ServiceIdAgentThread
{
    public string? ContextId
    {
        get { return this.ServiceThreadId; }
        internal set { this.ServiceThreadId = value; }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

第一次对话时,ContextId为空,服务端会生成一个新ID返回。后续对话带上这个ID,服务端就知道”哦,这是老朋友,我们之前聊过”,从而保持上下文连贯性。

三、实战案例:客户投诉处理系统

理论讲完了,来点实际的。我们看一个真实场景:客户投诉说收到的T恤数量不对。

3.1 系统架构

图片图片

三个专业智能体各司其职:

  • 发票智能体:查询订单和发票信息
  • 政策智能体:提供公司处理短缺货物的政策
  • 物流智能体:查询实际发货数量

3.2 服务端实现:暴露A2A端点

创建一个A2A服务端非常简单,只需几行代码:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// 创建发票智能体
AIAgent invoiceAgent = new OpenAIClient(apiKey)
    .GetChatClient("gpt-4o-mini")
    .CreateAIAgent(
        instructions: "你是发票查询专家",
        name: "InvoiceAgent",
        tools: invoiceQueryTools  // 绑定查询工具
    );

// 准备智能体名片
AgentCard agentCard = GetInvoiceAgentCard();

// 映射A2A端点 - 就这么简单!
app.MapA2A(
    invoiceAgent,
    path: "/",
    agentCard: agentCard
);

await app.RunAsync();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

MapA2A这个扩展方法做了很多”脏活累活”:

  • 注册/.well-known/agent-card.json端点返回名片
  • 注册/端点处理A2A消息
  • 自动处理消息序列化/反序列化
  • 管理会话状态

开发者只需要关注业务逻辑,协议细节全部被框架屏蔽了。

3.3 客户端实现:调用远程智能体

客户端更有意思。它把远程智能体当作”工具”来使用:

// 连接到三个远程智能体
var agentUrls = new[] {
    "http://localhost:5000/",  // 发票智能体
    "http://localhost:5001/",  // 政策智能体
    "http://localhost:5002/"   // 物流智能体
};

// 为每个URL创建A2A代理
var agents = await Task.WhenAll(
    agentUrls.Select(CreateAgentAsync)
);

// 把远程智能体转换为本地"工具"
var tools = agents.Select(agent => 
    (AITool)agent.AsAIFunction()
).ToList();

// 创建主控智能体,它会自动调用这些"工具"
var hostAgent = new OpenAIClient(apiKey)
    .GetChatClient("gpt-4o-mini")
    .CreateAIAgent(
        instructions: "你负责协调各个专业智能体来回答用户问题",
        name: "HostClient",
        tools: tools  // 远程智能体变成了本地工具!
    );
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

这里的魔法在于AsAIFunction()方法——它把一个远程智能体包装成本地函数工具。当主控智能体需要查发票时,它会”调用”这个工具,实际上是通过A2A协议向远程智能体发送消息。

3.4 运行效果

用户输入:

客户投诉订单TICKET-XYZ987,说收到的T恤数量不对
  • 1.

主控智能体的处理流程:

  1. 分析问题,决定需要查询发票、政策和物流信息
  2. 并发调用三个远程智能体(通过A2A协议)
  3. 汇总信息,生成回复

订单TICKET-XYZ987的详情:
- 发票号:INV789
- 订购T恤:150件,单价$10
- 实际发货:900件(物流扫描数据)

根据《短缺货物处理政策V2.1》:
1. 核实内部记录和物流数据
2. 确认差异后,为缺失商品开具信用额度
3.2个工作日内通知客户,引用发票号和信用凭证号

建议:请提供客户实际收到的T恤数量,以便进一步处理。
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

整个过程中,三个智能体各自独立运行,通过A2A协议无缝协作,就像人类团队一样高效。

四、技术亮点与设计哲学

4.1 统一抽象:AIAgent接口

Microsoft Agent Framework最聪明的地方在于:无论是本地智能体还是远程A2A智能体,都实现同一个AIAgent接口。

public abstract class AIAgent
{
    public abstract AgentThread GetNewThread();
    
    public abstract Task<AgentRunResponse> RunAsync(
        IEnumerable<ChatMessage> messages,
        AgentThread? thread = null,
        AgentRunOptions? options = null,
        CancellationToken cancellationToken = default
    );
    
    public abstract IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(...);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

这意味着:

  • 对开发者透明:调用本地智能体和远程智能体的代码完全一样
  • 易于测试:可以用本地mock替换远程智能体
  • 灵活部署:今天是本地调用,明天改成远程调用,代码不用改

4.2 流式响应:实时交互体验

A2A协议支持Server-Sent Events(SSE),让智能体可以流式返回结果:

public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(...)
{
    var a2aSseEvents = this._a2aClient
        .SendMessageStreamingAsync(params, cancellationToken)
        .ConfigureAwait(false);
    
    await foreach (var sseEvent in a2aSseEvents)
    {
        if (sseEvent.Data is AgentMessage message)
        {
            yield return new AgentRunResponseUpdate
            {
                AgentId = this.Id,
                Contents = message.Parts.Select(p => p.ToAIContent()).ToList(),
                // ...
            };
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

用户不用傻等30秒看到完整回复,而是像聊天一样,智能体”打字”的过程实时可见。这对用户体验的提升是巨大的。

4.3 错误处理:优雅降级

框架对异常情况做了周到的处理。比如会话ID不匹配:

private static void UpdateThreadConversationId(A2AAgentThread? thread, string? contextId)
{
    // 检测服务端返回的contextId是否与本地thread的ID一致
    if (thread.ContextId is not null && 
        contextId is not null && 
        thread.ContextId != contextId)
    {
        throw new InvalidOperationException(
            "服务端返回的contextId与本地thread不一致,可能是会话状态错乱"
        );
    }
    
    // 首次对话,保存服务端生成的ID
    thread.ContextId ??= contextId;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

这种防御性编程确保了系统的健壮性——即使出错,也能给出清晰的错误信息,而不是让开发者一头雾水。

4.4 扩展性:插件化架构

整个A2A实现采用扩展方法模式,不侵入核心框架:

// A2AClientExtensions.cs
public static AIAgent GetAIAgent(this A2AClient client, ...)
{
    return new A2AAgent(client, ...);
}

// EndpointRouteBuilderExtensions.cs
public static IEndpointConventionBuilder MapA2A(
    this IEndpointRouteBuilder endpoints, 
    AIAgent agent, 
    string path
)
{
    // 注册A2A端点
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

这种设计让A2A功能成为”可选插件”:

  • 不需要A2A?不引用相关NuGet包即可
  • 需要自定义协议?实现自己的扩展方法
  • 想换其他协议?不影响核心框架

五、实际应用场景

5.1 企业内部智能体生态

想象一个大型企业:

  • HR智能体:处理员工请假、薪资查询
  • IT智能体:处理设备申请、权限管理
  • 财务智能体:处理报销、预算查询
  • 法务智能体:提供合同审查、合规建议

员工只需要和一个”前台智能体”对话,它会自动协调后台各个专业智能体,就像真实的企业运作一样。

5.2 跨组织协作

更进一步,A2A协议是开放标准,不同公司的智能体也能互通:

供应商智能体 ←→ A2A协议 ←→ 采购商智能体
  • 1.

供应商的库存智能体可以直接和采购商的订单智能体对话,自动完成询价、下单、确认流程,无需人工介入。

5.3 多模态智能体协作

A2A协议支持多种输入输出模式(文本、图像、音频等):

DefaultInputModes = ["text", "image"],
DefaultOutputModes = ["text", "image", "audio"]
  • 1.
  • 2.

这意味着:

  • 视觉智能体分析图片
  • 语音智能体转录音频
  • 文本智能体生成回复
  • 它们通过A2A协议无缝配合

六、挑战与未来展望

6.1 当前的局限性

A2A协议还在快速演进中(目前还是草案状态),有些功能尚未完善:

  1. 长时间任务支持:当前主要支持即时消息,对于需要几小时甚至几天的任务,支持还不够完善
  2. 安全认证:跨组织调用时的身份验证、权限控制还需要更多标准化
  3. 服务发现:如何在海量智能体中找到合适的那一个?需要更好的注册中心机制

6.2 技术演进方向

更智能的路由:主控智能体如何决定调用哪个远程智能体?未来可能需要:

  • 智能体能力的语义理解
  • 基于历史表现的智能体推荐
  • 动态负载均衡

更丰富的交互模式

  • 智能体之间的”群聊”(多方协作)
  • 智能体的”订阅-发布”模式(事件驱动)
  • 智能体的”工作流编排”(复杂业务流程)

更强的可观测性

  • 分布式追踪(OpenTelemetry集成)
  • 智能体调用链可视化
  • 性能监控和异常告警

6.3 对开发者的意义

A2A技术的成熟将带来范式转变:

从”写代码”到”编排智能体”:未来的开发者可能不需要写太多业务逻辑代码,而是:

  1. 定义智能体的能力边界
  2. 设计智能体之间的协作关系
  3. 监控和优化智能体生态

**从”单体应用”到”智能体网络”**:就像微服务架构改变了后端开发,A2A可能会催生”智能体即服务”(Agent-as-a-Service)的新模式。

七、总结:智能体协作的新时代

回到文章开头的问题:AI智能体能像人类一样协作吗?

通过A2A技术,答案是肯定的。Microsoft Agent Framework的实现向我们展示了:

  1. 标准化协议让不同智能体能够”说同一种语言”
  2. 统一抽象让开发者无需关心底层通信细节
  3. 优雅设计让系统既强大又易用

更重要的是,A2A不仅仅是一个技术协议,它代表了一种新的思维方式:不要试图创造一个无所不能的超级智能体,而是让专业的智能体各司其职,通过协作解决复杂问题

这就像人类社会的运作方式——没有人能精通所有领域,但通过分工协作,我们建立了复杂的现代文明。AI智能体的未来,或许也是如此。

当你的应用需要处理复杂的多领域问题时,不妨考虑A2A架构:让每个智能体专注做好一件事,然后让它们像团队一样协作。这可能比训练一个”全能”模型更高效、更可维护、也更符合真实世界的运作方式。

文章来自:51CTO

 

Loading

作者 yinhua

发表回复