ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

多 Agent 协作别过度设计:A2A(Agent2Agent)协议

多 Agent 协作别过度设计:A2A(Agent2Agent)协议
摘要:很多开发者做多Agent协作时,分不清什么时候该上A2A协议。本文从工程落地角度通俗拆解A2A‑1.0,区分进程内协作与远程跨域Agent协作边界,结合Spring AI Alibaba实战案例,讲清楚A2A能干什么、什么必须业务自己实现,避免过度设计。

很多开发者在做AI Agent多智能体协作时,都会混淆一个核心问题:到底什么时候需要用A2A协议?本地多Agent协作能不能用A2A?A2A能解决哪些问题、哪些坑它管不了?

一、核心大前提:90%的本地多Agent,根本不需要A2A

首先纠正一个高频误区:A2A不是本地多Agent调度框架,它是跨机器、跨团队、跨安全域的AI智能体通信标准协议。

如果你的所有Agent角色都运行在同一个进程、同一个Spring服务内,直接本地方法调用即可,例如agent.run(input),简单高效、无网络开销、无协议冗余,完全没必要引入A2A协议。

二、严格界定:什么时候才需要启用A2A协议?

只有满足任意一条分布式、跨域协作场景,才考虑接入A2A协议,这是落地选型的核心标准:

  1. 独立部署:各个Agent拆分独立服务,部署在不同机器、不同集群,无法本地调用;
  2. 跨团队维护:不同Agent由不同业务团队独立开发、迭代、运维,需要标准化跨团队对接;
  3. 跨安全域调用:存在内外网隔离、甲乙双方服务对接、跨租户权限隔离等场景,需要标准化远程通信。

三、A2A的能力边界:能做什么、绝不做什么

搞懂边界,才能避免落地踩坑,这是A2A最核心的工程认知:

✅ A2A协议核心解决的问题

统一远程AI Agent的协作规范,标准化解决四大核心问题:远程Agent能力发现、标准化消息交互、长异步任务全链路跟踪、结构化结果/大文件产物交付。

❌ A2A不负责、不自动实现的能力

A2A只是通信层、数据格式层标准,不包含任何分布式一致性、任务容错能力,以下逻辑必须业务层自行实现:

  • 并发读写冲突处理、乐观锁版本控制;
  • 服务宕机后的任务接管、租约机制;
  • 接口重试导致的重复执行、幂等校验;
  • 分布式隔离令牌(fencingToken)、任务归属控制。

四、A2A核心名词拆解(通俗版+工程释义)

A2A整套协作体系由五大核心组件构成,全程对应外包工单协作通俗类比,秒懂核心逻辑:

1、Agent Card(Agent能力名片)

通俗类比:外包公司公开的宣传手册+接口说明书
工程释义:机器可读的标准化能力描述文件,是远程Agent的唯一对接入口。调用方对接前,必须先读取对方的Agent Card,明确核心信息:Agent能力范围、接口地址、入参出参Schema、认证方式、权限要求。

等同于调用第三方API前查阅的官方文档,A2A将人工文档标准化、机器自动化解析,实现Agent无感能力发现。

2、Task(全局唯一任务)

通俗类比:带唯一工单号的正式服务工单
工程释义:A2A的核心调度单元,每个任务拥有全局唯一taskId。区别于普通同步接口,A2A专为长异步Agent任务设计,任务可长时间执行、可中途暂停、可多轮恢复,不局限于单次请求响应。

3、Message(交互消息)

通俗类比:供需双方来回沟通的邮件、补充说明
工程释义:Agent间的标准化交互载体,支持多轮对话、参数补充、需求变更、状态同步,打破单次请求响应的局限,适配Agent复杂交互式协作场景。

4、Context(会话上下文)

通俗类比:工单专属归档文件夹
工程释义:全局会话容器,将同一个Task下的所有消息、操作日志、最终产物、状态变更全部关联绑定,实现全链路可追溯、可复盘。

