ACP 协议详解:和 MCP 什么关系?如果设计一个 AI IDE 这俩怎么配合?
ACP 协议详解:和 MCP 什么关系?如果设计一个 AI IDE 这俩怎么配合?
最近在研究多 Agent 协作的时候,发现除了 MCP 之外,还有个 ACP(Agent Communication Protocol)。刚开始以为是 MCP 的竞品,仔细看下来发现它们解决的压根不是同一层的问题。MCP 管的是"Agent 怎么调工具",ACP 管的是"Agent 之间怎么对话"。这篇先把 ACP 的核心概念捋一遍,跟 MCP 做个详细对比,最后用"设计一个 AI IDE"这个场景来说明两个协议到底怎么配合使用。
先聊问题:为什么需要 Agent 间通信协议?
现在做 AI Agent 的框架越来越多——LangChain、CrewAI、AutoGen、BeeAI... 每个框架内部的 Agent 协作都没问题,但跨框架的 Agent 怎么协作?
举个具体场景:
你的公司有三个 AI Agent: - 客服 Agent(用 LangChain 搭的) - 订单查询 Agent(用 CrewAI 搭的) - 退款审批 Agent(内部 Java 系统) 用户说:"我上周买的东西质量有问题,要退款" 客服 Agent 需要: 1. 调用订单查询 Agent → 查到订单信息 2. 把订单信息和退款理由发给退款审批 Agent 3. 拿到审批结果后回复用户 问题是:这三个 Agent 用不同框架写的,消息格式不一样,怎么通信?
以前的做法要么是硬编码适配(A 调 B 写一套,A 调 C 再写一套),要么全部塞到一个框架里(不现实,因为可能是不同团队甚至不同公司开发的)。
ACP 就是来解决这个"Agent 间通信标准"问题的——定义一套统一的消息格式和交互模式,不管 Agent 用什么框架实现的,都能互相对话。
ACP 是什么?
ACP 全称 Agent Communication Protocol(Agent 通信协议),由 IBM Research 发起,现在已经捐给了 Linux Foundation 做开放治理。
一句话概括:ACP 是 Agent 之间的"普通话"标准。
类比一下:
-
MCP = 定义了"人怎么使用工具"的标准(锤子怎么握、扳手怎么拧)
-
ACP = 定义了"人和人之间怎么沟通"的标准(说什么语言、遵循什么对话规则)
核心设计原则
1. 基于 REST——不需要专门的 SDK 就能用
ACP 用的是标准 HTTP REST 接口,不是 JSON-RPC。这意味着你拿 curl、Postman 甚至浏览器就能跟 Agent 通信,入门门槛极低:
# 用 curl 直接调一个 ACP Agent
curl -X POST http://agent-server/runs \
-H "Content-Type: application/json" \
-d '{
"agent_name": "order-query",
"input": [{
"parts": [{
"content": "查询订单 ORD-2025-001 的状态",
"content_type": "text/plain"
}]
}]
}'
(跟 MCP 的 JSON-RPC 比起来,确实更"Web 开发者友好"。)
2. 多模态原生支持
ACP 用 MIME Type 标识内容类型,天然支持文本、图片、音频、视频、JSON 等任意格式,不需要协议层面的扩展:
{
"parts": [
{ "content": "这是用户上传的故障截图", "content_type": "text/plain" },
{ "content": "<base64图片数据>", "content_type": "image/png" },
{ "content": "{\"orderId\": \"ORD-001\"}", "content_type": "application/json" }
]
}
3. 异步优先
Agent 的任务经常是长时间运行的(比如"分析这份 50 页的合同"),ACP 的默认模式就是异步——发出请求后拿到一个 taskId,然后轮询或订阅结果:
同步模式(简单任务):
POST /runs/wait → 等着拿结果
异步模式(长任务):
POST /runs → 返回 { "run_id": "xxx" }
GET /runs/xxx → 查询状态:running / completed / failed
流式模式(实时更新):
POST /runs/stream → SSE 推送中间结果
这三种模式的选择权在调用方。简单的查询用同步,复杂的分析用异步,需要实时反馈的用流式。
ACP 的架构
核心概念
ACP 生态 Agent Client (调用方) ↓ HTTP ACP Server (协议代理) ↓ Agent Registry (Agent 注册表) ↓ Agent A (LangChain) Agent B (CrewAI) Agent C (自定义)
-
Agent Client:调用方,想跟某个 Agent 对话的人/系统
-
ACP Server:协议代理,负责路由消息、管理 Agent 注册、处理认证授权
-
Agent Registry:Agent 的"通讯录",记录每个 Agent 的能力、地址、状态
-
Agents:实际干活的 Agent,可以用任何框架实现,只要遵守 ACP 的消息格式
Agent 的发现机制
跟 MCP 的 tools/list 类似,ACP 的 Agent 也需要"自我介绍"。但 ACP 的做法是通过 Agent 元数据 来注册:
{
"name": "order-query-agent",
"description": "查询订单状态、物流信息、退换货进度",
"version": "1.2.0",
"capabilities": ["order-status", "logistics-tracking", "return-status"],
"input_content_types": ["text/plain", "application/json"],
"output_content_types": ["text/plain", "application/json"],
"modalities": ["sync", "async", "stream"]
}
有意思的是,ACP 支持离线发现——Agent 不需要在线才能被发现,它的元数据可以打包在分发包里,通过注册表静态索引。这对 Serverless 架构很友好(Agent 可以被按需拉起,不需要常驻)。
Agent 生命周期
INITIALIZING → ACTIVE → DEGRADED → RETIRING → RETIRED INITIALIZING 启动中 ACTIVE 正常工作 DEGRADED 能用但有问题(比如依赖的服务不稳定) RETIRING 正在下线,不接新请求 RETIRED 已下线
这套生命周期管理在 MCP 里是没有的。MCP 的 Server 要么在要么不在,没有"降级"的概念。ACP 考虑到了生产环境里 Agent 可能部分可用的情况。
ACP vs MCP:详细对比
这是大家最关心的。先说结论:它们不是竞品,是互补的,解决不同层的问题。
定位差异
ACP 的领域 → Agent ←→ Agent 通信(多 Agent 协作、任务委派) MCP 的领域 → Agent ←→ Tool/Data 通信(调工具、查数据、读资源)
用一个具体例子说明:
场景:用户问"帮我查一下北京今天的天气,然后订一张明天去上海的机票" 这里涉及两种协议: 1. "天气 Agent" 和 "机票 Agent" 之间怎么协作? → 需要 ACP(Agent 间通信) → 天气 Agent 把结果发给机票 Agent,说"用户查了天气,现在需要订机票" 2. "天气 Agent" 怎么调天气 API?"机票 Agent" 怎么调订票接口? → 需要 MCP(Agent 调工具) → 天气 Agent 通过 MCP 调用 weather_api 工具 → 机票 Agent 通过 MCP 调用 booking_api 工具
技术层面对比
| 维度 | MCP | ACP |
|---|---|---|
| 发起方 | Anthropic(2024.11) | IBM Research(2025.05) |
| 解决什么 | Agent 怎么调工具/数据 | Agent 之间怎么通信 |
| 传输协议 | JSON-RPC 2.0 | RESTful HTTP |
| 通信模式 | 同步为主 + SSE 流式 | 同步 / 异步 / 流式 三种 |
| 状态管理 | 无状态(单次请求) | 有状态(Session 支持) |
| 内容格式 | JSON 为主 | MIME Type 多模态 |
| 发现机制 | tools/list 获取工具列表 |
Agent Registry 注册表 |
| 生命周期 | 无(Server 在/不在) | 完整生命周期管理 |
| 治理 | Linux Foundation | Linux Foundation |
| 生态规模 | 10000+ MCP Server | 早期阶段 |
消息格式对比
MCP 的调用方式(JSON-RPC):
// Agent 通过 MCP 调用一个工具
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": { "city": "北京" }
},
"id": 1
}
MCP 的消息是"调用函数"的语义——我要调哪个工具、传什么参数。它不关心调用方是谁、有什么背景。
ACP 的调用方式(REST):
// Agent A 给 Agent B 发消息
POST /runs
{
"agent_name": "weather-agent",
"input": [
{
"parts": [
{
"content": "请查询北京今天的天气,用户需要这个信息来决定明天的出行计划",
"content_type": "text/plain"
}
],
"role": "user"
}
],
"session_id": "session-abc-123"
}
ACP 的消息是"对话"的语义——我是谁、我的上下文是什么、我期望你做什么。有 Session 概念,能维持跨消息的状态。
我理解的差异核心是:MCP 把被调用方当"工具",是无脑执行的;ACP 把被调用方当"Agent",是有自主判断能力的。这决定了它们的消息格式一个像 RPC 调用,一个像对话交流。
什么时候用 MCP,什么时候用 ACP?
| 场景 | 选择 | 原因 |
|---|---|---|
| Agent 需要查数据库、调 API | MCP | 这是工具调用,MCP 的强项 |
| Agent 需要读文件、搜索网页 | MCP | 同上,工具/资源接入 |
| 两个 Agent 需要协作完成一个任务 | ACP | 需要对话、委派、状态跟踪 |
| 多个 Agent 组成工作流 | ACP | 需要任务编排和生命周期管理 |
| 单 Agent + 多工具 | MCP 就够了 | 不需要 Agent 间通信 |
| 多 Agent + 多工具 | MCP + ACP | MCP 管工具接入,ACP 管 Agent 协作 |
一个实际的多 Agent 系统架构:
用户请求 ↓ 编排 Agent(Orchestrator) → ACP → 分析 Agent → MCP → 日志系统 → ACP → 检索 Agent → MCP → 知识库 → ACP → 执行 Agent → MCP → 工单系统
Agent 之间用 ACP 协作("我查到了这些信息,你来分析一下"),每个 Agent 各自通过 MCP 调用自己需要的工具。两层协议各管各的。
番外:ACP 和 A2A 合并了
2025 年还有个重要事件:IBM 的 ACP 和 Google 的 A2A(Agent-to-Agent)在 Linux Foundation 下合并了。
A2A 是 Google 在 2025 年 4 月发布的,也是解决 Agent 间通信的协议。两个协议目标一样,各有各的思路:
-
ACP 更偏向本地优先(local-first),适合同一集群内的 Agent 协作
-
A2A 更偏向跨组织,适合不同公司/供应商的 Agent 在公网上协作
最后两边决定合力而不是内耗,ACP 团队从 2025 年 9 月开始并入 A2A 项目。现在 A2A 是 Agent 间通信的统一标准,继承了 ACP 的一些设计理念(REST 原生、多模态、异步优先)。
所以现在的全景图是:MCP 管 Agent↔Tool,A2A(含 ACP 的精华)管 Agent↔Agent。两者互补,不冲突。
整个协议栈长什么样?
把 MCP、ACP/A2A 放在一起看,AI Agent 的协议栈正在形成一个清晰的分层:
应用层 Agent 框架(LangChain / CrewAI / AutoGen) ↓ Agent 间通信层(A2A / 原 ACP) Agent 发现 / 任务委派 / 消息路由 / 状态管理 ↓ Agent-工具通信层(MCP) 工具调用 / 资源读取 / Prompt 模板 ↓ 模型层 LLM(Claude / GPT / Gemini / 开源模型) ↓ 基础设施层 HTTP / WebSocket / gRPC / SSE
每一层解决不同的问题,互不替代。之前写过 Function Calling、MCP、Skills 三者的区分,现在再加上 ACP/A2A 这一层,整个拼图就更完整了。
落地场景:如果让我设计一个 AI IDE
光聊协议太抽象,拿一个具体场景来说明——如果让我从零设计一个 AI IDE(类似 Cursor、Windsurf),ACP 和 MCP 分别扮演什么角色。
先看现在的 AI IDE 是怎么做的
Cursor 的 Shadow Workspace
Cursor 最核心的技术叫 Shadow Workspace(影子工作区)——在后台开一个隐藏的编辑器窗口,AI 在这个隐藏窗口里改代码、跑 Language Server 做 Lint 检查,确认没问题之后才把结果给用户看。
用户的编辑器窗口(正常使用) ↓ AI 提议修改 Shadow Window(隐藏窗口) → 应用代码修改 → 跑 Language Server(类型检查、Lint) → 收集错误信息 → 有错?→ AI 自我修正 → 重新检查 → 没错?→ 把修改呈现给用户
用户感知到的是"AI 一次就给了正确的代码",实际上 AI 可能在影子窗口里改了好几轮才通过 Lint。
Windsurf 的 Cascade Engine
Windsurf 走的另一条路——它的 Cascade 引擎先把代码库做静态分析,生成依赖图(AST 级别)。AI 改代码的时候不是"盲改",而是知道"改了 A 文件,B 和 C 文件会受影响",然后多文件同时修改。
两者的设计哲学差异挺明显的:Cursor 偏保守(改尽量少的代码、增量修复),Windsurf 偏激进(诊断确认后大范围改)。
AI IDE 的核心难题:代码幻觉
AI IDE 最大的问题就是幻觉——编造不存在的 API、引用不存在的包、写出语法正确但逻辑完全错的代码。代码幻觉尤其阴险,因为它可能语法完全正确、命名看起来合理:
# 幻觉示例:看起来完全合理,但 API 不存在 from pandas import DataFrame df = DataFrame(data) df.smart_fillna(method="contextual") # ← 这个方法不存在,AI 编的
LLM 本质上是概率模型——它不是在"查文档",它是在"猜下一个 token"。当训练数据里某个模式出现频率高,它就倾向于生成类似的东西,哪怕在当前上下文里是错的。
防幻觉大概分三层:
第一层:生成前——给足上下文(Grounding)
做法: 1. RAG 检索项目文档和依赖库文档 → AI 基于真实文档生成 2. 代码库 embedding 索引 → 改 UserService 前先检索它的定义和使用方式 3. LSP 信息注入 → 把 Language Server 的类型信息、方法签名喂给 AI
思路就是:与其让 AI 回忆,不如让 AI 查文档。
第二层:生成后——自动验证
验证手段: 1. LSP Lint 检查 → 类型错误、未定义变量、import 不存在 2. 编译/解释检查 → 编译型语言直接尝试编译 3. 单元测试 → AI 生成代码的同时生成测试,跑一遍 4. 多次生成 + 投票 → 多个独立生成结果一致,大概率是对的
第三层:人机协作——标记 + 审查
AI 生成的代码标记来源、Code Review 时重点看 AI 生成的部分、IDE 里对 AI 生成的 import 自动高亮(如果该 import 在项目里从未出现过)。
我的 AI IDE 架构设计:MCP + ACP 两层协议
现在把前面聊的 ACP 和 MCP 落到 IDE 这个场景里,架构就是这样的:
AI IDE 整体架构 用户交互层 编辑器 UI / 终端 / 聊天面板 / Diff 视图 ↓ Agent 编排层(用 A2A/ACP 协作) → A2A → 代码生成 Agent → A2A → 代码审查 Agent → A2A → 测试生成 Agent → A2A → 文档/解释 Agent ↓ MCP 工具接入层(每个 Agent 都能调) 文件系统 MCP Server Git MCP Server 终端 MCP Server LSP MCP Server 代码搜索 MCP Server 文档检索 MCP Server ↓ 验证层(Shadow Workspace) LSP 检查 / 编译验证 / 测试运行 / 幻觉检测
MCP 层——让 AI "看得见" 项目
MCP 在这里解决的是 Agent → 工具/数据 的连接。每个 Agent 都能通过 MCP 调用:
-
文件系统 MCP Server:读写文件、搜索文件、查看目录结构
-
Git MCP Server:查看 diff、commit 历史、分支信息
-
LSP MCP Server(防幻觉核心):获取类型定义、方法签名、诊断信息
-
终端 MCP Server:执行命令、运行测试
-
文档检索 MCP Server:RAG 检索项目文档、依赖库文档
-
代码搜索 MCP Server:语义搜索 + 关键词搜索
其中 LSP MCP Server 是对防幻觉最有价值的:
AI 想调用 user.getName() 没有 LSP 信息时: → AI 从训练数据"回忆",觉得 User 类应该有 getName() → 可能是幻觉 有 LSP 信息时: → MCP 调用 LSP → LSP 返回 User 类的完整方法列表 → AI 看到 User 类只有 getFullName() 没有 getName() → AI 生成 user.getFullName() → 准确
用真实的 LSP 数据替代模型的"记忆",这是代码场景下 Grounding 最直接的手段。
为什么用 MCP 而不是直接写 API?因为 MCP 的
tools/list机制让工具可以自描述——新增一个 MCP Server 不需要改 Agent 代码。这跟 LSP 的思路一脉相承:把 M×N 问题变成 M+N 问题。
A2A/ACP 层——多个 Agent 各司其职
单 Agent 做所有事情 context 会爆炸,而且不同任务需要的"思维模式"不一样——写代码需要创造力,Code Review 需要谨慎,写测试需要找边界条件。所以拆成多个 Agent,用 A2A/ACP 协作:
用户说:"帮我实现用户注册功能" 编排 Agent(Orchestrator) ↓ → A2A → 代码生成 Agent → MCP → 文件系统(读现有代码) → MCP → LSP(获取类型信息) → MCP → 文档检索(查 ORM 文档) → 生成代码 → 通过 A2A 发给审查 Agent ↓ → A2A → 代码审查 Agent → 收到代码 → MCP → LSP(跑 Lint 检查) → 检查安全漏洞(SQL 注入?XSS?) → 有问题 → A2A 反馈给生成 Agent,要求修改 → 没问题 → A2A 发给测试 Agent ↓ → A2A → 测试生成 Agent → MCP → 终端(跑测试) → 测试不过 → A2A 反馈给生成 Agent → 测试通过 → A2A 通知编排 Agent → 呈现给用户
A2A 协议里的几个概念在 IDE 场景下特别有用:
-
Agent Card:每个 Agent 声明自己的能力。编排 Agent 通过 Agent Card 知道"审查 Agent 能检查 Java 和 Python,但不支持 Rust"
-
Task 生命周期(
submitted → working → completed/failed):用户可以在 IDE 面板里看到每个 Agent 的工作状态 -
Artifact:Agent 的产出物。代码生成 Agent 的 artifact 是代码文件,测试 Agent 的 artifact 是测试报告
为什么用 A2A/ACP 而不是在一个 Agent 里用多轮 prompt?两个原因:1)每个 Agent 的 system prompt 可以针对性优化;2)Agent 之间的消息有 Session 概念,可以跟踪整个协作过程的状态。
验证层——幻觉专项检测
在 Shadow Workspace 的基础上,加一层专门针对 AI 幻觉的检测:
# 伪代码:幻觉检测器
class CodeHallucinationDetector:
def check_imports(self, generated_code, project_deps):
"""检查 AI 生成的 import 是否在项目依赖中"""
imports = extract_imports(generated_code)
for imp in imports:
if imp.package not in project_deps:
yield Warning(f"引用了项目未安装的包: {imp.package}")
def check_api_calls(self, generated_code, lsp_symbols):
"""检查 AI 调用的方法是否真实存在"""
calls = extract_method_calls(generated_code)
for call in calls:
if call.method not in lsp_symbols.get(call.receiver_type, []):
yield Warning(f"调用了不存在的方法: {call.receiver_type}.{call.method}")
这比单纯的 Lint 检查更有针对性——Lint 查语法和类型规范,幻觉检测查"这个东西在当前项目里存不存在"。
两层协议在 IDE 里的分工总结
| MCP | A2A/ACP | |
|---|---|---|
| 解决什么 | Agent 怎么使用 IDE 里的工具 | Agent 之间怎么协作 |
| 在 IDE 里的角色 | "手"——读文件、查类型、跑终端 | "嘴"——生成 Agent 和审查 Agent 对话 |
| 消息语义 | 函数调用:tools/call("lsp_get_definitions", ...) |
对话交流:"我生成了这段代码,请审查安全性" |
| 状态 | 无状态,调完就完 | 有状态,Session 贯穿整个任务 |
实际落地要考虑的
真要做还有几个现实问题:
-
延迟:多 Agent 来回传消息肯定比单 Agent 慢。简单任务直接用单 Agent + MCP,复杂任务再启动多 Agent 协作
-
成本:审查和测试 Agent 可以用便宜的小模型,只有代码生成 Agent 用大模型。大部分 MCP 工具调用不涉及 LLM,成本为零
-
上下文管理:100 万行代码不可能全塞进 context,靠依赖图 + RAG 检索按需获取
-
安全:MCP 工具有执行权限,所有操作需用户确认或做沙箱隔离
小结
ACP 和 MCP 的关系用一句话概括:MCP 让 Agent 能"用工具",ACP 让 Agent 能"找同事"。MCP 管的是纵向的 Agent→工具接入(向下),ACP/A2A 管的是横向的 Agent↔Agent 协作(平级)。做单 Agent 应用只需要 MCP,做多 Agent 协作系统两个都要用。
用 AI IDE 这个场景来看就很直观——MCP 接入文件系统、LSP、终端等开发工具,让每个 Agent 能"看见"项目、"操作"代码;A2A/ACP 让代码生成、审查、测试等多个 Agent 协作配合,各司其职。两层协议各管一层,谁也替代不了谁。
现在 ACP 已经并入 A2A 成为 Linux Foundation 下的统一标准,加上 MCP 也进了 Linux Foundation,整个生态正在走向标准化——对开发者来说是好事,以后不用再为"选哪个协议"纠结了。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)