目录

OpenClaw 使用外部 Coding Agent 的原理与调用链路

概述

OpenClaw 支持通过两种方式调用外部 coding agent(Codex、Claude Code、OpenCode、Pi 等):

方式 入口工具 执行环境 通信协议 适用场景
Bash(exec) exec tool OS 子进程(PTY/child) stdin/stdout 原始流 一次性任务、直接 CLI 调用
ACP sessions_spawn tool acpx CLI → ACP adapter JSON-RPC 2.0 over stdio 持久化会话、线程绑定、结构化交互

两条路径最终都由 OpenClaw 的内嵌 Pi Agent 触发——Pi Agent 是 OpenClaw 唯一的官方 agent runtime,外部 coding agent 作为 Pi 的"工具"被调用。


方式一:Bash(exec tool)直接调用

1.1 入口:coding-agent Skill

OpenClaw 通过 Skill 系统(skills/coding-agent/SKILL.md)教会 Pi Agent 何时以及如何调用外部 coding agent。Skill 定义了:

  • 触发条件:构建新功能、PR Review、大规模重构等场景
  • 前置条件anyBins: ["claude", "codex", "opencode", "pi"]——系统中至少有一个 CLI 可用
  • 每种 agent 的调用差异
    • Codex/Pi/OpenCode:需要 pty:true(交互式终端应用)
    • Claude Code:使用 --print --permission-mode bypassPermissions(非交互模式,不需要 PTY)

1.2 exec tool 执行链路

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Pi Agent 根据 Skill 决定调用某个 coding agent


exec tool (src/agents/bash-tools.exec.ts)
  参数: command, pty, workdir, background, timeout, elevated
  安全检查: host/security/ask 模式


runExecProcess() (src/agents/bash-tools.exec-runtime.ts)
  构造 SpawnInput,选择 PTY  child 模式


ProcessSupervisor.spawn()

├─ pty: true ────────────────────────────────────┐
  supervisor.spawn({ mode: "pty", ptyCommand })  
   createPtyAdapter() (src/process/supervisor/adapters/pty.ts)
   @lydell/node-pty spawn                       
   分配伪终端 (cols:120, rows:30)               
                                                  
├─ pty: false ───────────────────────────────────┐
  supervisor.spawn({ mode: "child", argv })      
   Node.js child_process.spawn                  
   stdio: pipe                                  
                                                  

外部 coding agent CLI 进程
  : codex exec "Build a REST API"
  : claude --print --permission-mode bypassPermissions "Fix the bug"


bash-process-registry (src/agents/bash-process-registry.ts)
  注册为 ProcessSession (runningSessions Map)
  缓冲 stdout/stderr 输出
  TTL 管理 (30min 默认)


process tool (src/agents/bash-tools.process.ts)
  后台任务管理: list/poll/log/write/submit/send-keys/kill


Pi Agent 读取输出,汇总结果返回用户

1.3 关键代码路径

文件 核心函数/类 职责
src/agents/bash-tools.exec.ts createExecTool() 定义 exec tool 的 schema、安全检查、参数解析
src/agents/bash-tools.exec-runtime.ts runExecProcess() 实际 spawn 逻辑,根据 usePty 分流
src/process/supervisor/adapters/pty.ts createPtyAdapter() PTY 适配器,使用 @lydell/node-pty
src/process/supervisor/types.ts ProcessSupervisor interface spawn/cancel/reconcile 接口
src/agents/bash-process-registry.ts runningSessions / finishedSessions 进程会话生命周期管理
src/agents/bash-tools.process.ts createProcessTool() 后台进程交互工具

1.4 Bash 方式的数据流

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
用户: "帮我用 Codex 重构 auth 模块"
  
  
OpenClaw Gateway (WebSocket)
  
  
Pi Agent (embedded, createAgentSession)
   Pi 识别到 coding-agent Skill,决定使用 exec tool
  
  
