ACP协议详解

你在编辑器里跟 AI 说「帮我把这个函数改改」,AI 真的去读了文件、改了代码。这件事背后至少有三个角色:编辑器、AI 程序,以及读文件改文件的那些能力。ACP(Agent Client Protocol)管的是第一段——编辑器和 AI 程序之间怎么说话。

1. 三个角色:先弄清楚谁管什么

在讲 ACP 之前,先把三个角色分清楚。后面所有协议,都只是在它们之间的连线上做文章。

1.1 编辑器(Editor)

VS Code、Zed、JetBrains——你写代码、看 diff、在聊天框里打字下指令的那个软件。

编辑器负责显示界面、启动和管理 Agent 进程、把你的话传给 Agent,以及把 Agent 的回复渲染到聊天面板和 diff 视图里。

1.2 Agent

Claude Code、Gemini CLI、Codex CLI、Copilot CLI——会思考、会干活的 AI 程序。

Agent 通常不是编辑器里嵌死的一个插件,而是编辑器启动的独立子进程。它自己琢磨任务、决定下一步、调用工具,还会一边干一边往外播报进度。关于 Agent 的最小结构(模型 + 循环 + 工具),可参考本系列 Agent 最小内核

1.3 工具(Tool)

读文件、写文件、搜代码、调 API——真正碰到硬盘和网络的是这些能力。

Agent 一般不直接乱摸你的电脑,它通过工具去触达真实世界。工具怎么暴露给 Agent,是 MCP 协议的事。

1.4 一张图记住分工

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TB
    User["你"]
    Editor["编辑器"]
    Agent["Agent"]
    Tools["工具 / 数据"]

    User -->|"打字下指令"| Editor
    Editor <-->|"ACP(本文主角)"| Agent
    Agent <-->|"MCP"| Tools

日常类比:编辑器是电视,Agent 是机顶盒,工具是机顶盒能调到的频道。ACP 是电视和机顶盒之间的 HDMI 线规格——换电视或换机顶盒,只要口子一样,插上就能用。

2. ACP 解决什么问题

三个角色分清楚之后,来看 ACP 到底在填哪条缝。

2.1 没有标准时,换编辑器就得重来

你在 VS Code 里配好了 Claude Code,聊天、看 diff 都正常。同事说 Zed 更快,你也想试——能把 VS Code 那套接入方式原封不动搬过去吗?

不能。不是因为 Claude Code 换了脑子,而是编辑器怎么「养」这个 Agent 程序,各家自己定规矩:

没有标准时,各家自己定后果
怎么启动 Agent(命令、参数、环境变量)VS Code 和 Zed 各写一套
怎么把用户输入发过去消息格式不一样
怎么接收 Agent 的回复有的等整段返回,有的要流式
怎么展示「Agent 正在读文件」UI 协议各搞各的

编辑器有 N 家,Agent 有 M 个,私下对接就是 N × M 条专用连接线。任何一方改了接口,对面的 N 条线(或 M 条线)都可能断。

2.2 同类问题,LSP 已经示范过一次

2016 年以前,VS Code 要单独给 TypeScript 写一套代码补全,再给 Go 写一套;Sublime 也得各写一遍。每种编辑器给每种语言都写一套集成,线的数量同样是 N × M。

微软搞了个 Language Server Protocol(LSP):编辑器只需要对接一次 LSP,所有语言工具自己变成 LSP Server 就行。连接线从 N × M 坍缩成 N + M。

ACP 把 LSP 故事里的「语言工具」换成「AI Agent」——编辑器实现一个 ACP Client,Agent 实现一个 ACP Server,双方用标准消息通信。连接线同样从乘变加。

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#EF4444', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#DC2626', 'lineColor': '#F87171'}}}%%
flowchart LR
    E1["VS Code"] --- A1["Claude Code"]
    E1 --- A2["Gemini CLI"]
    E1 --- A3["Codex CLI"]
    E2["JetBrains"] --- A1
    E2 --- A2
    E2 --- A3
    E3["Zed"] --- A1
    E3 --- A2
    E3 --- A3

