Skip to content

A2A 协议:Agent 间通信的开放标准

适用读者:需要构建多 Agent 系统、跨服务调用 Agent 的开发者 前置知识:理解 Agent 管道、IAgent 接口、Hosting 层基本用法 本文目标:从报文字段到底层数据流,讲透 A2A 协议的设计哲学、消息格式和常见误区


概述

什么是 A2A?

A2A(Agent-to-Agent)是由 Google 发起、Microsoft Agent Framework 原生实现的开放 Agent 间通信协议。它的目标是为不同框架、不同语言、不同运行时上的 Agent 提供一套通用的"互操作语言"。

想象一下:你有一个用 MAF(C#)写的客服 Agent,想调用另一个用 Python 写的订单查询 Agent——没有 A2A 的话,你需要手写 REST 客户端、定义自定义 schema、处理错误重试、管理任务生命周期。有了 A2A,两个 Agent 只需各自实现一个 A2AService 端点,就能像调用本地方法一样相互通信。

A2A 与 MCP 有何不同?

这是开发者最常混淆的点。两者的定位完全不同:

维度A2AMCP(Model Context Protocol)
通信双方Agent ↔ AgentLLM ↔ 工具/数据源
抽象层级任务编排层工具调用层
调用模式异步任务 + 长期运行同步请求-响应
状态管理Task 生命周期 + 上下文隔离无状态工具调用
典型场景多 Agent 协作、Orchestrator-Worker数据库查询、文件读写、API 调用

一句话总结:MCP 让 LLM 能调用工具,A2A 让 Agent 能找其他 Agent 干活

A2A 协议栈总览

A2A 定义了一个完整的通信栈,从上到下:

┌───────────────────────────────────────────────────┐
│                  Agent Card                        │ ← 发现/注册
│  名字 · 描述 · 技能列表 · 能力声明 · 安全需求       │
├───────────────────────────────────────────────────┤
│                    任务层 Task                      │ ← 编排
│  SendMessage · GetTask · CancelTask · Subscribe    │
│  状态机: SUBMITTED → WORKING → COMPLETED/FAILED   │
├───────────────────────────────────────────────────┤
│                    消息层 Message                   │ ← 通信
│  Message + Part:文本 · 文件 · 结构化数据 · 引用    │
│  context_id · task_id · role · metadata            │
├───────────────────────────────────────────────────┤
│                   传输层 Transport                   │ ← 传输
│  HTTP+JSON · gRPC · JSON-RPC · Server-Sent Events │
└───────────────────────────────────────────────────┘

核心概念

1. Agent Card:Agent 的名片

每个 A2A Agent 都必须暴露一个 AgentCard,它是 Agent 的自我声明。客户端通过读取 AgentCard 来了解这个 Agent 能做什么、怎么联系它。

json
{
  "name": "订单查询助手",
  "description": "查询用户订单状态、物流追踪信息",
  "version": "1.0.0",
  "provider": {
    "organization": "比特矩阵",
    "url": "https://www.bytemetrix.com"
  },
  "documentation_url": "https://docs.bytemetrix.com/order-agent",
  "icon_url": "https://cdn.bytemetrix.com/agent-icons/order-agent.png",

  "supported_interfaces": [
    {
      "url": "https://api.bytemetrix.com/a2a/order-agent",
      "protocol_binding": "HTTP+JSON",
      "protocol_version": "1.0"
    }
  ],

  "capabilities": {
    "streaming": true,
    "push_notifications": true
  },

  "skills": [
    {
      "id": "order_query",
      "name": "订单查询",
      "description": "根据订单号查询订单状态、物流信息",
      "tags": ["订单", "物流", "查询"],
      "examples": ["帮我查一下订单 ORD-2024-0001 的状态"]
    },
    {
      "id": "order_return",
      "name": "退货处理",
      "description": "处理退货申请、查询退货进度",
      "tags": ["退货", "售后"],
      "examples": ["我要退货,订单号是 ORD-2024-0001"]
    }
  ],

  "default_input_modes": ["text/plain"],
  "default_output_modes": ["text/plain", "application/json"]
}

关键字段:

  • supported_interfaces:该 Agent 支持的所有通信端点。可以同时暴露 HTTP+JSON 和 gRPC 接口,第一个为首选。
  • capabilities:能力声明——是否支持流式、推送通知、扩展 Agent Card 等。
  • skills:技能列表。这是 Agent 的"能力目录",客户端可以通过技能描述决定是否调用此 Agent。
  • security_schemes / security_requirements:安全认证需求(可选)。

⚠️ 重要AgentCard静态声明。Agent 说自己能做某件事(skill)并不代表一定能成功——服务器可能在运行时拒绝任务(REJECTED状态)。

MAF 中如何暴露 AgentCard

在 MAF 中,当你调用 AddA2AServer()MapA2A() 后,AgentCard 自动基于注册的 Agent 生成:

csharp
// AgentCard 由 MAF 自动生成,基于 Agent 的 Name 和默认配置
builder.Services.AddSingleton(sp =>
{
    var agent = sp.GetRequiredService<PirateAgent>();
    return agent; // 自动暴露为 A2A Agent
});

如果你需要自定义 AgentCard,可以显式提供:

csharp
builder.Services.AddA2AServer(options =>
{
    options.AgentCardProvider = async (context, cancellationToken) =>
    {
        return new AgentCard
        {
            Name = "支持助手",
            Description = "处理客服工单",
            Version = "1.0.0",
            Skills = new List<AgentSkill>
            {
                new()
                {
                    Id = "ticket",
                    Name = "工单处理",
                    Description = "创建、查询、关闭客服工单",
                    Tags = { "工单", "客服" }
                }
            }
        };
    };
});

2. Task:任务——A2A 的核心抽象

在 A2A 协议中,一次交互就是一个 Task。Task 是 A2A 协议中最核心的概念,它封装了:

  • 一个唯一的 task_id
  • 一个可选的 context_id(上下文分组)
  • 一个状态机(TaskStatus
  • 一组输出产物(Artifact
json
{
  "id": "task-001-abc-123",
  "context_id": "session-98765",
  "status": {
    "state": "WORKING",
    "timestamp": "2024-06-12T06:30:00Z"
  },
  "artifacts": [],
  "history": [],
  "metadata": {
    "source": "web",
    "priority": "high"
  }
}

Task 状态机

                    ┌──────────┐
                    │SUBMITTED │
                    └────┬─────┘

                    ┌────▼─────┐
           ┌───────┤  WORKING  │◄──────────┐
           │       └────┬──────┘           │
           │            │                  │
     ┌─────▼─────┐ ┌───▼────────┐    ┌────┴──────┐
     │ COMPLETED │ │INPUT_REQUIRED│    │AUTH_REQUIRED│
     └───────────┘ └───┬────────┘    └────┬──────┘
                        │                  │
                        │   (继续处理后)    │
                        └──────┬──────────┘

                          ┌────▼──────┐
                          │  WORKING   │
                          └────┬──────┘

                    ┌──────────┼──────────┐
                    │          │          │
              ┌─────▼──┐ ┌───▼────┐ ┌───▼────┐
              │FAILED  │ │CANCELED│ │REJECTED│
              └────────┘ └────────┘ └────────┘

  状态类型:
  ─────────
  终端状态:COMPLETED · FAILED · CANCELED · REJECTED
  中断状态:INPUT_REQUIRED · AUTH_REQUIRED(可恢复)
  活跃状态:SUBMITTED · WORKING

状态类型详解:

状态类型含义
SUBMITTED活跃任务已提交,等待处理
WORKING活跃正在处理中
COMPLETED终端 ✅成功完成
FAILED终端 ❌执行出错
CANCELED终端 🛑被客户端取消
REJECTED终端 🚫Agent 拒接(不匹配技能、策略拒绝)
INPUT_REQUIRED中断 ↩️Agent 需要更多输入才能继续
AUTH_REQUIRED中断 🔐需要认证

关键设计:中断状态(INPUT_REQUIRED / AUTH_REQUIRED)是 A2A 相比传统 REST API 最大的不同。Agent 不是简单地"接受请求→返回响应",它可以在对话中主动要求客户端提供更多信息,然后在同一个 Task 里继续处理。这让 A2A 天然支持多轮对话和人工介入场景。

3. Message 与 Part:通信的原子单位

Message 结构

json
{
  "message_id": "msg-xxx-001",
  "context_id": "session-98765",
  "task_id": "task-001-abc-123",
  "role": "USER",
  "parts": [
    {
      "text": "帮我查一下订单 ORD-2024-0001 的状态"
    }
  ],
  "metadata": {
    "source": "telegram"
  },
  "reference_task_ids": ["task-000-old-ref"]
}

Part 的四种类型

Part 是消息内容的容器,有四种形式:

jsonc
// 1. 文本 (text)
{ "text": "你好,有什么可以帮你的?" }

// 2. 文件原始字节 (raw) —— JSON 中为 base64 编码
{
  "raw": "iVBORw0KGgo...",
  "filename": "screenshot.png",
  "media_type": "image/png"
}

// 3. 文件 URL (url)
{
  "url": "https://cdn.example.com/report.pdf",
  "filename": "report.pdf",
  "media_type": "application/pdf"
}

// 4. 结构化数据 (data) —— 任意 JSON 值
{
  "data": {
    "order_id": "ORD-2024-0001",
    "status": "shipped",
    "estimated_delivery": "2024-06-15"
  },
  "media_type": "application/json"
}

4. Artifact:任务输出产物

当 Agent 完成一个任务,结果以 Artifact 形式返回:

json
{
  "artifact_id": "art-001",
  "name": "订单查询结果",
  "description": "用户请求的订单状态查询结果",
  "parts": [
    {
      "text": "订单 ORD-2024-0001 当前状态:已发货"
    },
    {
      "data": {
        "order_id": "ORD-2024-0001",
        "status": "shipped",
        "items": ["机械键盘", "鼠标垫"],
        "logistics": {
          "carrier": "顺丰速运",
          "tracking_number": "SF1234567890"
        }
      },
      "media_type": "application/json"
    }
  ]
}

协议方法详解

A2A 协议共定义了 8 个 RPC 方法,分为三组:

消息方法

SendMessage:发送消息(核心方法)

这是最常用的方法。发送一条消息给 Agent,Agent 返回一个 Task。

请求:

json
POST /message:send
Authorization: Bearer <token>

{
  "message": {
    "message_id": "msg-001",
    "context_id": "ctx-123",
    "role": "USER",
    "parts": [
      {
        "text": "我要退货,订单号 ORD-2024-0001"
      }
    ]
  },
  "configuration": {
    "accepted_output_modes": ["text/plain", "application/json"],
    "return_immediately": false,
    "history_length": 20
  }
}

关键参数:

  • return_immediatelyfalse(默认)则阻塞等待直到 Task 进入终端或中断状态;true 则立即返回 Task 引用(配合 GetTask / SubscribeToTask 异步查询结果)。
  • history_length:控制返回的对话历史数量。0 表示不要历史,不设置则不限制。
  • task_push_notification_config:可选的推送通知配置,让 Agent 在 Task 状态变化时主动通知客户端。

响应(return_immediately=false,等待完成):

json
{
  "task": {
    "id": "task-001-abc-123",
    "context_id": "ctx-123",
    "status": {
      "state": "COMPLETED",
      "timestamp": "2024-06-12T06:30:05Z"
    },
    "artifacts": [
      {
        "artifact_id": "art-001",
        "name": "退货申请结果",
        "parts": [
          {
            "text": "退货申请已提交,退货编号:RMA-2024-0001"
          }
        ]
      }
    ],
    "history": [
      {
        "message_id": "msg-001",
        "role": "USER",
        "parts": [{ "text": "我要退货,订单号 ORD-2024-0001" }]
      },
      {
        "message_id": "msg-002",
        "role": "AGENT",
        "parts": [{ "text": "退货申请已提交,退货编号:RMA-2024-0001" }]
      }
    ]
  }
}

响应(return_immediately=true,立即返回):

json
{
  "task": {
    "id": "task-001-abc-123",
    "context_id": "ctx-123",
    "status": {
      "state": "SUBMITTED",
      "timestamp": "2024-06-12T06:30:00Z"
    },
    "artifacts": []
  }
}

SendStreamingMessage:流式消息

适用于需要实时反馈的场景(如打字机效果、进度条)。请求体与 SendMessage 相同,但响应是 SSE(Server-Sent Events)流。

POST /message:stream

→ 请求体同 SendMessage

← 事件流(SSE):
data: {"type":"taskStatusUpdate","taskId":"task-001","contextId":"ctx-123","status":{"state":"SUBMITTED"}}

data: {"type":"taskStatusUpdate","taskId":"task-001","contextId":"ctx-123","status":{"state":"WORKING"}}

data: {"type":"taskStatusUpdate","taskId":"task-001","contextId":"ctx-123","status":{"state":"WORKING","message":{"role":"AGENT","parts":[{"text":"正在查询您的订单..."}]}}}

data: {"type":"taskArtifactUpdate","taskId":"task-001","contextId":"ctx-123","artifact":{"artifactId":"art-001","parts":[{"text":"退货申请已提交"}]},"append":false,"lastChunk":true}

data: {"type":"taskStatusUpdate","taskId":"task-001","contextId":"ctx-123","status":{"state":"COMPLETED"}}

流中包含两类事件:

  • taskStatusUpdate:状态变化通知
  • taskArtifactUpdate:产物更新通知(append=true 表示增量追加,lastChunk=true 表示最终块)

任务管理方法

GetTask:查询任务状态

json
GET /tasks/task-001-abc-123

→ 响应:完整 Task 对象(当前状态 + artifacts + history)

ListTasks:列出任务

json
GET /tasks?page_size=20&page_token=xxx

→ 响应:
{
  "tasks": [...],
  "next_page_token": "yyy"
}

CancelTask:取消任务

json
POST /tasks/task-001-abc-123:cancel

→ 响应:已取消的 Task(state="CANCELED"

SubscribeToTask:订阅任务更新

http
GET /tasks/task-001-abc-123:subscribe

→ SSE 流:持续推送 taskStatusUpdate 和 taskArtifactUpdate,直到任务进入终端状态

发现方法

GetExtendedAgentCard:获取 Agent Card

json
GET /extendedAgentCard

→ 响应:AgentCard 对象(同上面 AgentCard 示例)

扩展版 Agent Card 通常需要认证后才能获取,提供比公开信息更详细的能力声明。


⚠️ 上下文共享:最常见的误解

这是 A2A 协议中最容易踩坑的地方,值得专门解释。

误解 1:"A2A Agent 之间共享对话上下文"

❌ 错误理解:Agent A 向 Agent B 发了消息,Agent B 能"看到"Agent A 的所有对话历史。

✅ 事实:A2A 不自动共享上下文。Agent B 只知道自己 Task 范围内的消息。context_id 只是一个关联标识符,不是"上下文传输通道"。

❌ 错误模型:
Agent A 的完整对话 ──────────────→ Agent B

✅ 实际模型:
Agent A ──(SendMessage, 携带选定的消息)──→ Agent B
          Agent B 只知道这个 Task 的消息

误解 2:"context_id 等于会话共享"

❌ 错误理解:如果 Agent A 和 Agent B 使用相同的 context_id,它们就共享同一个会话。

✅ 事实context_id 只是一个用于关联 Task 的逻辑分组键。它让客户端能说"这堆消息属于同一个用户的同一通对话",但它负责数据同步。

两个 Task,同一个 context_id:
Task-1 (context_id="user-001")
Task-2 (context_id="user-001")

但 Task-2 不会"继承"Task-1 的消息历史,
除非客户端显式在 SendMessage 中设置 reference_task_ids

误解 3:"A2A 会自动传整个历史"

❌ 错误理解:每次 SendMessage 都会自动把之前的对话历史一起发过去。

✅ 事实history服务器返回的(由 history_length 控制),不是客户端发送的。客户端每次 SendMessage 只发送当前消息——如果想要历史,必须:

  1. 客户端自己维护历史
  2. 在单次请求中用 reference_task_ids 告知服务器去关联之前的 Task
  3. 或者完全由客户端在 message.parts 中显式拼接上下文

正确的上下文共享做法

方案 A:客户端拼接上下文(推荐)

json
{
  "message": {
    "message_id": "msg-003",
    "context_id": "ctx-123",
    "role": "USER",
    "parts": [
      {
        "text": "根据之前我们讨论的订单信息(上一步的结果:退货单已创建),请帮我联系物流方修改地址。"
      }
    ],
    "reference_task_ids": ["task-001", "task-002"]
  }
}

这里 reference_task_ids 告诉服务器"这个请求引用了之前 Task 的上下文",但服务器是否使用这些引用取决于 Agent 的实现。你不能假设服务器会自动合并历史。

方案 B:客户端显式携带历史

json
{
  "message": {
    "message_id": "msg-003",
    "context_id": "ctx-123",
    "role": "USER",
    "parts": [
      { "text": "之前的对话总结:" },
      { "text": "1. 用户查询订单 ORD-2024-0001 状态 → 已发货" },
      { "text": "2. 用户要求退货 → 退货单已创建 RMA-2024-0001" },
      { "text": "---" },
      { "text": "现在请帮我修改退货地址" }
    ]
  }
}

方案 C:利用 MAF 的 AgentSession(MAF 特有,非 A2A 标准)

在 MAF 中,如果你在同进程中用 A2AAgent 调用远程 Agent,MAF 会通过 AgentSession 维护会话上下文。但这不是 A2A 协议层面的行为,是 MAF 框架帮你做的——客户端(MAF)在幕后管理上下文串联。

总结:什么共享,什么不共享

概念是否跨 Task 共享说明
context_id✅ 联想标识只是键,不传数据
消息历史❌ 不自动共享需客户端管理或 reference_task_ids
reference_task_ids✅️ 引用声明通知"有关联",具体如何合并由 Agent 决定
Agent 内部状态❌ 绝对不共享每个 Agent 实例独立
AgentSession(MAF)🔶 MAF 框架层同进程内管理,跨 A2A 网络边界无效

MAF 中的 A2A 实现

A2AAgent 类

在 MAF 中,A2AAgent 是对远程 Agent 的本地代理。你不需要手动构造 HTTP 请求——A2AAgent 负责 A2A 协议的客户端实现。

csharp
// 1. 创建 A2A Agent 客户端(连接远程 Agent)
var a2aAgent = new A2AAgent(
    new Uri("https://order-service.example.com/a2a"),
    httpClient);

// 2. 像用本地 Agent 一样发消息
var response = await a2aAgent.RunAsync(
    "帮我查订单 ORD-2024-0001",
    cancellationToken: ct);

ChatClientAgent关键区别

特性ChatClientAgentA2AAgent
执行位置本地(本进程)远程(通过 HTTP/gRPC)
IChatClient✅ 需要本地配置❌ 不需要(远程管理)
中间件支持完整三层管道仅代理级中间件
调用模式进程内同步调用基于 Task 的异步编排
多轮对话通过 AgentSession通过 context_id 关联

服务端暴露 Agent

在 MAF 中通过 Hosting 层暴露 A2A 端点极为简单:

csharp
// Program.cs - 完整的最小化 A2A 服务端
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.A2A; // NuGet: Microsoft.Agents.AI.Hosting.A2A.AspNetCore

var builder = WebApplication.CreateBuilder(args);

// 1. 定义 Agent
var agent = new ChatClientAgent("support-agent",
    sp => sp.GetRequiredService<IChatClient>());

// 2. 注册到 DI
builder.AddAIAgent("support", agent);

// 3. 添加 A2A 服务
builder.Services.AddA2AServer();

var app = builder.Build();

// 4. 映射 A2A 端点
app.MapA2A(agent, path: "/a2a/support");

app.Run();

这会自动暴露:

GET    /a2a/support/extendedAgentCard    → AgentCard
POST   /a2a/support/message:send         → SendMessage
GET    /a2a/support/tasks/{id}            → GetTask
POST   /a2a/support/tasks/{id}:cancel     → CancelTask
GET    /a2a/support/tasks                 → ListTasks
GET    /a2a/support/tasks/{id}:subscribe  → SubscribeToTask

A2A Agent 中间件

A2A Agent 支持代理级中间件(即 Agent Run Middleware),但不支持聊天级中间件(因为没有本地 IChatClient):

csharp
// 为远程 A2A Agent 添加中间件
var a2aAgent = new A2AAgent(orderServiceUrl, httpClient)
    .Use(async (context, next) =>
    {
        Console.WriteLine($"[A2A] 发送消息至远程: {context.Input}");
        var result = await next(context);
        Console.WriteLine($"[A2A] 收到响应: {result}");
        return result;
    });

完整的多 Agent 协作示例

csharp
// 客服 Agent → 订单 Agent → 物流 Agent 的三级编排
public class CustomerServiceAgent : AIAgent
{
    private readonly A2AAgent _orderAgent;
    private readonly A2AAgent _logisticsAgent;

    public CustomerServiceAgent(
        IOptions<AgentOptions> options,
        IHttpClientFactory httpFactory) : base(options)
    {
        var http = httpFactory.CreateClient();
        _orderAgent = new A2AAgent(
            new Uri("https://order.internal/a2a"), http);
        _logisticsAgent = new A2AAgent(
            new Uri("https://logistics.internal/a2a"), http);
    }

    public override async Task<AgentResponse> RunAsync(AgentRequest request)
    {
        // 1. 先查询订单
        var orderResult = await _orderAgent.RunAsync(
            $"查询订单信息: {request.Input}",
            cancellationToken: request.CancellationToken);

        // 2. 根据订单结果查询物流
        var logisticsResult = await _logisticsAgent.RunAsync(
            $"根据以下信息查询物流: {orderResult.Message}",
            cancellationToken: request.CancellationToken);

        // 3. 合并结果返回
        return new AgentResponse
        {
            Message = $"📋 订单信息:\n{orderResult.Message}\n\n🚚 物流状态:\n{logisticsResult.Message}"
        };
    }
}

完整实战:搭建 A2A 服务端 + 客户端

服务端:暴露 Agent Card 和处理 SendMessage

csharp
// 文件: Programs.cs (服务端)
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.A2A;
using Microsoft.Agents.AI.Hosting;

var builder = WebApplication.CreateBuilder(args);

// 配置聊天客户端
builder.Services.AddChatClient(b => b
    .UseFunctionInvocation()
    .UseOpenAI(apiKey: "sk-xxx", model: "gpt-4o"));

// 定义 Agent
builder.AddAIAgent("weather", sp =>
    new ChatClientAgent("weather-agent",
        sp.GetRequiredService<IChatClient>()));

// 注册 A2A 服务
builder.Services.AddA2AServer();

var app = builder.Build();

// 暴露 A2A 端点
app.MapA2A("weather", path: "/a2a/weather");

app.Run();

客户端:通过 A2AAgent 调用远程 Agent

csharp
// 文件: Client.cs
using Microsoft.Agents.AI.A2A;

public class WeatherClient
{
    private readonly A2AAgent _agent;

    public WeatherClient()
    {
        _agent = new A2AAgent(
            new Uri("http://localhost:5000/a2a/weather"),
            new HttpClient());
    }

    public async Task QueryWeatherAsync(string city)
    {
        // 同步调用(等待完成)
        var result = await _agent.RunAsync(
            $"查询 {city} 的天气",
            cancellationToken: CancellationToken.None);

        Console.WriteLine(result.Message);

        // 或者流式调用
        await foreach (var chunk in _agent.RunStreamingAsync(
            $"查询 {city} 的天气并逐步展示",
            cancellationToken: CancellationToken.None))
        {
            Console.Write(chunk);
        }
    }
}

看看实际的 HTTP 报文

A2AAgent.RunAsync() 被调用时,实际发出的 HTTP 请求长这样:

请求报文:

http
POST /a2a/weather/message:send HTTP/1.1
Host: localhost:5000
Content-Type: application/json
Authorization: Bearer <token>

{
  "message": {
    "message_id": "msg-8a3f7e21",
    "role": "USER",
    "parts": [
      {
        "text": "查询 北京 的天气"
      }
    ]
  },
  "configuration": {
    "return_immediately": false,
    "accepted_output_modes": [
      "text/plain"
    ]
  }
}

响应报文:

http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "task": {
    "id": "task-x7k9m2n1",
    "status": {
      "state": "COMPLETED",
      "timestamp": "2024-06-12T06:35:00Z"
    },
    "artifacts": [
      {
        "artifact_id": "art-w4r5t6y7",
        "parts": [
          {
            "text": "🌤 北京 当前天气\n\n温度: 28°C\n湿度: 45%\n风向: 南风 3级\n空气质量: 良 (AQI 65)\n\n今天最高温 32°C,最低温 22°C,建议携带防晒用品。"
          }
        ]
      }
    ],
    "history": [
      {
        "message_id": "msg-8a3f7e21",
        "role": "USER",
        "parts": [{"text": "查询 北京 的天气"}]
      },
      {
        "message_id": "msg-9b4g8f32",
        "role": "AGENT",
        "parts": [{"text": "🌤 北京 当前天气..."}]
      }
    ]
  }
}

高级场景

Agent Card 签名(JWS)

为防止被篡改,AgentCard 可以附带 JSON Web Signature:

json
{
  "name": "订单查询助手",
  "signatures": [
    {
      "protected": "eyJhbGciOiJSUzI1NiJ9...",
      "signature": "cC9jL25hMER2VzRo...",
      "header": {
        "kid": "order-agent-key-2024"
      }
    }
  ]
}

推送通知

对于长期运行的任务(如数据分析、多步骤工作流),客户端可以注册推送通知:

json
{
  "message": {
    "message_id": "msg-001",
    "role": "USER",
    "parts": [{"text": "分析过去一年的销售数据"}]
  },
  "configuration": {
    "return_immediately": true,
    "task_push_notification_config": {
      "url": "https://my-app.com/a2a/notifications",
      "authentication": {
        "scheme": "Bearer",
        "credentials": "nt-xxx-yyy"
      }
    }
  }
}

当 Task 状态变化时,Agent 会主动向 url 发送通知:

http
POST /a2a/notifications HTTP/1.1
Content-Type: application/json

{
  "task_id": "task-001",
  "context_id": "ctx-123",
  "status": {
    "state": "COMPLETED"
  }
}

多协议的 Interface 绑定

一个 Agent 可以同时暴露多种协议:

json
{
  "supported_interfaces": [
    {
      "url": "https://api.example.com/a2a/rest",
      "protocol_binding": "HTTP+JSON",
      "protocol_version": "1.0"
    },
    {
      "url": "https://api.example.com/a2a/grpc",
      "protocol_binding": "GRPC",
      "protocol_version": "1.0"
    }
  ]
}

最佳实践

1. 异步优先

对于任何可能耗时超过几秒的任务,使用 return_immediately=true + SubscribeToTask 或推送通知,而非阻塞等待。

2. 合理的 history_length

json
{
  "configuration": {
    "history_length": 10
  }
}
  • 多数情况下 10-20 条消息足够
  • 设为 0 可节省带宽(客户端自己维护上下文时)
  • 不设置则服务器可能返回全部历史,产生大量冗余数据

3. 显式声明技能

在 AgentCard 中详细描述 skills,让调用方可以做出路由决策。技能声明越精确,多 Agent 系统的编排效果越好。

4. 安全认证

生产环境必须配置 security_schemes

json
{
  "security_schemes": {
    "bearerAuth": {
      "type": "http",
      "scheme": "bearer",
      "bearerFormat": "JWT"
    }
  },
  "security_requirements": [
    {
      "schemes": {
        "bearerAuth": ["read:orders", "write:orders"]
      }
    }
  ]
}

5. 容错设计

  • 调用远程 Agent 时始终设超时
  • 处理 REJECTED 状态——Agent 可能因为策略拒绝任务
  • 处理 INPUT_REQUIRED——Agent 需要更多信息时,客户端应准备好交互式补充
  • 检查 FAILED 状态的具体错误信息,不要简单重试

性能与安全

性能考虑

模式延迟适合场景
同步(return_immediately=false)毫秒-秒短查询、即时回答
异步+订阅秒-分钟数据分析、多步骤工作流
推送通知秒-小时后台批处理、定时任务
流式(SSE)低延迟打字机效果、进度更新

安全层

A2A 协议本身不指定具体的安全机制,但推荐:

  1. TLS:生产环境必须使用 HTTPS
  2. 认证:Bearer Token / OAuth2 / API Key
  3. 授权:基于 skill 的细粒度权限控制
  4. 签名:AgentCard 可附加 JWS 签名防篡改

与其他协议的关系

                    ┌──────────────────────┐
                    │      你的应用         │
                    └────────┬─────────────┘

              ┌──────────────┼──────────────┐
              │              │              │
        ┌─────▼────┐  ┌─────▼─────┐ ┌──────▼─────┐
        │ A2A      │  │ OpenAI    │ │ AG-UI      │
        │ 多Agent   │  │ 兼容端点   │ │ 调试界面   │
        │ 互操作    │  │ 单Agent   │ │            │
        └──────────┘  └───────────┘ └────────────┘
  • A2A:用于多 Agent 系统中的横向协作(Orchestrator ↔ Worker)
  • OpenAI 兼容端点:用于第三方客户端接入(如自定义 UI)
  • AG-UI:用于开发和调试(Web 交互界面)

参考资源

学而不思则罔,思而不学则殆