exec tool 调用:
  command: "codex exec --full-auto 'Refactor the auth module'"
  pty: true
  workdir: ~/project
  background: true
  
  
ProcessSupervisor.spawn({
  mode: "pty",
  ptyCommand: "codex exec --full-auto 'Refactor the auth module'"
})
  
  
@lydell/node-pty 分配伪终端
  └─ codex CLI 进程启动(pid: 12345
  
  
bash-process-registry 注册 ProcessSession
    sessionId: "ps_abc123"
    缓冲 stdout 输出
  
  
Pi Agent 使用 process tool 监控:
  process action:poll sessionId:"ps_abc123"     检查是否完成
  process action:log  sessionId:"ps_abc123"     获取输出日志
  
  
Codex 完成后  Pi Agent 汇总结果  返回用户

方式二:ACP(Agent Client Protocol)调用

2.1 架构总览

ACP 方式相比 Bash 方式多了两层抽象,提供了结构化的会话管理和事件流:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
OpenClaw Gateway
  
  
Pi Agent (embedded)
   sessions_spawn tool, runtime="acp"
  
  
spawnAcpDirect() ─── 策略检查、会话创建、线程绑定
  
  
AcpSessionManager.initializeSession()
  
  
AcpxRuntime (extensions/acpx/src/runtime.ts)      OpenClaw acpx 插件
   spawn child_process: acpx CLI
  
  
acpx CLI binary (npm: acpx@0.1.15)                外部 npm 
   spawn child_process: Agent ACP Adapter
   ACP 协议通信 (JSON-RPC 2.0 over stdio)
  
  
Agent ACP Adapter                                   第三方适配器
  (e.g. @zed-industries/codex-acp)
   调用底层 Codex/Claude API
  
  
实际 Coding Agent (Codex / Claude Code / etc.)

2.2 sessions_spawn tool 入口

sessions_spawnsrc/agents/tools/sessions-spawn-tool.ts)是 ACP 路径的入口工具:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const SESSIONS_SPAWN_RUNTIMES = ["subagent", "acp"] as const;

// 当 runtime="acp" 时:
if (runtime === "acp") {
  const result = await spawnAcpDirect({ task, agentId, cwd, mode, ... }, ctx);
}
// 当 runtime="subagent" 时:
else {
  const result = await spawnSubagentDirect({ ... }, ctx);
}