没有 ACP:三款编辑器 × 三款 Agent = 九条专用连接线。

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA'}}}%%
flowchart LR
    E1["VS Code"] --- P["ACP"]
    E2["JetBrains"] --- P
    E3["Zed"] --- P
    P --- A1["Claude Code"]
    P --- A2["Gemini CLI"]
    P --- A3["Codex CLI"]

有了 ACP:新增一个编辑器或 Agent,只加一条线。

2.3 ACP 是什么,又不是什么

ACP 是:编辑器和 Agent 之间的标准说话方式——怎么启动、怎么开协作、怎么传指令、怎么收进度。

ACP 不是:AI 算法、大模型、读文件的底层实现。跟 USB-C 不发明新设备、只统一接口形状一样。

角色和动机都清楚了,接下来看一次完整对话在 ACP 里怎么跑。

3. 一次对话怎么走

ACP 的定位清楚之后,来看它具体怎么跑。这里用一个叫「小码」的虚构 Agent 做例子——它就是一个普通程序,监听标准输入输出,接收和发送 JSON 格式的消息。

3.1 四步走完一个会话

你在 Zed 里对「小码」说:「把 utils.ts 里的 parseConfig 重构一下。」从你说完这句话,到聊天框出现「重构完成」,中间经过四个阶段:

阶段谁发起干什么日常说法
① 握手编辑器 → Agent确认协议版本、Agent 能做什么「喂,听得到吗?你支持回放历史吗?」
② 建会话编辑器 → Agent分配 sessionId,声明可用工具「这是工单号,文件读写权限在这」
③ 发任务编辑器 → Agent把你的话原样传过去「用户说:重构 parseConfig」
④ 边干边说Agent → 编辑器持续推送进度,不是一次性返回「我先读文件…读到了…改完了…搞定」

第一步:握手

编辑器启动「小码」子进程。双方先确认协议版本和能力。

编辑器说:「我是 ACP v1,你支持什么?」

「小码」回答:「ACP v1,我支持恢复历史会话,支持使用 MCP 工具。」

第二步:创建会话

编辑器告诉「小码」:「本次会话里,你可以用这两个工具——读文件、搜索代码。」

编辑器同时分配一个 sessionId(比如 sess_12345),后续多轮对话都在这个会话里累积。

第三步:发任务

用户输入「把 utils.ts 里的 parseConfig 重构一下」。编辑器通过 ACP 把这条消息发给「小码」。

第四步:边想边说

「小码」收到任务,开始干活。但它不是闷头干完才回一句,而是流式推送——像直播一样,想到什么就发什么,编辑器收到了就实时显示。

Agent 可能推送五类消息:

类型含义例子
agent_message_chunk流式输出的文字片段「我先读一下文件……」「parseConfig 接受三个参数……」
tool_call调用了某个工具读文件、改代码、搜索
tool_call_update工具执行的结果「读到了 120 行代码」「搜索到 3 处引用」
plan当前执行计划「步骤一:读文件 → 步骤二:分析依赖 → 步骤三:重构」
turn_end这一轮做完了告诉编辑器「我搞定了,等下一个指令」

编辑器收到这些消息后,实时渲染聊天面板、diff 视图和终端输出。这跟 LSP 不同——LSP 是「你问我答」,ACP 是「你问,我持续播报我在做什么」。

3.2 用一张图看全程

%%{init: {'theme': 'base', 'themeVariables': { 'actorBkg': '#3B82F6', 'actorTextColor': '#fff', 'actorBorder': '#2563EB', 'signalColor': '#60A5FA', 'noteBkgColor': '#EFF6FF'}}}%%
sequenceDiagram
    participant E as "编辑器"
    participant A as "小码 Agent"
    participant T as "工具"

    rect rgb(239, 246, 255, 0.6)
    Note over E,A: "第一步:握手"
    E->>A: initialize
    A-->>E: "ACP v1,支持 MCP"
    end

    rect rgb(236, 253, 245, 0.6)
    Note over E,A: "第二步:创建会话"
    E->>A: session/new
    A-->>E: "sessionId: sess_12345"
    end

    rect rgb(254, 243, 199, 0.6)
    Note over E,A: "第三步和第四步:干活"
    E->>A: session/prompt
    A-->>E: agent_message_chunk
    A-->>E: tool_call
    A->>T: "读文件"
    T-->>A: "120 行代码"
    A-->>E: tool_call_update
    A-->>E: agent_message_chunk
    A-->>E: tool_call
    A-->>E: tool_call_update
    A-->>E: agent_message_chunk
    A-->>E: turn_end
    end

