Agent Client Protocol全景解析包含哪些关键要素?

摘要:1.概述 Agent Client Protocol(ACP)是一个标准化通信协议,用于规范代码编辑器和集成开发环境(IDE)与编码智能体(Coding Agents)之间的交互。该协议同时支持本地和远程使用场景。 2.内容 2.1&
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.4Clients 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大数据挖掘从入门到进阶实战》也可以和新书配套使用,喜欢的朋友或同学, 可以在公告栏那里点击购买链接购买博主的书进行学习,在此感谢大家的支持。关注下面公众号,根据提示,可免费获取书籍的教学视频。