Agent 协议:MCP、A2A、AG-UI

Mar 06, 2026 · 10278 字
AI

如果你正在做 Agent 应用,几乎一定会遇到这三个问题:

  • Agent 怎么安全、标准地接外部工具和数据;
  • Agent 怎么和别的 Agent 协作;
  • Agent 怎么和用户界面稳定地实时交互。

MCP、A2A、AG-UI 对应的正好是这三个层面。很多团队早期会把它们混在一起理解,最后要么“全都自己封装一层”,要么把一个协议硬塞进不适合的场景,系统复杂度暴涨,维护成本也很快失控。

这篇文章把三者拆开讲清楚:

  • MCP 解决 Agent 与工具/上下文系统的连接问题;
  • A2A 解决 Agent 与 Agent 的跨系统协作问题;
  • AG-UI 解决 Agent 与用户界面的事件化交互问题。

MCP

MCP 全称是 Model Context Protocol,即模型上下文协议

它的核心目标很直白:给“LLM 应用(Host)和外部能力(Server)”之间定义一套统一的、可协商能力的协议层。这样应用不用为每个工具系统都单独写一套适配通信协议,也不需要把每个外部系统都改造成同一种 SDK 形态。

官方对 MCP 的定位是“上下文交换协议”,它不规定你的 Agent 如何推理,不规定你必须用哪家模型,也不规定你应用层怎么设计工作流。它只把“连接、发现、调用、通知、协商”这层标准化。

如果把一个完整 Agent 系统拆成三层:

  • 上层是用户交互;
  • 中层是 Agent 编排与决策;
  • 下层是工具、数据、执行环境。

MCP 很明确地工作在中下层边界,重点是把“可供 Agent 使用的能力”暴露成统一协议对象。

它本质上是“能力总线”,不是“业务编排框架”。

flowchart LR
    U[用户] --> H[MCP Host\n如 Claude Desktop / IDE Agent]
    H --> C[MCP Client]
    C <-->|JSON-RPC 2.0| S1[MCP Server A]
    C <-->|JSON-RPC 2.0| S2[MCP Server B]

    S1 --> T[Tools]
    S1 --> R[Resources]
    S1 --> P[Prompts]

    S2 --> T2[Tools]
    S2 --> R2[Resources]

    subgraph Transport
      ST[STDIO]
      SH[Streamable HTTP\nPOST/GET + 可选 SSE]
    end

    C -.基于传输层.- ST
    C -.基于传输层.- SH

协议组成

MCP 的消息格式基于 JSON-RPC 2.0。

网络层面:

  • 本地模式用标准输入输出流;
  • 远端模式用 HTTP(POST/GET),可选 SSE 做流式返回。

你可以把它理解为:

  • 语义标准统一在 JSON-RPC;
  • 传输通道按部署场景替换。

MCP 可以按两层来理解:

数据层

数据层基于 JSON-RPC 2.0,负责消息语义,主要包含:

  • 生命周期管理:initialize、能力协商、初始化完成通知等;
  • Server 侧能力:toolsresourcesprompts
  • Client 侧能力:samplingelicitationlogging 等;
  • 通知机制:列表变化、进度、状态更新。

这个分层的价值在于:你不用关心底层到底是本地进程管道还是网络连接,方法语义是一致的。

传输层

截至当前稳定规范,官方标准传输是两种:

  • stdio:本地子进程通信,适合本机工具;
  • Streamable HTTP:HTTP POST/GET,支持返回 JSON 或 text/event-stream 流。

规范明确指出:Streamable HTTP 取代了更早版本中的 HTTP+SSE 传输定义,语义更清晰,也更适合做远端部署和可恢复流。

核心原语

MCP 最关键的三类 Server 原语:

  • Tools:可执行动作,模型可调用;
  • Resources:上下文数据源,供应用和模型读取;
  • Prompts:可复用提示模板,常由用户触发。

官方还给了一个很好用的控制层级:

  • Prompts 偏用户控制;
  • Resources 偏应用控制;
  • Tools 偏模型控制。

这个区分非常实用,能避免“什么都做成 Tool”的反模式。

发送方与接收方是谁

