搭多 Agent 系统的时候,A2A、ACP、MCP 会一起冒出来。名字都像通信协议,我一开始也把它们当成同一类东西。把文档摊开对照之后才发现,它们根本不在同一层。
我现在是这样记的:
- A2A:Agent 和 Agent 之间如何协作
- ACP:客户端或编排器如何控制 Coding Agent
- MCP:Agent 如何连接工具、数据与外部系统
三个协议叠着用来,不是三选一。
一张表看懂核心区别
| 协议 | 全称 | 连接双方 | 主要用途 |
|---|---|---|---|
| A2A | Agent2Agent Protocol | Agent ↔ Agent | 能力发现、任务委派、状态跟踪与成果交付 |
| ACP | Agent Client Protocol | Client/IDE ↔ Coding Agent | 会话控制、流式输出、工具状态、权限与 diff |
| MCP | Model Context Protocol | AI Application/Agent ↔ Tool/Data | 连接文件、数据库、浏览器、搜索和工作流 |
如果硬要比:A2A 是团队之间怎么交接任务,ACP 是我怎么操作一个 Coding Agent,MCP 是这个 Agent 手里有什么工具。
A2A:Agent 对 Agent
A2A 是用于 AI Agent 互操作与协作的开放标准。项目最初由 Google 发起,随后贡献给 Linux Foundation 管理。
一个典型的软件开发流程可能是:
主管 Agent A:“请完成用户认证模块”
A → B:实现后端A → C:设计测试B/C → A:报告进度A → D:整合结果D → E:最终验收重点不在遥控对方的终端。它们用同一套语义交换任务、进度和成果就行。根据 A2A 规范,核心对象有:
AgentCard:描述 Agent 的身份、能力、技能、接口和安全要求Task:可跟踪、可中断、可能持续很久的工作单元Message:任务上下文中的沟通内容Artifact:代码、报告、文件或结构化数据等产物TaskStatus:任务当前状态及相关消息
任务可以处于已提交、执行中、需要输入、完成、失败、取消或拒绝等状态。协议同时支持普通请求、流式事件和异步推送,适合跨进程、跨服务甚至跨组织的长任务。
A2A 1.0 定义了 JSON-RPC、gRPC 和 HTTP+JSON/REST 等绑定。Agent 之间不必共享内部 prompt、记忆、模型或私有工具,只需通过 AgentCard 声明对外能力,并以消息与产物完成协作。内部怎么跑仍然自己说了算,对外只暴露卡片、消息和产物,安全边界也跟着收小。
A2A 不等于 CLI 遥控协议
A2A 不能直接让任意两个现有 Coding Agent CLI 自动互联。以 Codex CLI 和 Claude Code 为例,除非产品本身提供 A2A 接口,否则仍需要:
- 原生实现 A2A client/server;或者
- 在 CLI 外增加 adapter,将其会话和结果映射成 A2A 对象。
因此,A2A 更适合表达「把什么任务交给谁、任务进行到哪里、最后交付了什么」,而不是直接驱动终端输入输出。
ACP:控制 Coding Agent
这里说的 ACP 指 Agent Client Protocol。行业中还存在同样缩写的 Agent Communication Protocol,看资料时得对一下上下文。
ACP 标准化了代码编辑器、IDE 或编排器与 Coding Agent 之间的通信,定位类似 Coding Agent 领域的 LSP:客户端不需要为每个 Agent 单独解析一套 JSONL、PTY 输出和会话格式。
编辑器或编排器 ── ACP ── Coding AgentACP 覆盖的典型能力包括:
- 初始化 Agent 并协商能力
- 创建、恢复和管理会话
- 发送用户 prompt
- 接收流式文本与执行计划
- 展示工具调用状态、文件修改和 diff
- 处理权限请求
- 取消任务
- 协商终端、文件系统与 MCP 能力
本地 Agent 通常作为客户端的子进程运行,通过 JSON-RPC over stdio 保持双向长连接。远程 Agent 可以使用 HTTP 或 WebSocket,不过 ACP 官方文档仍将完整的远程支持标注为持续完善中的能力。
这让一个 Rust 编排器可以同时管理多个 ACP Agent:
let codex = start_acp_agent("codex");let claude = start_acp_agent("claude");let opencode = start_acp_agent("opencode");let pi = start_acp_agent("pi");这段代码表达的是统一接口的目标,而不是声称四个命令都原生实现了 ACP。按照 ACP Agent 列表,OpenCode 提供 ACP 接入,而 Codex CLI、Claude Agent 和 Pi 等可以通过相应 adapter 接入。真正接进去时,启动命令、能力范围和会话恢复方式仍应以各 adapter 为准。
MCP:让 Agent 使用工具和数据
MCP 是连接 AI 应用与外部系统的开放标准。它不管另一个 Agent 怎么规划任务,只管当前这个 Agent 怎么拿上下文、调动作、用现成工作流。
常见 MCP 能力包括:
- 读取本地文件或知识库
- 查询数据库
- 调用搜索引擎和浏览器
- 操作 Git、工单或云服务
- 使用服务端提供的资源与提示模板
MCP server 暴露能力,MCP client 负责发现并调用这些能力。对 Agent 来说,数据库查询、文件读取和浏览器操作通常是边界清晰的工具调用;如果对端具备自主规划、多轮协商和长任务状态,我更愿意把它建成 A2A Agent,而不是随手包成一个 MCP 工具。
ACP 与 MCP 也可以直接配合。ACP 客户端可以把用户配置的 MCP server 信息交给 Agent,让 Agent 直接连接;客户端若要暴露自己的能力,也可以提供 MCP server,而不需要把 ACP 和 MCP 硬塞进同一条连接。
放进多 Agent 系统
三个协议叠起来,我实际会排成这样:
flowchart TD O["Rust 编排器"] -->|"ACP"| A["主管 Coding Agent"] O -->|"ACP"| B["执行 Agent"] O -->|"ACP"| C["审查 Agent"] A <-->|"A2A 任务与消息"| B A <-->|"A2A 任务与消息"| C B -->|"MCP"| T["文件、Git、测试与数据工具"] C -->|"MCP"| T各层职责可以进一步划分为:
ACP:启动并控制 Codex、Claude、OpenCode、Pi 等会话A2A:表达 Agent 之间的任务委派、状态和成果MCP:让每个 Agent 使用文件、Git、数据库等外部能力调度器:管理 DAG、并发、超时、重试、权限与持久化协议只负责定义交互边界。谁先执行、失败后是否重试、多个结果如何合并、哪些操作需要人工批准,仍然是调度器的职责。
延迟差在哪
对于本机实时控制,ACP 的 stdio 长连接通常比以下间接方式更直接:
- SQLite 轮询
- 文件邮箱
- Hook 定期检查
- 模拟终端键盘输入
- MQTT 跨设备转发
但 ACP 只解决「客户端如何控制 Agent」。它不会保证任何 Agent 都能在模型生成途中接受任意消息。能否立即 steer、排队、取消或中断,最终仍取决于 Agent 及其 ACP 实现暴露了哪些能力。
A2A 位于更高层,重点是可靠、可追踪的任务协作,尤其适合长任务和跨服务通信,并不以本机最低延迟为首要目标。MCP 的延迟则主要取决于具体工具、传输和外部服务。
对我这种以 Rust 为核心的多 Coding Agent 系统,通道是这样分的:
本机实时控制:ACPAgent 任务语义:A2A工具与数据能力:MCP持久化和 DAG:Rust 调度器控 harness 走 ACP。任务交给谁、进行到哪、交付了什么,走 A2A。文件、Git、数据库走 MCP。A2A 不负责驱动终端。