Agent Client Protocol 全景解析

1.概述

Agent Client Protocol(ACP)是一个标准化通信协议,用于规范代码编辑器和集成开发环境(IDE)与编码智能体(Coding Agents)之间的交互。该协议同时支持本地和远程使用场景。

2.内容

2.1 为什么需要 ACP?

AI 编码智能体(Coding Agents)与代码编辑器 / IDE 之间虽然联系紧密,但互操作性却并非默认标准。

目前,每个编辑器都需要为它想支持的每一个智能体开发自定义集成;而每个智能体也必须实现针对不同编辑器的专有 API,才能触达用户。这导致了一系列问题:

  • 集成成本高:每新增一个智能体与编辑器的组合,都需要进行大量定制开发;
  • 兼容性有限:单个智能体通常只能支持少数几种编辑器;
  • 开发者锁定:开发者一旦选择某个智能体,往往就被迫接受其支持的有限界面和功能。

ACP(Agent Client Protocol)正是为了解决这些问题而设计。它提供了一个标准化协议,用于规范编码智能体与编辑器之间的通信,类似于 Language Server Protocol (LSP) 当年标准化语言服务器集成的方式。只要智能体实现了 ACP,就能与任何支持 ACP 的编辑器无缝协作;只要编辑器支持 ACP,就能立即接入整个 ACP 生态中的所有兼容智能体。这种解耦设计让智能体和编辑器双方都能独立快速创新,同时赋予开发者真正的自由------可以根据自身工作流选择最适合的工具,而不再受集成限制。

ACP 的核心设计理念是:用户主要在代码编辑器中工作,并希望能够随时调用智能体来协助完成特定任务。

ACP 同时支持本地和远程两种部署场景:

  • 本地智能体:作为代码编辑器的子进程运行,通过标准输入输出(stdio)使用 JSON-RPC 进行通信;
  • 远程智能体:可部署在云端或独立基础设施上,通过 HTTP 或 WebSocket 进行通信。

目前,对远程智能体的完整支持仍在积极开发中。我们正在与多家智能体平台密切合作,以确保协议能够满足云托管和远程部署场景的特定需求。ACP 在可能的情况下会复用 MCP(Model Context Protocol)中已定义的 JSON 表示,同时新增了一些专为智能体编码体验设计的自定义类型,例如显示代码差异(diffs)。用户可读文本的默认格式为 Markdown,这既提供了足够的格式灵活性来呈现丰富的排版,又避免了要求代码编辑器必须具备 HTML 渲染能力。

2.2 架构设计

Agent Client Protocol(ACP)定义了一套标准接口,用于实现 AI 智能体(AI Agents)与客户端应用(主要是代码编辑器)之间的通信。ACP 的架构设计注重灵活性、可扩展性以及平台无关性,能够适应不同的开发环境和集成需求。

2.2.1 设计理念

ACP 的架构遵循以下几项核心原则:

  • MCP 友好:协议基于 JSON-RPC 构建,并尽可能复用 MCP(Model Context Protocol)中已定义的数据类型。这样,集成方无需为常见数据类型重新设计另一套表示方式,显著降低了集成成本。
  • 以用户体验为核心(UX-first):协议专注于解决与 AI 智能体交互时的用户体验挑战。它提供了足够的灵活性,能够清晰地呈现智能体的意图和操作过程,同时避免引入不必要的抽象复杂度。
  • 可信赖:ACP 适用于用户在代码编辑器中与自己信任的 AI 模型进行交互的场景。用户仍可完全控制智能体的工具调用(Tool Calls),而代码编辑器则负责为智能体提供对本地文件和 MCP 服务器的安全访问权限。

2.2.2 连接与启动流程

当用户尝试连接到一个智能体时,代码编辑器会按需启动该智能体的子进程,所有通信均通过标准输入/输出(stdin/stdout)进行。每个连接支持多个并发会话(Sessions),这意味着用户可以同时进行多条思考路径(multiple trains of thought),实现并行处理多个任务或对话。

ACP 大量采用 JSON-RPC 通知(Notifications) 机制,允许智能体以实时流式(streaming) 的方式向编辑器界面推送更新。这使得用户能够即时看到智能体的思考过程、代码生成进度或执行结果,从而提供更加流畅和响应迅速的交互体验。

此外,ACP 还充分利用了 JSON-RPC 的双向请求能力,让智能体能够主动向代码编辑器发起请求。例如,当智能体需要执行某个工具调用(Tool Call)时,可以向编辑器请求相应的权限(如读取或修改本地文件),从而实现更安全、可控的协作流程。

这种通知与请求相结合的设计,既保证了高效的实时更新,又保留了必要的用户控制权,让 AI 智能体与编辑器的协作更加自然、灵活且可靠。

2.2.3 MCP

在实际使用中,代码编辑器通常已配置了用户自定义的 MCP 服务器(Model Context Protocol Servers)。当用户向智能体发送提示词(Prompt)时,编辑器会将这些 MCP 服务器的配置信息一并转发给智能体。这使得智能体能够直接连接并访问这些 MCP 服务器,而无需经过编辑器作为中间代理。这种设计带来了显著优势:

  • 智能体可以更高效地读取项目上下文、代码库结构、文档等丰富信息;
  • 减少了数据在编辑器与智能体之间多次转发的开销,提升整体响应速度;
  • 让智能体能够像"原生集成"一样,充分利用用户已配置的外部知识源和工具。

通过这种配置传递机制,ACP 实现了编辑器、智能体与 MCP 服务器之间的高效协同,进一步增强了 AI 编码智能体的实际生产力。

代码编辑器自身也可能希望向智能体提供基于 MCP 的工具和服务。

为了避免在同一个通信通道上同时运行 MCP 和 ACP 协议带来的复杂性,ACP 推荐采用以下优雅的设计:

编辑器可以将自己作为一个 MCP 服务器,并把其配置信息传递给智能体。这样,智能体就可以像连接其他 MCP 服务器一样,直接与编辑器提供的 MCP 服务进行交互。

由于部分智能体可能仅支持通过 stdio(标准输入输出) 来访问 MCP,代码编辑器可以提供一个轻量级的代理(Proxy),负责将智能体的 MCP 请求通过隧道(Tunnel)转发回编辑器自身进行处理。

这种方式具有以下显著优势:

  • 协议职责清晰,MCP 负责工具调用与上下文访问,ACP 专注于智能体与编辑器的核心交互;
  • 避免了协议冲突和 socket 复用带来的技术复杂性;
  • 保持了极高的灵活性,既支持功能强大的编辑器原生工具,又兼容仅支持 stdio 的智能体实现;
  • 为未来扩展提供了良好的基础。

通过这一机制,代码编辑器不仅能消费外部 MCP 服务,还能主动将自身能力暴露给 AI 智能体,实现真正的双向赋能。

2.3 Agent

Agent Client Protocol(ACP)兼容智能体列表

以下智能体已实现 Agent Client Protocol(ACP),可以与任何支持 ACP 的代码编辑器客户端无缝协作:

  • AgentPool
  • Augment Code
  • AutoDev
  • Blackbox AI
  • Claude Agent (via Zed's SDK adapter)
  • Cline
  • Codex CLI (via Zed's adapter)
  • Code Assistant
  • Cursor
  • Docker's cagent
  • fast-agent
  • Factory Droid
  • fount
  • Gemini CLI
  • GitHub Copilot (in public preview)
  • Goose
  • Junie by JetBrains
  • Kimi CLI
  • Kiro CLI
  • Minion Code
  • Mistral Vibe
  • OpenClaw
  • OpenCode
  • OpenHands
  • Pi (via pi-acp adapter)
  • Qoder CLI
  • Qwen Code
  • Stakpak
  • VT Code

2.4 Clients

Clients, Frameworks, Connectors 与相关工具

以下项目直接实现了 Agent Client Protocol(ACP),或提供了将 ACP 智能体连接到其他环境的能力,同时也涵盖了支持相邻编码智能体工作流的相关工具。

这些项目共同构成了 ACP 生态的重要组成部分,帮助开发者更轻松地集成和使用标准化智能体协议。

2.4.1 编辑器和IDE

  • Chrome ACP (Chrome extension / PWA)
  • Emacs via agent-shell.el
  • JetBrains
  • neovim
    • through the CodeCompanion plugin
    • through the carlos-algms/agentic.nvim plugin
    • through the yetone/avante.nvim plugin
  • Obsidian --- through the Agent Client plugin
  • Unity Agent Client (Unity editor)
  • Visual Studio Code --- through the ACP Client extension
  • Zed

2.4.2 客户端和apps

  • ACP UI
  • acpx (CLI)
  • gemini-cli-desktop
  • Agent Studio
  • AionUi
  • aizen
  • DeepChat
  • fabriqa.ai
  • Harnss
  • iflow-cli
  • Lody
  • Minion Mind --- through the Agent Client plugin
  • Mitto
  • Nori CLI
  • Ngent
  • RayClaw
  • RLM Code
  • Sidequery (coming soon)
  • Tidewave
  • Toad
  • Web Browser with AI SDK (powered by @mcpc/acp-ai-provider)

3.ACP Java SDK

ACP 采用 子进程(Subprocess)模型 进行通信。

客户端(可以是你的应用程序,也可以是 Zed、VS Code 等代码编辑器)会将 AI 智能体作为子进程启动,并通过 标准输入/输出(stdin/stdout) 使用 JSON-RPC 消息进行双向通信。

整个交互过程分为三个主要阶段:

  • 初始化阶段(Initialize):客户端与智能体交换协议版本号和各自支持的能力(Capabilities),完成握手协商,确保双方兼容。
  • 会话阶段(Session):客户端创建一个会话,并向智能体提供工作目录(Working Directory)等上下文信息,为后续交互建立环境基础。
  • 提示与响应阶段(Prompt):客户端向智能体发送用户提示词(Prompt)或其他消息,智能体则以流式(Streaming) 方式返回响应、代码生成结果、思考过程或工具调用请求等。

3.1 Claude Code Maven

复制代码
<dependency>
    <groupId>org.springaicommunity</groupId>
    <artifactId>claude-code-sdk</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

简单问题代码示例如下:

复制代码
import org.springaicommunity.claude.agent.sdk.Query;

// Simplest usage - one line
String answer = Query.text("What is 2+2?");
System.out.println(answer);  // "4"

简单问题增加参数:

复制代码
import org.springaicommunity.claude.agent.sdk.Query;
import org.springaicommunity.claude.agent.sdk.QueryOptions;
import java.time.Duration;

QueryOptions options = QueryOptions.builder()
    .model("claude-sonnet-4-20250514")
    .appendSystemPrompt("Be concise")
    .timeout(Duration.ofMinutes(5))
    .build();

String response = Query.text("Explain Java", options);
System.out.println(response);

获取返回的元数据:

复制代码
import org.springaicommunity.claude.agent.sdk.Query;
import org.springaicommunity.claude.agent.sdk.types.QueryResult;

QueryResult result = Query.execute("Write a haiku about Java");

System.out.println(result.text().orElse(""));
System.out.println("Cost: $" + result.metadata().cost().calculateTotal());
System.out.println("Duration: " + result.metadata().getDuration().toMillis() + "ms");
System.out.println("Model: " + result.metadata().model());

流式问答:

复制代码
for (Message msg : Query.query("Explain recursion")) {
    if (msg instanceof AssistantMessage am) {
        am.getTextContent().ifPresent(System.out::print);
    }
}

// Or with Stream API
Query.stream("Explain recursion")
    .filter(msg -> msg instanceof AssistantMessage)
    .forEach(msg -> System.out.println(msg));

工厂适配模式:

复制代码
// Sync client with fluent builder
try (ClaudeSyncClient client = ClaudeClient.sync()
        .workingDirectory(Path.of("."))
        .model("claude-sonnet-4-20250514")
        .systemPrompt("You are helpful")
        .timeout(Duration.ofMinutes(5))
        .build()) {
    // Use client
}

// Async client
ClaudeAsyncClient client = ClaudeClient.async()
    .workingDirectory(Path.of("."))
    .permissionMode(PermissionMode.BYPASS_PERMISSIONS)
    .build();

多轮对话:

复制代码
import org.springaicommunity.claude.agent.sdk.ClaudeClient;
import org.springaicommunity.claude.agent.sdk.ClaudeSyncClient;
import org.springaicommunity.claude.agent.sdk.parsing.ParsedMessage;
import org.springaicommunity.claude.agent.sdk.types.AssistantMessage;
import java.util.Iterator;

try (ClaudeSyncClient client = ClaudeClient.sync()
        .workingDirectory(Path.of("."))
        .build()) {

    // First turn
    client.connect("My favorite color is blue. Remember this.");
    Iterator<ParsedMessage> response = client.receiveResponse();
    while (response.hasNext()) {
        ParsedMessage msg = response.next();
        if (msg.isRegularMessage() && msg.asMessage() instanceof AssistantMessage am) {
            am.getTextContent().ifPresent(System.out::println);
        }
    }

    // Second turn - Claude remembers context
    client.query("What is my favorite color?");
    response = client.receiveResponse();
    while (response.hasNext()) {
        ParsedMessage msg = response.next();
        if (msg.isRegularMessage() && msg.asMessage() instanceof AssistantMessage am) {
            am.getTextContent().ifPresent(System.out::println);  // "blue"
        }
    }
}

使用Hooks:

复制代码
import org.springaicommunity.claude.agent.sdk.hooks.HookRegistry;
import org.springaicommunity.claude.agent.sdk.hooks.HookInput;
import org.springaicommunity.claude.agent.sdk.hooks.HookOutput;

HookRegistry hookRegistry = new HookRegistry();

// Block dangerous commands
hookRegistry.registerPreToolUse("Bash", input -> {
    if (input instanceof HookInput.PreToolUseInput preToolUse) {
        String cmd = preToolUse.getArgument("command", String.class).orElse("");
        if (cmd.contains("rm -rf")) {
            return HookOutput.block("Dangerous command blocked");
        }
    }
    return HookOutput.allow();
});

// Log all tool results
hookRegistry.registerPostToolUse(input -> {
    if (input instanceof HookInput.PostToolUseInput postToolUse) {
        System.out.println("Tool completed: " + postToolUse.toolName());
    }
    return HookOutput.allow();
});

try (ClaudeSyncClient client = ClaudeClient.sync()
        .workingDirectory(Path.of("."))
        .permissionMode(PermissionMode.DEFAULT)
        .hookRegistry(hookRegistry)
        .build()) {
    // Hooks intercept tool calls
}

使用MCP:

复制代码
import org.springaicommunity.claude.agent.sdk.mcp.McpServerConfig;

// External MCP server (subprocess)
McpServerConfig npmServer = McpServerConfig.command("npx")
    .args("-y", "@anthropic/mcp-server-filesystem")
    .env("HOME", System.getProperty("user.home"))
    .build();

// In-process SDK MCP server
McpServerConfig sdkServer = McpServerConfig.sdk(myMcpServer);

try (ClaudeSyncClient client = ClaudeClient.sync()
        .workingDirectory(Path.of("."))
        .mcpServer("filesystem", npmServer)
        .mcpServer("custom", sdkServer)
        .build()) {
    // MCP tools available to Claude
}

文本响应:

复制代码
ClaudeAsyncClient client = ClaudeClient.async()
    .workingDirectory(Path.of("."))
    .permissionMode(PermissionMode.BYPASS_PERMISSIONS)
    .build();

// Simple text response
client.connect("Hello!").text()
    .doOnSuccess(System.out::println)
    .subscribe();  // Non-blocking

多轮对话:

复制代码
// Elegant multi-turn via flatMap
client.connect("My favorite color is blue.").text()
    .flatMap(r1 -> client.query("What is my favorite color?").text())
    .doOnSuccess(System.out::println)  // "blue"
    .subscribe();

流式:

复制代码
// Stream text as it arrives
client.query("Explain recursion").textStream()
    .doOnNext(System.out::print)
    .subscribe();

访问全部信息:

复制代码
// Access all message types for metadata
client.query("List files").messages()
    .doOnNext(msg -> {
        if (msg instanceof AssistantMessage am) {
            am.text().ifPresent(System.out::println);
        } else if (msg instanceof ResultMessage rm) {
            System.out.printf("Cost: $%.6f%n", rm.totalCostUsd());
        }
    })
    .subscribe();

使用SSE:

复制代码
import org.springframework.web.bind.annotation.*;
import org.springframework.http.MediaType;
import reactor.core.publisher.Flux;

@RestController
public class ChatController {
    private final ClaudeAsyncClient client;

    public ChatController() {
        this.client = ClaudeClient.async()
            .workingDirectory(Path.of("."))
            .permissionMode(PermissionMode.BYPASS_PERMISSIONS)
            .build();
    }

    @GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> chat(@RequestParam String message) {
        return client.query(message).textStream();
    }
}

4.总结

Agent Client Protocol(ACP)是一个标准化协议,旨在打破 AI 编码智能体与代码编辑器之间的集成壁垒。

通过 JSON-RPC + 子进程模型,ACP 实现了智能体与编辑器的解耦:一次实现,即可多端兼容。它支持实时流式响应、MCP 配置传递,并提供 Hooks 与 TurnSpec 等机制,提升交互体验。

ACP 的核心目标是让开发者自由选择最佳编辑器与最强智能体,彻底告别重复集成与工具锁定,推动 AI 编程生态走向开放与互联。

5.结束语

这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!

另外,博主出新书了《Hadoop与Spark大数据全景解析》、同时已出版的《深入理解Hive》、《Kafka并不难学》和《Hadoop大数据挖掘从入门到进阶实战》也可以和新书配套使用,喜欢的朋友或同学, 可以在公告栏那里点击购买链接购买博主的书进行学习,在此感谢大家的支持。关注下面公众号,根据提示,可免费获取书籍的教学视频。

相关推荐
实在智能RPA2 小时前
实在Agent 制造业落地案例:探寻工业大模型从实验室走向车间的实战路径
人工智能·ai
it_czz4 小时前
Everything Claude Code (ECC) 完整调研报告(二)
ai·everything
AI英德西牛仔4 小时前
AI复制的文字带星号
人工智能·ai·chatgpt·豆包·deepseek·ds随心转
向成科技4 小时前
当“超轻量AI”遇上“最强国产芯”
人工智能·物联网·ai·芯片·国产化·硬件·主板
远见阁4 小时前
智能体是如何“思考”的:ReAct模式
人工智能·ai·ai智能体
L-影5 小时前
为什么你的数据里藏着“隐形圈子”?聊聊AI中的聚类
人工智能·ai·数据挖掘·聚类
我叫张小白。5 小时前
Dify系列(一):平台安装部署+界面操作
docker·ai·语言模型·大模型·dify·智能体
GISer_Jing5 小时前
AI Agent操作系统架构师:Harness Engineer解析
前端·人工智能·ai·aigc
roman_日积跬步-终至千里6 小时前
【大模型语言基础(2)】文本如何变成数字 — 分词与嵌入
ai