A2A协议实战:两个Agent怎么聊

A2A协议实战:两个Agent怎么聊

摘要 :上篇写了 MCP 让 Agent 调工具,这篇写 A2A(Agent-to-Agent)让 Agent 调另一个 Agent。MCP 是 AI 世界的 REST API,A2A 是 AI 世界的微服务间通信。核心机制是一张"名片"------每个 Agent 对外暴露 /.well-known/agent.json,写上自己能干什么、怎么调。调用方先看名片再交互,不硬编码 API 路径。


上篇用 MCP 写了一个天气查询工具。Claude 想知道北京天气,调 get_weather,拿到结果,回答用户。整条链路是 Agent → 工具,单向调用。

但真实场景往往不是这样。你有一个 Agent 负责查天气,另一个 Agent 负责做决策------比如"今天适不适合打球"。查天气的 Agent 不管业务逻辑,做决策的 Agent 不管天气数据。它们需要互相通信

问题来了:Agent 之间怎么聊?


A2A 是什么

Google 在 2025 年 4 月发布了 A2A(Agent-to-Agent)协议,目标很明确------建立一张"智能体互联网"(Internet of Agents),让不同厂商、不同框架开发的 Agent 能互相通信(来源:Google DeepMind 官方博客,2025年4月)。

用后端的话说,MCP 解决的是 Agent ↔ 工具 的通信,A2A 解决的是 Agent ↔ Agent 的通信。两个协议的定位互补:

scss 复制代码
           MCP                      A2A
    Agent ──────► 工具        Agent ──────► Agent
    (单向调用)                 (双向协作)

A2A 的核心机制非常简单:每张 Agent 对外暴露一张"名片"

这个名字叫 Agent Card,路径固定为 /.well-known/agent.json。任何 Agent 想知道另一个 Agent 能干什么、怎么调,第一步就是去读这张名片。名片里写了什么?名字、能力描述、调用端点、输入格式、鉴权方式------看完这张名片,你就知道对方是干什么的、怎么跟它交互。

这跟你用了十年的微服务注册中心一个道理。Nacos 里每个服务注册自己的名字、IP、端口、健康状态,其他服务从注册中心拉列表,动态发现、动态调用。A2A 的名片机制就是 Agent 世界的服务发现------只是它不需要一个集中的注册中心,每张名片挂在自己服务下面就行。

架构长这样:

typescript 复制代码
                        ┌────────────────────────────┐
                        │     Agent Card (名片)      │
                        │  /.well-known/agent.json   │
                        │─────────────────────────── │
                        │  name: "weather-agent"     │
                        │  description: "查天气"      │
                        │  url: /api/tasks/weather   │
                        │  input_schema: {           │
                        │    city: string            │
                        │  }                         │
                        │  auth: bearer_token        │
                        └────────────┬──────────────┘
                                     │
                                     │ ① 先看名片:你是谁?怎么调?
          ┌──────────────────────────┼──────────────────────────┐
          │                          ▼                          │
   ┌──────┴──────┐          ┌───────────────┐          ┌───────┴──────┐
   │ BasketBall  │  ② 提交  │  A2A Protocol │  ③ 处理  │   Weather    │
   │   Agent     │ ───────► │  (任务式异步)   │ ───────► │   Agent      │
   │             │          │               │          │              │
   │  "能不能     │ ◄─────── │  JSON-RPC     │ ◄─────── │  "晴天,26°C"  │
   │   打球?"    │  ④ 返回  │               │   结果    │              │
   └─────────────┘          └───────────────┘          └──────────────┘

重点看流程:BasketBall Agent 不硬编码 Weather Agent 的 API 地址。它只做一件事------先发一个 GET 请求到 /.well-known/agent.json,读出 Weather Agent 的调用端点和参数格式,然后再提交任务。Weather Agent 随便改路由、改端口,只要名片同步更新,BasketBall Agent 下次读名片就能自动适配。

不硬编码路径,靠名片动态发现------这就是 A2A 跟传统 API 调用的本质区别。

这个 /.well-known/ 路径前缀跟 Web 的 robots.txt 一样,是协议的硬约定。我第一次做的时候想换成 /api/agent-card.json,觉得路径爱叫什么叫什么。结果客户端 SDK 只认 /.well-known/,直接报 discovery failed。


两个 Agent 怎么聊:天气 + 打球

说设计,不贴完整代码。用上面的 Weather Agent + BasketBall Agent 把整条链路走通。

Weather Agent:暴露能力

Weather Agent 只干一件事:接收城市名,返回天气。它对外暴露两样东西------名片和任务端点。

名片 /.well-known/agent.json 长这样:

json 复制代码
{
  "name": "weather-agent",
  "description": "查询指定城市的实时天气,返回温度、天气状况、风速",
  "url": "http://localhost:8081",
  "capabilities": {
    "streaming": false
  },
  "skills": [
    {
      "id": "check_weather",
      "name": "查天气",
      "description": "输入城市名,返回该城市当前的温度、天气状况和风速",
      "input_schema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "城市名称,如 北京、上海"
          }
        },
        "required": ["city"]
      }
    }
  ]
}

