Agent来了0x0e:A2A是什么?

2 阅读6分钟

前言

前面学了 MCP,他解决的是 LLM 和物理世界的沟通问题。那么 AI 中 Agent 之间是怎么通讯的?

我们前面在学习 MAS 时了解了 多Agent的通信机制。今天再去了解另一种多 Agent 的通讯协议:A2A。

定义

A2A(Agent to Agent),是一个为 Agent 间通信与协作设计的新一代开放协议标准。

  • 核心:定义了一套通用的 “语言”和交互契约,使得不同供应商、不同框架(如LangChain、CrewAI、AutoGPT)构建的智能体能够像人类团队一样直接对话与协作
  • 本质:基于成熟的现代Web标准(如HTTP、JSON-RPC、Server-Sent Events)构建
    • 同步请求/响应:用于即时查询和指令。

    • 流式更新(SSE) :用于任务执行进度的实时反馈。

    • 异步通知(Webhook) :用于长周期任务的最终结果回调。

和经典范式对比

维度ACL (如 FIPA-ACL)CNP (合同网协议)Blackboard (黑板模型)A2A (Agent-to-Agent)
核心思想定义智能体间通信的语义语言(说什么,什么意思)。基于招标 - 投标 - 中标的市场机制动态分配任务。通过共享的中央数据空间(黑板)进行异步、间接的协作。定义智能体间通信的开放网络协议(怎么连接,如何交互)。
交互模式直接、结构化消息传递。集中式拍卖(管理者 - 投标者)。去中心化、通过共享内存读写。对等、多模式(同步 / 异步 / 流式)。
耦合度语义紧耦合:通信双方必须对消息内容有共同理解。流程紧耦合:必须遵循固定的招标 - 投标流程。数据空间耦合:所有智能体依赖同一黑板。协议松耦合:只依赖标准协议接口,内部实现自由。
主要问题过于复杂、学术化,难以在实际工程中大规模应用。通信开销大,协商效率低,不适用于需要快速响应的场景。黑板可能成为性能和单点故障瓶颈,状态一致性管理复杂。生态初建:工具链、最佳实践仍在发展中。
A2A 的先进性工程友好:用现代 Web 标准替代复杂的学术语言,大幅降低落地门槛。灵活高效:支持动态、即时的任务委托与结果流式返回,无需完整的拍卖流程,更适合现代 AI 智能体的交互特性。去中心化对等:每个智能体都是独立服务,通过标准协议直接通信,避免了中央黑板带来的瓶颈和单点风险。生态互操作性:核心突破在于打破框架和厂商壁垒,旨在构建开放的智能体互联网。

MCP + A2A

通常开发中,A2A 总是与 MCP 协同工作:

  • A2A:智能体间水平协作
  • MCP:智能体对工具的垂直调用

image.png

Coding

项目背景

我们通过一个加减法来简单看下 A2A 的应用。

  • 加法:A2A server本地计算
  • 减法:借助 MCP 获取

server

使用 FastAPI 构建服务。

app = FastAPI(title=__name__)

Agent Card

Agent Card(智能体卡片) 是 A2A(Agent-to-Agent)协议的核心元数据标准,是JSON 格式的 “数字名片” ,由 A2A 服务端(Server/Agent)发布,用于自描述身份、能力、接入地址、认证方式、支持技能,让 A2A 客户端 / 其他 Agent 自动发现、理解并正确调用该服务,彻底替代 “写死配置“。

  • 核心作用:能力发现、自动适配、标准化交互、动态协作
  • capabilities
"capabilities": {
"streaming": true, // 支持流式响应 "pushNotifications": false, // 不支持推送 
"stateTransitionHistory": true,
"supportsMCP": true // 集成MCP外部工具(对应你之前的场景) 
}
  • authentication
"authentication": { 
"schemes": ["Bearer"], 
"scopes": ["weather:read", 
"weather:forecast"] 
}
    # A2A核心:Agent Card(服务说明书)A2A Server计算实现
    AGENT_CARD = {
        # 基础身份信息(是谁)
        "agent_id": "math_mixed_orchestrator_v1",
        "name": "混合运算编排智能体",
        "description": "支持本地加法和MCP减法运算",
        "endpoint": "http://localhost:8888/a2a",
        # 能力支持(能做什么特性)
        "capabilities": [
            {
                "method": "add",  # 本地加法方法
                "params": ["a", "b"],
                "returns": "sum"
            },
            {
                "method": "minus",  # MCP减法方法
                "params": ["a", "b"],
                "returns": "difference"
            }
        ]
    }

