概述
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 的"工具"被调用。
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
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.)
|
sessions_spawn(src/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)负责策略检查和会话初始化:
-
策略检查:
- ACP 是否被策略启用(
acp.enabled)
- 沙箱限制(ACP 必须在宿主机运行,沙箱会话不能 spawn ACP)
- Agent 策略校验(
resolveAcpAgentPolicyError)
-
创建会话 key:agent:<agentId>:acp:<uuid>
-
Gateway 注册:通过 callGateway("sessions.patch", { key: sessionKey }) 在 gateway 注册新会话
-
初始化 ACP Runtime:
1
2
3
4
5
|
const acpManager = getAcpSessionManager();
await acpManager.initializeSession({
cfg, sessionKey, agent: targetAgentId,
mode: runtimeMode, cwd, backendId
});
|
-
线程绑定(可选):通过 bindingService.bind() 将 ACP 会话绑定到 Discord/Telegram 线程
-
分发任务:通过 callGateway("agent", { key: sessionKey, text: task }) 触发 agent 事件循环
2.4 AcpxRuntime:OpenClaw 与 acpx 的桥梁
AcpxRuntime(extensions/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 不直接调用 codex 或 claude 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 结果通知回父会话 |
设计亮点
- 协议标准化:ACP 路径通过 Agent Client Protocol 实现了与任意 coding agent 的标准化交互,新增 agent 只需提供 ACP 适配器
- 双层 spawn 隔离:OpenClaw spawn acpx → acpx spawn agent-acp,两层 child_process 都使用 stdio pipe,实现进程隔离
- 权限集中管控:acpx 作为"虚拟编辑器"统一管控文件读写权限,agent 自身不直接操作文件系统
- 灵活降级:Bash 路径作为简单直接的备选,当 ACP 不可用或不适用时仍能调用 coding agent
- 自动安装:acpx 插件支持自动检测版本并安装匹配的 acpx binary(
ensureAcpx())
- 会话持久化:ACP 方式支持
loadSession 恢复之前的会话上下文,适合长期交互场景