MCP 的参与者有三个角色:

  • MCP Host:比如桌面 AI 客户端、IDE 插件容器;
  • MCP Client:Host 内部创建的连接组件;
  • MCP Server:真正提供能力的一端。

消息方向并不是单向。

常见方向:

  • Client -> Server:tools/listtools/callresources/read
  • Server -> Client:通知、请求采样、请求用户补充信息。

因此它不是传统“纯后端 API”模型,而是可双向交互的协议会话。

示例

官方教程里最常见的是 Python FastMCP,非常适合从零起步。我们以此做一个最小 Weather Server,暴露两个工具:

  • get_alerts
  • get_forecast
uv init weather
cd weather
uv venv
uv add "mcp[cli]" httpx

然后编写以下脚本:

from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather")
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"

async def make_nws_request(url: str) -> dict[str, Any] | None:
    headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
    async with httpx.AsyncClient() as client:
        try:
            resp = await client.get(url, headers=headers, timeout=30.0)
            resp.raise_for_status()
            return resp.json()
        except Exception:
            return None

@mcp.tool()
async def get_alerts(state: str) -> str:
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)
    if not data or "features" not in data:
        return "Unable to fetch alerts or no alerts found."
    return "No active alerts for this state." if not data["features"] else "Fetched"

def main():
    mcp.run(transport="stdio")

if __name__ == "__main__":
    main()

A2A

A2A 的全称是 Agent2Agent Protocol,常见中文是 智能体到智能体协议

它的定位是让不同组织、不同框架、不同部署环境里的 Agent 可以标准化协作,避免一对一私有协议集成。

A2A 关心的是“Agent 间任务协作”,不是“Agent 调单个工具”。

A2A 想解决的问题是:

  • Agent 不该被迫“伪装成工具”才能协作;
  • 跨框架多 Agent 场景不该每次都重做通信层;
  • 长任务、流式、异步通知应该是协议内建能力。

它强调“不暴露内部实现细节的协作”,也就是 opaque execution。你知道对方能力和协议入口,但不需要知道对方内部 memory、toolchain、模型编排细节。

sequenceDiagram
    participant Client as A2A Client(客户端 Agent)
    participant Server as A2A Server(远端 Agent)
    participant Auth as Auth Server

    Client->>Server: GET /.well-known/agent-card.json
    Server-->>Client: Agent Card(能力、技能、认证要求)

    Client->>Auth: 按 Agent Card 的安全方案取凭证
    Auth-->>Client: Token / Credential

    Client->>Server: SendMessage(JSON-RPC over HTTP)
    alt 即时结果
        Server-->>Client: Message
    else 长任务
        Server-->>Client: Task(submitted / working ...)
        Server-->>Client: SSE 推送 TaskStatusUpdate / ArtifactUpdate
        opt 超长任务
            Server-->>Client: Webhook Push Notification
        end
    end

协议组成

A2A 的核心绑定里最常见的是:

  • JSON-RPC 2.0 over HTTP(S)
  • SSE 用于流式事件;
  • 规范还定义了 gRPCHTTP+JSON/REST 绑定。

对大多数团队来说,JSON-RPC over HTTPS + SSE 是第一落地点,迁移成本最低。

核心对象

A2A 的模型对象非常完整,工程上重点记住这几类:

  • Agent Card:Agent 的“能力名片”,含 endpoint、skills、认证方式;
  • Task:有生命周期的工作单元;
  • Message:一次通信回合,带 role
  • Part:Message/Artifact 的最小内容单元;
  • Artifact:任务产出物,可以增量更新。

这套对象组合的意义是:

  • 可以先协商能力,再发任务;
  • 可以即时回复,也可以切成长任务;
  • 可以在任务执行中持续流式更新结果;
  • 可以把结果沉淀成结构化产物,而不是只吐一段文本。

发送方与接收方

在 A2A 里:

  • 发送请求的一般是 A2A Client
  • 处理请求的一般是 A2A Server(Remote Agent)。

Role 语义也在协议里有定义:

  • ROLE_USER:客户端到服务端的消息;
  • ROLE_AGENT:服务端到客户端的消息。

如果任务很长,Server 还会作为主动发送方,通过 SSE 或 Webhook 把状态变化发给 Client。

