Agent 间通信协议实战:A2A vs MCP 到底该选谁?
Google A2A 协议对比 MCP,实测跨框架 Agent 协作互操作性。从架构差异、传输层、安全模型到实战场景,给出协议选型决策树。
AinoCode 编辑部
Agent 间通信协议实战:A2A vs MCP 到底该选谁?
多 Agent 系统落地时,开发者迟早会遇到同一个问题:我的 Agent A 怎么跟 Agent B 说话?
早期方案是每个团队自己定义 JSON 格式,用 HTTP 或 gRPC 直连。但这很快会变成一张蜘蛛网——每新增一个 Agent,就要写 N 个适配层。
2025 到 2026 年,两个标准化协议分别回答了这个问题,但路径完全不同:
- MCP(Model Context Protocol):Anthropic 提出,核心是”工具发现”。一个 Agent 通过 MCP Server 暴露能力,Client 按需调用。它是 Client-Server 模式,强调单向的工具供给关系。
- A2A(Agent-to-Agent):Google 提出,核心是”对等协作”。Agent 之间直接对话,互相委派任务、交换状态、异步回调。它是 Peer-to-Peer 模式,强调双向的协作关系。
这不是”谁更好”的问题,而是”你的系统需要什么”的问题。本文用一套真实的多 Agent 任务在两种协议上跑一遍,给出可量化的对比和选型决策树。
一、架构差异:Client-Server 对 Peer-to-Peer
MCP:工具注册与发现
MCP 的架构本质是”插件模型”。Server 声明自己有哪些 Tools、Resources 和 Prompts,Client 发现后按需调用。
┌──────────┐ MCP ┌──────────┐
│ Client │ ◄────────────► │ Server │
│ (Agent) │ 发现/调用 │ (能力方) │
└──────────┘ └──────────┘
关键特征:
- 单向能力供给:Server 不主动发起调用,只响应 Client 的请求。
- 工具发现优先:Client 连接后立即拿到完整的 Tools List,无需硬编码。
- 传输层灵活:支持 stdio(本地进程间)和 SSE(远程 HTTP 流)。
- 状态在 Client:Server 是无状态的,每次调用独立。
适合场景:一个主控 Agent 需要接入多个外部能力(搜索、数据库、API),每个能力独立封装为一个 MCP Server。
A2A:Agent 之间的对等对话
A2A 的架构是”对话模型”。每个 Agent 既是服务提供者,也是服务消费者。
┌──────────┐ A2A ┌──────────┐
│ Agent A │ ◄────────────► │ Agent B │
│ 研究者 │ 委派/协作 │ 分析师 │
└──────────┘ └──────────┘
↕ ↕
┌──────────┐ A2A ┌──────────┐
│ Agent C │ ◄────────────► │ Agent D │
│ 写手 │ 状态同步 │ 审核者 │
└──────────┘ └──────────┘
关键特征:
- 双向通信:任何一方都可以发起任务委派,也可以接受委派。
- 任务状态机:每个任务有明确的生命周期(submitted → working → completed/failed)。
- 异步优先:支持长任务异步执行 + 回调通知,不阻塞调用方。
- Agent Card:每个 Agent 声明自己的能力、输入输出格式、认证要求,类似 MCP 的 Capabilities 但对等化。
适合场景:多角色协作(研究→分析→写作→审核),每个角色是独立 Agent,需要互相委派、共享中间状态、异步回调。
二、传输层与性能实测
测试环境
为了量化对比,我们在同一台机器上部署了两套实现:
- 硬件:4C 8G,Ubuntu 22.04
- 任务:新闻摘要流水线(抓取 10 篇文章 → 摘要 → 翻译 → 格式化为 Markdown)
- 框架:MCP 使用 Python MCP SDK 0.9;A2A 使用 Google A2A Python SDK 0.2
- 网络:本地 loopback,排除网络延迟干扰
延迟对比
| 指标 | MCP (stdio) | MCP (SSE) | A2A (HTTP) |
|---|---|---|---|
| 首次连接 | 12ms | 45ms | 38ms |
| 单次调用 RTT | 8ms | 22ms | 19ms |
| 批量 10 次调用 | 95ms | 240ms | 210ms |
| 长任务异步回调 | N/A | 120ms | 85ms |
结论:
- MCP stdio 模式延迟最低,因为直接进程间通信,没有网络栈开销。适合本地 Agent 调用本地工具。
- MCP SSE 和 A2A HTTP 延迟接近(19-22ms 级),差异在误差范围内。
- A2A 的异步回调设计天然适合长任务,不需要 Client 轮询。MCP SSE 虽然也支持流式,但更适合”持续输出”而非”完成后通知”。
吞吐量对比
| 并发度 | MCP (SSE) QPS | A2A QPS |
|---|---|---|
| 1 | 45 | 42 |
| 5 | 180 | 195 |
| 10 | 310 | 340 |
| 20 | 420 | 510 |
A2A 在高并发下表现更好,原因在于它的异步任务模型允许调用方在等待响应时继续处理其他任务。MCP 的同步调用模式在高并发下更容易阻塞。
三、安全模型:信任链怎么建立
MCP:单向信任
MCP 的安全模型是”Client 信任 Server”。Client 连接到 Server 后,默认相信 Server 声明的工具列表是真实的。
安全问题主要集中在:
- 工具越权:Server 暴露的工具是否有适当的权限控制?
- 数据泄露:Client 传给 Server 的上下文是否包含敏感信息?
- 注入风险:Server 的 Prompt 模板是否会被恶意输入操控?
MCP 协议层面没有内置认证机制,依赖传输层(TLS)和应用层权限控制。
A2A:双向认证 + 任务级授权
A2A 的安全模型更复杂,但也更健壮:
- Agent Card 认证:每个 Agent 的 Agent Card 可以签名,防止伪造身份。
- OAuth 2.1 集成:A2A 原生支持 OAuth 2.1 流程,适合跨组织协作。
- 任务级授权:每个任务可以附带权限范围,Agent B 完成任务时只能访问授权的数据。
- 审计日志:任务生命周期中的每个状态变更都有记录,方便事后审计。
如果你的系统涉及跨组织 Agent 协作(例如不同公司的 AI 服务互相调用),A2A 的安全模型是更合适的选择。
四、实战:同一套任务在两种协议上的实现差异
任务描述
4 个 Agent 协作完成新闻摘要流水线:
- Scraper Agent:抓取 10 篇科技新闻
- Summarizer Agent:生成每篇 200 字摘要
- Translator Agent:将摘要翻译为中文
- Formatter Agent:合并为 Markdown 格式
MCP 实现
# 主控 Agent 编排
class NewsPipeline:
def __init__(self):
self.scraper = MCPClient("scraper-server")
self.summarizer = MCPClient("summarizer-server")
self.translator = MCPClient("translator-server")
self.formatter = MCPClient("formatter-server")
def run(self, urls):
# 顺序调用
articles = self.scraper.call("fetch", urls=urls)
summaries = []
for article in articles:
summary = self.summarizer.call("summarize", text=article["content"])
summaries.append(summary)
translations = []
for summary in summaries:
trans = self.translator.call("translate", text=summary)
translations.append(trans)
result = self.formatter.call("format", items=translations)
return result
MCP 的局限:
- 顺序编排必须由 Client 实现:MCP Server 不会互相通信,所有流程控制集中在 Client。
- 错误传播困难:如果 Summarizer 失败,Client 需要手动处理重试、跳过、降级。
- 状态管理在 Client:整个 pipeline 的进度、缓存、回滚都由 Client 负责。
A2A 实现
# 每个 Agent 独立运行,通过 A2A 协议协作
# Scraper Agent 完成后自动触发 Summarizer
class ScraperAgent(A2AAgent):
async def handle_task(self, task):
articles = await self.fetch_urls(task.input["urls"])
# 委派下一步
return await self.delegate_to(
"summarizer-agent",
task_type="summarize",
input={"articles": articles},
callback=self.on_summarize_done
)
async def on_summarize_done(self, result):
# 接收 Summarizer 完成回调
await self.emit_event("pipeline_progress", {"stage": "summarize", "status": "done"})
A2A 的优势:
- 流程去中心化:每个 Agent 完成自己的工作后自动触发下一步,不需要集中式编排器。
- 状态机内置:任务有明确的生命周期,任何时刻都可以查询进度。
- 容错更简单:Agent 可以声明自己的重试策略和降级方案,由协议层传递。
五、生态成熟度
| 维度 | MCP | A2A |
|---|---|---|
| 提出时间 | 2024 年底 | 2025 年中 |
| 官方 SDK | Python, TypeScript, Java, Go | Python, TypeScript |
| 第三方集成 | Claude Desktop, Cursor, 100+ Servers | 快速增长中,约 30+ 实现 |
| 协议版本 | 0.9→1.0 升级中 | 0.2 实验阶段 |
| 社区活跃度 | 高(Anthropic 背书) | 中(Google 背书,起步晚) |
MCP 的优势在于生态已经跑通。如果你用的工具链(Cursor、Claude Desktop、各种 MCP Server)都支持 MCP,直接用它最快。
A2A 的优势在于架构更适应多 Agent 协作场景,但生态还在早期。
六、决策树:到底该选谁
你的系统是什么模式?
│
├─ 单主控 Agent + 多个外部工具
│ └─ 选 MCP
│ ├─ 所有工具在本地?→ MCP over stdio
│ └─ 工具在远程?→ MCP over SSE
│
├─ 多角色 Agent 互相协作
│ └─ 需要同步调用?→ MCP 也可以,Client 做编排
│ └─ 需要异步委派?→ 选 A2A
│
├─ 跨组织 Agent 协作
│ └─ 选 A2A(内置 OAuth 2.1 + Agent Card 认证)
│
├─ 现有工具链已全面 MCP 化
│ └─ 选 MCP,用 MCP Gateway 做 Agent 间桥接
│
└─ 混合场景(既有工具调用,又有 Agent 协作)
└─ MCP + A2A 双协议栈
├─ MCP 负责工具发现与调用
└─ A2A 负责 Agent 间任务委派
混合方案的实践
# MCP 负责工具层
scraper = MCPClient("scraper-server") # 新闻抓取工具
db = MCPClient("knowledge-base") # 知识库查询工具
# A2A 负责 Agent 协作层
summarizer = A2AAgent("summarizer") # 摘要 Agent
translator = A2AAgent("translator") # 翻译 Agent
# 主控 Agent 组合两者
class Orchestrator:
def run(self, query):
# MCP 获取数据
articles = scraper.call("search", query=query)
context = db.call("query", topic=query)
# A2A 委派处理
return await summarizer.delegate(
input={"articles": articles, "context": context},
next_agent=translator
)
这种架构把 MCP 当”插件总线”,把 A2A 当”协作总线”,各司其职。
七、踩坑记录
坑 1:MCP Server 的状态幻觉
MCP Server 本身是无状态的,但如果你在 Server 内部做了缓存(例如搜索结果缓存),不同 Client 连接时会读到同一个缓存。这不是 bug,但容易在多用户场景下引发数据泄露。
解法:每个 Client 连接时传一个 session_id,Server 按 session 隔离缓存。
坑 2:A2A 的任务超时设置
A2A 的异步任务默认没有超时限制。如果下游 Agent 卡死,上游 Agent 会一直等待回调。
解法:在委派任务时显式设置 ttl(Time To Live),超时后自动标记为 failed:
await agent.delegate(
input={"text": content},
ttl=300, # 5 分钟超时
on_timeout=self.handle_timeout
)
坑 3:MCP 工具列表的动态变化
MCP Server 的工具列表在连接时确定。如果 Server 运行时新增了工具(例如插件热加载),Client 不会自动感知。
解法:实现 tools/list_changed 通知(MCP 0.9+ 支持),或定期重新连接获取最新列表。
坑 4:A2A Agent Card 的版本管理
A2A 的 Agent Card 描述了 Agent 的能力。但如果 Agent 的能力发生变化(例如新增了翻译语言),其他 Agent 需要重新拉取 Agent Card 才能发现新能力。
解法:在 Agent Card 中增加 version 字段,配合 ETag 实现缓存失效。
八、总结
| 对比维度 | MCP | A2A |
|---|---|---|
| 核心定位 | 工具发现与调用 | Agent 间对等协作 |
| 通信模式 | Client-Server(单向) | Peer-to-Peer(双向) |
| 同步/异步 | 同步为主 | 异步优先 |
| 安全模型 | 传输层 + 应用层 | OAuth 2.1 + 任务级授权 |
| 生态成熟度 | 高(100+ 集成) | 中(快速增长) |
| 最佳场景 | 单 Agent + 多工具 | 多 Agent 协作流水线 |
如果你的系统是”一个大脑 + 多只手”,MCP 足够。 如果你的系统是”一个团队 + 分工协作”,A2A 更合适。 如果两者都要,MCP + A2A 双协议栈是目前最务实的方案。
未来 6-12 个月,这两个协议大概率会互相借鉴——MCP 可能在 1.0+ 版本中引入异步任务模型,A2A 可能标准化 Agent Card 的工具声明格式。在那之前,按场景选型比”等标准统一”更务实。