5、Artifact(任务产物)

通俗类比:外包最终交付的报告、成果文件
工程释义:任务最终交付结果,支持多种形态:结构化JSON数据、PDF报告、代码文件、数据资源URI。针对大文件场景,A2A默认传递资源引用URI,不阻塞消息体,保障传输效率。

五、A2A细粒度任务状态机(核心重点)

普通接口只有「成功/失败」两种状态,完全无法适配Agent长任务、可暂停、可恢复的场景。A2A定义了全套细粒度任务状态枚举,精准描述任务全生命周期:

任务状态枚举状态含义场景说明
TASK_STATE_UNSPECIFIED未知/未初始化任务默认初始状态,未提交未初始化
TASK_STATE_SUBMITTED任务已提交任务创建完成,等待Agent开始处理
TASK_STATE_WORKING任务执行中远程Agent正在正常执行业务逻辑
TASK_STATE_INPUT_REQUIRED暂停‑需外部输入任务中断,缺少业务参数/用户指令,等待补充后恢复
TASK_STATE_AUTH_REQUIRED暂停‑需权限授权任务中断,缺少资源访问权限、登录授权,授权后可继续执行
TASK_STATE_COMPLETED任务成功完成执行完毕,可读取Artifact产物
TASK_STATE_FAILED任务执行失败程序异常、业务校验失败,任务终止
TASK_STATE_CANCELED任务主动取消调用方主动终止未完成任务
TASK_STATE_REJECTED任务被拒绝参数非法、权限不足,Agent直接拒收任务

核心重点:INPUT_REQUIRED、AUTH_REQUIRED是A2A的精髓,任务既非成功也非失败,属于挂起暂停状态,完美适配Agent长任务交互式协作场景。

六、关键避坑:A2A不解决分布式一致性问题

这是A2A落地最大的误区,再次重点强调:A2A只定义通信格式、任务字段、交互规范,不提供任何分布式容错能力。

以下核心分布式能力,必须由业务代码自行实现:

  • 多Worker并发修改同一Task:A2A无乐观锁、无版本控制,需业务自行加version字段防冲突;
  • 服务宕机任务接管:A2A无租约机制,需自行实现leaseUntil、ownerAgentId归属管控;
  • 重试重复执行:A2A无幂等机制,需业务自行做幂等校验、fencingToken隔离。

七、落地选型对比:进程内DAG VS 分布式A2A

一张表搞定所有场景选型,彻底告别纠结:

部署场景调用方式是否需要A2A是否需要分布式防护逻辑
所有Agent同进程、同一个Spring服务本地方法调用 agent.run()不需要不需要
Agent独立部署、跨团队/跨安全域网络远程调用需要完整A2A协议必须实现Version、租约、幂等、令牌隔离

八、实战案例:需求Agent + 代码Agent,基于Spring AI Alibaba

以「需求整理Agent + 代码生成Agent」为例,对比进程内借鉴A2A思想和远程微服务启用完整A2A‑1.0协议两种落地模式,基于Spring AI Alibaba框架。

场景1:同进程部署(Spring AI Alibaba,只借鉴A2A思想,不引入A2A协议)

两个Agent都在同一个Spring Boot应用中,使用Spring AI Alibaba构建Agent实例,没有网络调用,借鉴A2A结构化数据契约的设计思想,拒绝大段自然语言传递参数。

❌ 错误做法:Agent之间传递大段自然语言文本

需求Agent输出文本:“我整理好了登录模块需求,需要做token校验,接口响应不能太慢,麻烦你实现一下”

问题:Token开销高,LLM自由转述容易丢失约束条件,下游Agent需要做文本理解,输出不稳定,无法做结构化参数校验。

✅ 正确做法:Agent之间传递结构化POJO对象
借助Spring AI Alibaba的StructuredOutputConverter,强制需求Agent输出强类型RequirementOutput对象,而不是自由文本字符串。

1. 先定义POJO实体

