"MCP 解决了 Agent 和工具之间的通信,但 Agent 和 Agent 之间呢?"
一个真实的问题
你用 Claude 写了一个财务分析 Agent,同事用 Gemini 写了一个数据采集 Agent。财务分析 Agent 需要数据采集 Agent 提供最新的市场数据。
怎么对接?
最直觉的做法:开一个 REST API,财务 Agent 调采集 Agent 的接口。但问题来了------
- 接口要自己定义:你俩得坐下来商量请求/响应格式,写一份接口文档
- 状态要自己管:数据采集是个长任务(可能要跑 5 分钟),你得自己实现轮询或回调
- 能力发现要自己做:财务 Agent 怎么知道采集 Agent 能做什么?硬编码?
- 换一个 Agent 要重来:明天采集 Agent 从 Gemini 换成 Llama,所有对接代码全改
这就是 2024 年多 Agent 系统的现状:每一对 Agent 之间的对接,都是一次定制开发。
如果你的系统里有 5 个 Agent,两两互通需要 C(5,2) = 10 条定制管道。有 20 个 Agent 呢?190 条。这个 N² 爆炸问题,和微服务早期没有 gRPC / OpenAPI 时一模一样。
A2A 是什么:一句话版本
A2A(Agent-to-Agent Protocol)是 Agent 之间的 HTTP------一个标准化的通信协议,让任何框架、任何模型构建的 Agent 都能互相发现、互相委托任务、互相交换结果。
据 Linux Foundation 2026 年 4 月公告,A2A 发布一年内已有 150+ 组织支持,包括 Google、Microsoft、AWS、Salesforce、SAP、ServiceNow、IBM 等,Google ADK、LangGraph、CrewAI、LlamaIndex、Semantic Kernel 均已原生集成。
它的时间线:
| 时间 | 事件 |
|---|---|
| 2025 年 4 月 | Google 发布 A2A 协议 |
| 2025 年 6 月 | 捐献给 Linux Foundation,成为厂商中立标准 |
| 2026 年初 | v1.0 正式发布,进入生产可用阶段 |
| 2026 年 3 月 | v1.2 发布,新增签名 Agent Card、公开 RFC 流程 |
| 2026 年 4 月 | 150+ 组织支持,多个行业进入生产部署 |
先搞清楚:A2A 和 MCP 不是竞争关系
这是最常被问的问题,答案很简单------它们在不同的层。
css
┌──────────────────────────────────────────┐
│ 多 Agent 系统 │
│ │
│ Agent A ◄──── A2A ────► Agent B │ ← Agent 之间:用 A2A
│ │ │ │
│ MCP MCP │ ← Agent 到工具:用 MCP
│ │ │ │
│ 数据库 API 文件 搜索引擎 │
└──────────────────────────────────────────┘
| 维度 | MCP | A2A |
|---|---|---|
| 解决什么 | Agent 怎么调工具 | Agent 怎么委托另一个 Agent |
| 类比 | USB 接口(连接外设) | HTTP 协议(服务之间通信) |
| 交互模式 | 无状态请求/响应 | 有状态长任务(submitted → working → completed) |
| 是否知道对方 | MCP Server 对 Agent 透明 | Agent 有自己的"想法",是黑盒 |
| 规范维护 | Anthropic | Linux Foundation |
一句话:MCP 让 Agent 有工具可用,A2A 让 Agent 有同事可用。 生产系统两个都需要。
据 Augment Code 2026 年分析,2026 年的主流多 Agent 架构已经形成 MCP + A2A 双层模型:单 Agent 用 MCP 访问工具和数据,多 Agent 协作用 A2A 做任务委托和结果交换。
A2A 的三个核心概念
1. Agent Card:Agent 的"简历"
每个支持 A2A 的 Agent 都在 /.well-known/agent-card.json 发布一份 JSON 描述符(据 A2A 官方规范 v1.2),告诉世界"我是谁、我能做什么、怎么跟我说话"。
json
{
"name": "market-data-collector",
"description": "实时采集全球主要交易所的市场数据",
"version": "2.1.0",
"url": "https://agents.example.com/market-data",
"capabilities": {
"streaming": true,
"pushNotifications": true
},
"skills": [
{
"id": "fetch-realtime-quotes",
"name": "实时行情采集",
"description": "获取指定交易对的最新价格、成交量、深度",
"inputModes": ["text"],
"outputModes": ["text", "application/json"]
},
{
"id": "historical-klines",
"name": "历史K线数据",
"description": "获取指定时间范围的OHLCV K线数据",
"inputModes": ["text"],
"outputModes": ["application/json"]
}
],
"authentication": {
"schemes": ["Bearer"]
}
}
这和微服务的 OpenAPI Spec 是一个思路------先发现,再调用。客户端 Agent 读取 Agent Card 后,就知道对方有哪些 skill、接受什么输入格式、返回什么输出。不需要任何硬编码。
一个常见问题:Client Agent 怎么知道去哪里找 Agent Card?
A2A 规范本身只定义了路径格式(/.well-known/agent-card.json),不规定你如何初次发现对方的域名 ------这和 HTTP 不规定"你怎么知道服务器 IP"是同一个设计哲学。据 A2A 官方 Agent Discovery 文档,有三种实际做法:
| 发现方式 | 机制 | 适用场景 |
|---|---|---|
| Well-Known URI | 已知对方域名,直接 GET https://domain/.well-known/agent-card.json |
公开部署、有独立域名的 Agent |
| 注册表(Registry) | Agent 启动时把自己的 Card 注册到中心注册表,调用方按 skill 查询 | 内部系统有几十个 Agent,需要动态发现 |
| 直接配置 | 把 Agent Card URL 写进环境变量 / 配置文件 | 开发调试、已知合作方、数量少 |
注意 :A2A 规范没有标准化 Registry API ,注册表的查询接口由各平台自己定义。目前有社区项目在做(如 a2a-registry.dev),尚未成为标准的一部分。
实际工程中最常见的路径是:开发期用直接配置,生产系统 Agent 多了再引入 Registry。
2. Task:有状态的工作单元
A2A 里的核心交互单元是 Task,不是简单的 request/response。因为 Agent 之间的工作往往是长时间的------数据采集要跑 5 分钟,代码审查要跑 3 分钟,你不能干等着。
Task 有一个标准状态机(据 A2A 规范):
css
submitted → working → completed
→ failed
→ canceled
↕
input-required ← Agent 需要更多信息时回到这个状态
这个设计很关键:input-required 状态让 Agent 之间可以"对话" ,而不只是单向发指令。比如数据采集 Agent 发现你要的交易对不存在,它可以把任务设为 input-required,附上"请确认交易对名称"的消息。调用方看到后补充信息,任务继续执行。
3. JSON-RPC 2.0:传输层
A2A 在传输层选择了 HTTP + JSON-RPC 2.0。这个选择很务实------几乎所有语言和平台都支持 HTTP 和 JSON,没有额外的依赖。
三种交互方式:
| 方式 | 方法 | 适用场景 |
|---|---|---|
| 同步 | message/send |
快速任务(< 30s) |
| 流式 | message/stream |
需要实时进度反馈 |
| 推送 | Push Notification | 超长任务,客户端不想保持连接 |
跑一个完整的例子
下面用 Python 实现一个最小的 A2A 交互:一个"翻译 Agent"作为 A2A Server,一个"调用方"作为 A2A Client。
python
"""
A2A 协议最小完整示例
演示 Agent Card 发布 → Agent 发现 → Task 发送 → 结果接收的完整流程。
运行前提:
pip install a2a-python uvicorn httpx
"""
import asyncio
import json
import httpx
from dataclasses import dataclass, field, asdict
from typing import Optional
# ============================================================
# 第一部分:A2A Server(翻译 Agent)
# ============================================================
AGENT_CARD = {
"name": "translator-agent",
"description": "将任意文本翻译为目标语言",
"version": "1.0.0",
"url": "http://localhost:8000",
"capabilities": {"streaming": False, "pushNotifications": False},
"skills": [
{
"id": "translate",
"name": "文本翻译",
"description": "将输入文本翻译为指定的目标语言",
"inputModes": ["text"],
"outputModes": ["text"],
}
],
}
@dataclass
class Task:
id: str
status: str = "submitted"
artifacts: list = field(default_factory=list)
messages: list = field(default_factory=list)
tasks: dict[str, Task] = {}
def handle_jsonrpc(request: dict) -> dict:
"""处理 JSON-RPC 2.0 请求"""
method = request.get("method")
params = request.get("params", {})
req_id = request.get("id")
if method == "message/send":
task_id = params.get("message", {}).get("taskId", f"task-{len(tasks)+1}")
user_text = ""
for part in params.get("message", {}).get("parts", []):
if part.get("kind") == "text":
user_text = part["text"]
task = Task(id=task_id, status="working")
tasks[task_id] = task
# 模拟翻译(生产环境调用 LLM)
translated = f"[Translated] {user_text}"
task.status = "completed"
task.artifacts = [
{"parts": [{"kind": "text", "text": translated}]}
]
return {
"jsonrpc": "2.0",
"id": req_id,
"result": asdict(task),
}
return {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32601, "message": f"Method not found: {method}"},
}
# ============================================================
# 第二部分:A2A Client(调用方)
# ============================================================
async def discover_agent(base_url: str) -> dict:
"""第一步:读取 Agent Card,了解对方能做什么"""
async with httpx.AsyncClient() as client:
resp = await client.get(f"{base_url}/.well-known/agent-card.json")
return resp.json()
async def send_task(base_url: str, text: str) -> dict:
"""第二步:发送 JSON-RPC 请求,委托翻译任务"""
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": text}],
}
},
}
async with httpx.AsyncClient() as client:
resp = await client.post(base_url, json=payload)
return resp.json()
async def demo_client():
"""完整客户端流程:发现 → 委托 → 获取结果"""
base_url = "http://localhost:8000"
# Step 1: 发现
card = await discover_agent(base_url)
print(f"发现 Agent: {card['name']}")
print(f" 技能: {[s['name'] for s in card['skills']]}")
# Step 2: 委托任务
result = await send_task(base_url, "今天天气真好,适合写代码")
task = result.get("result", {})
print(f" 任务状态: {task['status']}")
print(f" 翻译结果: {task['artifacts'][0]['parts'][0]['text']}")
# ============================================================
# 第三部分:用 uvicorn 启动 Server(简易版)
# ============================================================
if __name__ == "__main__":
import uvicorn
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
async def agent_card_endpoint(request: Request):
return JSONResponse(AGENT_CARD)
async def jsonrpc_endpoint(request: Request):
body = await request.json()
return JSONResponse(handle_jsonrpc(body))
app = Starlette(routes=[
Route("/.well-known/agent-card.json", agent_card_endpoint),
Route("/", jsonrpc_endpoint, methods=["POST"]),
])
print("A2A Server 启动: http://localhost:8000")
print("Agent Card: http://localhost:8000/.well-known/agent-card.json")
print("\n另开终端运行客户端: python -c 'import asyncio; from a2a_demo import demo_client; asyncio.run(demo_client())'")
uvicorn.run(app, host="0.0.0.0", port=8000)
运行后你会看到:
ini
发现 Agent: translator-agent
技能: ['文本翻译']
任务状态: completed
翻译结果: [Translated] 今天天气真好,适合写代码
整个流程的关键点:Client 不需要知道 Server 内部用的是什么模型、什么框架。它只需要读 Agent Card、发 JSON-RPC 请求、等 Task 完成。明天 Server 从 Claude 换成 Gemini,Client 代码一行不用改。
生产环境的四个工程要点
1. 签名 Agent Card:防止冒充
v1.2 引入了签名 Agent Card (据 A2A 规范 v1.2),用加密签名做域名验证。生产环境必须启用------否则任何人都可以伪造一个 Agent Card,声称自己是你的"财务分析 Agent"。
2. Task 超时和重试
Agent 任务不像 API 调用那样快速返回。数据采集可能要跑 10 分钟,代码审查可能要跑 5 分钟。你需要:
- 设定合理的超时阈值(建议按 skill 粒度配置)
- 用
task/get轮询状态,或用message/stream获取实时进度 - 处理
failed状态的重试逻辑
3. 认证和授权
Agent Card 的 authentication 字段声明了支持的认证方式。生产环境至少用 Bearer Token,企业内网可以用 mTLS。关键原则:Agent 之间的信任不应该比微服务之间更低。
4. 多 Agent 编排
当系统有超过 3 个 Agent 时,建议引入一个编排 Agent(Orchestrator),负责:
- 维护所有 Agent Card 的注册表
- 根据任务描述匹配最合适的 Agent
- 管理 Task 的依赖关系和执行顺序
- 处理失败 Agent 的降级策略
这和微服务架构里的 API Gateway / Service Mesh 是同一个思路。
哪些场景该用 A2A,哪些不该
| 场景 | 用 A2A? | 理由 |
|---|---|---|
| 两个不同团队的 Agent 需要协作 | 用 | 跨团队 = 接口标准化 = A2A 的核心价值 |
| 一个 Agent 调外部搜索引擎 | 不用,用 MCP | 搜索引擎是工具,不是 Agent |
| Agent 需要长时间执行并报告进度 | 用 | Task 状态机 + 流式反馈 |
| 同一个进程内的两个 Agent 函数 | 不用 | 直接函数调用,加协议只是增加延迟 |
| 跨公司的 Agent 互操作 | 用 | A2A 是厂商中立标准,双方不需要用同一框架 |
| Agent 读写数据库 | 不用,用 MCP | 数据库是资源,不是自主决策的 Agent |
判断标准很简单:对方有没有自己的"想法"? 有自己的推理能力、能自主决策的用 A2A;只是被动执行指令的用 MCP。
工程结论
-
A2A 解决了多 Agent 系统的 N² 互联问题------标准协议让 N 个 Agent 只需要 N 次适配,而不是 N(N-1)/2 次定制对接。这和 HTTP 之于 Web、gRPC 之于微服务的意义完全一样。
-
A2A 和 MCP 是互补关系,不是替代关系------MCP 是 Agent 到工具的"USB 接口",A2A 是 Agent 到 Agent 的"HTTP 协议"。2026 年的主流多 Agent 架构已经形成 MCP + A2A 双层标准模型。
-
协议已经生产可用------据 Linux Foundation 2026 年 4 月数据,150+ 组织支持 A2A,Google ADK、LangGraph、CrewAI 等主流框架均已原生集成,金融、供应链、IT 运维等行业已有生产部署案例。v1.2 引入的签名 Agent Card 解决了生产环境的身份验证问题。
-
现在就该学------A2A 的核心概念只有三个(Agent Card、Task、JSON-RPC),比学 Kubernetes 简单一个数量级。等你需要跨团队、跨公司的 Agent 协作时再学,就晚了。
参考来源: