适用版本:A2A Protocol v0.2+(2025 年 Google 与合作伙伴联合发布的开放标准)
目录
- 一、A2A 协议概述
- 二、核心概念
- 三、协议规范
- 四、Agent Card
- 五、消息与任务流
- 六、Python SDK 实战
- 七、TypeScript SDK 实战
- 八、多智能体编排模式
- 九、流式与异步任务
- 十、安全与认证
- 十一、与 MCP 的关系
- 十二、生产部署
- 十三、可观测性
- 十四、常见问题
- 十五、完整示例
一、A2A 协议概述
A2A(Agent2Agent) 是 Google 联合 50+ 合作伙伴(Salesforce、SAP、Atlassian、LangChain、MongoDB 等)于 2025 年发布的开放协议,目标是让异构 AI 智能体之间能跨厂商、跨框架、跨语言互相通信与协作。
1.1 解决的问题
| 痛点 | A2A 的方案 |
|---|---|
| 不同厂商 agent 各自封闭 | 统一 HTTP + JSON-RPC 通信契约 |
| 无法发现对方能力 | 标准化 Agent Card |
| 长任务无法跟踪 | Task 生命周期 + 流式更新 |
| 异步协作无统一规范 | SSE / Webhook 推送机制 |
| 多模态难以传递 | Parts(text / file / data)抽象 |
1.2 与 MCP 的边界
- MCP(Model Context Protocol):解决"模型 ↔ 工具/数据源"的连接,是 agent 内部 的工具协议
- A2A:解决"agent ↔ agent"的协作,是 agent 之间 的通信协议
二者互补不冲突,生产系统中常一起使用。
二、核心概念
┌─────────────┐ Agent Card ┌─────────────┐
│ Client │ ───────────> │ Server │
│ Agent │ (发现) │ Agent │
│ │ │ │
│ │ Task / Msg │ │
│ │ <──────────> │ │
└─────────────┘ (协作) └─────────────┘
2.1 关键实体
| 实体 | 说明 |
|---|---|
| Agent Card | 智能体的"名片",描述能力、端点、认证方式 |
| Task | 一次有状态的协作任务,有完整生命周期 |
| Message | Task 内的一次发言(用户/agent) |
| Part | Message 的最小单元(文本/文件/结构化数据) |
| Artifact | Task 产出的成果物 |
| Skill | Agent 声明的某项能力 |
2.2 任务状态机
submitted → working → input-required ──┐
│ │
├──> completed │
├──> failed │
├──> canceled │
└──> rejected <───────┘
三、协议规范
A2A 基于 HTTP(S) + JSON-RPC 2.0,主流方法集:
| 方法 | 作用 |
|---|---|
message/send | 同步发送消息并等待响应 |
message/stream | 流式发送(SSE 返回) |
tasks/get | 查询任务状态 |
tasks/cancel | 取消任务 |
tasks/pushNotificationConfig/set | 配置 webhook 推送 |
tasks/resubscribe | 断线后重新订阅流 |
3.1 请求示例
POST /a2a HTTP/1.1
Host: agent.example.com
Authorization: Bearer <token>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "req-1",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [
{ "kind": "text", "text": "帮我分析这份销售数据" }
],
"messageId": "msg-001"
}
}
}
3.2 响应示例
{
"jsonrpc": "2.0",
"id": "req-1",
"result": {
"id": "task-abc",
"status": { "state": "completed" },
"artifacts": [
{
"artifactId": "art-1",
"parts": [
{ "kind": "text", "text": "Q3 销售额同比增长 23%..." }
]
}
]
}
}
四、Agent Card
每个 A2A Agent 必须暴露一份 Agent Card,默认地址:
https://<host>/.well-known/agent.json
4.1 完整 Agent Card 示例
{
"name": "Sales Analyst Agent",
"description": "分析销售数据并生成洞察报告",
"url": "https://sales-agent.example.com/a2a",
"version": "1.2.0",
"protocolVersion": "0.2.5",
"provider": {
"organization": "Example Corp",
"url": "https://example.com"
},
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": true
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["text/plain", "application/json", "image/png"],
"skills": [
{
"id": "analyze-sales",
"name": "销售数据分析",
"description": "对 CSV/Excel 销售数据做趋势分析",
"tags": ["analysis", "sales"],
"examples": [
"分析 2024 年 Q4 的销售趋势",
"对比东西区销售业绩"
],
"inputModes": ["application/json", "text/csv"],
"outputModes": ["text/markdown", "image/png"]
}
],
"securitySchemes": {
"bearer": { "type": "http", "scheme": "bearer" }
},
"security": [{ "bearer": [] }]
}
4.2 关键字段
capabilities.streaming:是否支持message/streamcapabilities.pushNotifications:是否支持 webhook 推送skills:能力清单,用于 client agent 选择路由securitySchemes:复用 OpenAPI 3.0 的安全方案定义
五、消息与任务流
5.1 Message 与 Part
type Part =
| { kind: "text"; text: string }
| { kind: "file"; file: { name: string; mimeType: string; bytes?: string; uri?: string } }
| { kind: "data"; data: Record<string, unknown> };
interface Message {
role: "user" | "agent";
parts: Part[];
messageId: string;
taskId?: string;
contextId?: string;
}
5.2 Task 生命周期
1. client 发送 message/send,server 创建 Task(state=submitted)
2. server 进入 working,可多次推送 status-update
3. 若需补充信息 → input-required,等待 client 再次 send
4. 完成 → completed,附带 artifacts
5.3 状态更新事件
{
"kind": "status-update",
"taskId": "task-abc",
"status": {
"state": "working",
"message": {
"role": "agent",
"parts": [{ "kind": "text", "text": "正在加载数据..." }]
},
"timestamp": "2026-05-09T10:23:45Z"
},
"final": false
}
六、Python SDK 实战
官方 SDK:a2a-sdk(基于 FastAPI/httpx)
pip install a2a-sdk uvicorn
6.1 服务端:实现一个 Agent
from a2a.server import A2AServer, AgentExecutor
from a2a.types import AgentCard, Skill, Task, Message, TextPart, TaskStatus, TaskState
from a2a.server.events import EventQueue
class SalesAnalystExecutor(AgentExecutor):
async def execute(self, context, event_queue: EventQueue):
task = context.current_task
user_text = context.get_user_input()
await event_queue.enqueue_event(
TaskStatus(state=TaskState.working,
message=Message(role="agent",
parts=[TextPart(text="正在分析...")]))
)
result = await self._analyze(user_text)
await event_queue.enqueue_event(
TaskStatus(state=TaskState.completed,
message=Message(role="agent",
parts=[TextPart(text=result)]))
)
async def cancel(self, context, event_queue):
await event_queue.enqueue_event(
TaskStatus(state=TaskState.canceled)
)
async def _analyze(self, text: str) -> str:
return f"分析完成:{text} 的销售额同比增长 23%"
agent_card = AgentCard(
name="Sales Analyst",
description="销售数据分析智能体",
url="https://sales-agent.example.com/a2a",
version="1.0.0",
protocolVersion="0.2.5",
capabilities={"streaming": True, "pushNotifications": True},
defaultInputModes=["text/plain"],
defaultOutputModes=["text/plain", "text/markdown"],
skills=[Skill(
id="analyze-sales",
name="销售数据分析",
description="销售趋势分析",
tags=["sales", "analysis"],
)],
)
server = A2AServer(
agent_card=agent_card,
executor=SalesAnalystExecutor(),
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(server.app, host="0.0.0.0", port=8000)
6.2 客户端:调用其他 Agent
from a2a.client import A2AClient
from a2a.types import Message, TextPart
import asyncio
async def main():
client = await A2AClient.from_agent_card_url(
"https://sales-agent.example.com/.well-known/agent.json",
auth_token="your-bearer-token",
)
response = await client.send_message(Message(
role="user",
parts=[TextPart(text="分析 Q3 销售数据")],
messageId="msg-001",
))
for artifact in response.artifacts or []:
for part in artifact.parts:
if part.kind == "text":
print(part.text)
asyncio.run(main())
6.3 流式订阅
async def stream_demo():
client = await A2AClient.from_agent_card_url(...)
async for event in client.send_message_streaming(message):
if event.kind == "status-update":
print(f"[{event.status.state}]", event.status.message)
elif event.kind == "artifact-update":
print("产出:", event.artifact)
if getattr(event, "final", False):
break
七、TypeScript SDK 实战
官方 SDK:@a2a-js/sdk
npm i @a2a-js/sdk @a2a-js/sdk-server express
7.1 服务端
import express from "express";
import {
A2AExpressApp,
DefaultRequestHandler,
AgentExecutor,
ExecutionEventBus,
} from "@a2a-js/sdk-server";
import type { AgentCard, RequestContext } from "@a2a-js/sdk";
const agentCard: AgentCard = {
name: "Translator Agent",
description: "多语种翻译智能体",
url: "https://translator.example.com/a2a",
version: "1.0.0",
protocolVersion: "0.2.5",
capabilities: { streaming: true },
defaultInputModes: ["text/plain"],
defaultOutputModes: ["text/plain"],
skills: [{
id: "translate",
name: "翻译",
description: "中英互译",
tags: ["nlp", "translate"],
}],
};
class TranslatorExecutor implements AgentExecutor {
async execute(ctx: RequestContext, bus: ExecutionEventBus) {
const userText = ctx.userMessage.parts
.filter(p => p.kind === "text")
.map(p => (p as any).text).join("");
bus.publish({
kind: "status-update",
taskId: ctx.taskId,
status: { state: "working" },
final: false,
});
const translated = await translate(userText);
bus.publish({
kind: "artifact-update",
taskId: ctx.taskId,
artifact: {
artifactId: crypto.randomUUID(),
parts: [{ kind: "text", text: translated }],
},
});
bus.publish({
kind: "status-update",
taskId: ctx.taskId,
status: { state: "completed" },
final: true,
});
}
async cancel(ctx: RequestContext, bus: ExecutionEventBus) {
bus.publish({
kind: "status-update",
taskId: ctx.taskId,
status: { state: "canceled" },
final: true,
});
}
}
async function translate(text: string): Promise<string> {
return `[translated] ${text}`;
}
const handler = new DefaultRequestHandler(agentCard, new TranslatorExecutor());
const app = express();
new A2AExpressApp(handler).setupRoutes(app);
app.listen(8001, () => console.log("Translator agent on :8001"));
7.2 客户端
import { A2AClient } from "@a2a-js/sdk";
const client = await A2AClient.fromCardUrl(
"https://translator.example.com/.well-known/agent.json",
{ headers: { Authorization: "Bearer xxx" } },
);
const result = await client.sendMessage({
role: "user",
parts: [{ kind: "text", text: "Hello world" }],
messageId: crypto.randomUUID(),
});
console.log(result);
// 流式
for await (const event of client.sendMessageStreaming(msg)) {
console.log(event);
}
八、多智能体编排模式
8.1 路由模式(Router)
一个"调度 agent"根据用户请求路由到合适的下游 agent。
class RouterExecutor(AgentExecutor):
def __init__(self, downstream: dict[str, A2AClient]):
self.downstream = downstream
async def execute(self, ctx, bus):
intent = await classify_intent(ctx.user_input)
target = self.downstream[intent]
async for ev in target.send_message_streaming(ctx.user_message):
await bus.enqueue_event(ev)
8.2 流水线模式(Pipeline)
A → B → C 串行处理。
async def pipeline(text):
a_out = await agent_a.send_message(make_msg(text))
b_out = await agent_b.send_message(make_msg(extract_text(a_out)))
c_out = await agent_c.send_message(make_msg(extract_text(b_out)))
return c_out
8.3 并行 + 聚合(Map-Reduce)
async def parallel_query(question):
tasks = [
agent_search.send_message(make_msg(question)),
agent_kb.send_message(make_msg(question)),
agent_db.send_message(make_msg(question)),
]
results = await asyncio.gather(*tasks)
return await agent_aggregator.send_message(
make_msg("\n".join(extract_text(r) for r in results))
)
8.4 协商模式(Negotiation)
通过 input-required 状态多轮往返。
async def negotiate(client, initial_msg):
task = await client.send_message(initial_msg)
while task.status.state == "input-required":
clarification = await ask_user(task.status.message)
task = await client.send_message(clarification, task_id=task.id)
return task
九、流式与异步任务
9.1 SSE 流(默认)
POST /a2a
Accept: text/event-stream
{ "jsonrpc": "2.0", "method": "message/stream", ... }
返回:
event: status-update
data: {"taskId":"...","status":{"state":"working"},"final":false}
event: artifact-update
data: {"taskId":"...","artifact":{...}}
event: status-update
data: {"taskId":"...","status":{"state":"completed"},"final":true}
9.2 Webhook 推送(长任务)
await client.set_push_notification_config(
task_id=task.id,
config={
"url": "https://my-app.com/webhook/a2a",
"token": "shared-secret",
"authentication": {"schemes": ["bearer"]},
}
)
Server 在状态变化时主动 POST 到 webhook URL。
9.3 断线重连
async for ev in client.resubscribe(task_id="task-abc"):
...
服务端必须保留事件历史(受 stateTransitionHistory 能力位控制)。
十、安全与认证
10.1 认证方案
A2A 复用 OpenAPI Security Schemes:
| 方案 | 用途 |
|---|---|
apiKey | API Key 鉴权(header/query/cookie) |
http: bearer | OAuth 2 / JWT Bearer |
oauth2 | 完整 OAuth 2.0 流程 |
openIdConnect | OIDC |
mTLS | 互信 TLS(服务端到服务端推荐) |
10.2 OAuth 2.0 配置
{
"securitySchemes": {
"oauth2": {
"type": "oauth2",
"flows": {
"clientCredentials": {
"tokenUrl": "https://auth.example.com/token",
"scopes": {
"agent.read": "查询任务",
"agent.write": "创建任务"
}
}
}
}
},
"security": [{ "oauth2": ["agent.write"] }]
}
10.3 安全最佳实践
- 默认全部 HTTPS,拒绝 HTTP(开发除外)
- 校验对方 Agent Card 来源:防止 card 投毒
- 沙箱执行不可信 agent 返回:返回内容可能含 prompt injection
- 限制 webhook 出站:防 SSRF(白名单域名)
- Token 定期轮换:尤其 Bearer 长生命周期
- 签名验证:webhook 推送加 HMAC 签名头
def verify_webhook(req, secret):
sig = req.headers.get("X-A2A-Signature")
expected = hmac.new(secret.encode(), req.body, "sha256").hexdigest()
if not hmac.compare_digest(sig, expected):
raise PermissionError("invalid signature")
十一、与 MCP 的关系
┌───────────────────────────────────────────┐
│ Client Agent (LLM-driven) │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ MCP Tools │ │ A2A Client │ │
│ └─────────────┘ └──────────────┘ │
│ │ │ │
└─────────┼───────────────────────┼─────────┘
│ MCP │ A2A
▼ ▼
┌────────────┐ ┌────────────┐
│ Tool / DB │ │ Other │
│ / FS │ │ Agent │
└────────────┘ └────────────┘
| MCP | A2A | |
|---|---|---|
| 通信对象 | 模型 ↔ 工具/资源 | Agent ↔ Agent |
| 抽象 | tools / resources / prompts | tasks / messages / artifacts |
| 状态 | 通常无状态 | 有状态(task 生命周期) |
| 传输 | stdio / HTTP+SSE | HTTP + JSON-RPC + SSE |
| 典型用法 | LLM 调用本地工具 | 跨组织/跨厂商 agent 协作 |
十二、生产部署
12.1 部署拓扑
┌──────────────┐
│ API Gateway │ ← TLS 终止 / 限流
└──────┬───────┘
│
┌────────────┼────────────┐
│ │ │
┌──────▼─┐ ┌──────▼─┐ ┌─────▼──┐
│ Agent A│ │ Agent B│ │ Agent C│ ← 多实例 + 负载均衡
└────┬───┘ └────┬───┘ └───┬────┘
└────────┬───┴───┬───────┘
│ │
┌────▼───┐ ┌▼────────┐
│ Redis │ │Postgres │ ← Task 持久化
│(队列) │ │(状态) │
└────────┘ └─────────┘
12.2 Task 持久化
生产环境务必将 Task 状态持久化,否则进程重启丢失:
class PostgresTaskStore:
async def save(self, task: Task): ...
async def load(self, task_id: str) -> Task: ...
async def append_event(self, task_id, event): ...
server = A2AServer(
agent_card=agent_card,
executor=executor,
task_store=PostgresTaskStore(dsn="postgresql://..."),
)
12.3 横向扩展
- Stateless agent:无 task store 依赖时直接 K8s HPA
- Stateful agent:用 Redis Streams 做事件总线,多实例消费
- 粘性路由:同一 task 路由到同一实例,减少跨节点同步
12.4 Dockerfile 示例
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8000/.well-known/agent.json || exit 1
CMD ["uvicorn", "main:server.app", "--host", "0.0.0.0", "--port", "8000"]
十三、可观测性
13.1 必备指标
| 指标 | 说明 |
|---|---|
a2a_task_total{state} | 各状态任务数 |
a2a_task_duration_seconds | 任务耗时直方图 |
a2a_message_size_bytes | 消息大小 |
a2a_active_streams | 当前活跃 SSE 流 |
a2a_webhook_failures_total | webhook 失败次数 |
13.2 OpenTelemetry 集成
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
FastAPIInstrumentor.instrument_app(server.app)
tracer = trace.get_tracer("a2a")
class InstrumentedExecutor(AgentExecutor):
async def execute(self, ctx, bus):
with tracer.start_as_current_span("a2a.execute") as span:
span.set_attribute("a2a.task.id", ctx.task_id)
...
13.3 跨 Agent 链路追踪
A2A 推荐通过 traceparent HTTP header 透传 W3C Trace Context:
headers = {
"Authorization": "Bearer ...",
"traceparent": current_traceparent(),
}
十四、常见问题
14.1 Agent Card 拉不到
- 确认
/.well-known/agent.json路径未被网关拦截 - CORS 头:跨域调用需
Access-Control-Allow-Origin - 检查 TLS 证书是否被对方信任
14.2 SSE 连接频繁断开
- 网关默认超时(如 nginx
proxy_read_timeout60s)→ 调到 600s+ - 中间代理不支持 SSE → 启用 webhook 模式
- 客户端无心跳 → server 周期发送
comment行(: keepalive\n\n)
14.3 Task 状态丢失
- 多实例部署但用了内存 task store → 切换 Redis/Postgres
- 进程崩溃 → 用 systemd / K8s 自动拉起 + 持久化 store
14.4 消息体过大
- 大文件用
file.uri引用对象存储 URL,而非file.bytes内嵌 - 上传带签名的 S3/OSS 临时 URL,agent 之间共享引用
14.5 跨语言 / 跨框架兼容性
A2A 是协议标准,不绑定 SDK。只要遵守 JSON-RPC + Agent Card 规范,Python agent 可与 Node/Java/Go agent 互通。验证工具:A2A Inspector。
十五、完整示例
一个"研究 + 写作"双 agent 协作系统:
User → Coordinator Agent
│
├── (research) → Research Agent → Web Search
│
└── (writing) → Writer Agent
15.1 Research Agent(Python)
from a2a.server import A2AServer, AgentExecutor
from a2a.types import AgentCard, Skill, Message, TextPart, TaskState, TaskStatus
class ResearchExecutor(AgentExecutor):
async def execute(self, ctx, bus):
query = ctx.get_user_input()
await bus.enqueue_event(TaskStatus(state=TaskState.working))
sources = await web_search(query)
summary = await summarize(sources)
await bus.enqueue_event(TaskStatus(
state=TaskState.completed,
message=Message(role="agent", parts=[TextPart(text=summary)])
))
async def cancel(self, ctx, bus):
await bus.enqueue_event(TaskStatus(state=TaskState.canceled))
research_card = AgentCard(
name="Research Agent",
url="https://research.example.com/a2a",
version="1.0.0",
protocolVersion="0.2.5",
capabilities={"streaming": True},
defaultInputModes=["text/plain"],
defaultOutputModes=["text/markdown"],
skills=[Skill(id="research", name="网络研究", description="...", tags=["search"])],
)
15.2 Coordinator Agent(TypeScript)
import { A2AClient } from "@a2a-js/sdk";
const research = await A2AClient.fromCardUrl("https://research.example.com/.well-known/agent.json");
const writer = await A2AClient.fromCardUrl("https://writer.example.com/.well-known/agent.json");
class CoordinatorExecutor {
async execute(ctx, bus) {
const topic = extractText(ctx.userMessage);
bus.publish({ kind: "status-update", status: { state: "working",
message: { role: "agent", parts: [{ kind: "text", text: "研究中..." }] }}, final: false });
const r = await research.sendMessage({ role: "user",
parts: [{ kind: "text", text: topic }], messageId: crypto.randomUUID() });
bus.publish({ kind: "status-update", status: { state: "working",
message: { role: "agent", parts: [{ kind: "text", text: "撰写中..." }] }}, final: false });
const w = await writer.sendMessage({ role: "user",
parts: [{ kind: "text", text: extractText(r) }], messageId: crypto.randomUUID() });
bus.publish({
kind: "artifact-update",
artifact: { artifactId: crypto.randomUUID(), parts: w.artifacts[0].parts },
});
bus.publish({ kind: "status-update", status: { state: "completed" }, final: true });
}
}
15.3 端到端测试
curl https://coordinator.example.com/a2a \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0", "id": "1",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "写一篇关于量子计算商业化的文章"}],
"messageId": "msg-1"
}
}
}'
附录:协议字段速查
| 字段 | 位置 | 必填 | 说明 |
|---|---|---|---|
protocolVersion | AgentCard | ✓ | A2A 协议版本 |
capabilities.streaming | AgentCard | ✗ | 支持 SSE |
capabilities.pushNotifications | AgentCard | ✗ | 支持 webhook |
skills[].id | AgentCard | ✓ | 能力唯一 ID |
messageId | Message | ✓ | 消息 UUID |
taskId | Message/Event | ✓ | 任务 UUID |
contextId | Message | ✗ | 多轮上下文 ID |
parts[].kind | Part | ✓ | text/file/data |
参考资源:
- 规范:a2a-protocol.org
- Python SDK:github.com/a2aproject/…
- JS SDK:github.com/a2aproject/…
- Inspector:github.com/a2aproject/…