注意时序图里 Agent 读文件的那一步:Agent 和工具之间走的是 MCP(下一节展开)。编辑器不直接参与,它只通过 ACP 收到 tool_calltool_call_update,然后在 UI 里展示「正在读 utils.ts」「读完了」。

3.3 为什么是流式推送?

AI Agent 的工作方式是多步推理,不是单次查询。「重构一个函数」需要先读文件、再分析、再改、可能还要跑测试。如果走请求 - 响应模式,每一步都等 Agent 返回再问下一步,效率极低。

流式推送让 Agent 自己驱动整个过程,编辑器只负责渲染——Agent 变成主动方,编辑器变成被动展示层。

一次对话走通之后,把 ACP 放进更大的协议图景里看。

4. 三层协议:ACP、MCP、A2A

AI Agent 生态里现在有三个核心协议,各管一层:

ACPMCPA2A
连谁编辑器 ↔ AgentAgent ↔ 工具Agent ↔ Agent
推手Zed + JetBrainsAnthropicGoogle(LF 托管)
解决编辑器怎么接入 AgentAgent 怎么调用外部工具和数据Agent 之间怎么发现、委派、协作
传输JSON-RPC over stdioJSON-RPC over stdio/HTTP+SSEHTTP + JSON-RPC + SSE
核心抽象Session(会话)Tool / Resource / PromptTask(任务) + Agent Card

记住三句口诀:

  • Editor 管 Agent → ACP
  • Agent 管工具 → MCP
  • Agent 管 Agent → A2A

一句话串起来:编辑器通过 ACP 管理 Agent,Agent 通过 MCP 使用工具,Agent 之间通过 A2A 委派任务。你在 IDE 里写代码的日常,主要碰到的是前两个。

4.1 MCP 在 ACP 会话里怎么接入

上面时序图有一个容易忽略的设计:在第二步「创建会话」时,编辑器把可用的 MCP Server 配置通过 ACP 的 session/new 传给 Agent。

Agent 不需要自己管理工具配置——编辑器说「你有这些工具可以用」,Agent 收到后就能通过 MCP 去调。对编辑器来说,工具调用是「黑盒」:它只看到 Agent 在调什么、结果如何,不用管 MCP 底层怎么连的。

MCP 的详细机制见本系列 MCP 协议详解

协议分层讲完了,下面落到传输格式和接入方法。

5. 技术细节

5.1 传输与消息格式

ACP 工作在编辑器和 Agent 之间。本地 Agent 运行为编辑器的子进程,通过 stdio 通信——编辑器 spawn Agent 进程,双方用 JSON-RPC 2.0 消息来回交换。远程 Agent 走 HTTP/WebSocket,但目前主力是 stdio。

会话结束时,编辑器发 session/end 或直接关闭子进程。支持 session/load 的 Agent 下次启动时可以恢复历史会话。

5.2 Agent 开发者快速接入

如果你想让自己的 Agent 接入 ACP,只需实现四个 JSON-RPC 方法:

方法方向作用
initializeClient → Server握手,返回 agentCapabilities
session/newClient → Server接收 MCP Server 声明,创建会话
session/promptClient → Server接收用户消息
session/updateServer → Client流式推送思考、工具调用、结果

Agent 是编辑器的子进程,监听 stdin、写 stdout,解析和发送 JSON-RPC 2.0 消息。任何语言都能实现。Java 生态有 Spring AI ACP SDK 可以直接用。完成后在 ACP Registry 提交 agent.json,所有 ACP 编辑器就能发现你的 Agent。

