MCP 解决了 Agent 和工具之间的通信问题。但如果两个 Agent 需要协作呢?Google 在 2025 年提出的 A2A 协议,就是为这个场景设计的。
从一个真实的协作场景说起
想象这样一个任务:
"分析我们公司 Q1 的销售数据,生成报告,并把关键指标发到 Slack"
一个 Agent 独立完成这个任务,需要:
- 连接数据库读数据
- 做数据分析
- 生成可视化图表
- 写报告文字
- 调用 Slack API 发消息
这五件事,每件都有专门的工具和专门的 AI Agent 擅长做。让一个 Agent 全包,不如把任务分配给专门的 Agent 团队来做。
这就是 A2A(Agent-to-Agent)协议要解决的问题。
A2A 是什么
A2A(Agent-to-Agent Protocol)是 Google 于 2025 年提出的开放协议,目的是让不同的 AI Agent 之间能够:
- 互相发现:知道对方的存在和能力
- 互相通信:用标准格式传递任务和结果
- 互相协作:把复杂任务拆解,分配给最合适的 Agent 执行
css
A2A 之前:
Agent A → 直接调用工具(只能做自己会的事)
A2A 之后:
Agent A → 发现 Agent B(擅长数据分析)
→ 委托 B 分析数据
→ B 返回结果给 A
→ A 继续后续任务
A2A vs MCP:解决的不同问题
这是最容易混淆的地方:
| 协议 | 连接对象 | 核心问题 |
|---|---|---|
| MCP | Agent ↔ 工具/数据 | "AI 怎么调用外部工具?" |
| A2A | Agent ↔ Agent | "AI 怎么和另一个 AI 协作?" |
类比:
- MCP 是给 Agent 装了一个工具箱
- A2A 是让多个 Agent 组建了一个团队
两者不互斥,实际系统中经常同时使用:
- 每个 Agent 通过 MCP 连接自己的工具
- 多个 Agent 通过 A2A 协作完成复杂任务
A2A 的核心概念
Agent Card(能力名片)
每个 A2A Agent 都有一个 Agent Card,描述自己的能力,以 JSON 格式托管在固定位置(/.well-known/agent.json):
json
{
"name": "DataAnalystAgent",
"description": "专门负责数据分析、统计计算和趋势识别的 Agent",
"url": "https://data-agent.example.com",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": true
},
"skills": [
{
"id": "analyze-sales",
"name": "销售数据分析",
"description": "分析销售数据,生成趋势报告和预测",
"inputModes": ["text", "data"],
"outputModes": ["text", "data", "file"]
},
{
"id": "visualize-data",
"name": "数据可视化",
"description": "将数据转化为图表(柱状图、折线图、饼图等)",
"inputModes": ["data"],
"outputModes": ["file", "image"]
}
],
"defaultInputMode": "text",
"defaultOutputMode": "text"
}
Task(任务单元)
Agent 之间通过 Task 传递工作。一个 Task 有完整的生命周期:
bash
submitted → working → (streaming updates) → completed/failed
json
// 发送任务:A 委托 B 分析数据
{
"id": "task-001",
"message": {
"role": "user",
"parts": [
{"text": "分析这份 CSV 数据,找出 Q1 销售额最高的 5 个产品"},
{"file": {"name": "sales_q1.csv", "data": "base64encodeddata..."}}
]
}
}
// 任务更新(流式,实时进度)
{
"id": "task-001",
"status": {"state": "working"},
"message": {
"role": "agent",
"parts": [{"text": "正在读取 CSV 文件,共 1234 行..."}]
}
}
// 任务完成
{
"id": "task-001",
"status": {"state": "completed"},
"artifacts": [
{
"name": "analysis_result",
"parts": [{"text": "分析结果:销售额TOP5产品为..."}]
}
]
}
动手实现:一个最简单的 A2A Agent
用 Python 实现一个天气查询 A2A Agent:
python
# weather_agent.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
import httpx
import uvicorn
app = FastAPI()
# Agent Card:托管在 /.well-known/agent.json
AGENT_CARD = {
"name": "WeatherAgent",
"description": "实时天气查询 Agent,支持全球城市天气查询",
"url": "http://localhost:8001",
"version": "1.0.0",
"capabilities": {"streaming": False},
"skills": [
{
"id": "get-weather",
"name": "天气查询",
"description": "查询指定城市的实时天气和未来预报",
"inputModes": ["text"],
"outputModes": ["text"]
}
]
}
# Task 数据模型
class TaskRequest(BaseModel):
id: str
message: dict
class TaskResponse(BaseModel):
id: str
status: dict
artifacts: Optional[list] = None
@app.get("/.well-known/agent.json")
def get_agent_card():
"""对外暴露 Agent 能力名片"""
return AGENT_CARD
@app.post("/tasks/send")
async def handle_task(request: TaskRequest) -> TaskResponse:
"""接收并处理任务"""
# 提取用户消息
user_text = ""
for part in request.message.get("parts", []):
if "text" in part:
user_text += part["text"]
# 解析城市名(简化处理)
city = extract_city(user_text)
if not city:
return TaskResponse(
id=request.id,
status={"state": "failed"},
artifacts=[{"parts": [{"text": "请提供城市名称"}]}]
)
# 查询天气(这里用模拟数据)
weather_data = await query_weather(city)
return TaskResponse(
id=request.id,
status={"state": "completed"},
artifacts=[{
"name": "weather_result",
"parts": [{"text": weather_data}]
}]
)
def extract_city(text: str) -> str:
"""简单的城市名提取(实际应用中用 NLP)"""
# 简化:直接返回用户输入的第一个名词
words = text.replace("天气", "").replace("查询", "").strip()
return words if words else ""
async def query_weather(city: str) -> str:
"""查询天气(示例使用模拟数据)"""
# 实际应用中调用真实天气 API
return f"{city}今天天气晴,气温 22°C,东风 3 级,空气质量优"
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8001)
主 Agent 如何发现和调用子 Agent
python
# orchestrator.py:编排多个 A2A Agent 完成复杂任务
import httpx
import asyncio
import uuid
class A2AOrchestrator:
def __init__(self):
self.known_agents = {}
async def discover_agent(self, base_url: str) -> dict:
"""发现 Agent:读取其 Agent Card"""
async with httpx.AsyncClient() as client:
r = await client.get(f"{base_url}/.well-known/agent.json")
card = r.json()
self.known_agents[card["name"]] = {
"url": base_url,
"card": card
}
print(f"发现 Agent:{card['name']} - {card['description']}")
return card
async def send_task(self, agent_name: str, message: str) -> str:
"""向指定 Agent 发送任务,等待结果"""
if agent_name not in self.known_agents:
raise ValueError(f"未知 Agent: {agent_name}")
agent_url = self.known_agents[agent_name]["url"]
task_id = str(uuid.uuid4())
async with httpx.AsyncClient() as client:
response = await client.post(
f"{agent_url}/tasks/send",
json={
"id": task_id,
"message": {
"role": "user",
"parts": [{"text": message}]
}
},
timeout=30.0
)
result = response.json()
if result["status"]["state"] == "completed":
# 提取文本结果
for artifact in result.get("artifacts", []):
for part in artifact.get("parts", []):
if "text" in part:
return part["text"]
return f"任务失败:{result['status']}"
# 使用示例
async def main():
orchestrator = A2AOrchestrator()
# 发现可用的 Agent
await orchestrator.discover_agent("http://localhost:8001") # WeatherAgent
await orchestrator.discover_agent("http://localhost:8002") # DataAnalystAgent
# 协调任务
print("\n--- 开始任务协调 ---")
# 让天气 Agent 查询天气
weather = await orchestrator.send_task("WeatherAgent", "查询上海的天气")
print(f"天气结果:{weather}")
# 任务链:把天气数据传给数据分析 Agent
trend = await orchestrator.send_task(
"DataAnalystAgent",
f"基于以下天气数据分析本周出行建议:{weather}"
)
print(f"出行建议:{trend}")
asyncio.run(main())
总结
A2A 协议的核心价值:
- 标准化 Agent 发现:通过 Agent Card,Agent 能自动发现其他 Agent 的能力
- 标准化任务传递:统一的 Task 格式,跨 Agent、跨厂商通用
- 支持复杂协作:多 Agent 编排、流式更新、长任务跟踪
A2A 和 MCP 合在一起,构成了"AI 团队"的完整基础设施:
- MCP:每个团队成员和工具的接口
- A2A:团队成员之间的协作协议
下一篇将继续深入 A2A 的进阶内容:流式通信、认证安全、多轮对话任务。