基于 A2A 协议的生产实践
基于 A2A 协议的生产实践
一、什么是 A2A 协议?
Agent2Agent(A2A)协议是由 Google 于 2025 年发布的一项开放标准,旨在解决 AI 智能体生态中的核心挑战:如何让不同团队、不同技术栈、不同组织开发的 AI 智能体之间进行高效通信与协作。
1.1 A2A 解决的核心问题
假设用户让 AI 助手规划一次国际旅行,这个请求可能需要多个专业智能体协同完成:
- 航班预订智能体 — 搜索和预订航班
- 酒店预订智能体 — 管理住宿安排
- 本地旅游智能体 — 提供景点推荐
- 财务服务智能体 — 处理货币兑换和理财建议
如果没有统一的通信协议,集成这些多样化的智能体需要大量定制的点对点方案,系统将难以扩展和维护。A2A 正是为此而生。
1.2 A2A 与 MCP 的区别
| 对比维度 | A2A 协议 | MCP 协议 |
|---|---|---|
| 核心定位 | 智能体之间的对等协作 | AI 模型到工具/资源的连接 |
| 交互特点 | 有状态、多轮对话、协商式 | 无状态、单次调用、事务式 |
| 应用场景 | 智能体委托、协作项目管理 | 函数调用、API 查询、数据检索 |
一句话总结:MCP 让智能体能用工具,A2A 让智能体之间能对话协作。两者互补而非竞争。
二、A2A 协议核心概念
2.1 协议架构
用户 → A2A Client(客户端智能体)→ A2A Server(远程智能体)→ 执行结果 → 客户端 → 用户
- A2A Client:代表用户向远程智能体发起请求的应用或智能体
- A2A Server:实现 A2A 协议 HTTP 端点的 AI 智能体或系统
2.2 核心通信要素
(1)Agent Card — 智能体名片
Agent Card 是一个 JSON 元数据文档,通常发布在 /.well-known/agent.json,描述智能体的完整信息:
{
"name": "智能翻译助手",
"description": "专业的多语言翻译服务",
"url": "https://api.translator.com/a2a",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"skills": [
{
"id": "translate",
"name": "文本翻译",
"description": "支持中英日韩等多语言互译",
"inputModes": ["text"],
"outputModes": ["text"]
}
]
}
(2)Task — 任务
任务是 A2A 协议的核心工作单元,具有完整的生命周期:
submitted → working → completed
↘ failed
working → input-required → working
(3)Message — 消息
- 角色区分:
user(客户端发送)或agent(服务端发送) - 内容载体:包含一个或多个
Part对象
(4)Part — 内容部件
| Part 类型 | 用途 | 示例 |
|---|---|---|
| TextPart | 纯文本 | 指令、问答 |
| FilePart | 文件传输 | 文档、图片 |
| DataPart | 结构化数据 | JSON 表单、参数 |
(5)Artifact — 产物
智能体完成任务后生成的输出结果,包含名称、内容部件等信息。
2.3 通信机制
| 机制 | 适用场景 | 技术实现 |
|---|---|---|
| Request/Response | 简单查询、快速任务 | HTTP 请求 + 轮询 |
| Streaming | 实时更新、增量结果 | Server-Sent Events |
| Push Notifications | 长时任务、异步处理 | Webhook 回调 |
三、Java 生产实践
下面通过一个完整的示例,演示如何用 Java + Spring Boot 构建符合 A2A 协议的智能体服务。
3.1 项目结构
a2a-demo/
├── pom.xml
└── src/main/java/com/example/a2a/
├── A2aApplication.java
├── model/
│ ├── AgentCard.java
│ ├── AgentSkill.java
│ ├── JSONRPCRequest.java
│ ├── JSONRPCResponse.java
│ ├── JSONRPCError.java
│ ├── Message.java
│ ├── TextPart.java
│ ├── Task.java
│ ├── TaskStatus.java
│ ├── TaskState.java
│ ├── TaskSendParams.java
│ └── Artifact.java
├── controller/
│ └── A2AController.java
├── client/
│ └── A2AClient.java
└── service/
└── TaskHandlerService.java
3.2 Maven 依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>a2a-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
3.3 数据模型定义
TaskState — 任务状态枚举
package com.example.a2a.model;
public enum TaskState {
SUBMITTED,
WORKING,
INPUT_REQUIRED,
COMPLETED,
FAILED,
CANCELED
}
TextPart — 文本内容部件
package com.example.a2a.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TextPart {
private final String type = "text";
private String text;
public TextPart() {}
public TextPart(String text) {
this.text = text;
}
}
Message — 消息
package com.example.a2a.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.List;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Message {
private String role; // "user" 或 "agent"
private List<TextPart> parts;
private String messageId;
public Message() {}
public Message(String role, List<TextPart> parts, String messageId) {
this.role = role;
this.parts = parts;
this.messageId = messageId;
}
}
TaskStatus — 任务状态
package com.example.a2a.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.time.Instant;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TaskStatus {
private TaskState state;
private Instant timestamp;
private Message message;
public TaskStatus() {}
public TaskStatus(TaskState state, Message message) {
this.state = state;
this.timestamp = Instant.now();
this.message = message;
}
}
Artifact — 产物
package com.example.a2a.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.List;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Artifact {
private String artifactId;
private String name;
private List<TextPart> parts;
public Artifact() {}
public Artifact(String artifactId, String name, List<TextPart> parts) {
this.artifactId = artifactId;
this.name = name;
this.parts = parts;
}
}
Task — 任务
package com.example.a2a.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.List;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Task {
private String id;
private String contextId;
private TaskStatus status;
private List<Artifact> artifacts;
private List<Message> history;
public Task() {}
public Task(String id, TaskStatus status) {
this.id = id;
this.status = status;
}
}
TaskSendParams — 任务发送参数
package com.example.a2a.model;
import lombok.Data;
@Data
public class TaskSendParams {
private String id;
private String contextId;
private Message message;
}
JSON-RPC 请求与响应
package com.example.a2a.model;
import lombok.Data;
@Data
public class JSONRPCRequest {
private String jsonrpc = "2.0";
private String method;
private Object id;
private TaskSendParams params;
}
package com.example.a2a.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JSONRPCResponse {
private final String jsonrpc = "2.0";
private Object id;
private Object result;
private JSONRPCError error;
public JSONRPCResponse() {}
public JSONRPCResponse(Object id, Object result, JSONRPCError error) {
this.id = id;
this.result = result;
this.error = error;
}
}
package com.example.a2a.model;
import lombok.Data;
@Data
public class JSONRPCError {
private int code;
private String message;
public JSONRPCError() {}
public JSONRPCError(int code, String message) {
this.code = code;
this.message = message;
}
}
AgentCard & AgentSkill — 智能体名片
package com.example.a2a.model;
import lombok.Data;
import java.util.*;
@Data
public class AgentSkill {
private String id;
private String name;
private String description;
private List<String> inputModes;
private List<String> outputModes;
public AgentSkill(String id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
this.inputModes = List.of("text");
this.outputModes = List.of("text");
}
}
package com.example.a2a.model;
import lombok.Data;
import java.util.*;
@Data
public class AgentCard {
private String name;
private String description;
private String url;
private String version;
private Map<String, Boolean> capabilities;
private List<AgentSkill> skills;
public AgentCard() {
this.capabilities = new HashMap<>();
this.skills = new ArrayList<>();
}
}
3.4 任务处理服务
package com.example.a2a.service;
import com.example.a2a.model.*;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class TaskHandlerService {
/** 内存中存储任务(生产环境应使用 Redis 或数据库) */
private final Map<String, Task> taskStore = new ConcurrentHashMap<>();
/**
* 处理 tasks/send 请求
*/
public Task handleTaskSend(TaskSendParams params) {
String taskId = params.getId();
if (taskId == null || taskId.isEmpty()) {
taskId = UUID.randomUUID().toString();
}
// 1. 提取用户消息
Message userMessage = params.getMessage();
String userInput = extractTextFromMessage(userMessage);
// 2. 执行业务逻辑(这里以文本摘要为例)
String result = processBusinessLogic(userInput);
// 3. 构建响应消息
Message agentMessage = new Message(
"agent",
List.of(new TextPart(result)),
UUID.randomUUID().toString()
);
// 4. 构建任务对象
Task task = new Task();
task.setId(taskId);
task.setContextId(params.getContextId());
task.setStatus(new TaskStatus(TaskState.COMPLETED, agentMessage));
task.setHistory(List.of(userMessage, agentMessage));
task.setArtifacts(List.of(
new Artifact(UUID.randomUUID().toString(), "summary",
List.of(new TextPart(result)))
));
// 5. 存储任务
taskStore.put(taskId, task);
return task;
}
/**
* 处理 tasks/get 请求 — 查询任务状态
*/
public Task handleTaskGet(String taskId) {
return taskStore.get(taskId);
}
/**
* 处理 tasks/cancel 请求 — 取消任务
*/
public Task handleTaskCancel(String taskId) {
Task task = taskStore.get(taskId);
if (task != null) {
task.setStatus(new TaskStatus(TaskState.CANCELED, null));
}
return task;
}
/**
* 业务逻辑处理(示例:文本摘要)
* 生产环境中可替换为调用 LLM API(如 OpenAI、Gemini 等)
*/
private String processBusinessLogic(String input) {
if (input == null || input.isEmpty()) {
return "未收到有效输入。";
}
if (input.length() > 100) {
return "【摘要】" + input.substring(0, 100) + "...(共 " + input.length() + " 字)";
}
return "【处理结果】已收到您的消息:" + input;
}
private String extractTextFromMessage(Message message) {
if (message == null || message.getParts() == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (TextPart part : message.getParts()) {
if (part.getText() != null) {
sb.append(part.getText());
}
}
return sb.toString();
}
}
3.5 A2A 控制器(核心端点)
package com.example.a2a.controller;
import com.example.a2a.model.*;
import com.example.a2a.service.TaskHandlerService;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
public class A2AController {
private final TaskHandlerService taskHandler;
public A2AController(TaskHandlerService taskHandler) {
this.taskHandler = taskHandler;
}
/**
* Agent Card 发现端点
* 客户端通过 GET /.well-known/agent.json 获取智能体信息
*/
@GetMapping("/.well-known/agent.json")
public AgentCard getAgentCard() {
AgentCard card = new AgentCard();
card.setName("文本摘要智能体");
card.setDescription("接收文本内容并生成摘要的 AI 智能体");
card.setUrl("http://localhost:8080/a2a");
card.setVersion("1.0.0");
card.setCapabilities(Map.of(
"streaming", false,
"pushNotifications", false
));
card.setSkills(List.of(
new AgentSkill("summarize", "文本摘要",
"接收长文本并生成简洁摘要")
));
return card;
}
/**
* A2A 协议主端点 — 处理所有 JSON-RPC 2.0 请求
*/
@PostMapping("/a2a")
public JSONRPCResponse handleA2ARequest(@RequestBody JSONRPCRequest request) {
String method = request.getMethod();
try {
switch (method) {
case "tasks/send": {
Task task = taskHandler.handleTaskSend(request.getParams());
return new JSONRPCResponse(request.getId(), task, null);
}
case "tasks/get": {
String taskId = request.getParams().getId();
Task task = taskHandler.handleTaskGet(taskId);
if (task == null) {
return new JSONRPCResponse(request.getId(), null,
new JSONRPCError(-32001, "Task not found: " + taskId));
}
return new JSONRPCResponse(request.getId(), task, null);
}
case "tasks/cancel": {
String taskId = request.getParams().getId();
Task task = taskHandler.handleTaskCancel(taskId);
if (task == null) {
return new JSONRPCResponse(request.getId(), null,
new JSONRPCError(-32001, "Task not found: " + taskId));
}
return new JSONRPCResponse(request.getId(), task, null);
}
default:
return new JSONRPCResponse(request.getId(), null,
new JSONRPCError(-32601, "Method not found: " + method));
}
} catch (Exception e) {
return new JSONRPCResponse(request.getId(), null,
new JSONRPCError(-32603, "Internal error: " + e.getMessage()));
}
}
}
3.6 A2A 客户端实现
package com.example.a2a.client;
import com.example.a2a.model.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
import java.util.*;
/**
* A2A 协议客户端
* 用于发现远程智能体、发送任务、查询状态
*/
public class A2AClient {
private final String serverBaseUrl;
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public A2AClient(String serverBaseUrl) {
this.serverBaseUrl = serverBaseUrl;
this.httpClient = HttpClient.newHttpClient();
this.objectMapper = new ObjectMapper();
this.objectMapper.findAndRegisterModules();
}
/**
* 发现远程智能体 — 获取 Agent Card
*/
public AgentCard discoverAgent() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serverBaseUrl + "/.well-known/agent.json"))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
return objectMapper.readValue(response.body(), AgentCard.class);
}
/**
* 发送任务给远程智能体
*/
public JSONRPCResponse sendTask(String text) throws Exception {
// 1. 构建任务参数
TaskSendParams params = new TaskSendParams();
params.setId(UUID.randomUUID().toString());
params.setMessage(new Message(
"user",
List.of(new TextPart(text)),
UUID.randomUUID().toString()
));
// 2. 构建 JSON-RPC 请求
JSONRPCRequest rpcRequest = new JSONRPCRequest();
rpcRequest.setMethod("tasks/send");
rpcRequest.setId(UUID.randomUUID().toString());
rpcRequest.setParams(params);
// 3. 发送 HTTP 请求
String body = objectMapper.writeValueAsString(rpcRequest);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(serverBaseUrl + "/a2a"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
return objectMapper.readValue(response.body(), JSONRPCResponse.class);
}
/**
* 使用示例
*/
public static void main(String[] args) throws Exception {
A2AClient client = new A2AClient("http://localhost:8080");
// 1. 发现智能体
AgentCard card = client.discoverAgent();
System.out.println("发现智能体: " + card.getName());
System.out.println("描述: " + card.getDescription());
System.out.println("技能列表: " + card.getSkills());
System.out.println("---");
// 2. 发送任务
JSONRPCResponse response = client.sendTask(
"A2A协议是Google发布的开放标准,用于实现不同AI智能体之间的通信与协作。"
+ "它基于JSON-RPC 2.0和HTTP(S),支持流式传输和推送通知等企业级特性。"
);
System.out.println("任务结果: " + response.getResult());
}
}
四、生产环境实践要点
4.1 Agent Card 的版本管理
在生产环境中,Agent Card 需要进行版本化管理:
@GetMapping("/.well-known/agent.json")
public AgentCard getAgentCard(@RequestParam(defaultValue = "latest") String version) {
// 从配置中心动态加载对应版本的 Agent Card
return agentCardRegistry.getCard(version);
}
4.2 任务持久化
示例中使用了 ConcurrentHashMap 存储任务,生产环境应切换为持久化存储:
@Service
public class PersistentTaskStore {
@Autowired
private RedisTemplate<String, Task> redisTemplate;
private static final String TASK_KEY_PREFIX = "a2a:task:";
private static final long TASK_TTL_HOURS = 24;
public void storeTask(Task task) {
String key = TASK_KEY_PREFIX + task.getId();
redisTemplate.opsForValue().set(key, task, TASK_TTL_HOURS, TimeUnit.HOURS);
}
public Task getTask(String taskId) {
return redisTemplate.opsForValue().get(TASK_KEY_PREFIX + taskId);
}
}
4.3 安全认证
生产环境必须加入认证机制,推荐使用 OAuth 2.0 / Bearer Token:
@Component
public class A2AAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\":\"Missing or invalid token\"}");
return false;
}
String token = authHeader.substring(7);
if (!tokenValidator.validate(token)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
return true;
}
}
4.4 Streaming 支持(SSE)
对于长时间运行的任务,应使用 Server-Sent Events 实现流式响应:
@PostMapping(value = "/a2a/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handleStreamingRequest(@RequestBody JSONRPCRequest request) {
SseEmitter emitter = new SseEmitter(300_000L); // 5 分钟超时
CompletableFuture.runAsync(() -> {
try {
// 发送状态更新:working
emitter.send(SseEmitter.event()
.name("status")
.data(new TaskStatus(TaskState.WORKING, null)));
// 模拟分步处理
String result = processLongRunningTask(request.getParams());
// 发送最终结果
Message agentMsg = new Message("agent",
List.of(new TextPart(result)),
UUID.randomUUID().toString());
emitter.send(SseEmitter.event()
.name("status")
.data(new TaskStatus(TaskState.COMPLETED, agentMsg)));
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
});
return emitter;
}
4.5 多智能体编排模式
在真实生产场景中,通常需要一个编排智能体来协调多个专业智能体:
@Service
public class OrchestratorService {
private final Map<String, A2AClient> agentClients = new HashMap<>();
@PostConstruct
public void init() {
// 注册多个远程智能体
agentClients.put("translator", new A2AClient("http://translator-agent:8080"));
agentClients.put("summarizer", new A2AClient("http://summarizer-agent:8081"));
agentClients.put("analyzer", new A2AClient("http://analyzer-agent:8082"));
}
/**
* 编排多个智能体完成复杂任务
* 例如:先翻译 → 再摘要 → 再分析
*/
public String orchestrate(String input) throws Exception {
// Step 1: 翻译
JSONRPCResponse translateResult = agentClients.get("translator").sendTask(input);
String translated = extractResult(translateResult);
// Step 2: 摘要
JSONRPCResponse summaryResult = agentClients.get("summarizer").sendTask(translated);
String summary = extractResult(summaryResult);
// Step 3: 分析
JSONRPCResponse analysisResult = agentClients.get("analyzer").sendTask(summary);
return extractResult(analysisResult);
}
private String extractResult(JSONRPCResponse response) {
// 从 JSON-RPC 响应中提取文本结果(省略具体实现)
return response.getResult().toString();
}
}
五、完整交互流程示例
以下展示一次完整的 A2A 通信过程:
请求:发送任务
curl -X POST http://localhost:8080/a2a \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/send",
"id": "req-001",
"params": {
"id": "task-001",
"message": {
"role": "user",
"messageId": "msg-001",
"parts": [
{
"type": "text",
"text": "请帮我总结一下A2A协议的核心要点"
}
]
}
}
}'
响应:任务结果
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"id": "task-001",
"status": {
"state": "COMPLETED",
"timestamp": "2025-04-20T10:30:00Z",
"message": {
"role": "agent",
"messageId": "agent-msg-001",
"parts": [
{
"type": "text",
"text": "【处理结果】已收到您的消息:请帮我总结一下A2A协议的核心要点"
}
]
}
},
"artifacts": [
{
"artifactId": "artifact-001",
"name": "summary",
"parts": [
{
"type": "text",
"text": "【处理结果】已收到您的消息:请帮我总结一下A2A协议的核心要点"
}
]
}
]
}
}
六、总结
| 实践要点 | 说明 |
|---|---|
| Agent Card | 作为智能体的"名片",是服务发现的基础,需版本化管理 |
| JSON-RPC 2.0 | 统一的通信格式,简化客户端和服务端的集成成本 |
| Task 生命周期 | 支持有状态的多轮交互,适用于复杂业务场景 |
| Streaming(SSE) | 长时任务的实时反馈机制,提升用户体验 |
| 安全认证 | 生产环境必备,推荐 OAuth 2.0 + Bearer Token |
| 多智能体编排 | 通过编排模式让多个专业智能体协作完成复杂任务 |
A2A 协议为构建可互操作的 AI 智能体生态提供了坚实基础。结合 Java + Spring Boot 的技术栈,我们可以快速构建生产级别的 A2A 服务,实现智能体之间的无缝协作。
参考资料
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)