接入方法有了,但协议本身还有几个明确的边界。

6. 当前局限

ACP 解决了编辑器与 Agent 的连接问题,但有三个方向目前还没覆盖:

  • 远程 Agent 规范未定。当前主力是 stdio(本地子进程),HTTP/WebSocket 的远程方案仍在草案阶段。云端 Agent 接入还没有标准路径。
  • 安全与权限模型缺失。ACP 没有定义 Agent 能访问哪些文件、能执行哪些命令。权限控制目前全靠编辑器自己实现,各家做法不一。
  • 多 Agent 协同不在范畴内。ACP 是一对一的 Editor-Agent 协议。多个 Agent 协作走 A2A,不是 ACP 的事。

这些不是设计缺陷,是有意的范围收窄——先把编辑器 - Agent 这一层做标准化,再逐步扩展。

局限归局限,生态侧已经在快速落地。

7. 生态现状

7.1 编辑器支持

编辑器状态说明
Zed✅ 原生ACP 发起方,协议设计者
JetBrains 全家桶✅ 官方IntelliJ、PyCharm、WebStorm 等全线支持
VS Code🔧 社区适配中通过扩展实现
Neovim🔧 社区适配中社区插件
Emacs🔧 早期探索社区方案

7.2 Agent 支持

Agent状态说明
Claude Code✅ 已支持Anthropic 出品
Gemini CLI✅ 已支持--acp 参数启用
Codex CLI✅ 已支持OpenAI 出品
GitHub Copilot CLI✅ 已支持微软/GitHub
Kiro✅ 已支持AWS
OpenCode✅ 已支持开源社区

7.3 ACP Registry

2026 年 1 月上线的 Agent 发现和分发平台,类似 npm 之于 Node 包。Agent 开发者提交一个 agent.json 清单文件,声明名称、版本、分发方式(npx / uvx / binary)。合并后,所有 ACP 兼容的编辑器都能发现并一键安装。

1
2
3
4
5
6
7
8
{
"id": "my-agent",
"name": "My Coding Agent",
"version": "1.2.0",
"distribution": {
"npx": { "package": "@myorg/my-agent" }
}
}

注册一次覆盖所有编辑器。发现成本接近零。

最后补充两个容易踩坑的细节:名称歧义,以及 ACP 为什么在 2025 年才出现。

8. 附录

8.1 名称歧义

「ACP」三个字母同时被三种协议使用:

  • Agent Client Protocol:编辑器连接 AI 编程 Agent(即本文所述)
  • Agent Communication Protocol:IBM Research 做的 Agent 间通信协议,2025 年 8 月已并入 A2A
  • Agentic Commerce Protocol:电商场景

本文只涉及第一种。搜索资料时注意区分。

8.2 为什么是 2025 年

ACP 的技术细节已经铺开了。但还有一个问题:编辑器存在了几十年,AI 编程助手从 2021 年就有了,为什么 ACP 偏偏在 2025 年出现?

表面原因:2025 年两侧同时爆发。Agent 不再是编辑器内置的插件,而是独立进程,有自己的推理循环和工具链。编辑器也从「代码编辑工具」变成「Agent 运行环境」。N × M 的集成痛不可忍受。

根本原因:2024 年之前,大模型的工具调用不够可靠——Agent 会幻觉、会死循环,成功率不高。没人愿意为不稳定的东西建标准。2024 年底 Claude 3.5、GPT-4o、Gemini 1.5 跨过了可靠性阈值,在真实代码库上证明了「Agent 可以用」。可靠性催生使用量,使用量催生集成压力,集成压力催生协议。

这不是 ACP 特有的故事。HTTP 出现在 Web 内容爆发之后,LSP 出现在语言工具链爆发之后,USB 出现在外设种类爆发之后。能力爆发产生需求爆炸,N × M 集成成本压倒所有参与者——协议就成了博弈论里的纳什均衡。

协议不是被设计出来的,是被算出来的。