import com.fasterxml.jackson.annotation.JsonClassDescription;
import java.util.List;

@JsonClassDescription("登录模块开发需求输出对象")
public class RequirementOutput {
private String taskId;
private String taskType;
private List<String> dependencies;
private List<String> acceptanceCriteria;
private String requirementDesc;
private String artifactUri;

// getter / setter 省略
}

2. Agent内部使用StructuredOutputConverter强制结构化输出

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.converter.BeanOutputConverter;

/**
* 需求整理Agent,Spring AI Alibaba 进程内Agent
*/
public class RequirementAgent {

private final ChatClient chatClient;
// 结构化输出转换器,指定目标POJO
private final BeanOutputConverter<RequirementOutput> outputConverter
= new BeanOutputConverter<>(RequirementOutput.class);

public RequirementAgent(ChatClient chatClient) {
this.chatClient = chatClient;
}

public RequirementOutput run(String userInput) {
return chatClient.prompt()
.user(u -> u.text("整理下面业务需求,输出标准化开发需求:{input}\n{formatInstructions}")
.param("input", userInput)
.param("formatInstructions", outputConverter.getFormatInstructions()))
.call()
.entity(outputConverter); // 直接反序列化为Java对象
}
}

3. 编排器直接本地调用串联两个Agent

// 编排器代码,同一个Spring进程内
RequirementAgent requirementAgent;
CodeAgent codeAgent;

// 用户原始输入
String userInput = "实现登录接口,需要token校验,接口响应小于200ms,依赖user_db";

// 需求Agent直接返回强类型对象,不是字符串
RequirementOutput reqOut = requirementAgent.run(userInput);
// 直接把POJO传给下游代码Agent,无自然语言中转
CodeResult codeRes = codeAgent.run(reqOut);
说明:该模式只是借鉴A2A的“结构化产物”思想,没有Agent Card、没有网络Task状态机、没有Artifact协议对象,属于进程内最优方案。
关键点:BeanOutputConverter会自动把JSON Schema传给大模型,强制LLM输出符合POJO结构,避免自由文本带来的信息丢失。

场景2:跨团队远程部署,Spring AI Alibaba对接A2A‑1.0协议(带代码样例支撑)

业务背景:

  • A团队:编排服务(Spring Boot + Spring AI Alibaba),内部运行需求Agent;
  • B团队:独立微服务,部署代码Agent,对外暴露A2A 1.0标准HTTP接口;
  • 两套服务属于不同服务、不同团队,跨安全域,必须走网络调用,使用完整A2A协议。
说明:Spring AI Alibaba目前没有封装完整A2A客户端SDK,业务侧基于RestTemplate/WebClient封装A2A HTTP调用,下面是简化可运行伪代码,还原完整流程。
import org.springframework.web.reactive.function.client.WebClient;
import java.util.UUID;

/**
* A团队编排服务:A2A远程调用B团队代码Agent
*/
public class A2ACodeAgentClient {

private final WebClient webClient;
// B团队代码Agent服务地址
private static final String AGENT_BASE_URL = "http://code-agent-service";

public A2ACodeAgentClient(WebClient.Builder builder) {
this.webClient = builder.baseUrl(AGENT_BASE_URL).build();
}

// 步骤1:拉取远程Agent Card(能力名片)
public String fetchAgentCard() {
return webClient.get()
.uri("/.well-known/agent-card")
.retrieve()
.bodyToMono(String.class)
.block();
}

// 步骤2:创建A2A Task,生成全局taskId
public String createA2ATask() {
String taskId = "a2a-task-" + UUID.randomUUID();
// 请求体省略A2A标准task对象
webClient.post()
.uri("/v1/tasks")
.bodyValue("""
{
"taskId":"%s",
"state":"TASK_STATE_SUBMITTED"
}
""".formatted(taskId))
.retrieve()
.toBodilessEntity()
.block();
return taskId;
}

// 步骤3:发送Message,携带结构化需求payload
public void sendMessage(String taskId, RequirementOutput reqOut) {
webClient.post()
.uri("/v1/tasks/%s/messages".formatted(taskId))
.bodyValue("""
{
"taskId":"%s",
"payload":%s
}
""".formatted(taskId, Jackson.toJson(reqOut)))
.retrieve()
.toBodilessEntity()
.block();
}

// 轮询任务状态,生产环境建议改为webhook回调
public TaskStatus pollTaskStatus(String taskId) {
String resp = webClient.get()
.uri("/v1/tasks/%s".formatted(taskId))
.retrieve()
.bodyToMono(String.class)
.block();
return parseTaskStatus(resp);
}

// 发送授权信息,恢复挂起任务 AUTH_REQUIRED
public void resumeTaskWithAuth(String taskId, String authToken) {
webClient.post()
.uri("/v1/tasks/%s/messages".formatted(taskId))
.bodyValue("""
{
"taskId":"%s",
"payload":{"authToken":"%s"}
}
""".formatted(taskId, authToken))
.retrieve()
.toBodilessEntity()
.block();
}
}

