扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

A2A是什么,不好理解?那我帮你运行起来!

03/18 11:26
117
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

作者:小傅哥,博客:https://bugstack.cn

chatgpt(22年)-> LLM 进阶(23年)->  MCP 协议(24年)-> Skils 技能(25年)-> A2A 标准(25年),这几年 AI 发展的非常迅速,定义了协议,开发了组件,发布了产品。每个阶段,都有非常牛皮的代表性的内容,车速非常快,不知道在坐的各位有没有掉队。

针对这些 LLM 大模型的相关知识,小傅哥也分别提供了相关实战类项目,包括;AI 问答助手(23年)OpenAi(ChatGPT/ChatGLM) 微服务应用体系构(23年)OpenAI 代码自动评审组件(24年)AI Agent 智能体 - 拖拉拽(25年)AI MCP Gateway 网关服务系统(25年)AI Agent 脚手架 + 场景应用(26年),通过这些实战项目,实践各类标准协议和技术组件(spring ai、google adk...)的使用,以及提供非常出色的解决方案。

我也检索过很多a2a资料,大部分都没有运行起来,都是理论。这也是很多伙伴学习时候困惑的地方。另外后续的 ai agent 智能体项目,还会涉及到 a2a 协议,所以先给大家做个相关技术的使用方便大家快速实践理解。

文末提供了20个实战项目(6个AI、5个业务、8个组件、1套源码),以及各类编程技术小册等。欢迎一起加入学习。

一、A2A 是什么?

A2A (Agent2Agent) 协议是 Google于2025年4月推出、并由 Linux基金会托管的开源开放标准。它作为AI智能体之间的“通用语言”,旨在实现不同框架、不同厂商构建的智能体(Agent)间跨平台发现、通信与协作。

随后25年底,Google 发布了 a2a adk 组件(迭代速度很快)。之后借助代理开发工具包 (Google ADK),我们可以构建复杂的多代理系统,其中不同的代理需要使用代理对代理 (A2A) 协议进行协作和交互!本节提供了一个全面的指南,指导您构建强大的多代理系统,使代理能够安全高效地进行通信和协作。

框架:https://google.github.io/adk-docs/a2a/

源码:https://github.com/google/adk-java

资料:https://www.ibm.com/cn-zh/think/topics/agent2agent-protocol

文档:https://a2aprotocol.ai/docs/

整套智能体架构(含A2A);

    左侧,是 LLM 模型智能体构建,基于 Spring AI 框架实现。这部分包括,AiApi 使用、RAG、MCP、Skills 的组合构建。右侧,是 AI Agent 智能体编排,基于 Google ADK 框架实现。这套框架,提供了智能体工作流组装,插件回调钩子等各项基于 Google 发布的协议进行构建的。之后,右上方,是 Google ADK 26年基于 A2A 协议实现的框架组件,方便我们设计出远程的 A2A 服务对接,把远程服务,转换为本地功能组件,编排进智能体中。因此 A2A 协议的作用,就是让一个远程的智能体,可以像构建本地智能体一样,连接起来进行使用。

基于 a2a  的协议,也有很多三方框架,但我们需要的是一个整体的解决方案,单一一个功能,还不能解决所有问题。所以这里优先选择 google adk 框架。

二、实践案例

1. 编程环境

    jdk 17google adk 0.8spring ai 1.1.0-M3

相关的版本包,已经在测试工程中的 pom 里引入了。可以打开工程查看。

2. 工程结构

    • 工程:https://github.com/fuzhengwei/xfg-dev-tech-google-adk-a2a-server说明:基于 Google ADK 框架,构建 A2A 服务端和客户端。注意,服务端的启动方式为

quarkus.sh

     方式启动服务,之后使用客户端连接。注意:这部分还有一些关于关于 Spring AI + Google ADK 的基础使用知识,是在 ApiTest 中有案例。也可以学习项目 《AI Agent 脚手架 + 场景应用》

3. 测试案例

public class ApiTest {

    public static void main(String[] args) throws Exception {
        OpenAiApi openAiApi = OpenAiApi.builder()
                .baseUrl("https://apis.****.cn")
                .apiKey("sk-tIYdEUUnMqKX3bRf756a31449a4942***需要配置你的key")
                .completionsPath("v1/chat/completions")
                .embeddingsPath("v1/embeddings")
                .build();

        ChatModel chatModel = OpenAiChatModel.builder()
                .openAiApi(openAiApi)
                .defaultOptions(OpenAiChatOptions.builder()
                        .model("gpt-4o")
                        .build())
                .build();

        // agent 测试
        LlmAgent agent = LlmAgent.builder()
                .name("test")
                .description("Chess coach agent")
                .model(new SpringAI(chatModel))
                .instruction("""
                        You are a knowledgeable chess coach
                        who helps chess players train and sharpen their chess skills.
                        """)
                .build();

        InMemoryRunner runner = new InMemoryRunner(agent);

        Session session = runner
                .sessionService()
                .createSession("test", "fzw")
                .blockingGet();

        Flowable<Event> events = runner.runAsync("fzw", session.id(),
                Content.fromParts(Part.fromText("1+1")));

        System.out.print("nAgent > ");
        events.blockingForEach(event -> System.out.println(event.stringifyContent()));
    }

}
    • 这是一个 Spring AI + Google ADK 构建的简单智能体,由 LlmAgent 构建时候,创建模型