Agent Card

A2A 里最容易被低估的对象就是 Agent Card。

它承担了几个关键职责:

  • 发现:客户端知道“这个 Agent 是谁、会什么”;
  • 连接:知道该请求哪个 endpoint;
  • 安全:知道该用哪种认证方案;
  • 协议选择:知道支持哪些 transport / binding;
  • 扩展声明:知道是否需要扩展能力。

规范里还给了 well-known 地址约定:

  • https://{server_domain}/.well-known/agent-card.json

这让大规模注册发现和自动化编排更可行。

Task 生命周期

A2A 的工程价值,很大一部分来自它把任务生命周期协议化了。

一个任务不是只有“成功/失败”两种状态,而是可以经历:

  • submitted
  • working
  • input-required
  • auth-required
  • completed / failed / canceled / rejected

这会直接改善两类体验:

  • 产品体验:用户能看到任务进度,不是一直转圈;
  • 系统体验:调度器可以根据状态做超时、重试、分派。

示例

这里我们使用官方的 @a2a-js/sdk 包。

npm install @a2a-js/sdk express

然后编写核心代码:

import express from "express";
import { v4 as uuidv4 } from "uuid";
import type { AgentCard, Message } from "@a2a-js/sdk";
import {
  AgentExecutor,
  DefaultRequestHandler,
  InMemoryTaskStore,
  RequestContext,
  ExecutionEventBus,
} from "@a2a-js/sdk/server";
import {
  AGENT_CARD_PATH,
  agentCardHandler,
  jsonRpcHandler,
  UserBuilder,
} from "@a2a-js/sdk/server/express";

const card: AgentCard = {
  name: "Hello Agent",
  description: "A simple A2A hello server",
  protocolVersion: "0.3.0",
  version: "0.1.0",
  url: "http://localhost:4000/a2a/jsonrpc",
  skills: [
    { id: "chat", name: "Chat", description: "Say hello", tags: ["chat"] },
  ],
  capabilities: { pushNotifications: false },
  defaultInputModes: ["text"],
  defaultOutputModes: ["text"],
};

class HelloExecutor implements AgentExecutor {
  async execute(ctx: RequestContext, bus: ExecutionEventBus): Promise<void> {
    const msg: Message = {
      kind: "message",
      messageId: uuidv4(),
      role: "agent",
      parts: [
        { kind: "text", text: `Hello from A2A, context=${ctx.contextId}` },
      ],
      contextId: ctx.contextId,
    };
    bus.publish(msg);
    bus.finished();
  }
  cancelTask = async (): Promise<void> => {};
}

const handler = new DefaultRequestHandler(
  card,
  new InMemoryTaskStore(),
  new HelloExecutor(),
);
const app = express();

app.use(
  `/${AGENT_CARD_PATH}`,
  agentCardHandler({ agentCardProvider: handler }),
);
app.use(
  "/a2a/jsonrpc",
  jsonRpcHandler({
    requestHandler: handler,
    userBuilder: UserBuilder.noAuthentication,
  }),
);

app.listen(4000);

AG-UI

AG-UI 的全称是 Agent-User Interaction Protocol,中文可以叫 智能体-用户交互协议

它的关注点和 MCP、A2A 不同:它解决的是“用户界面如何稳定地消费 Agent 执行过程中的事件流”,并允许用户输入、工具反馈、状态变化在同一条交互通道里双向流动。

AG-UI 主要在“前端应用 <-> Agent 后端”之间工作。

它不是 LLM 推理协议,不是工具调用协议,也不是跨 Agent 协议。它是“交互编排协议”:

  • 把长时间运行的 agent run 拆成可消费事件;
  • 把工具调用、状态变更、文本流、活动进度统一事件化;
  • 让前端用一致方式渲染和反馈。

这件事看起来简单,实际上是 Agent 产品化最容易崩的地方。没有协议化事件层,UI 往往会陷入“几十个 websocket/HTTP 回调拼起来”的不可维护状态。