route

用 FastAPI 为每个 API 设置路由。

@app.get("/agent_card")
async def get_agent_card(request: Request):
    return AGENT_CARD
@app.post("/a2a")
async def handle_a2a_request(request: Request):
    # 解析A2A标准请求(JSON-RPC简化版)
    try:
        # ✅ 正确:在异步函数内用 await 调用 request.json()
        req_data = await request.json()
    except Exception as e:
        return JSONResponse(
            content={"error": f"解析请求失败: {str(e)}", "id": None},
            status_code=400
        )

    method = req_data.get("method")
    params = req_data.get("params", {})
    req_id = req_data.get("id")

    # A2A 服务端自行计算
    if method == "add":
        # 本地直接计算加法(无需MCP)
        try:
            a, b = params["a"], params["b"]
            result = a + b
            return {
                "jsonrpc": "2.0",
                "result": {"sum": result},
                "id": req_id
            }
        except KeyError:
            return {"error": "Missing 'a' or 'b' parameter", "id": req_id}, 400

client

def call_a2a_agent(agent_card_url, method, params):
    """
    按A2A协议调用智能体
    """
    # 1. 发现:获取Agent Card(可选,这里假设已知端点)
    agent_card = requests.get(agent_card_url).json()

    # 2. 构造A2A请求(JSON-RPC格式)
    a2a_request = {
        "jsonrpc": "2.0",
        "method": method,
        "params": params,
        "id": 1  # 请求ID,用于匹配响应
    }

    # 3. 发送请求到服务端点
    endpoint = agent_card["endpoint"]   # 从Agent Card中获取
    # endpoint = "http://localhost:8888/a2a"
    response = requests.post(endpoint, json=a2a_request)

    return response.json()


if __name__ == "__main__":
    # 调用加法服务:计算1+2
    result = call_a2a_agent(
        agent_card_url="http://localhost:8888/agent_card",  # Agent Card地址
        method="add",
        params={"a": 1, "b": 2}
    )
    print(f"A2A调用结果:{result['result']['sum']}")  # 输出:3

A2A + MCP

a2a_server

a2a路由:

elif method == "minus":
    # 调用MCP工具(减法计算)
    try:
        difference = await call_mcp_minus_tool(
            tool_name="calculate_minus",
            tool_params={"a": params["a"], "b": params["b"]},
            mcp_server_cmd=["python", "mcp_minus_server.py"]  # 启动MCP服务端
        )
        return {
            "jsonrpc": "2.0",
            "result": {"difference": difference},
            "id": req_id
        }
    except Exception as e:
        return {"error": f"MCP调用失败: {str(e)}", "id": req_id}, 500

MCP 调用代码:

# MCP客户端封装:调用减法工具(stdio传输)
# ------------------------------
async def call_mcp_minus_tool(tool_name: str, tool_params: dict, mcp_server_cmd: list) -> int:
    """
    用MCP客户端连接stdio服务端,调用工具并返回结果
    """
    # 1. 启动MCP服务端进程,用stdio_client创建流(连接stdin/stdout)
    async with stdio_client(mcp_server_cmd) as (read_stream, write_stream):
        # 2. 初始化MCP会话(完成协议握手)
        async with ClientSession(read_stream, write_stream) as session:
            await session.initialize()  # 必须:握手确认
            # 3. 调用MCP工具(calculate_minus)
            result = await session.call_tool(tool_name, tool_params)
            # 4. 提取结果(根据fastmcp返回结构调整,此处假设result.output是工具返回值)
            # 若返回结构是{"output": 66},则改为result["output"]
            return result.output

mcp_server

和之前学的 MCP 基础是一样的。

# 创建一个MCP服务器
mcp = FastMCP("减法计算工具")

# 使用装饰器定义一个工具(Tool)
@mcp.tool()
# 原有工具:计算BMI
def calculate_minus(a: int, b: int) -> int:
    """
    计算两个数的减法
    """
    return a - b


if __name__ == "__main__":
    # 启动 MCP 服务,使用标准输入输出方式
    mcp.run(transport='stdio')

Running

uv run mcp_minus_server.py
uv run a2a_server.py
uv run a2a_client.py

image.png

image.png

源码

github