new SpringAI(chatModel)

     关联到 Spring AI 框架。之后,由 Google ADK 构建的智能体,使用内存记忆,创建会话之后进行测试。也可以学习项目 《AI Agent 脚手架 + 场景应用》 锻炼使用相关内容。

4. A2A 测试

4.1 服务端

这部分内容的学习,可以打开案例代码,方便对比。

4.1.1 服务提供

@ApplicationScoped
publicclass AgentExecutorProducer {

    @ConfigProperty(name = "my.adk.app.name", defaultValue = "default-app")
    String appName;

    @Produces
    public AgentExecutor agentExecutor() {
        returnnew com.google.adk.a2a.executor.AgentExecutor.Builder()
                .agent(Agent.ROOT_AGENT)
                .appName(appName)
                .sessionService(new InMemorySessionService())
                .artifactService(new InMemoryArtifactService())
                .agentExecutorConfig(AgentExecutorConfig.builder().build())
                .build();
    }
    
}
    这部分是把智能体提供出去,通过 google adk 框架,创建 AgentExecutor 实例。之后 Agent.ROOT_AGENT 类似于上面的测试案例,智能体构建的部分。你可以创建任何之后通过 AgentExecutor 发布出去。

4.1.2 卡片提供

@ApplicationScoped
publicclass AgentCardProducer {

    @Produces
    @PublicAgentCard
    public AgentCard agentCard() {
        try (InputStream is = getClass().getResourceAsStream("/agent/agent.json")) {
            if (is == null) {
                thrownew RuntimeException("agent.json not found in resources");
            }

            // Read the JSON file content
            String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);

            // Use the SDK's built-in mapper to convert JSON string to AgentCard record
            return Utils.OBJECT_MAPPER.readValue(json, AgentCard.class);

        } catch (Exception e) {
            thrownew RuntimeException("Failed to load AgentCard from JSON", e);
        }
    }

}

/agent/agent.json

{
  "capabilities": {"streaming": true},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["application/json"],
"description": "一个专门检查数字是否为素数的智能体。它可以高效地确定单个数字或数字列表的素性。",
"name": "check_prime_agent",
"skills": [
    {
      "id": "prime_checking",
      "name": "Prime Number Checking",
      "description": "使用高效的数学算法检查列表中的数字是否为素数",
      "tags": ["mathematical", "computation", "prime", "numbers"]
    }
  ],
"preferredTransport": "JSONRPC",
"url": "http://localhost:9090",
"version": "1.0.0"
}

    • 这里要构建一个智能体卡,A2A 的协议中,把服务包装成一个卡片的概念。agent.json 配置的是

Agent.ROOT_AGENT

     智能体的信息。如果你之前学习过小傅哥的 AI MCP 网关项目,会对 JSONRPC 有印象, MCP 协议和 A2A 协议,都是走到 JSONRPC 标准进行的通信。
4.2 客户端

4.2.1 构建端

