A2A(Agent-to-Agent)协议为Agent之间的通信提供了标准化方案。本文将深入剖析A2A协议的设计原理、服务端实现、客户端集成,以及安全认证机制。
环境准备
本文示例代码基于以下技术栈:
| 组件 | 版本要求 |
|---|---|
| JDK | 17+ |
| Spring Boot | 3.2+ |
| Spring AI | 2.0.0-M3+ |
| spring-ai-agent-utils | 0.7.0 |
Maven依赖:
xml
<dependencies>
<!-- Spring AI 核心 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>2.0.0-M3</version>
</dependency>
<!-- Spring AI Agent Utils -->
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>spring-ai-agent-utils</artifactId>
<version>0.7.0</version>
</dependency>
<!-- Spring WebFlux (用于响应式HTTP客户端) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
一、A2A协议概述
1.1 当前Agent生态的碎片化

1.2 A2A协议的设计目标
核心理念:像HTTP统一Web通信一样,统一Agent间通信

二、协议规范详解
2.1 核心概念
| 概念 | 说明 | 类比 |
|---|---|---|
| Agent Card | Agent元数据描述 | OpenAPI Spec |
| Task | 执行单元 | HTTP Request |
| Artifact | 执行产物 | HTTP Response Body |
| Message | 通信消息 | WebSocket Message |
| Part | 消息部分 | MIME Part |
2.2 Agent Card规范
Agent Card是Agent的"身份证",描述其能力、接口和元数据:
json
{
"schemaVersion": "1.0",
"agentId": "payment-expert-agent",
"name": "Payment Expert Agent",
"description": "处理支付系统相关任务的专业Agent",
"version": "2.1.0",
"provider": {
"name": "Deep Blue Team",
"url": "https://deepblue.example.com",
"contact": "support@deepblue.example.com"
},
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": true,
"artifacts": true
},
"authentication": {
"schemes": ["bearer", "api_key"],
"credentialsUrl": "https://auth.deepblue.example.com/credentials"
},
"endpoints": {
"task": "https://payment-agent.example.com/a2a/tasks",
"taskSubscribe": "wss://payment-agent.example.com/a2a/tasks/subscribe",
"agentCard": "https://payment-agent.example.com/.well-known/agent.json"
},
"skills": [
{
"id": "payment-integration",
"name": "支付网关集成",
"description": "集成支付宝、微信支付、Stripe等支付网关",
"tags": ["payment", "integration", "gateway"],
"inputSchema": {
"type": "object",
"properties": {
"gateway": {
"type": "string",
"enum": ["alipay", "wechat", "stripe"]
},
"action": {
"type": "string",
"enum": ["create", "query", "refund"]
},
"orderId": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["gateway", "action"]
},
"outputSchema": {
"type": "object",
"properties": {
"success": { "type": "boolean" },
"transactionId": { "type": "string" },
"message": { "type": "string" }
}
}
},
{
"id": "order-management",
"name": "订单状态管理",
"description": "管理订单生命周期状态转换"
}
],
"metadata": {
"language": "zh-CN",
"region": "CN",
"responseTime": {
"p50": "500ms",
"p95": "2s",
"p99": "5s"
},
"rateLimit": {
"requests": 100,
"window": "1m"
}
}
}
Agent Card字段详解:
| 字段路径 | 必需 | 说明 |
|---|---|---|
schemaVersion |
✅ | 协议版本 |
agentId |
✅ | Agent唯一标识 |
name |
✅ | 显示名称 |
description |
✅ | 功能描述 |
version |
✅ | Agent版本 |
provider |
❌ | 提供者信息 |
capabilities |
✅ | 能力声明 |
authentication.schemes |
✅ | 认证方式 |
endpoints.task |
✅ | 任务提交端点 |
skills |
✅ | 技能列表 |
skills[].inputSchema |
❌ | 输入JSON Schema |
skills[].outputSchema |
❌ | 输出JSON Schema |
2.3 Task生命周期

