A2A 协议深度解析(上):当 AI Agent 需要和另一个 AI Agent 说话

MCP 解决了 Agent 和工具之间的通信问题。但如果两个 Agent 需要协作呢?Google 在 2025 年提出的 A2A 协议,就是为这个场景设计的。


从一个真实的协作场景说起

想象这样一个任务:

"分析我们公司 Q1 的销售数据,生成报告,并把关键指标发到 Slack"

一个 Agent 独立完成这个任务,需要:

  • 连接数据库读数据
  • 做数据分析
  • 生成可视化图表
  • 写报告文字
  • 调用 Slack API 发消息

这五件事,每件都有专门的工具和专门的 AI Agent 擅长做。让一个 Agent 全包,不如把任务分配给专门的 Agent 团队来做。

这就是 A2A(Agent-to-Agent)协议要解决的问题。


A2A 是什么

A2A(Agent-to-Agent Protocol)是 Google 于 2025 年提出的开放协议,目的是让不同的 AI Agent 之间能够:

  1. 互相发现:知道对方的存在和能力
  2. 互相通信:用标准格式传递任务和结果
  3. 互相协作:把复杂任务拆解,分配给最合适的 Agent 执行
css 复制代码
A2A 之前:
Agent A → 直接调用工具(只能做自己会的事)

A2A 之后:
Agent A → 发现 Agent B(擅长数据分析)
       → 委托 B 分析数据
       → B 返回结果给 A
       → A 继续后续任务

A2A vs MCP:解决的不同问题

这是最容易混淆的地方:

协议 连接对象 核心问题
MCP Agent ↔ 工具/数据 "AI 怎么调用外部工具?"
A2A Agent ↔ Agent "AI 怎么和另一个 AI 协作?"

类比

  • MCP 是给 Agent 装了一个工具箱
  • A2A 是让多个 Agent 组建了一个团队

两者不互斥,实际系统中经常同时使用:

  • 每个 Agent 通过 MCP 连接自己的工具
  • 多个 Agent 通过 A2A 协作完成复杂任务

A2A 的核心概念

Agent Card(能力名片)

每个 A2A Agent 都有一个 Agent Card,描述自己的能力,以 JSON 格式托管在固定位置(/.well-known/agent.json):

json 复制代码
{
  "name": "DataAnalystAgent",
  "description": "专门负责数据分析、统计计算和趋势识别的 Agent",
  "url": "https://data-agent.example.com",
  "version": "1.0.0",
  "capabilities": {
    "streaming": true,
    "pushNotifications": true
  },
  "skills": [
    {
      "id": "analyze-sales",
      "name": "销售数据分析",
      "description": "分析销售数据,生成趋势报告和预测",
      "inputModes": ["text", "data"],
      "outputModes": ["text", "data", "file"]
    },
    {
      "id": "visualize-data",
      "name": "数据可视化",
      "description": "将数据转化为图表(柱状图、折线图、饼图等)",
      "inputModes": ["data"],
      "outputModes": ["file", "image"]
    }
  ],
  "defaultInputMode": "text",
  "defaultOutputMode": "text"
}

Task(任务单元)

Agent 之间通过 Task 传递工作。一个 Task 有完整的生命周期:

bash 复制代码
submitted → working → (streaming updates) → completed/failed
json 复制代码
// 发送任务:A 委托 B 分析数据
{
  "id": "task-001",
  "message": {
    "role": "user",
    "parts": [
      {"text": "分析这份 CSV 数据,找出 Q1 销售额最高的 5 个产品"},
      {"file": {"name": "sales_q1.csv", "data": "base64encodeddata..."}}
    ]
  }
}

// 任务更新(流式,实时进度)
{
  "id": "task-001",
  "status": {"state": "working"},
  "message": {
    "role": "agent",
    "parts": [{"text": "正在读取 CSV 文件,共 1234 行..."}]
  }
}

// 任务完成
{
  "id": "task-001",
  "status": {"state": "completed"},
  "artifacts": [
    {
      "name": "analysis_result",
      "parts": [{"text": "分析结果:销售额TOP5产品为..."}]
    }
  ]
}

动手实现:一个最简单的 A2A Agent

用 Python 实现一个天气查询 A2A Agent:

python 复制代码
# weather_agent.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
import httpx
import uvicorn

app = FastAPI()

# Agent Card:托管在 /.well-known/agent.json
AGENT_CARD = {
    "name": "WeatherAgent",
    "description": "实时天气查询 Agent,支持全球城市天气查询",
    "url": "http://localhost:8001",
    "version": "1.0.0",
    "capabilities": {"streaming": False},
    "skills": [
        {
            "id": "get-weather",
            "name": "天气查询",
            "description": "查询指定城市的实时天气和未来预报",
            "inputModes": ["text"],
            "outputModes": ["text"]
        }
    ]
}

# Task 数据模型
class TaskRequest(BaseModel):
    id: str
    message: dict

class TaskResponse(BaseModel):
    id: str
    status: dict
    artifacts: Optional[list] = None

@app.get("/.well-known/agent.json")
def get_agent_card():
    """对外暴露 Agent 能力名片"""
    return AGENT_CARD

@app.post("/tasks/send")
async def handle_task(request: TaskRequest) -> TaskResponse:
    """接收并处理任务"""
    # 提取用户消息
    user_text = ""
    for part in request.message.get("parts", []):
        if "text" in part:
            user_text += part["text"]
    
    # 解析城市名(简化处理)
    city = extract_city(user_text)
    
    if not city:
        return TaskResponse(
            id=request.id,
            status={"state": "failed"},
            artifacts=[{"parts": [{"text": "请提供城市名称"}]}]
        )
    
    # 查询天气(这里用模拟数据)
    weather_data = await query_weather(city)
    
    return TaskResponse(
        id=request.id,
        status={"state": "completed"},
        artifacts=[{
            "name": "weather_result",
            "parts": [{"text": weather_data}]
        }]
    )

def extract_city(text: str) -> str:
    """简单的城市名提取(实际应用中用 NLP)"""
    # 简化:直接返回用户输入的第一个名词
    words = text.replace("天气", "").replace("查询", "").strip()
    return words if words else ""

async def query_weather(city: str) -> str:
    """查询天气(示例使用模拟数据)"""
    # 实际应用中调用真实天气 API
    return f"{city}今天天气晴,气温 22°C,东风 3 级,空气质量优"

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8001)

主 Agent 如何发现和调用子 Agent

python 复制代码
# orchestrator.py:编排多个 A2A Agent 完成复杂任务

import httpx
import asyncio
import uuid

class A2AOrchestrator:
    def __init__(self):
        self.known_agents = {}
    
    async def discover_agent(self, base_url: str) -> dict:
        """发现 Agent:读取其 Agent Card"""
        async with httpx.AsyncClient() as client:
            r = await client.get(f"{base_url}/.well-known/agent.json")
            card = r.json()
            self.known_agents[card["name"]] = {
                "url": base_url,
                "card": card
            }
            print(f"发现 Agent:{card['name']} - {card['description']}")
            return card
    
    async def send_task(self, agent_name: str, message: str) -> str:
        """向指定 Agent 发送任务,等待结果"""
        if agent_name not in self.known_agents:
            raise ValueError(f"未知 Agent: {agent_name}")
        
        agent_url = self.known_agents[agent_name]["url"]
        task_id = str(uuid.uuid4())
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                f"{agent_url}/tasks/send",
                json={
                    "id": task_id,
                    "message": {
                        "role": "user",
                        "parts": [{"text": message}]
                    }
                },
                timeout=30.0
            )
            result = response.json()
        
        if result["status"]["state"] == "completed":
            # 提取文本结果
            for artifact in result.get("artifacts", []):
                for part in artifact.get("parts", []):
                    if "text" in part:
                        return part["text"]
        
        return f"任务失败:{result['status']}"


# 使用示例
async def main():
    orchestrator = A2AOrchestrator()
    
    # 发现可用的 Agent
    await orchestrator.discover_agent("http://localhost:8001")  # WeatherAgent
    await orchestrator.discover_agent("http://localhost:8002")  # DataAnalystAgent
    
    # 协调任务
    print("\n--- 开始任务协调 ---")
    
    # 让天气 Agent 查询天气
    weather = await orchestrator.send_task("WeatherAgent", "查询上海的天气")
    print(f"天气结果:{weather}")
    
    # 任务链:把天气数据传给数据分析 Agent
    trend = await orchestrator.send_task(
        "DataAnalystAgent",
        f"基于以下天气数据分析本周出行建议:{weather}"
    )
    print(f"出行建议:{trend}")

asyncio.run(main())

总结

A2A 协议的核心价值:

  1. 标准化 Agent 发现:通过 Agent Card,Agent 能自动发现其他 Agent 的能力
  2. 标准化任务传递:统一的 Task 格式,跨 Agent、跨厂商通用
  3. 支持复杂协作:多 Agent 编排、流式更新、长任务跟踪

A2A 和 MCP 合在一起,构成了"AI 团队"的完整基础设施:

  • MCP:每个团队成员和工具的接口
  • A2A:团队成员之间的协作协议

下一篇将继续深入 A2A 的进阶内容:流式通信、认证安全、多轮对话任务。


相关推荐
listhi5202 小时前
基于模型预测控制的自动避障与汽车跟踪MATLAB实现
人工智能·matlab·汽车
zzh940772 小时前
Gemini 3.1 Pro vs Claude 3.5:原生多模态与镜像AI的巅峰对决
人工智能
peixiuhui2 小时前
赋能智慧能源,连接储能未来:G8501网关,您储能系统的“智慧大脑”与“神经中枢”
人工智能·mqtt·能源·边缘计算·iot·储能·iotgateway
Flying pigs~~2 小时前
BERT及其变体、GPT、ELMo
人工智能·深度学习·自然语言处理·大模型·bert·文本分析处理
balmtv2 小时前
GPT-5.4镜像站技术深度拆解:从“大一统”架构到智能体原生时代的全面跃迁
人工智能·gpt
tobias.b2 小时前
什么是数据挖掘?
人工智能·数据挖掘
袖手蹲2 小时前
Arduino UNO Q 板载 Nanobot 自动化编程指南之三
人工智能
枫叶林FYL2 小时前
【自然语言处理 NLP】深度学习与表示学习
人工智能·深度学习·机器学习
北顾笙9802 小时前
深度学习day05
人工智能·深度学习