编排主流程伪代码:

// 编排入口
public void runRemoteAgentFlow(String userInput){
// 1、本地需求Agent输出结构化对象(Spring AI Alibaba结构化输出)
RequirementOutput reqOut = requirementAgent.run(userInput);

A2ACodeAgentClient a2AClient = new A2ACodeAgentClient(webClientBuilder);

// 2、拉取远程Agent Card做能力校验
String agentCard = a2AClient.fetchAgentCard();

//3、创建A2A Task
String taskId = a2AClient.createA2ATask();

//4、发送结构化需求到远程Agent
a2AClient.sendMessage(taskId, reqOut);

//5、轮询状态
while(true){
TaskStatus status = a2AClient.pollTaskStatus(taskId);
if("TASK_STATE_AUTH_REQUIRED".equals(status.getState())){
// 任务挂起,业务获取授权凭证,恢复任务
String authToken = getBizAuthToken();
a2AClient.resumeTaskWithAuth(taskId, authToken);
}else if("TASK_STATE_COMPLETED".equals(status.getState())){
// 读取Artifact产物URI,下载结果文件
List<Artifact> artifacts = status.getArtifacts();
for (Artifact artifact : artifacts) {
downloadArtifact(artifact.getUri());
}
break;
}else if(List.of("TASK_STATE_FAILED","TASK_STATE_REJECTED","TASK_STATE_CANCELED").contains(status.getState())){
throw new RuntimeException("A2A任务异常:"+status.getState());
}
}
}
⚠️重要工程提醒
虽然使用A2A协议完成通信,但Spring业务侧仍然需要自己在数据库维护:
version乐观锁版本号、ownerAgentId任务持有者、leaseUntil租约过期时间、fencingToken隔离令牌,处理Worker宕机、并发更新、重试幂等问题。A2A协议不会帮你实现这部分分布式逻辑。
补充:生产环境不建议轮询,优先使用A2A规范webhook回调接收状态变更;示例仅演示协议交互逻辑,异常处理、超时、重试、序列化做了简化。

九、全文总结(核心干货复盘)

  1. A2A是跨域远程Agent通信协议,本地同进程Spring应用内多Agent直接方法调用,无需过度设计;
  2. 核心适用场景:Agent独立部署、跨团队维护、跨安全域调用;
  3. 五大核心组件:AgentCard(能力发现)、Task(长任务调度)、Message(多轮交互)、Context(链路追溯)、Artifact(产物交付);
  4. 核心价值:标准化长任务生命周期,支持任务暂停、外部输入、授权恢复,解决远程Agent互通;
  5. 核心边界:A2A只管通信与数据格式,分布式一致性、容错、幂等、租约全部业务自研;
  6. Spring AI Alibaba中:进程内优先用BeanOutputConverter做结构化POJO传参;只有远程跨服务时,才对接完整A2A协议。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进