flowchart LR
    UI[用户界面\nWeb/App] <--> Client[AG-UI Client\n如 HttpAgent]
    Client <--> Backend[Agent Backend\nLangGraph / CrewAI / ADK 等]

    Backend --> E1[Lifecycle Events]
    Backend --> E2[Text Events]
    Backend --> E3[Tool Events]
    Backend --> E4[State Events]
    Backend --> E5[Custom/Raw Events]

    subgraph Transport
      T1[HTTP SSE]
      T2[HTTP Binary]
      T3[WebSocket / Webhook]
    end

    Client -.传输无关.- T1
    Client -.传输无关.- T2
    Client -.可扩展.- T3

AG-UI 官方文档给的边界非常实用:

  • MCP:Agent <-> 工具与数据;
  • A2A:Agent <-> Agent;
  • AG-UI:Agent <-> 用户应用。

在真实系统里,一个 Agent 经常会三者同时使用:

  • 前端与 Agent run 用 AG-UI;
  • Agent 内部访问工具用 MCP;
  • Agent 需要委派其它 Agent 用 A2A。

边界清晰之后,代码层的分层会稳定很多。

协议组成

AG-UI 明确是 transport-agnostic(传输无关)的。

官方常见形态:

  • HTTP SSE:易调试、兼容面广;
  • HTTP Binary:更高性能与更低带宽;
  • 也支持 WebSocket、webhook 等模式。

它的关键不是“只能用某一种传输”,而是“事件语义保持一致”。

事件模型

AG-UI 是典型事件协议,核心结构是 BaseEvent 与事件类型集合。

常见事件类别:

  • 生命周期事件:RUN_STARTEDRUN_FINISHEDRUN_ERRORSTEP_STARTEDSTEP_FINISHED
  • 文本事件:TEXT_MESSAGE_STARTTEXT_MESSAGE_CONTENTTEXT_MESSAGE_END
  • 工具事件:TOOL_CALL_STARTTOOL_CALL_ARGSTOOL_CALL_END
  • 状态事件:STATE_SNAPSHOTSTATE_DELTAMESSAGES_SNAPSHOT
  • 活动事件:ACTIVITY_SNAPSHOTACTIVITY_DELTA
  • 特殊事件:RAWCUSTOM

发送方与接收方

AG-UI 的发送方与接收方会在一个 run 生命周期内反复切换:

  • 后端 Agent -> 前端:连续发事件流;
  • 前端 -> 后端:发送用户输入、交互动作、上下文补充、控制指令。

这意味着 AG-UI 天生是双向交互模型,而不是“前端拉一次结果”的静态接口。

示例

如果你要快速做一个可演示、可迭代的前端 Agent 应用,AG-UI 官方推荐路径是先用 CLI 脚手架。

npx create-ag-ui-app@latest
npm run dev

脚手架会生成一套可运行的客户端与服务端组合,包含 AG-UI 事件流处理。默认示例里可以直接访问本地页面验证交互。

基于 HttpAgent 思路,可以抽象成:

import { HttpAgent, EventType } from "@ag-ui/client";

const agent = new HttpAgent({
  url: "http://localhost:8787/agent",
  agentId: "demo-agent",
  threadId: "thread-001",
});

agent
  .runAgent({
    tools: [],
    context: [],
  })
  .subscribe({
    next: (event) => {
      if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
        // 把文本增量写入 UI
      }
      if (event.type === EventType.STATE_DELTA) {
        // 应用状态补丁
      }
    },
  });

AG-UI 后端适配通常只做三件事:

  • 把框架原生回调翻译成 AG-UI 事件;
  • 保证事件顺序和 run/step 边界;
  • 把用户输入与控制信号映射回框架执行层。

这也是为什么它适合接在 LangGraph、CrewAI、ADK、Mastra 等不同框架前面,做统一交互层。

AG-UI 的 STATE_SNAPSHOT + STATE_DELTA 组合值得重点用好:

  • 首屏或重连先发 Snapshot;
  • 持续交互阶段只发 Delta;
  • Delta 采用可回放的补丁语义;
  • 前端维护本地状态机,不把 UI 逻辑塞进后端 prompt。

这样可以显著降低前端抖动、重绘和状态错乱问题。

粤ICP备2025414119号 粤公网安备44030002006951号

© 2026 Saurlax · Powered by Astro