Agent 协议体系:MCP、A2A 与 AG-UI
问题
MCP、A2A、AG-UI 分别解决什么问题?一个前端如何与 Agent、工具和其他 Agent 协作?如何设计流式事件、共享状态、任务生命周期、授权和人工审批?
三类协议处于不同边界:
- MCP:Agent/AI 应用连接工具、资源和提示。
- A2A:独立 Agent 之间发现能力、委派任务和交换产物。
- AG-UI:Agent 与用户界面之间同步运行、消息、工具、状态和人工操作。
它们可以组合,但不是每个系统都要全部使用。单体聊天应用通常只需自有 BFF 流协议;只有跨工具、跨 Agent 或跨前端框架互操作需求明确时才引入协议。
一、协议栈全景
| 边界 | 关心什么 | 不关心什么 |
|---|---|---|
| UI ↔ Agent | 运行状态、流式内容、工具可视化、共享状态、审批 | Agent 内部模型如何实现 |
| Agent ↔ Tool | 能力发现、调用、资源、授权和传输 | 用户界面如何渲染 |
| Agent ↔ Agent | 能力发现、任务、消息、产物和长任务 | 对方内部工具和记忆细节 |
二、MCP:工具与上下文边界
MCP 的 Host 为每个 Server 建立一个 Client,Server 提供 Tools、Resources、Prompts。它适合:
- IDE 接入文件、Git、监控、数据库和知识库。
- 企业 Agent 平台统一工具协议。
- 远程服务通过 Streamable HTTP 和 OAuth 接入。
MCP 不等于多 Agent 协作;MCP Server 通常是能力提供者,不需要表现为有长期目标和自主任务状态的 Agent。详见 MCP 协议。
三、A2A:独立 Agent 协作边界
A2A 1.0 面向彼此独立、可能内部不透明的 Agent 系统。主要概念:
- Agent Card / 能力发现:描述身份、能力、端点、认证和支持模态。
- Message:用户/Agent 之间的内容交换。
- Task:有生命周期的协作工作。
- Artifact:任务产出的文件、结构化数据或其他结果。
- Streaming / Push:长任务进度和异步更新。
为什么不直接共享内部思维和内存
A2A 的价值在于通过稳定任务契约协作,而不是访问对方内部 prompt、工具或隐藏推理。这样各 Agent 可以独立升级,也减少敏感信息和实现耦合。
四、AG-UI:Agent 与前端边界
AG-UI 用事件流连接 Agent 后端与交互界面。前端关心的事件大致包括:
- Run started/finished/error。
- Text message start/content/end。
- Tool call start/args/end/result。
- State snapshot/delta。
- Activity、步骤和自定义事件。
- Human-in-the-loop 请求与响应。
type AgentUIEvent =
| { type: 'run-started'; runId: string; threadId: string }
| { type: 'text-delta'; messageId: string; delta: string }
| { type: 'tool-call'; callId: string; tool: string; args: unknown }
| { type: 'tool-result'; callId: string; result: unknown }
| { type: 'state-delta'; patch: unknown }
| { type: 'approval-required'; requestId: string; action: ActionPreview }
| { type: 'run-finished'; runId: string }
| { type: 'run-error'; runId: string; errorCode: string };
这与 AI SDK UI Message Stream 有重叠:二者都可以承载文本、工具和数据 parts。选型取决于生态互操作需求;不要在同一边界叠加两套语义相同但状态规则不同的协议。
五、A2UI 与生成式 UI
“A2UI”通常指 Agent 用声明式 UI 描述与客户端组件目录协作的方向。无论使用何种具体协议,生产设计都应遵循:
- Agent 只能选择白名单组件和动作。
- Props 经过 Schema 和业务校验。
- 组件版本可迁移、可持久化、可回放。
- 点击动作回到受控 API,并重新鉴权。
- 不执行 Agent 生成的任意 JavaScript/JSX。
这比传输任意代码更适合跨 Web、移动端和桌面端。
六、统一任务生命周期
协议名称不同,但前端最好映射到稳定领域状态:
type TaskState =
| 'queued'
| 'running'
| 'input-required'
| 'approval-required'
| 'completed'
| 'failed'
| 'cancelled';
interface AgentTask {
id: string;
state: TaskState;
version: number;
artifacts: ArtifactRef[];
updatedAt: string;
}
关键不变量:
completed/failed/cancelled是终态,不能被迟到事件覆盖。- 使用单调递增序号或版本处理重连与乱序。
- 写操作有幂等键,重放事件不会重复执行。
- “用户取消等待”和“远端任务已经取消”是两个状态。
- Artifact 通过受权 URL 获取,不把大文件塞进事件流。
七、前端事件归一化
不要让 React 组件直接依赖三个协议:
interface ProtocolAdapter<TEvent> {
connect(signal: AbortSignal): AsyncIterable<TEvent>;
toDomainEvent(event: TEvent): AgentUIEvent | null;
submitInput(requestId: string, value: unknown): Promise<void>;
cancel(runId: string): Promise<void>;
}
架构建议:
这样协议升级只影响适配层,UI 继续依赖稳定的领域模型。
八、Human-in-the-loop
interface ActionPreview {
tool: string;
resource: { type: string; id: string; label: string };
operation: string;
importantArgs: Record<string, unknown>;
reversible: boolean;
expiresAt: string;
}
审批流程必须:
- 在副作用发生前展示,而不是执行后通知。
- 绑定用户、租户、工具、资源、参数哈希和有效期。
- 支持拒绝、修改参数和取消。
- Agent 不能把自然语言“好的”直接当作任意动作授权。
- Tool/Agent 服务收到确认后仍要最终鉴权。
九、安全与信任边界
MCP
- 工具 Server 是供应链依赖。
- 使用最小权限、OAuth audience、禁止 token passthrough。
- 工具描述和结果都可能含间接注入。
A2A
- 远端 Agent 是外部系统,不信任其声明、消息和 Artifact。
- 验证 Agent 身份、能力来源、任务授权和回调目标。
- 防止任务委派无限递归和费用转移。
AG-UI
- UI 事件不能直接触发本地敏感 API。
- Tool args、状态 patch 和 Markdown 都要校验。
- 重连、重放和迟到事件不能重复写操作。
十、何时不需要协议
如果只有一个前端、一个 BFF、几个内部工具,简单的类型化 SSE + REST 往往更好。引入协议的收益应该来自:
- 需要接入多个第三方工具或 Agent。
- 需要替换 Host/前端而保持后端兼容。
- 需要长任务、能力发现或跨组织协作。
- 生态已有稳定 SDK、TCK 和运维工具。
否则协议适配、版本、认证、观测和故障语义会成为额外成本。
常见面试问题
Q1: MCP、A2A、AG-UI 一句话区别是什么?
答案:MCP 连工具和数据;A2A 连独立 Agent;AG-UI 连 Agent 与用户界面。
Q2: MCP Server 是 Agent 吗?
答案:通常不是。它提供工具、资源和 Prompt,不必拥有自主目标、任务生命周期或模型。Agent 可以通过 MCP 使用它。
Q3: A2A 为什么需要 Task?
答案:Agent 协作可能耗时、需要补充输入并产生多个 Artifact,单次请求响应不足以表示完整生命周期。
Q4: AG-UI 和普通 SSE 有什么区别?
答案:SSE 是传输机制;AG-UI 定义 Agent 运行、文本、工具、状态和人工交互的事件语义。也可以用其他传输承载类似事件。
Q5: AI SDK UI 与 AG-UI 如何选择?
答案:AI SDK 生态内聊天和工具 UI 优先其 UIMessage 协议;需要跨 Agent 框架互操作时评估 AG-UI。不要让 UI 同时处理两套重复状态。
Q6: Agent Card 能完全信任吗?
答案:不能。它是能力声明,需要结合可信发现、身份认证、域名/证书和组织策略验证,调用时仍要授权与限额。
Q7: 如何处理 Agent 长任务?
答案:持久化任务状态,使用事件序号、重连游标、Artifact 引用和通知;区分客户端断线与任务取消。
Q8: 为什么需要协议适配层?
答案:避免 React 组件绑定某个协议版本,把多种外部事件归一为稳定领域状态,便于测试、迁移和重放。
Q9: 多 Agent 如何防止无限委派?
答案:限制委派深度、总任务数、时间、token 和费用;传播原始授权范围;检测循环任务 ID 和重复目标。
Q10: Human-in-the-loop 最大的安全坑是什么?
答案:把模糊聊天回复当授权,或审批后参数被替换。确认必须绑定具体动作、资源和参数哈希,并由服务端验证。
Q11: A2UI 为什么比生成 JSX 更安全?
答案:声明式组件目录限制了可执行能力,Props 可校验,交互回到受控 API;任意 JSX/JS 会扩大代码执行与供应链风险。
Q12: 什么时候应该自己定义协议?
答案:只有现有协议无法覆盖稳定业务边界且互操作价值有限时。即使自定义,也应明确版本、事件顺序、幂等、错误和取消语义。