2.4 Task Request/Response格式
Task Request:
json
{
"id": "task-550e8400-e29b-41d4-a716-446655440000",
"sessionId": "session-123",
"skillId": "payment-integration",
"input": {
"gateway": "alipay",
"action": "create",
"orderId": "ORD-2024-001",
"amount": 99.99,
"currency": "CNY"
},
"metadata": {
"priority": "high",
"deadline": "2024-12-31T23:59:59Z",
"callbackUrl": "https://client.example.com/callback"
},
"requestedOutputModes": ["text", "artifact"]
}
Task Response:
json
{
"id": "task-550e8400-e29b-41d4-a716-446655440000",
"sessionId": "session-123",
"status": "COMPLETED",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:45Z",
"output": {
"text": "支付订单创建成功,交易号: ALI2024011510304512345678",
"artifacts": [
{
"id": "artifact-001",
"name": "payment-qr-code.png",
"type": "image/png",
"content": "base64-encoded-image-data...",
"metadata": {
"size": 10240,
"expiresAt": "2024-01-15T11:00:00Z"
}
}
]
},
"history": [
{
"timestamp": "2024-01-15T10:30:00Z",
"status": "SUBMITTED",
"message": "任务已接收"
},
{
"timestamp": "2024-01-15T10:30:05Z",
"status": "WORKING",
"message": "正在创建支付订单..."
},
{
"timestamp": "2024-01-15T10:30:45Z",
"status": "COMPLETED",
"message": "订单创建完成"
}
],
"metrics": {
"durationMs": 45000,
"tokenUsage": {
"input": 150,
"output": 80
}
}
}
2.5 流式响应(SSE)
对于长时间运行的任务,支持Server-Sent Events流式推送:
vbnet
GET /a2a/tasks/{taskId}/subscribe HTTP/1.1
Accept: text/event-stream
Authorization: Bearer <token>
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
event: status
data: {"status": "WORKING", "message": "正在处理支付请求..."}
event: message
data: {"type": "progress", "percent": 30, "detail": "验证订单信息"}
event: message
data: {"type": "progress", "percent": 60, "detail": "调用支付网关"}
event: artifact
data: {"id": "art-001", "name": "qr-code.png", "type": "image/png",
"content": "base64..."}
event: status
data: {"status": "COMPLETED", "message": "支付订单创建成功"}
event: done
data: {}
三、服务端实现
3.1 Spring Boot A2A Server
依赖配置:
xml
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>spring-ai-a2a-server</artifactId>
<version>0.2.0</version>
</dependency>
配置类:
java
@Configuration
@EnableA2AServer
public class A2AServerConfig {
@Bean
public AgentCard agentCard() {
return AgentCard.builder()
.schemaVersion("1.0")
.agentId("payment-expert-agent")
.name("Payment Expert Agent")
.description("支付系统专业Agent")
.version("2.1.0")
.provider(ProviderInfo.builder()
.name("Deep Blue Team")
.url("https://deepblue.example.com")
.build())
.capabilities(Capabilities.builder()
.streaming(true)
.pushNotifications(true)
.artifacts(true)
.build())
.authentication(Authentication.builder()
.schemes(List.of("bearer", "api_key"))
.build())
.skills(List.of(
Skill.builder()
.id("payment-integration")
.name("支付网关集成")
.description("集成主流支付网关")
.inputSchema(loadSchema("schemas/payment-input.json"))
.outputSchema(loadSchema("schemas/payment-output.json"))
.build(),
Skill.builder()
.id("order-management")
.name("订单管理")
.description("管理订单状态")
.build()
))
.build();
}
@Bean
public A2ATaskHandler taskHandler() {
return A2ATaskHandler.builder()
.skill("payment-integration", new PaymentIntegrationHandler())
.skill("order-management", new OrderManagementHandler())
.build();
}
}
3.2 Skill Handler实现
java
@Component
public class PaymentIntegrationHandler implements SkillHandler {
private final PaymentService paymentService;
private final ArtifactStore artifactStore;
@Override
public String getSkillId() {
return "payment-integration";
}
@Override
public SkillResult execute(TaskInput input, TaskContext context) {
String gateway = input.getString("gateway");
String action = input.getString("action");
// 状态更新回调
context.updateStatus(TaskStatus.WORKING, "正在处理" + gateway + "支付...");
try {
switch (action) {
case "create":
return handleCreatePayment(input, context);
case "query":
return handleQueryPayment(input, context);
case "refund":
return handleRefund(input, context);
default:
throw new IllegalArgumentException("未知操作: " + action);
}
} catch (PaymentException e) {
context.updateStatus(TaskStatus.FAILED, e.getMessage());
return SkillResult.failure(e.getMessage());
}
}
private SkillResult handleCreatePayment(TaskInput input, TaskContext context) {
String orderId = input.getString("orderId");
BigDecimal amount = input.getBigDecimal("amount");
String currency = input.getString("currency", "CNY");
context.updateProgress(20, "验证订单信息");
// 创建支付订单
PaymentRequest request = PaymentRequest.builder()
.orderId(orderId)
.amount(amount)
.currency(currency)
.build();
context.updateProgress(40, "调用支付网关");
PaymentResult result = paymentService.createPayment(
input.getString("gateway"), request);
context.updateProgress(80, "生成支付二维码");
// 生成二维码产物
Artifact qrCode = artifactStore.store(
Artifact.builder()
.name("payment-qr-code.png")
.type("image/png")
.content(generateQRCode(result.getPayUrl()))
.build()
);
return SkillResult.success()
.text("支付订单创建成功,交易号: " + result.getTransactionId())
.artifact(qrCode)
.metadata(Map.of(
"transactionId", result.getTransactionId(),
"payUrl", result.getPayUrl()
));
}
}
3.3 端点配置
A2A Server自动暴露以下端点:
| 端点 | 方法 | 说明 |
|---|---|---|
/.well-known/agent.json |
GET | Agent Card |
/a2a/tasks |
POST | 创建任务 |
/a2a/tasks/{id} |
GET | 查询任务状态 |
/a2a/tasks/{id} |
DELETE | 取消任务 |
/a2a/tasks/{id}/subscribe |
GET | SSE订阅任务更新 |
/a2a/tasks/{id}/artifacts/{artifactId} |
GET | 下载产物 |
Controller实现(框架自动生成,此处展示原理):
java
@RestController
@RequestMapping("/a2a")
public class A2ATaskController {
private final A2ATaskService taskService;
private final SseEmitterService sseService;
@PostMapping("/tasks")
public ResponseEntity<TaskResponse> createTask(
@RequestBody TaskRequest request,
@RequestHeader("Authorization") String auth) {
// 验证认证
if (!authenticate(auth)) {
return ResponseEntity.status(401).build();
}
// 创建任务
TaskResponse response = taskService.createTask(request);
return ResponseEntity.accepted().body(response);
}
@GetMapping("/tasks/{id}")
public ResponseEntity<TaskResponse> getTask(
@PathVariable String id,
@RequestHeader("Authorization") String auth) {
return taskService.getTask(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@GetMapping("/tasks/{id}/subscribe")
public SseEmitter subscribeTask(
@PathVariable String id,
@RequestHeader("Authorization") String auth) {
return sseService.createEmitter(id);
}
}
3.4 流式推送实现
java
@Service
public class SseEmitterService {
private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
public SseEmitter createEmitter(String taskId) {
SseEmitter emitter = new SseEmitter(300_000L); // 5分钟超时
emitter.onCompletion(() -> emitters.remove(taskId));
emitter.onTimeout(() -> emitters.remove(taskId));
emitters.put(taskId, emitter);
return emitter;
}
public void sendStatus(String taskId, TaskStatus status, String message) {
SseEmitter emitter = emitters.get(taskId);
if (emitter != null) {
try {
emitter.send(SseEmitter.event()
.name("status")
.data(Map.of(
"status", status.name(),
"message", message
)));
} catch (IOException e) {
emitters.remove(taskId);
}
}
}
public void sendProgress(String taskId, int percent, String detail) {
SseEmitter emitter = emitters.get(taskId);
if (emitter != null) {
try {
emitter.send(SseEmitter.event()
.name("message")
.data(Map.of(
"type", "progress",
"percent", percent,
"detail", detail
)));
} catch (IOException e) {
emitters.remove(taskId);
}
}
}
public void complete(String taskId) {
SseEmitter emitter = emitters.remove(taskId);
if (emitter != null) {
emitter.send(SseEmitter.event().name("done").data("{}"));
emitter.complete();
}
}
}
四、客户端集成
4.1 A2A Client配置
java
@Configuration
public class A2AClientConfig {
@Bean
public A2AClient a2aClient(WebClient.Builder webClientBuilder) {
return A2AClient.builder()
.webClientBuilder(webClientBuilder)
.defaultTimeout(Duration.ofMinutes(5))
.retryConfig(RetryConfig.builder()
.maxAttempts(3)
.backoff(Duration.ofSeconds(1))
.build())
.build();
}
}
4.2 发现远程Agent
java
@Service
public class AgentDiscoveryService {
private final A2AClient a2aClient;
private final Map<String, AgentCard> cachedCards = new ConcurrentHashMap<>();
/**
* 从well-known端点获取Agent Card
*/
public AgentCard discoverAgent(String baseUrl) {
String cardUrl = baseUrl + "/.well-known/agent.json";
return cachedCards.computeIfAbsent(baseUrl, url -> {
return a2aClient.fetchAgentCard(url)
.block(Duration.ofSeconds(10));
});
}
/**
* 根据能力搜索Agent
*/
public List<AgentCard> searchByCapability(String capability) {
// 从注册中心查询
String registryUrl = "https://agent-registry.example.com/search";
return a2aClient.searchRegistry(registryUrl,
Map.of("capability", capability))
.collectList()
.block();
}
/**
* 验证Agent Card
*/
public ValidationResult validateCard(AgentCard card) {
List<String> errors = new ArrayList<>();
// 检查必需字段
if (card.getAgentId() == null) {
errors.add("缺少agentId");
}
if (card.getSkills() == null || card.getSkills().isEmpty()) {
errors.add("缺少skills定义");
}
// 检查端点可达性
try {
a2aClient.ping(card.getEndpoints().getTask())
.block(Duration.ofSeconds(5));
} catch (Exception e) {
errors.add("端点不可达: " + e.getMessage());
}
return new ValidationResult(errors.isEmpty(), errors);
}
}
4.3 提交任务
java
@Service
public class A2ATaskClient {
private final A2AClient a2aClient;
/**
* 同步提交任务(阻塞等待结果)
*/
public TaskResponse submitAndWait(AgentCard agent, TaskRequest request) {
return a2aClient.submitTask(agent.getEndpoints().getTask(), request)
.flatMap(response -> {
if (response.getStatus() == TaskStatus.COMPLETED) {
return Mono.just(response);
}
// 等待任务完成
return pollUntilComplete(agent, response.getId());
})
.block(Duration.ofMinutes(10));
}
/**
* 异步提交任务(返回Mono)
*/
public Mono<TaskResponse> submitAsync(AgentCard agent, TaskRequest request) {
return a2aClient.submitTask(agent.getEndpoints().getTask(), request);
}
/**
* 流式订阅任务
*/
public Flux<TaskEvent> subscribe(AgentCard agent, String taskId) {
String subscribeUrl = agent.getEndpoints().getTaskSubscribe();
return a2aClient.subscribeTask(subscribeUrl, taskId);
}
/**
* 轮询任务状态
*/
private Mono<TaskResponse> pollUntilComplete(AgentCard agent, String taskId) {
return Mono.defer(() ->
a2aClient.getTask(agent.getEndpoints().getTask(), taskId)
)
.flatMap(response -> {
if (response.getStatus().isTerminal()) {
return Mono.just(response);
}
// 等待1秒后重试
return Mono.delay(Duration.ofSeconds(1))
.then(pollUntilComplete(agent, taskId));
});
}
}
4.4 与Spring AI Agent集成
java
@Configuration
public class A2ASubagentConfig {
@Bean
public SubagentConfig a2aPaymentSubagent(AgentDiscoveryService discovery) {
// 发现远程Agent
AgentCard paymentAgent = discovery.discoverAgent(
"https://payment-agent.example.com");
return SubagentConfig.builder()
.name("payment-a2a")
.description("远程支付Agent (A2A)")
.model("a2a:" + paymentAgent.getAgentId()) // 特殊标记
.metadata(Map.of(
"a2a_agent_card", JsonUtils.toJson(paymentAgent),
"a2a_endpoint", paymentAgent.getEndpoints().getTask()
))
.build();
}
@Bean
public A2ASubagentExecutor a2aExecutor(A2ATaskClient taskClient) {
return new A2ASubagentExecutor(taskClient);
}
}
A2A Subagent执行器:
java
public class A2ASubagentExecutor implements SubagentExecutor {
private final A2ATaskClient taskClient;
@Override
public SubagentResult execute(SubagentTask task) {
// 从metadata获取Agent Card
AgentCard agentCard = JsonUtils.fromJson(
task.getMetadata().get("a2a_agent_card"),
AgentCard.class
);
// 构建A2A Task Request
TaskRequest request = TaskRequest.builder()
.id(UUID.randomUUID().toString())
.sessionId(task.getSessionId())
.input(extractInput(task))
.build();
// 提交任务
TaskResponse response = taskClient.submitAndWait(agentCard, request);
// 转换结果
return SubagentResult.builder()
.success(response.getStatus() == TaskStatus.COMPLETED)
.summary(response.getOutput().getText())
.artifacts(convertArtifacts(response.getOutput().getArtifacts()))
.build();
}
private Map<String, Object> extractInput(SubagentTask task) {
Map<String, Object> input = new HashMap<>();
input.put("goal", task.getGoal());
input.put("context", task.getContext());
return input;
}
}
五、安全与认证
5.1 认证方案
A2A支持多种认证方式:

5.2 服务端认证实现
java
@Configuration
@EnableWebSecurity
public class A2ASecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/.well-known/**").permitAll()
.requestMatchers("/a2a/**").authenticated()
)
// API Key认证
.addFilterBefore(new ApiKeyFilter(), UsernamePasswordAuthenticationFilter.class)
// Bearer Token认证
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
@Component
public class ApiKeyFilter extends OncePerRequestFilter {
@Value("${a2a.api-keys}")
private Set<String> validApiKeys;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) {
String apiKey = request.getHeader("X-API-Key");
if (apiKey != null && validApiKeys.contains(apiKey)) {
Authentication auth = new ApiKeyAuthentication(apiKey);
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(request, response);
}
}
5.3 客户端认证配置
java
@Bean
public A2AClient a2aClientWithAuth(WebClient.Builder builder) {
return A2AClient.builder()
.webClientBuilder(builder
.defaultHeader("X-API-Key", "${A2A_API_KEY}")
// 或使用OAuth2
.filter(oauth2Filter())
)
.build();
}
private ExchangeFilterFunction oauth2Filter() {
return (request, next) -> {
// 从OAuth2服务器获取token
return getToken()
.flatMap(token -> {
ClientRequest authorizedRequest = ClientRequest.from(request)
.headers(headers -> headers.setBearerAuth(token))
.build();
return next.exchange(authorizedRequest);
});
};
}
5.4 权限控制
java
@Component
public class A2APermissionEvaluator {
/**
* 检查客户端是否有权限调用特定skill
*/
public boolean hasPermission(Authentication auth, String skillId) {
// 从token中提取权限
Set<String> permissions = extractPermissions(auth);
// 权限规则
Map<String, Set<String>> skillPermissions = Map.of(
"payment-integration", Set.of("payment:write", "payment:read"),
"order-management", Set.of("order:write", "order:read"),
"refund", Set.of("refund:write") // 敏感操作需要特殊权限
);
Set<String> required = skillPermissions.getOrDefault(skillId, Set.of());
return permissions.containsAll(required);
}
private Set<String> extractPermissions(Authentication auth) {
if (auth instanceof ApiKeyAuthentication) {
// API Key关联的权限
return getPermissionsForApiKey(auth.getCredentials().toString());
} else if (auth.getPrincipal() instanceof Jwt jwt) {
// JWT中的权限声明
return jwt.getClaimAsStringSet("permissions")
.stream()
.collect(Collectors.toSet());
}
return Set.of();
}
}
六、监控与可观测性
6.1 指标收集
java
@Configuration
public class A2AMetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> a2aMetrics() {
return registry -> {
// 任务计数器
Counter.builder("a2a.tasks.total")
.description("Total A2A tasks")
.tag("agent", "payment-expert")
.register(registry);
// 任务延迟直方图
DistributionSummary.builder("a2a.task.duration")
.description("A2A task duration")
.baseUnit("milliseconds")
.register(registry);
};
}
}
@Aspect
@Component
public class A2AMetricsAspect {
private final MeterRegistry meterRegistry;
@Around("@annotation(A2ATask)")
public Object measureTask(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
String skillId = extractSkillId(pjp);
try {
Object result = pjp.proceed();
// 成功计数
meterRegistry.counter("a2a.tasks.total",
"skill", skillId,
"status", "success"
).increment();
return result;
} catch (Exception e) {
// 失败计数
meterRegistry.counter("a2a.tasks.total",
"skill", skillId,
"status", "failure",
"error", e.getClass().getSimpleName()
).increment();
throw e;
} finally {
// 记录延迟
long duration = System.currentTimeMillis() - start;
meterRegistry.summary("a2a.task.duration",
"skill", skillId
).record(duration);
}
}
}
6.2 分布式追踪
java
@Configuration
public class TracingConfig {
@Bean
public Tracing tracing() {
return Tracing.newBuilder()
.localServiceName("payment-a2a-agent")
.spanReporter(AsyncReporter.create(URLConnectionSender.create(
"http://zipkin:9411/api/v2/spans"
)))
.propagationFactory(B3Propagation.FACTORY)
.build();
}
@Bean
public HttpTracing httpTracing(Tracing tracing) {
return HttpTracing.create(tracing);
}
}
// 在任务处理中添加追踪
@Override
public SkillResult execute(TaskInput input, TaskContext context) {
Span span = tracer.nextSpan()
.name("a2a.task." + getSkillId())
.tag("task.id", context.getTaskId())
.tag("session.id", context.getSessionId())
.start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
// 执行任务
return doExecute(input, context);
} finally {
span.finish();
}
}
6.3 日志聚合
java
@Slf4j
@Component
public class A2ATaskLogger {
private final StructuredArguments TASK_ID = keyValue("taskId");
private final StructuredArguments SESSION_ID = keyValue("sessionId");
private final StructuredArguments SKILL_ID = keyValue("skillId");
public void logTaskStart(TaskContext context) {
log.info("A2A task started",
TASK_ID.value(context.getTaskId()),
SESSION_ID.value(context.getSessionId()),
SKILL_ID.value(context.getSkillId()),
keyValue("input", context.getInput())
);
}
public void logTaskComplete(TaskContext context, SkillResult result) {
log.info("A2A task completed",
TASK_ID.value(context.getTaskId()),
keyValue("success", result.isSuccess()),
keyValue("durationMs", context.getDuration().toMillis()),
keyValue("artifacts", result.getArtifacts().size())
);
}
public void logTaskError(TaskContext context, Exception e) {
log.error("A2A task failed",
TASK_ID.value(context.getTaskId()),
keyValue("error", e.getMessage()),
keyValue("stackTrace", Arrays.toString(e.getStackTrace()))
);
}
}
七、Agent注册中心
7.1 设计目标
当Agent数量增多时,需要注册中心进行管理:

7.2 注册中心实现
java
@Service
public class AgentRegistryService {
private final RedisTemplate<String, String> redis;
private static final String REGISTRY_KEY = "a2a:agents";
private static final String CAPABILITY_INDEX = "a2a:capabilities";
/**
* 注册Agent
*/
public void register(AgentCard card) {
String agentJson = JsonUtils.toJson(card);
// 存储Agent Card
redis.opsForHash().put(REGISTRY_KEY, card.getAgentId(), agentJson);
// 索引能力
for (Skill skill : card.getSkills()) {
redis.opsForSet().add(
CAPABILITY_INDEX + ":" + skill.getId(),
card.getAgentId()
);
}
// 设置过期时间(用于健康检查)
redis.expire(
REGISTRY_KEY + ":heartbeat:" + card.getAgentId(),
Duration.ofSeconds(60)
);
}
/**
* 按能力发现Agent
*/
public List<AgentCard> findByCapability(String capability) {
Set<String> agentIds = redis.opsForSet().members(
CAPABILITY_INDEX + ":" + capability
);
if (agentIds == null || agentIds.isEmpty()) {
return List.of();
}
return agentIds.stream()
.map(id -> redis.opsForHash().get(REGISTRY_KEY, id))
.filter(Objects::nonNull)
.map(json -> JsonUtils.fromJson(json.toString(), AgentCard.class))
.filter(this::isHealthy)
.collect(Collectors.toList());
}
/**
* 健康检查
*/
private boolean isHealthy(AgentCard card) {
String heartbeatKey = REGISTRY_KEY + ":heartbeat:" + card.getAgentId();
return Boolean.TRUE.equals(redis.hasKey(heartbeatKey));
}
}
7.3 心跳与健康检查
java
@Scheduled(fixedRate = 30000) // 每30秒
public void sendHeartbeat() {
redis.opsForValue().set(
REGISTRY_KEY + ":heartbeat:" + agentCard.getAgentId(),
Instant.now().toString(),
Duration.ofSeconds(60)
);
}
@Scheduled(fixedRate = 60000) // 每分钟清理
public void cleanupStaleAgents() {
Set<Object> agentIds = redis.opsForHash().keys(REGISTRY_KEY);
for (Object id : agentIds) {
String heartbeatKey = REGISTRY_KEY + ":heartbeat:" + id;
if (!Boolean.TRUE.equals(redis.hasKey(heartbeatKey))) {
// 移除不健康的Agent
AgentCard card = JsonUtils.fromJson(
redis.opsForHash().get(REGISTRY_KEY, id).toString(),
AgentCard.class
);
unregister(card);
log.warn("Removed stale agent: {}", id);
}
}
}
八、最佳实践与踩坑指南
8.1 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Agent Card获取失败 | 网络问题或端点错误 | 添加重试机制,缓存Agent Card |
| 任务超时 | 任务执行时间过长 | 设置合理timeout,使用SSE流式返回 |
| 认证失败 | Token过期或权限不足 | 实现Token刷新,检查权限配置 |
| 流式连接断开 | 网络不稳定 | 自动重连,保存任务状态 |
| 跨域问题 | CORS配置缺失 | 配置allowedOrigins |
8.2 版本兼容性
A2A协议需要考虑多个版本的兼容性问题:
版本兼容性矩阵:
| 客户端版本 | 服务端版本 | 兼容性 | 说明 |
|---|---|---|---|
| 1.0 | 1.0 | ✅ 完全兼容 | 基础功能 |
| 1.0 | 1.1 | ✅ 向后兼容 | 新增可选字段 |
| 1.1 | 1.0 | ⚠️ 部分兼容 | 新功能不可用 |
| 2.0 | 1.x | ❌ 不兼容 | 协议重大变更 |
版本兼容性策略:
java
@Component
public class VersionCompatibilityChecker {
private static final String CURRENT_VERSION = "1.2.0";
private static final String MIN_SUPPORTED_VERSION = "1.0.0";
/**
* 检查兼容性
*/
public CompatibilityResult checkCompatibility(AgentCard card) {
String serverVersion = card.getSchemaVersion();
if (serverVersion == null) {
return CompatibilityResult.incompatible("缺少版本信息");
}
// 主版本号必须相同
int clientMajor = getMajorVersion(CURRENT_VERSION);
int serverMajor = getMajorVersion(serverVersion);
if (clientMajor != serverMajor) {
return CompatibilityResult.incompatible(
String.format("主版本不匹配: 客户端=%d, 服务端=%d",
clientMajor, serverMajor));
}
// 服务端版本不能低于最低支持版本
if (compareVersions(serverVersion, MIN_SUPPORTED_VERSION) < 0) {
return CompatibilityResult.incompatible(
String.format("服务端版本过低: %s < %s",
serverVersion, MIN_SUPPORTED_VERSION));
}
// 确定可用功能集
FeatureSet features = determineAvailableFeatures(
CURRENT_VERSION, serverVersion);
return CompatibilityResult.compatible(features);
}
/**
* 确定两端都支持的功能集
*/
private FeatureSet determineAvailableFeatures(
String clientVersion, String serverVersion) {
FeatureSet features = new FeatureSet();
// 基础功能(1.0+)
features.enable("basic_tasks");
features.enable("agent_card");
// 1.1+ 功能
if (compareVersions(clientVersion, "1.1.0") >= 0 &&
compareVersions(serverVersion, "1.1.0") >= 0) {
features.enable("streaming");
features.enable("progress_callback");
}
// 1.2+ 功能
if (compareVersions(clientVersion, "1.2.0") >= 0 &&
compareVersions(serverVersion, "1.2.0") >= 0) {
features.enable("artifacts");
features.enable("multipart_response");
}
return features;
}
private int compareVersions(String v1, String v2) {
String[] parts1 = v1.split("\\.");
String[] parts2 = v2.split("\\.");
for (int i = 0; i < Math.max(parts1.length, parts2.length); i++) {
int num1 = i < parts1.length ? Integer.parseInt(parts1[i]) : 0;
int num2 = i < parts2.length ? Integer.parseInt(parts2[i]) : 0;
if (num1 != num2) {
return Integer.compare(num1, num2);
}
}
return 0;
}
private int getMajorVersion(String version) {
return Integer.parseInt(version.split("\\.")[0]);
}
}
降级策略:
java
@Component
public class A2AClientWithFallback {
private final A2AClient client;
private final VersionCompatibilityChecker compatibilityChecker;
public Mono<TaskResponse> submitTask(AgentCard agent, TaskRequest request) {
return Mono.fromSupplier(() -> compatibilityChecker.checkCompatibility(agent))
.flatMap(result -> {
if (!result.isCompatible()) {
return Mono.error(new IncompatibleVersionException(result.reason()));
}
// 根据可用功能集调整请求
TaskRequest adjustedRequest = adjustRequest(request, result.features());
return client.submitTask(agent.getEndpoints().getTask(), adjustedRequest);
});
}
/**
* 根据功能集降级请求
*/
private TaskRequest adjustRequest(TaskRequest request, FeatureSet features) {
TaskRequest.Builder builder = TaskRequest.from(request);
// 如果不支持streaming,移除回调配置
if (!features.has("streaming")) {
builder.callbackUrl(null);
}
// 如果不支持artifacts,移除产物请求
if (!features.has("artifacts")) {
builder.requestArtifacts(false);
}
return builder.build();
}
}
版本协商流程:

java
@Component
public class VersionCompatibilityChecker {
public boolean isCompatible(AgentCard card) {
String serverVersion = card.getSchemaVersion();
String clientVersion = "1.0";
// 语义化版本比较
return compareVersions(clientVersion, serverVersion) >= 0;
}
private int compareVersions(String v1, String v2) {
String[] parts1 = v1.split("\\.");
String[] parts2 = v2.split("\\.");
for (int i = 0; i < Math.max(parts1.length, parts2.length); i++) {
int num1 = i < parts1.length ? Integer.parseInt(parts1[i]) : 0;
int num2 = i < parts2.length ? Integer.parseInt(parts2[i]) : 0;
if (num1 != num2) {
return Integer.compare(num1, num2);
}
}
return 0;
}
}
8.3 错误处理规范
java
public class A2AErrorHandler {
/**
* 标准化错误响应
*/
public TaskResponse handleError(String taskId, Exception e) {
A2AError error = classifyError(e);
return TaskResponse.builder()
.id(taskId)
.status(TaskStatus.FAILED)
.error(ErrorInfo.builder()
.code(error.getCode())
.message(error.getMessage())
.details(error.getDetails())
.retryable(error.isRetryable())
.build())
.build();
}
private A2AError classifyError(Exception e) {
if (e instanceof AuthenticationException) {
return A2AError.builder()
.code("AUTH_FAILED")
.message("认证失败")
.retryable(false)
.build();
}
if (e instanceof RateLimitExceededException) {
return A2AError.builder()
.code("RATE_LIMIT")
.message("请求频率超限")
.retryable(true)
.details(Map.of("retryAfter", "60s"))
.build();
}
if (e instanceof TimeoutException) {
return A2AError.builder()
.code("TIMEOUT")
.message("任务执行超时")
.retryable(true)
.build();
}
// 默认内部错误
return A2AError.builder()
.code("INTERNAL_ERROR")
.message("内部服务错误")
.retryable(true)
.build();
}
}
九、总结
A2A协议的核心价值:
- 标准化通信 → 统一Agent间交互协议
- 能力发现 → Agent Card描述能力边界
- 互操作性 → 跨平台、跨厂商协作
- 可扩展性 → 支持流式、产物、回调
与其他模式的协作:
| 模式 | 与A2A的关系 |
|---|---|
| Subagent | 远程Subagent通过A2A协议通信 |
| Agent Skills | Skill可以包含A2A端点配置 |
| TodoWriteTool | A2A任务可以更新Todo状态 |
| AutoMemoryTools | 保存A2A Agent偏好 |
协议选择指南:
