ACP (Agent Client Protocol) (中文 (繁體))
什麼是 ACP?
Section titled “什麼是 ACP?”ACP(Agent Client Protocol,代理程式用戶端協定)是 OmniRoute 的一種 「以 CLI 作為後端」的傳輸方式。ACP 不會攔截傳送至 AI 提供者的 HTTP API 呼叫,而是以子行程方式啟動 CLI 代理程式,並透過其原生介面傳入提示詞。
為什麼要使用 ACP?
Section titled “為什麼要使用 ACP?”| 優點 | 說明 |
|---|---|
| 不需要 API 金鑰 | 使用你現有的 CLI 驗證 |
| 原生協定 | 使用各 CLI 的原生輸入/輸出格式 |
| 自動探索 | 偵測系統上已安裝的 CLI |
| 15 個內建代理程式 | 已針對熱門 CLI 工具完成預先設定 |
| 自訂代理程式 | 透過設定新增你自己的 CLI 工具 |
| 行程管理 | 處理生命週期(啟動、傳送、終止) |
支援的 CLI 代理程式
Section titled “支援的 CLI 代理程式”ACP 開箱即支援 15 個內建 CLI 代理程式:
| 代理程式 ID | 顯示名稱 | 執行檔 | 協定 |
|---|---|---|---|
codex |
OpenAI Codex CLI | codex |
stdio |
claude |
Claude Code CLI | claude |
stdio |
goose |
Goose CLI | goose |
stdio |
openclaw |
OpenClaw | openclaw |
stdio |
aider |
Aider | aider |
stdio |
opencode |
OpenCode | opencode |
stdio |
cline |
Cline | cline |
stdio |
qwen |
Qwen Code | qwen --acp |
stdio |
forge |
ForgeCode | forge |
stdio |
amazon-q |
Amazon Q Developer | q |
stdio |
interpreter |
Open Interpreter | interpreter |
stdio |
cursor-cli |
Cursor CLI | cursor |
stdio |
warp |
Warp AI | warp |
stdio |
gemini |
Gemini CLI | gemini |
stdio |
zcode |
ZCode | zcode |
stdio |
自訂代理程式
Section titled “自訂代理程式”你可以透過設定新增自己的 CLI 代理程式。自訂代理程式支援與內建代理程式相同的功能。
步驟 1:安裝 CLI 代理程式
Section titled “步驟 1:安裝 CLI 代理程式”# 範例:安裝 Claude Code CLInpm install -g @anthropic-ai/claude-code
# 驗證安裝claude --version步驟 2:ACP 自動偵測
Section titled “步驟 2:ACP 自動偵測”ACP 會自動偵測系統上已安裝的 CLI 代理程式,無須進行任何設定!
步驟 3:使用 ACP 傳輸
Section titled “步驟 3:使用 ACP 傳輸”偵測到代理程式後,即可將 ACP 用作任何受支援提供者的傳輸方式。當 CLI 可用時,OmniRoute 會自動使用 ACP。
ACP 的運作方式
Section titled “ACP 的運作方式”┌─────────────────┐│ OmniRoute ││ (HTTP Proxy) │└────────┬────────┘ │ │ spawn() ▼┌─────────────────┐│ 子行程 ││ (CLI 代理程式)││ ││ stdin ◄──────┤ 傳送提示詞│ stdout ──────►│ 接收回應│ stderr ──────►│ 接收錯誤└─────────────────┘行程生命週期
Section titled “行程生命週期”- 啟動 — ACP 為 CLI 代理程式建立子行程
- 傳送 — ACP 將提示詞寫入行程的 stdin
- 接收 — ACP 從 stdout/stderr 讀取回應
- 閒置偵測 — ACP 等待 2 秒無活動後,才會將回應視為完成
- 終止 — ACP 終止行程(先傳送 SIGTERM,5 秒後再傳送 SIGKILL)
ACP 使用 stdio(標準輸入/輸出)與 CLI 代理程式通訊。其協定如下:
- 傳送提示詞 — 寫入 stdin,並附加換行字元
- 等待回應 — 從 stdout 讀取內容,直到進入閒置狀態(2 秒無輸出)
- 逾時 — 預設為 120 秒(可設定)
API 參考
Section titled “API 參考”detectInstalledAgents()
Section titled “detectInstalledAgents()”偵測系統上所有已安裝的 CLI 代理程式。結果會快取 60 秒。
import { detectInstalledAgents } from "@/lib/acp";
const agents = detectInstalledAgents();// 傳回:CliAgentInfo[]
interface CliAgentInfo { id: string; // 例如:"codex"、"claude" name: string; // 顯示名稱 binary: string; // 要啟動的二進位檔名稱 versionCommand: string; // 版本偵測命令 version: string | null; // 偵測到的版本(若未安裝則為 null) installed: boolean; // 代理程式是否已安裝 providerAlias: string; // OmniRoute 中的提供者 ID spawnArgs: string[]; // 啟動時要傳遞的引數 protocol: "stdio" | "http"; // 通訊協定 isCustom?: boolean; // 是否為使用者定義的自訂代理程式}getAvailableAgents()
Section titled “getAvailableAgents()”僅取得已安裝且可供 ACP 使用的代理程式。
import { getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();// 傳回:CliAgentInfo[](僅包含已安裝的代理程式)getAgentById(id)
Section titled “getAgentById(id)”依 ID 取得特定代理程式。
import { getAgentById } from "@/lib/acp";
const agent = getAgentById("claude");// 傳回:CliAgentInfo | undefinedsetCustomAgents(agents)
Section titled “setCustomAgents(agents)”從設定中設置自訂代理程式定義。
import { setCustomAgents } from "@/lib/acp";
setCustomAgents([ { id: "my-custom-cli", name: "My Custom CLI", binary: "mycli", versionCommand: "mycli --version", providerAlias: "my-provider", spawnArgs: [], protocol: "stdio", },]);acpManager.spawn(agentId, binary, args, env)
Section titled “acpManager.spawn(agentId, binary, args, env)”啟動新的 CLI 代理程式處理程序。
import { acpManager } from "@/lib/acp";
const session = acpManager.spawn("claude", "claude", ["--print", "--output-format", "json"], { /* 自訂環境變數 */});// 傳回:AcpSession允許的代理程式 ID:["claude", "codex", "gemini", "qwen"]
acpManager.sendPrompt(sessionId, prompt, timeoutMs)
Section titled “acpManager.sendPrompt(sessionId, prompt, timeoutMs)”將提示傳送至 CLI 代理程式並收集回應。
import { acpManager } from "@/lib/acp";
const response = await acpManager.sendPrompt( "acp-claude-1234567890-abc123", "What is 2+2?", 120000 // 逾時時間為 2 分鐘);// 傳回:Promise<string>acpManager.kill(sessionId)
Section titled “acpManager.kill(sessionId)”終止工作階段並進行清理。
import { acpManager } from "@/lib/acp";
const killed = acpManager.kill("acp-claude-1234567890-abc123");// 傳回:booleanacpManager.getActiveSessions()
Section titled “acpManager.getActiveSessions()”取得所有作用中的工作階段。
import { acpManager } from "@/lib/acp";
const sessions = acpManager.getActiveSessions();// 傳回:AcpSession[]acpManager.killAll()
Section titled “acpManager.killAll()”終止所有工作階段。
import { acpManager } from "@/lib/acp";
acpManager.killAll();工作階段介面
Section titled “工作階段介面”interface AcpSession { id: string; // 唯一工作階段 ID agentId: string; // 代理程式 ID(例如:"claude") process: ChildProcess; // 子處理程序控制代碼 alive: boolean; // 處理程序是否仍在執行 stdoutBuffer: string; // 累積的 stdout 緩衝區 stderrBuffer: string; // 累積的 stderr 緩衝區 createdAt: Date; // 建立時間戳記}AcpManager 擴充了 EventEmitter,並會發出下列事件:
stdout
Section titled “stdout”當 CLI 代理程式寫入 stdout 時發出。
acpManager.on("stdout", ({ sessionId, data }) => { console.log(`[${sessionId}] stdout: ${data}`);});stderr
Section titled “stderr”當 CLI 代理程式寫入 stderr 時發出。
acpManager.on("stderr", ({ sessionId, data }) => { console.error(`[${sessionId}] stderr: ${data}`);});當 CLI 代理程式處理程序結束時發出。
acpManager.on("exit", ({ sessionId, code, signal }) => { console.log(`[${sessionId}] exited with code ${code}, signal ${signal}`);});當 CLI 代理程式處理程序發生錯誤時發出。
acpManager.on("error", ({ sessionId, error }) => { console.error(`[${sessionId}] error: ${error}`);});ACP 會繼承父程序的所有環境變數,也可以使用自訂環境變數加以擴充:
acpManager.spawn("claude", "claude", [], { ANTHROPIC_API_KEY: "sk-...", DEBUG: "true",});每個代理程式在登錄檔中都有預設的啟動參數。你可以覆寫這些參數:
acpManager.spawn("claude", "claude", ["--print", "--verbose"], {});預設的提示逾時時間為 120 秒(2 分鐘)。你可以覆寫此設定:
await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 分鐘代理程式偵測結果會快取 60 秒,以避免成本高昂的檔案系統掃描。若要強制重新整理:
import { refreshAgentCache } from "@/lib/acp";
refreshAgentCache();防止命令注入
Section titled “防止命令注入”ACP 會驗證版本命令,以防止命令注入攻擊:
const DISALLOWED_VERSION_COMMAND_CHARS = /[;&|<>`$\r\n]/;包含下列字元的版本命令將被拒絕:
;— 命令分隔符號&— 背景程序|— 管線<,>— 重新導向`— 命令替換$— 變數展開\r,\n— 換行符號
二進位檔名稱驗證
Section titled “二進位檔名稱驗證”ACP 會驗證版本命令中的二進位檔名稱是否符合預期的二進位檔名稱(自訂代理程式除外)。
每個 ACP 工作階段都在其各自的子程序中執行。工作階段結束或逾時時,該程序會被終止。
- 首次呼叫:約 50-200ms(為每個代理程式執行
version命令) - 快取呼叫:<1ms(從快取傳回)
- 快取 TTL:60 秒
- 啟動:約 50-100ms
- 傳送提示:約 10-50ms
- 等待回應:取決於 CLI 代理程式(通常為 1-30 秒)
- 終止:約 5 秒(SIGTERM)+ 立即執行(SIGKILL)
- 每個工作階段的記憶體:約 10-50MB(取決於 CLI 代理程式)
- CPU:極少(受 I/O 限制)
- 磁碟:無
「Unknown agent」錯誤
Section titled “「Unknown agent」錯誤”問題:acpManager.spawn() 擲回 Unknown agent: <id>
解決方案:spawn() 僅允許使用下列代理程式:
claudecodexgeminiqwen
其他代理程式必須手動啟動,或透過自訂代理程式定義啟動。
「Session not alive」錯誤
Section titled “「Session not alive」錯誤”問題:acpManager.sendPrompt() 擲回 Session ${sessionId} is not alive
解決方案:工作階段可能已結束或遭到終止。請檢查工作階段狀態:
const session = acpManager.getSession(sessionId);if (!session?.alive) { // 重新啟動工作階段 acpManager.spawn("claude", "claude", [], {});}「ACP timeout」錯誤
Section titled “「ACP timeout」錯誤”問題:acpManager.sendPrompt() 擲回 ACP timeout after 120000ms
解決方案:增加逾時時間:
await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 分鐘未偵測到 CLI
Section titled “未偵測到 CLI”問題:detectInstalledAgents() 找不到你的 CLI
解決方案:
- 檢查 PATH:確保 CLI 位於系統 PATH 中
- 檢查版本命令:手動執行
claude --version - 檢查權限:確保 CLI 可執行
- 自訂代理程式:為非標準 CLI 新增自訂代理程式定義
問題:ACP 無法執行 CLI
解決方案:
- 檢查檔案權限:
chmod +x /usr/local/bin/claude - 檢查擁有權:確保 OmniRoute 具有讀取/執行權限
- 檢查 SELinux/AppArmor:其可能會阻止程序啟動
範例 1:啟動並使用 Claude Code
Section titled “範例 1:啟動並使用 Claude Code”import { acpManager, detectInstalledAgents } from "@/lib/acp";
// 偵測已安裝的代理程式const agents = detectInstalledAgents();const claude = agents.find((a) => a.id === "claude");
if (claude?.installed) { // 啟動新的工作階段 const session = acpManager.spawn("claude", claude.binary, ["--print", "--output-format", "json"]);
// 傳送提示詞 const response = await acpManager.sendPrompt( session.id, "Explain quantum computing in 100 words" );
console.log("Claude's response:", response);
// 清理資源 acpManager.kill(session.id);}範例 2:具備備援機制的自動探索
Section titled “範例 2:具備備援機制的自動探索”import { acpManager, getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();
// 優先嘗試 Claude,並以 Codex 作為備援let agentId = "claude";if (!available.find((a) => a.id === "claude")) { if (available.find((a) => a.id === "codex")) { agentId = "codex"; } else { throw new Error("No ACP-compatible CLI agent found"); }}
const agent = available.find((a) => a.id === agentId)!;const session = acpManager.spawn(agentId, agent.binary, agent.spawnArgs);
const response = await acpManager.sendPrompt(session.id, "Hello!");
acpManager.kill(session.id);範例 3:自訂代理程式
Section titled “範例 3:自訂代理程式”import { setCustomAgents, detectInstalledAgents } from "@/lib/acp";
// 註冊自訂 CLI 代理程式setCustomAgents([ { id: "my-llm-cli", name: "My LLM CLI", binary: "myllm", versionCommand: "myllm --version", providerAlias: "my-llm-provider", spawnArgs: ["--format", "json"], protocol: "stdio", },]);
// 現在 detectInstalledAgents() 將包含 "my-llm-cli"const agents = detectInstalledAgents();- API 參考 — REST API 端點
- 提供者參考 — 全部 352 個提供者
- MCP 伺服器 — Model Context Protocol 整合
- A2A 伺服器 — 代理程式對代理程式協定
- 雲端代理程式 — 雲端型代理程式
- AionUi 專案 — ACP 自動偵測的靈感來源
- ACP 原始碼 — 實作細節
manager.ts— 程序生命週期管理registry.ts— 代理程式探索與註冊index.ts— 公開 API 匯出
HagiCode
HagiCode 是智慧代理程式開發工作台,結合結構化工作流程、多代理程式執行與 Hero Dungeon 介面,將想法化為交付成果。
以更聰明、更快速且更有趣的智慧代理程式工作流程,打造實用的軟體。

- Smart結構化流程將意圖轉化為從構想到交付的可執行步驟。
- Efficient多代理程式工作流程讓研究、實作與審查並行進行。
- FunHero Dungeon 讓長時間的程式協作更直覺、更有參與感。