public finalclass A2AAgent {

    privatestaticfinal Random RANDOM = new Random();

    privatestaticfinal OpenAiApi openAiApi = OpenAiApi.builder()
            .baseUrl("https://apis.****.cn")
            .apiKey("sk-tIYdEUUnMqKX3bRf756a31449a4942***需要配置你的key")
            .completionsPath("v1/chat/completions")
            .embeddingsPath("v1/embeddings")
            .build();

    privatestaticfinal ChatModel chatModel = OpenAiChatModel.builder()
            .openAiApi(openAiApi)
            .defaultOptions(OpenAiChatOptions.builder()
                    .model("gpt-4.1")
                    .build())
            .build();

    @SuppressWarnings("unchecked")
    public static ImmutableMap<String, Object> rollDie(int sides, ToolContext toolContext) {
        ArrayList<Integer> rolls =
                (ArrayList<Integer>) toolContext.state().computeIfAbsent("rolls", k -> new ArrayList<>());
        int result = RANDOM.nextInt(Math.max(sides, 1)) + 1;
        rolls.add(result);
        return ImmutableMap.of("result", result);
    }

    publicstaticfinal LlmAgent ROLL_AGENT =
            LlmAgent.builder()
                    .name("roll_agent")
                    .model(new SpringAI(chatModel))
                    .description("处理不同面数骰子的投掷。")
                    .instruction(
                            """
                                      当被要求掷骰子时,始终调用 roll_die 工具并指定面数(如果未指定,默认为 6)。不要编造结果。
                                    """)
                    .tools(ImmutableList.of(FunctionTool.create(A2AAgent.class, "rollDie")))
                    .build();

    public static LlmAgent createRootAgent(String primeAgentBaseUrl) {
        // 远程 agent
        BaseAgent primeAgent = createRemoteAgent(primeAgentBaseUrl);

        // 本地 agent
        return LlmAgent.builder()
                .name("root_agent")
                .model(new SpringAI(chatModel))
                .instruction(
                        """
                                  你可以在本地掷骰子,并将素数检查委托给远程的 prime_agent。
                                  1. 当用户要求掷骰子时,将请求路由给 roll_agent。
                                  2. 当用户要求检查素数时,委托给 prime_agent。
                                  3. 如果用户要求先掷骰子然后检查,先调用 roll_agent,然后将结果传给 prime_agent。
                                  在讨论素数之前,始终先简述骰子结果。
                                """)
                .subAgents(ImmutableList.of(ROLL_AGENT, primeAgent))
                .build();
    }

    private static BaseAgent createRemoteAgent(String primeAgentBaseUrl) {

        String agentCardUrl = primeAgentBaseUrl + "/.well-known/agent-card.json";
        AgentCard publicAgentCard =
                new A2ACardResolver(new JdkA2AHttpClient(), primeAgentBaseUrl, agentCardUrl).getAgentCard();

        Client a2aClient =
                Client.builder(publicAgentCard)
                        .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig())
                        .clientConfig(
                                new ClientConfig.Builder()
                                        .setStreaming(publicAgentCard.capabilities().streaming())
                                        .build())
                        .build();

        return RemoteA2AAgent.builder()
                .name(publicAgentCard.name())
                .a2aClient(a2aClient)
                .agentCard(publicAgentCard)
                .build();
    }

}
    • createRootAgent 是构建入口,一个是创建 createRemoteAgent 远程智能体,一个是 LlmAgent.builder()
    •  构建本地智能体。之后在构建本地智能体的时候,通过 subAgents 把远程智能体填充到本地的智能体里去了。这个编排的方式有多种多样的。createRemoteAgent 远程构建,这是固定协议路径

/.well-known/agent-card.json

    拿到智能体卡片以后(一个卡片上有各类的信息),之后通过 a2aClient 创建客户端,在通过 RemoteA2AAgent 完成本地智能体的转换。

4.2.2 使用端

public finalclass A2AAgentRun {
    privatefinal String userId;
    privatefinal String sessionId;
    privatefinal Runner runner;

    public A2AAgentRun(BaseAgent agent) {
        this.userId = "test_user";
        String appName = "A2AAgentApp";
        this.sessionId = UUID.randomUUID().toString();

        InMemoryArtifactService artifactService = new InMemoryArtifactService();
        InMemorySessionService sessionService = new InMemorySessionService();
        this.runner =
                new Runner(agent, appName, artifactService, sessionService, /* memoryService= */null);

        ConcurrentMap<String, Object> initialState = new ConcurrentHashMap<>();
        var unused =
                sessionService.createSession(appName, userId, initialState, sessionId).blockingGet();
    }
    
    // ... 省略部分

    public static void main(String[] args) throws InterruptedException {
        BaseAgent agent = A2AAgent.createRootAgent("http://localhost:9090");
        A2AAgentRun a2aRun = new A2AAgentRun(agent);

        List<Event> events =
                a2aRun.run("掷一个6面的骰子。").toList().timeout(90, TimeUnit.SECONDS).blockingGet();

        events.forEach(A2AAgentRun::printOutEvent);

        events =
                a2aRun.run("这是素数吗?").toList().timeout(90, TimeUnit.SECONDS).blockingGet();

        events.forEach(A2AAgentRun::printOutEvent);
    }

}
    A2AAgentRun 测试入口,连接远程的智能体,之后做相关的调用验证。

三、测试验证

1. 前置准备

    1. IntelliJ IDEA 右侧的 maven 点击执行 clean -> install 构建通过命令启动服务,

mvn quarkus:dev -pl xfg-dev-tech-app

2. 访问服务

2.1 服务首页
    地址:http://localhost:9090/q/dev-ui/welcome说明:访问首页,localhost:9090 会看到上面的地址信息,这个是 a2a 协议的入口。
2.2 服务协议
    地址:http://localhost:9090/.well-known/agent-card.json说明:点击 agent-card.json 可以看到具体的服务协议信息。

3. 调用验证

    点击运行,你可以看到它在进行多个智能体的调用(可能模型原因不准,但核心点在于多个智能体的调用)。好啦,到这关于 A2A 整个内容就演示完了,可以时刻关注 google adk a2a 协议的迭代,这部分内容后续还会有一些调整。

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

作者小傅哥多年从事一线互联网Java开发,从19年开始编写工作和学习历程的技术汇总,旨在为大家提供一个较清晰详细的核心技能学习文档。如果本文能为您提供帮助,请给予支持(关注、点赞、分享)!