input_schema 是关键。它是标准的 JSON Schema 格式,LLM 读到这个 Schema 之后能自动理解参数格式。但 Schema 写错了 LLM 不会告诉你------它直接给你一个它"理解"的参数。有一次我 Schema 里声明 {"type": "string"},代码里却传了嵌套 object,LLM 按 string 理解去构造请求,参数对不上,查了半天。另外 required 数组别漏------你写了三个参数但 required 只列两个,LLM 就当第三个是可选的,直接不传。

任务端点 /api/tasks/weather 负责接收请求、处理查询、返回结果。A2A 协议用任务式异步模型------调用方提交一个 task,服务方处理完返回结果。本质上跟你写的异步 REST 接口差不多,只是协议层面统一了 task 的状态机(submitted → processing → completed/failed)。

BasketBall Agent:读名片、做决策

BasketBall Agent 的逻辑分三步:

第一步,读名片。 它知道 Weather Agent 的地址,但不知道具体端点。所以先发一个 GET 请求到 http://weather-agent:8081/.well-known/agent.json,拿到上面的 JSON。读完它知道了三件事:这个 Agent 能查天气、调用端点是 /api/tasks/weather、需要的参数是 {"city": "string"}

第二步,提交任务。 根据名片里的 input_schema,BasketBall Agent 构造请求:

json 复制代码
{
  "task": "check_weather",
  "params": {
    "city": "北京"
  }
}

POST 到 http://weather-agent:8081/api/tasks/weather。Weather Agent 处理完返回 {"city": "北京", "temperature": 26, "condition": "晴", "wind_speed": 3}

第三步,做决策。 BasketBall Agent 拿到天气数据,跑自己的逻辑:温度 26°C,晴天,风速 3 级------"能打球"。如果是暴雨,风速 10 级,它就返回"今天取消,改室内"。

整条链路不复杂,但关键点只有一个:BasketBall Agent 从头到尾没有硬编码 Weather Agent 的 API 路径。所有路由信息都来自那张名片。Weather Agent 升级改版把端点从 /api/tasks/weather 改成 /v2/tasks/weather-check,只要名片同步更新,BasketBall Agent 不用重新部署。

为什么不用 MCP

有人会问:MCP 不也能让 Agent 调工具吗?BasketBall Agent 直接用 MCP 调天气工具不就行了?

区别在粒度。MCP 是 Agent 调工具 ------查天气是一个工具函数,Agent 在推理链上决定要不要调它。A2A 是 Agent 调另一个 Agent------Weather Agent 是一个独立服务,有自己的推理能力、自己的状态管理、自己独立的生命周期。它可能不只是返回天气数据,还可能自己调用其他工具、做数据清洗、甚至根据天气推荐穿衣建议------这些是 BasketBall Agent 不需要关心的。

简单说:MCP 暴露的是函数,A2A 暴露的是能力。函数是无状态的工具调用,Agent 是有状态的协作方。


MCP vs A2A

维度 MCP A2A
谁调谁 Client/Claude → 工具服务 Agent → Agent
通信方向 单向(Agent 调工具,工具返回结果) 双向(两个 Agent 可以互相调用)
核心机制 @tool 装饰器注册函数 Agent Card 名片动态发现
后端类比 REST API(客户端调服务端) 微服务间 RPC(服务调服务)
触发方式 LLM 推理链中决定调用 Agent 业务逻辑中主动调用
状态管理 工具自身无状态 Agent 可以维护对话状态和上下文
协议提出方 Anthropic(2024年11月) Google DeepMind(2025年4月)
适用场景 Agent 需要外部工具(数据库、API、搜索) 多 Agent 协作(编排、委派、链式调用)

一句话总结:Agent 接了 MCP 拿到工具,再通过 A2A 把它查到的结果传给另一个 Agent。 两个协议不是替代关系,是组合关系。


A2A 和 MCP 不是竞争对手。MCP 让一个 Agent 有了手------能调工具、拿数据。A2A 让多个 Agent 有了嘴------能把拿到的东西告诉彼此。手和嘴都有了,多 Agent 协作才不是 PPT 上的概念。


作者:唐悦玮 | 公众号同名

从后端出发,用 AI 拓展到全栈的工程师。

相关推荐
刘棕霆1 小时前
22—AI Skill 测评中断后怎么续跑:active-pipeline.json 断点恢复设计
aigc·ai编程·测试
leeyi2 小时前
Batch 处理:并发控制与可中断批处理
aigc·agent·ai编程
kfaino10 小时前
码农的AI翻身(三)你好,我叫 Embedding
后端·ai编程
_山海12 小时前
OpenSpec-基于SDD规格驱动开发
ai编程·vibecoding
唐老板19 小时前
MCP协议实战:从零写个Agent工具
ai编程·mcp
counterxing20 小时前
最近发现一个 Mac 工具,有点像把 Raycast、语音输入法、截图和录屏塞到了一起
macos·ai编程·claude
薛定喵的谔20 小时前
Term Proxy — 用 Tauri 2 打造跨平台终端配置管理工具
electron·ai编程·全栈
小溪彼岸21 小时前
CC Switch可视化管理Skill、提示词、会话
aigc·ai编程