参数说明:

  • runtime: "subagent"(默认,内部 Pi 子会话)或 "acp"(外部 coding agent)
  • mode: "run"(一次性)或 "session"(持久化/线程绑定)
  • thread: 是否绑定到当前消息线程
  • streamTo: "parent" 可将 ACP 事件流式传回父会话
  • agentId: 目标 agent 标识(如 "codex""claude"

2.3 spawnAcpDirect() 策略与初始化

spawnAcpDirect()src/agents/acp-spawn.ts)负责策略检查和会话初始化:

  1. 策略检查

    • ACP 是否被策略启用(acp.enabled
    • 沙箱限制(ACP 必须在宿主机运行,沙箱会话不能 spawn ACP)
    • Agent 策略校验(resolveAcpAgentPolicyError
  2. 创建会话 keyagent:<agentId>:acp:<uuid>

  3. Gateway 注册:通过 callGateway("sessions.patch", { key: sessionKey }) 在 gateway 注册新会话

  4. 初始化 ACP Runtime

    1
    2
    3
    4
    5
    
    const acpManager = getAcpSessionManager();
    await acpManager.initializeSession({
      cfg, sessionKey, agent: targetAgentId,
      mode: runtimeMode, cwd, backendId
    });
    
  5. 线程绑定(可选):通过 bindingService.bind() 将 ACP 会话绑定到 Discord/Telegram 线程

  6. 分发任务:通过 callGateway("agent", { key: sessionKey, text: task }) 触发 agent 事件循环

2.4 AcpxRuntime:OpenClaw 与 acpx 的桥梁

AcpxRuntimeextensions/acpx/src/runtime.ts)实现了 AcpRuntime 接口,是 OpenClaw 插件层与 acpx CLI 之间的桥梁。

AcpRuntime 接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
interface AcpRuntime {
  ensureSession(input): Promise<AcpRuntimeHandle>;    // 创建/复用会话
  runTurn(input): AsyncIterable<AcpRuntimeEvent>;     // 执行一轮对话
  cancel(input): Promise<void>;                        // 取消当前对话
  close(input): Promise<void>;                         // 关闭会话
  getCapabilities(): AcpRuntimeCapabilities;           // 查询能力
  getStatus(input): Promise<AcpRuntimeStatus>;         // 查询状态
  setMode(input): Promise<void>;                       // 设置模式
  setConfigOption(input): Promise<void>;               // 设置配置
  doctor(): Promise<AcpRuntimeDoctorReport>;           // 健康检查
}

全局 Backend 注册

AcpxRuntime 通过 registerAcpRuntimeBackend() 注册到全局注册表(src/acp/runtime/registry.ts),使其对整个系统可用:

1
2
3
4
5
6
// extensions/acpx/src/service.ts
registerAcpRuntimeBackend({
  id: "acpx",
  runtime,
  healthy: () => runtime?.isHealthy() ?? false,
});

核心方法实现

ensureSession — 创建或复用 agent 会话:

1
2
3
acpx --format json --json-strict --cwd /path <agent> sessions ensure --name <key>
  ↓ 如果失败 fallback ↓
acpx --format json --json-strict --cwd /path <agent> sessions new --name <key>

runTurn — 执行一轮 prompt:

1
2
3
4
acpx --format json --json-strict --cwd /path \
  --approve-reads --non-interactive-permissions fail \
  --timeout 300 --ttl 0.1 \
  <agent> prompt --session <name> --file -
  • 通过 stdin 写入 prompt 文本
  • 通过 stdout 逐行读取 JSON 事件(parsePromptEventLine
  • 返回 AsyncIterable<AcpRuntimeEvent>

cancel / close

1
2
acpx --format json --json-strict --cwd /path <agent> cancel --session <name>
acpx --format json --json-strict --cwd /path <agent> sessions close <name>

2.5 acpx CLI:统一的 Agent 协调器

acpx(npm 包 acpx@0.1.15,源码仓库 github.com/openclaw/acpx)是一个独立的命令行工具,职责是通过 ACP 协议与各种 coding agent 通信。

Agent 注册表

acpx 内部维护了一个 agent 名称到启动命令的映射:

1
2
3
4
5
6
7
AGENT_REGISTRY = {
  codex:    "npx @zed-industries/codex-acp",       // Codex 的 ACP 适配器
  claude:   "npx -y @zed-industries/claude-agent-acp",  // Claude Code 的 ACP 适配器
  gemini:   "gemini",                               // Gemini 原生支持
  opencode: "npx -y opencode-ai acp",               // OpenCode 的 ACP 适配器
  pi:       "npx pi-acp"                            // Pi 的 ACP 适配器
};

关键设计:acpx 不直接调用 codexclaude CLI,而是调用各 agent 的 ACP 协议适配器*-acp 包)。这些适配器由 Zed Industries 等第三方维护,负责将 ACP 协议翻译为各 agent 的内部 API。

acpx 启动 Agent 的过程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 1. 解析命令
const { command, args } = splitCommandLine("npx @zed-industries/codex-acp");

// 2. Spawn 子进程
const child = spawn(command, args, {
  cwd: options.cwd,
  env: buildAgentEnvironment(authCredentials),  // 注入认证环境变量
  stdio: ["pipe", "pipe", "pipe"]               // stdin/stdout 用于 ACP 通信
});

// 3. 建立 ACP 连接 (NDJSON 双向流)
const input  = Writable.toWeb(child.stdin);     // acpx → agent
const output = Readable.toWeb(child.stdout);    // agent → acpx
const stream = ndJsonStream(input, output);     // NDJSON 编解码
const connection = new ClientSideConnection(handlers, stream);

// 4. 协议初始化握手
const initResult = await connection.initialize({
  protocolVersion: PROTOCOL_VERSION,
  clientCapabilities: {
    fs: { readTextFile: true, writeTextFile: true },
    terminal: true
  },
  clientInfo: { name: "acpx", version: "0.1.0" }
});

// 5. 认证(如果 agent 要求)
await authenticateIfRequired(connection, initResult.authMethods);

ACP 协议通信方向

方向 方法 说明
acpx → agent initialize 协议握手,声明能力
acpx → agent prompt 发送用户任务/指令
acpx → agent cancel 取消当前任务
acpx → agent loadSession 恢复已有会话
acpx → agent setSessionMode 切换 agent 模式
agent → acpx session/update 流式输出(文本、思考、工具调用)
agent → acpx requestPermission 请求操作权限
agent → acpx readTextFile 读取文件
agent → acpx writeTextFile 写入文件
agent → acpx createTerminal 创建终端执行命令
agent → acpx terminalOutput 终端输出数据
agent → acpx killTerminal 终止终端

acpx 在此扮演 ACP 协议中 Client(编辑器) 的角色,为 agent 提供文件系统访问和终端能力。

acpx 的权限控制

acpx 内部实现了 FileSystemHandlers 类,根据 permissionMode 控制 agent 的文件操作:

模式 读文件 写文件
approve-all 允许 允许
approve-reads 允许 需确认(非交互模式拒绝/失败)
deny-all 拒绝 拒绝

OpenClaw 默认使用 approve-reads,即 agent 可以自由读文件,但写操作受限。

2.6 ACP 事件流解析

AcpxRuntime.runTurn() 逐行读取 acpx stdout 的 JSON 事件,通过 parsePromptEventLine() 解析为统一的 AcpRuntimeEvent

acpx 输出的事件 type 解析后的 AcpRuntimeEvent type 说明
text text_delta (stream: “output”) agent 输出文本片段
thought text_delta (stream: “thought”) agent 思考过程
tool_call / tool_call_update tool_call 工具调用状态
agent_message_chunk text_delta (stream: “output”) agent 消息文本块
agent_thought_chunk text_delta (stream: “thought”) agent 思考文本块
usage_update status token 使用量
available_commands_update status 可用命令更新
current_mode_update status 模式切换
done done 任务完成
error error 错误

同时支持 JSON-RPC 2.0 session/update 通知格式——这是 ACP 协议的原生格式。

2.7 ACP 方式的完整数据流

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
用户: "用 Codex 给我在这个 thread 里搞个 snake game"
  
  
OpenClaw Gateway (WebSocket)  Discord/Telegram thread
  
  
Pi Agent (embedded, createAgentSession)
   识别到应使用 ACP 方式(thread-bound 场景)
   调用 sessions_spawn tool, runtime="acp"
  
  
createSessionsSpawnTool.execute()
   runtime="acp"  spawnAcpDirect()
  
  
spawnAcpDirect() (src/agents/acp-spawn.ts)
   1. 策略检查: ACP 启用?沙箱限制?Agent 策略?
   2. 生成 sessionKey: "agent:codex:acp:<uuid>"
   3. Gateway 注册: sessions.patch
   4. ACP Runtime 初始化:
      acpManager.initializeSession({ agent: "codex", ... })
   5. 线程绑定: bindingService.bind()
   6. 分发任务: callGateway("agent", { key, text: task })
  
  
AcpxRuntime.ensureSession() (extensions/acpx/src/runtime.ts)
   spawn: acpx --format json codex sessions ensure --name <key>
   返回: AcpRuntimeHandle { sessionKey, runtimeSessionName, ... }
  
  
AcpxRuntime.runTurn(prompt)  执行 prompt
   spawn: acpx --format json --approve-reads --cwd /path \
          codex prompt --session <name> --file -
   stdin.end(promptText)      写入用户任务
   stdout readline            逐行读取 JSON events
  
   ─── acpx CLI 内部 ───
  
   resolveAgentCommand("codex")
      "npx @zed-industries/codex-acp"
  
   splitCommandLine  spawn("npx", ["@zed-industries/codex-acp"])
     stdio: ["pipe", "pipe", "pipe"]
  
   ClientSideConnection over ndJsonStream(stdin, stdout)
      connection.initialize()      ACP 握手
      connection.prompt({          发送 prompt
         sessionId, prompt: [{ type: "text", text }]
       })
  
   ─── @zed-industries/codex-acp 内部 ───
  
   调用 Codex API / runtime
   执行编码任务(读写文件、运行命令等)
   通过 ACP 协议发回 session/update 通知
  
   ─── 回到 acpx ───
  
   agent 请求 readTextFile  acpx FileSystemHandlers 处理
   agent 请求 writeTextFile  acpx 检查权限后执行
   agent 请求 createTerminal  acpx 创建子终端
   agent 发送 session/update  acpx 转为 JSON 事件输出到 stdout
  
   ─── 回到 OpenClaw ───
  
  AcpxRuntime.runTurn() 逐行解析:
   parsePromptEventLine(line)  AcpRuntimeEvent
     { type: "text_delta", text: "...", stream: "output" }
     { type: "tool_call", text: "edit file", status: "running" }
     { type: "done" }
  
  
  ACP 事件  流式传回 Pi Agent  Gateway  用户消息 thread

两种方式的对比

维度 Bash (exec) ACP (sessions_spawn)
调用层级 Pi → exec tool → ProcessSupervisor → CLI 进程 Pi → sessions_spawn → acpManager → AcpxRuntime → acpx CLI → ACP adapter → agent
通信协议 原始 stdin/stdout 文本流 JSON-RPC 2.0 (ACP 协议)
会话管理 bash-process-registry(内存,无持久化) acpx session store(支持持久化和恢复)
输出解析 原始文本,Pi 自行理解 结构化 JSON 事件(text_delta, tool_call, done, error)
权限控制 agent CLI 自身的权限机制 acpx FileSystemHandlers(approve-all/approve-reads/deny-all)
线程绑定 不支持 支持绑定到 Discord/Telegram 线程
流式回传 通过 process tool 手动轮询 支持 streamTo=“parent” 自动流式回传
PTY 支持 原生支持 PTY(交互式 CLI) 不使用 PTY(child_process + pipe)
多 agent 协同 手动管理多个后台进程 通过 subagent-registry 自动管理生命周期
适用场景 快速一次性任务、需要 PTY 的交互式 CLI 持久会话、线程绑定、需要结构化事件流的场景

核心组件关系图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
                          ┌──────────────────────────────┐
                               OpenClaw Gateway         
                               (WebSocket API)          
                          └──────────┬───────────────────┘
                                     
                          ┌──────────▼───────────────────┐
                              Pi Agent (embedded)        
                            createAgentSession()         
                            @mariozechner/pi-coding-agent
                          └──┬───────────────────────┬───┘
                                                    
                    ┌────────▼─────────┐   ┌────────▼──────────┐
                       exec tool          sessions_spawn     
                     (Bash 方式)           tool (ACP 方式)    
                    └────────┬─────────┘   └────────┬──────────┘
                                                    
                    ┌────────▼─────────┐   ┌────────▼──────────┐
                     ProcessSupervisor     spawnAcpDirect()  
                      ├─ PTY adapter       ├─ 策略检查       
                      └─ child adapter     ├─ 会话注册       
                    └────────┬─────────┘     └─ 线程绑定       
                                          └────────┬──────────┘
                                                    
                    ┌────────▼─────────┐   ┌────────▼──────────┐
                     bash-process-        AcpxRuntime        
                     registry             (extensions/acpx)  
                      进程生命周期管理       ensureSession()   
                    └────────┬─────────┘     runTurn()         
                                          └────────┬──────────┘
                                                    
                    ┌────────▼─────────┐   ┌────────▼──────────┐
                     外部 CLI 进程         acpx CLI binary   
                     codex exec ...       (npm: acpx@0.1.15)
                     claude --print ..   └────────┬──────────┘
                     opencode run ...              
                    └──────────────────┘   ┌────────▼──────────┐
                                            ACP adapter       
                                            @zed-industries/  
                                              codex-acp       
                                              claude-agent-acp
                                           └────────┬──────────┘
                                                     
                                           ┌────────▼──────────┐
                                            Agent runtime     
                                            (Codex/Claude API)
                                           └───────────────────┘

关键源码文件索引

Bash 路径

文件 说明
skills/coding-agent/SKILL.md Coding agent skill 定义(触发条件、调用方式)
src/agents/bash-tools.exec.ts exec tool 定义(参数、安全检查)
src/agents/bash-tools.exec-runtime.ts exec 执行逻辑(spawn 分流)
src/agents/bash-tools.exec-types.ts exec 类型定义
src/agents/bash-tools.process.ts process tool(后台进程管理)
src/agents/bash-process-registry.ts 进程会话注册表
src/process/supervisor/adapters/pty.ts PTY 适配器
src/process/supervisor/types.ts ProcessSupervisor 接口

ACP 路径

文件 说明
src/agents/tools/sessions-spawn-tool.ts sessions_spawn tool 定义(runtime 分流)
src/agents/acp-spawn.ts ACP 会话 spawn 逻辑(策略、初始化、线程绑定)
src/acp/runtime/types.ts AcpRuntime 接口定义
src/acp/runtime/registry.ts ACP runtime 全局注册表
extensions/acpx/src/runtime.ts AcpxRuntime 实现(OpenClaw → acpx 桥梁)
extensions/acpx/src/config.ts acpx 插件配置(版本、权限模式)
extensions/acpx/src/service.ts acpx 插件服务(注册、启动、健康检查)
extensions/acpx/src/ensure.ts acpx binary 版本检查与自动安装
extensions/acpx/src/runtime-internals/process.ts 子进程 spawn 工具函数
extensions/acpx/src/runtime-internals/events.ts ACP 事件解析(JSON → AcpRuntimeEvent)
extensions/acpx/src/runtime-internals/jsonrpc.ts JSON-RPC 2.0 消息识别
extensions/acpx/src/runtime-internals/shared.ts 共享工具函数(权限参数、session key 解析)

共享基础设施

文件 说明
src/auto-reply/reply/agent-runner.ts 顶层 reply agent 编排器
src/auto-reply/reply/agent-runner-execution.ts agent turn 执行与 fallback 逻辑
src/agents/subagent-registry.ts 子 agent 生命周期跟踪
src/agents/subagent-announce.ts 子 agent 结果通知回父会话

设计亮点

  1. 协议标准化:ACP 路径通过 Agent Client Protocol 实现了与任意 coding agent 的标准化交互,新增 agent 只需提供 ACP 适配器
  2. 双层 spawn 隔离:OpenClaw spawn acpx → acpx spawn agent-acp,两层 child_process 都使用 stdio pipe,实现进程隔离
  3. 权限集中管控:acpx 作为"虚拟编辑器"统一管控文件读写权限,agent 自身不直接操作文件系统
  4. 灵活降级:Bash 路径作为简单直接的备选,当 ACP 不可用或不适用时仍能调用 coding agent
  5. 自动安装:acpx 插件支持自动检测版本并安装匹配的 acpx binary(ensureAcpx()
  6. 会话持久化:ACP 方式支持 loadSession 恢复之前的会话上下文,适合长期交互场景