chore(i18n): refresh zh-CN translations
This commit is contained in:
parent
3de4b0f305
commit
10231fe186
@ -1,31 +1,31 @@
|
||||
---
|
||||
read_when:
|
||||
- 运行或调试 Gateway 网关进程
|
||||
summary: Gateway 网关服务、生命周期与运维运行手册
|
||||
title: Gateway 网关运行手册
|
||||
summary: Gateway 网关服务、生命周期与运维的操作手册
|
||||
title: Gateway 网关操作手册
|
||||
x-i18n:
|
||||
generated_at: "2026-04-24T20:10:21Z"
|
||||
generated_at: "2026-04-26T00:39:25Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: a1d82474bc6485cc14a0be74154e08ba54455031cdae37916de5bc615d3e01a4
|
||||
source_hash: 696ad47a7e2a85a27a6dbe0a220dc9d5b1320542854f162ae58ea4ede0195efa
|
||||
source_path: gateway/index.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
将此页面用于 Gateway 网关服务的第 1 天启动和第 2 天运维操作。
|
||||
将此页面用于 Gateway 网关服务的第 1 天启动和第 2 天运维。
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="深度故障排除" icon="siren" href="/zh-CN/gateway/troubleshooting">
|
||||
以症状为起点的诊断,提供精确的命令阶梯和日志特征。
|
||||
按症状优先的诊断指南,提供精确的命令排查路径和日志特征。
|
||||
</Card>
|
||||
<Card title="配置" icon="sliders" href="/zh-CN/gateway/configuration">
|
||||
面向任务的设置指南 + 完整配置参考。
|
||||
</Card>
|
||||
<Card title="密钥管理" icon="key-round" href="/zh-CN/gateway/secrets">
|
||||
SecretRef 契约、运行时快照行为,以及迁移 / 重载操作。
|
||||
`SecretRef` 契约、运行时快照行为,以及迁移/重载操作。
|
||||
</Card>
|
||||
<Card title="密钥计划契约" icon="shield-check" href="/zh-CN/gateway/secrets-plan-contract">
|
||||
精确的 `secrets apply` 目标 / 路径规则,以及仅引用 auth-profile 行为。
|
||||
精确的 `secrets apply` 目标/路径规则,以及仅引用的 auth-profile 行为。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@ -36,7 +36,7 @@ x-i18n:
|
||||
|
||||
```bash
|
||||
openclaw gateway --port 18789
|
||||
# 调试 / 跟踪会镜像到标准输入输出
|
||||
# 调试/跟踪镜像输出到 stdio
|
||||
openclaw gateway --port 18789 --verbose
|
||||
# 强制终止所选端口上的监听器,然后启动
|
||||
openclaw gateway --force
|
||||
@ -52,7 +52,7 @@ openclaw status
|
||||
openclaw logs --follow
|
||||
```
|
||||
|
||||
健康基线:`Runtime: running`、`Connectivity probe: ok`,以及与你预期一致的 `Capability: ...`。当你需要读取作用域的 RPC 证明,而不只是可达性时,请使用 `openclaw gateway status --require-rpc`。
|
||||
健康基线:`Runtime: running`、`Connectivity probe: ok`,以及与你预期一致的 `Capability: ...`。当你需要读取范围的 RPC 证明,而不只是可达性时,请使用 `openclaw gateway status --require-rpc`。
|
||||
|
||||
</Step>
|
||||
|
||||
@ -62,35 +62,35 @@ openclaw logs --follow
|
||||
openclaw channels status --probe
|
||||
```
|
||||
|
||||
在 Gateway 网关可达的情况下,这会针对每个账户运行实时渠道探测和可选审计。
|
||||
如果 Gateway 网关不可达,CLI 会回退为仅配置的渠道摘要,
|
||||
当 Gateway 网关可达时,这会运行按账户划分的实时渠道探测和可选审计。
|
||||
如果 Gateway 网关不可达,CLI 会回退为仅基于配置的渠道摘要,
|
||||
而不是实时探测输出。
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
Gateway 网关配置重载会监视当前生效的配置文件路径(从 profile / state 默认值解析,或在设置时使用 `OPENCLAW_CONFIG_PATH`)。
|
||||
默认模式是 `gateway.reload.mode="hybrid"`。
|
||||
首次成功加载后,运行中的进程会提供当前生效的内存中配置快照;成功重载会以原子方式替换该快照。
|
||||
Gateway 网关配置重载会监视当前活动配置文件路径(从 profile/state 默认值解析得出,或在设置时使用 `OPENCLAW_CONFIG_PATH`)。
|
||||
默认模式为 `gateway.reload.mode="hybrid"`。
|
||||
在首次成功加载后,运行中的进程会提供当前活动的内存配置快照;成功重载后会以原子方式替换该快照。
|
||||
</Note>
|
||||
|
||||
## 运行时模型
|
||||
|
||||
- 一个始终在线的进程,用于路由、控制平面和渠道连接。
|
||||
- 一个单一复用端口,用于:
|
||||
- WebSocket 控制 / RPC
|
||||
- HTTP API,兼容 OpenAI(`/v1/models`、`/v1/embeddings`、`/v1/chat/completions`、`/v1/responses`、`/tools/invoke`)
|
||||
- 控制 UI 和钩子
|
||||
- 一个始终运行的进程,用于路由、控制平面和渠道连接。
|
||||
- 单个复用端口,用于:
|
||||
- WebSocket 控制/RPC
|
||||
- HTTP API、兼容 OpenAI(`/v1/models`、`/v1/embeddings`、`/v1/chat/completions`、`/v1/responses`、`/tools/invoke`)
|
||||
- 控制 UI 和 hooks
|
||||
- 默认绑定模式:`loopback`。
|
||||
- 默认要求身份验证。共享密钥设置使用
|
||||
`gateway.auth.token` / `gateway.auth.password`(或
|
||||
`OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`),非 loopback
|
||||
`OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`),而非 loopback
|
||||
的反向代理设置可使用 `gateway.auth.mode: "trusted-proxy"`。
|
||||
|
||||
## 兼容 OpenAI 的端点
|
||||
|
||||
OpenClaw 当前最具杠杆效应的兼容性接口是:
|
||||
OpenClaw 目前最具杠杆效应的兼容性接口是:
|
||||
|
||||
- `GET /v1/models`
|
||||
- `GET /v1/models/{id}`
|
||||
@ -98,41 +98,47 @@ OpenClaw 当前最具杠杆效应的兼容性接口是:
|
||||
- `POST /v1/chat/completions`
|
||||
- `POST /v1/responses`
|
||||
|
||||
这组接口的重要性在于:
|
||||
为什么这组接口很重要:
|
||||
|
||||
- 大多数 Open WebUI、LobeChat 和 LibreChat 集成会首先探测 `/v1/models`。
|
||||
- 许多 RAG 和记忆流水线期望使用 `/v1/embeddings`。
|
||||
- 原生智能体客户端越来越倾向于使用 `/v1/responses`。
|
||||
- 大多数 Open WebUI、LobeChat 和 LibreChat 集成会先探测 `/v1/models`。
|
||||
- 许多 RAG 和记忆流水线依赖 `/v1/embeddings`。
|
||||
- 原生面向智能体的客户端正越来越倾向于使用 `/v1/responses`。
|
||||
|
||||
规划说明:
|
||||
|
||||
- `/v1/models` 以智能体优先:它返回 `openclaw`、`openclaw/default` 和 `openclaw/<agentId>`。
|
||||
- `/v1/models` 以智能体优先:它会返回 `openclaw`、`openclaw/default` 和 `openclaw/<agentId>`。
|
||||
- `openclaw/default` 是稳定别名,始终映射到已配置的默认智能体。
|
||||
- 当你想覆盖后端提供商 / 模型时,请使用 `x-openclaw-model`;否则,所选智能体的常规模型和嵌入设置会继续生效。
|
||||
- 当你想要后端 provider/模型覆盖时,请使用 `x-openclaw-model`;否则,所选智能体的常规模型和嵌入设置仍保持控制。
|
||||
|
||||
以上所有接口都运行在主 Gateway 网关端口上,并使用与 Gateway 网关其余 HTTP API 相同的受信任操作员身份验证边界。
|
||||
所有这些都运行在 Gateway 网关主端口上,并使用与 Gateway 网关其余 HTTP API 相同的受信任操作员身份验证边界。
|
||||
|
||||
### 端口和绑定优先级
|
||||
### 端口与绑定优先级
|
||||
|
||||
| 设置 | 解析顺序 |
|
||||
| ------------ | ------------------------------------------------------------- |
|
||||
| Gateway 网关端口 | `--port` → `OPENCLAW_GATEWAY_PORT` → `gateway.port` → `18789` |
|
||||
| 绑定模式 | CLI / override → `gateway.bind` → `loopback` |
|
||||
| 绑定模式 | CLI/override → `gateway.bind` → `loopback` |
|
||||
|
||||
当 Gateway 网关启动为非 loopback 绑定模式预置本地
|
||||
控制 UI 来源时,会使用相同的实际端口和绑定。例如,`--bind lan --port 3000`
|
||||
会在运行时校验开始之前预置 `http://localhost:3000` 和 `http://127.0.0.1:3000`。
|
||||
任何远程浏览器来源(例如 HTTPS 代理 URL)都需要显式添加到
|
||||
`gateway.controlUi.allowedOrigins` 中。
|
||||
|
||||
### 热重载模式
|
||||
|
||||
| `gateway.reload.mode` | 行为 |
|
||||
| --------------------- | ------------------------------------------ |
|
||||
| `off` | 不进行配置重载 |
|
||||
| `hot` | 仅应用可安全热更新的变更 |
|
||||
| `off` | 不重载配置 |
|
||||
| `hot` | 仅应用热安全变更 |
|
||||
| `restart` | 遇到需要重启的变更时重启 |
|
||||
| `hybrid`(默认) | 安全时热应用,需要时重启 |
|
||||
| `hybrid`(默认) | 安全时热应用,需要时重启 |
|
||||
|
||||
## 操作员命令集
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
openclaw gateway status --deep # 增加系统级服务扫描
|
||||
openclaw gateway status --deep # 添加系统级服务扫描
|
||||
openclaw gateway status --json
|
||||
openclaw gateway install
|
||||
openclaw gateway restart
|
||||
@ -142,15 +148,15 @@ openclaw logs --follow
|
||||
openclaw doctor
|
||||
```
|
||||
|
||||
`gateway status --deep` 用于额外的服务发现(LaunchDaemons / systemd system
|
||||
units / schtasks),而不是更深层的 RPC 健康探测。
|
||||
`gateway status --deep` 用于额外的服务发现(LaunchDaemons/systemd system
|
||||
units/schtasks),而不是更深入的 RPC 健康探测。
|
||||
|
||||
## 多个 Gateway 网关(同一主机)
|
||||
|
||||
大多数安装应在每台机器上运行一个 Gateway 网关。单个 Gateway 网关可以托管多个
|
||||
智能体和渠道。
|
||||
|
||||
只有在你明确需要隔离或救援机器人时,才需要多个 Gateway 网关。
|
||||
只有在你有意需要隔离环境或救援机器人时,才需要多个 Gateway 网关。
|
||||
|
||||
有用的检查:
|
||||
|
||||
@ -161,11 +167,11 @@ openclaw gateway probe
|
||||
|
||||
预期结果:
|
||||
|
||||
- `gateway status --deep` 可能报告 `Other gateway-like services detected (best effort)`
|
||||
并在仍存在过时的 launchd / systemd / schtasks 安装时打印清理提示。
|
||||
- `gateway probe` 在有多个目标
|
||||
响应时可能会警告 `multiple reachable gateways`。
|
||||
- 如果这是有意设置,请为每个 Gateway 网关隔离端口、配置 / 状态以及工作区根目录。
|
||||
- `gateway status --deep` 可能会报告 `Other gateway-like services detected (best effort)`
|
||||
并在仍存在陈旧的 launchd/systemd/schtasks 安装时输出清理提示。
|
||||
- `gateway probe` 可能会在多个目标
|
||||
都响应时警告 `multiple reachable gateways`。
|
||||
- 如果这是有意为之,请为每个 Gateway 网关隔离端口、配置/状态以及工作区根目录。
|
||||
|
||||
每个实例的检查清单:
|
||||
|
||||
@ -183,20 +189,22 @@ OPENCLAW_CONFIG_PATH=~/.openclaw/b.json OPENCLAW_STATE_DIR=~/.openclaw-b opencla
|
||||
|
||||
详细设置:[/gateway/multiple-gateways](/zh-CN/gateway/multiple-gateways)。
|
||||
|
||||
## VoiceClaw 实时 brain 端点
|
||||
## VoiceClaw 实时大脑端点
|
||||
|
||||
OpenClaw 在
|
||||
`/voiceclaw/realtime` 提供兼容 VoiceClaw 的实时 WebSocket 端点。当 VoiceClaw 桌面客户端应直接与实时 OpenClaw brain 通信,而不是通过单独的中继进程时,请使用它。
|
||||
`/voiceclaw/realtime` 提供一个兼容 VoiceClaw 的实时 WebSocket 端点。当 VoiceClaw 桌面客户端应直接与实时 OpenClaw 大脑通信,而不是通过单独的中继进程时,请使用它。
|
||||
|
||||
该端点使用 Gemini Live 处理实时音频,并通过将 OpenClaw 工具直接暴露给 Gemini Live 来调用 OpenClaw 作为 brain。工具调用会立即返回
|
||||
`working` 结果,以保持语音轮次响应迅速;随后 OpenClaw 会异步执行实际工具,并将结果注入回实时会话中。在 Gateway 网关进程环境中设置 `GEMINI_API_KEY`。如果
|
||||
Gateway 网关身份验证已启用,桌面客户端会在其第一个 `session.config` 消息中发送 gateway token 或 password。
|
||||
该端点使用 Gemini Live 处理实时音频,并通过将 OpenClaw 工具直接暴露给 Gemini Live 来调用 OpenClaw 作为大脑。工具调用会立即返回
|
||||
`working` 结果,以保持语音轮次响应迅速;随后 OpenClaw
|
||||
会异步执行实际工具,并将结果重新注入实时会话。在
|
||||
Gateway 网关进程环境中设置 `GEMINI_API_KEY`。如果
|
||||
Gateway 网关身份验证已启用,桌面客户端会在其首个 `session.config` 消息中发送 Gateway 网关 token 或 password。
|
||||
|
||||
实时 brain 访问会运行经所有者授权的 OpenClaw 智能体命令。请将
|
||||
`gateway.auth.mode: "none"` 限制在仅 loopback 的测试实例中。非本地实时 brain 连接需要 Gateway 网关身份验证。
|
||||
实时大脑访问会运行经所有者授权的 OpenClaw 智能体命令。请将
|
||||
`gateway.auth.mode: "none"` 限制在仅 loopback 的测试实例中。非本地的实时大脑连接需要 Gateway 网关身份验证。
|
||||
|
||||
对于隔离的测试 Gateway 网关,请运行一个使用独立端口、配置
|
||||
和状态的单独实例:
|
||||
对于隔离的测试 Gateway 网关,请使用独立的端口、配置
|
||||
和状态运行单独实例:
|
||||
|
||||
```bash
|
||||
OPENCLAW_CONFIG_PATH=/path/to/openclaw-realtime/openclaw.json \
|
||||
@ -214,7 +222,7 @@ ws://127.0.0.1:19789/voiceclaw/realtime
|
||||
|
||||
## 远程访问
|
||||
|
||||
首选:Tailscale / VPN。
|
||||
首选:Tailscale/VPN。
|
||||
备选:SSH 隧道。
|
||||
|
||||
```bash
|
||||
@ -224,16 +232,16 @@ ssh -N -L 18789:127.0.0.1:18789 user@host
|
||||
然后在本地将客户端连接到 `ws://127.0.0.1:18789`。
|
||||
|
||||
<Warning>
|
||||
SSH 隧道不会绕过 Gateway 网关身份验证。对于共享密钥身份验证,客户端即使通过隧道
|
||||
仍必须发送 `token` / `password`。对于带身份标识的模式,
|
||||
SSH 隧道不会绕过 Gateway 网关身份验证。对于共享密钥身份验证,客户端即使
|
||||
通过隧道连接,仍然必须发送 `token`/`password`。对于携带身份的模式,
|
||||
请求仍然必须满足该身份验证路径。
|
||||
</Warning>
|
||||
|
||||
参见:[Remote Gateway](/zh-CN/gateway/remote)、[Authentication](/zh-CN/gateway/authentication)、[Tailscale](/zh-CN/gateway/tailscale)。
|
||||
参见:[远程 Gateway 网关](/zh-CN/gateway/remote)、[身份验证](/zh-CN/gateway/authentication)、[Tailscale](/zh-CN/gateway/tailscale)。
|
||||
|
||||
## 监管和服务生命周期
|
||||
## 监督与服务生命周期
|
||||
|
||||
对于接近生产环境的可靠性,请使用受监管的运行方式。
|
||||
为了获得接近生产环境的可靠性,请使用受监督运行。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="macOS(launchd)">
|
||||
@ -249,7 +257,7 @@ LaunchAgent 标签为 `ai.openclaw.gateway`(默认)或 `ai.openclaw.<profile
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Linux(systemd user)">
|
||||
<Tab title="Linux(systemd 用户服务)">
|
||||
|
||||
```bash
|
||||
openclaw gateway install
|
||||
@ -257,13 +265,13 @@ systemctl --user enable --now openclaw-gateway[-<profile>].service
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
若要在注销后保持持久运行,请启用 lingering:
|
||||
如需在注销后仍保持运行,请启用 lingering:
|
||||
|
||||
```bash
|
||||
sudo loginctl enable-linger <user>
|
||||
```
|
||||
|
||||
当你需要自定义安装路径时,可使用如下手动 user-unit 示例:
|
||||
当你需要自定义安装路径时,手动 user unit 示例:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
@ -296,24 +304,22 @@ openclaw gateway stop
|
||||
```
|
||||
|
||||
Windows 原生托管启动使用名为 `OpenClaw Gateway`
|
||||
的计划任务(命名 profile 时为 `OpenClaw Gateway (<profile>)`)。如果计划任务
|
||||
创建被拒绝,OpenClaw 会回退到每用户 Startup 文件夹启动器,
|
||||
该启动器指向状态目录中的 `gateway.cmd`。
|
||||
的计划任务(命名 profile 时为 `OpenClaw Gateway (<profile>)`)。如果计划任务创建被拒绝,OpenClaw 会回退为使用位于状态目录中 `gateway.cmd` 的每用户 Startup 文件夹启动器。
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Linux(系统服务)">
|
||||
|
||||
对于多用户 / 始终在线主机,请使用系统 unit。
|
||||
对多用户/始终运行的主机,请使用系统服务单元。
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now openclaw-gateway[-<profile>].service
|
||||
```
|
||||
|
||||
使用与 user unit 相同的服务主体,但将其安装到
|
||||
`/etc/systemd/system/openclaw-gateway[-<profile>].service` 下,并在你的 `openclaw` 二进制文件位于其他位置时调整
|
||||
`ExecStart=`。
|
||||
使用与用户服务单元相同的服务主体,但将其安装到
|
||||
`/etc/systemd/system/openclaw-gateway[-<profile>].service` 下,并在
|
||||
你的 `openclaw` 二进制位于其他位置时调整 `ExecStart=`。
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@ -326,32 +332,32 @@ openclaw --dev gateway --allow-unconfigured
|
||||
openclaw --dev status
|
||||
```
|
||||
|
||||
默认包括隔离的状态 / 配置,以及基础 Gateway 网关端口 `19001`。
|
||||
默认值包括隔离的状态/配置以及基础 Gateway 网关端口 `19001`。
|
||||
|
||||
## 协议快速参考(操作员视角)
|
||||
|
||||
- 第一个客户端帧必须是 `connect`。
|
||||
- Gateway 网关返回 `hello-ok` 快照(`presence`、`health`、`stateVersion`、`uptimeMs`、limits / policy)。
|
||||
- Gateway 网关返回 `hello-ok` 快照(`presence`、`health`、`stateVersion`、`uptimeMs`、limits/policy)。
|
||||
- `hello-ok.features.methods` / `events` 是保守的发现列表,而不是
|
||||
每个可调用辅助路由的自动生成转储。
|
||||
每个可调用辅助路由的自动生成清单。
|
||||
- 请求:`req(method, params)` → `res(ok/payload|error)`。
|
||||
- 常见事件包括 `connect.challenge`、`agent`、`chat`、
|
||||
`session.message`、`session.tool`、`sessions.changed`、`presence`、`tick`、
|
||||
`health`、`heartbeat`、pairing / approval 生命周期事件,以及 `shutdown`。
|
||||
`health`、`heartbeat`、配对/批准生命周期事件,以及 `shutdown`。
|
||||
|
||||
智能体运行分为两个阶段:
|
||||
智能体运行分两个阶段:
|
||||
|
||||
1. 立即接受确认(`status:"accepted"`)
|
||||
2. 最终完成响应(`status:"ok"|"error"`),中间会流式发送 `agent` 事件。
|
||||
2. 最终完成响应(`status:"ok"|"error"`),其间会流式发送 `agent` 事件。
|
||||
|
||||
完整协议文档参见:[Gateway Protocol](/zh-CN/gateway/protocol)。
|
||||
完整协议文档: [Gateway 协议](/zh-CN/gateway/protocol)。
|
||||
|
||||
## 运维检查
|
||||
## 运行检查
|
||||
|
||||
### 存活性
|
||||
|
||||
- 打开 WS 并发送 `connect`。
|
||||
- 期望收到带快照的 `hello-ok` 响应。
|
||||
- 预期收到带快照的 `hello-ok` 响应。
|
||||
|
||||
### 就绪性
|
||||
|
||||
@ -363,23 +369,23 @@ openclaw health
|
||||
|
||||
### 缺口恢复
|
||||
|
||||
事件不会重放。出现序列缺口时,请先刷新状态(`health`、`system-presence`),再继续。
|
||||
事件不会重放。出现序列缺口时,继续之前请刷新状态(`health`、`system-presence`)。
|
||||
|
||||
## 常见故障特征
|
||||
|
||||
| 特征 | 可能问题 |
|
||||
| 特征 | 可能的问题 |
|
||||
| -------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| `refusing to bind gateway ... without auth` | 非 loopback 绑定,且没有有效的 Gateway 网关身份验证路径 |
|
||||
| `refusing to bind gateway ... without auth` | 非 loopback 绑定且没有有效的 Gateway 网关身份验证路径 |
|
||||
| `another gateway instance is already listening` / `EADDRINUSE` | 端口冲突 |
|
||||
| `Gateway start blocked: set gateway.mode=local` | 配置设置为远程模式,或损坏配置中缺少本地模式标记 |
|
||||
| `Gateway start blocked: set gateway.mode=local` | 配置被设为远程模式,或损坏配置中缺少本地模式标记 |
|
||||
| `unauthorized` during connect | 客户端与 Gateway 网关之间的身份验证不匹配 |
|
||||
|
||||
如需完整诊断阶梯,请参见 [Gateway 故障排除](/zh-CN/gateway/troubleshooting)。
|
||||
如需完整的诊断排查路径,请使用 [Gateway 故障排除](/zh-CN/gateway/troubleshooting)。
|
||||
|
||||
## 安全保证
|
||||
|
||||
- 当 Gateway 网关不可用时,Gateway 网关协议客户端会快速失败(不会隐式回退到直连渠道)。
|
||||
- 无效的 / 非 `connect` 首帧会被拒绝并关闭连接。
|
||||
- 当 Gateway 网关不可用时,Gateway 网关协议客户端会快速失败(不会隐式回退到直接渠道)。
|
||||
- 无效的/非 `connect` 首帧会被拒绝并关闭。
|
||||
- 优雅关闭会在 socket 关闭前发出 `shutdown` 事件。
|
||||
|
||||
---
|
||||
@ -396,6 +402,6 @@ openclaw health
|
||||
## 相关
|
||||
|
||||
- [配置](/zh-CN/gateway/configuration)
|
||||
- [Gateway 网关故障排除](/zh-CN/gateway/troubleshooting)
|
||||
- [Gateway 故障排除](/zh-CN/gateway/troubleshooting)
|
||||
- [远程访问](/zh-CN/gateway/remote)
|
||||
- [密钥管理](/zh-CN/gateway/secrets)
|
||||
|
||||
@ -7,22 +7,22 @@ sidebarTitle: Live tests
|
||||
summary: 实时(涉及网络)的测试:模型矩阵、CLI 后端、ACP、媒体提供商、凭证
|
||||
title: 测试:实时测试套件
|
||||
x-i18n:
|
||||
generated_at: "2026-04-25T20:48:49Z"
|
||||
generated_at: "2026-04-26T00:39:25Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 70f3539054e1e19f8a8f312e9b571a1e5894f868c3bc963756db4de9b6aaf8b0
|
||||
source_hash: 669d68dc80d0bf86942635c792f64f1edc7a23684c880cb66799401dee3d127f
|
||||
source_path: help/testing-live.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
有关快速开始、QA 运行器、单元/集成测试套件以及 Docker 流程,请参阅
|
||||
[测试](/zh-CN/help/testing)。本页介绍 **实时**(涉及网络)的测试
|
||||
关于快速开始、QA 运行器、单元/集成测试套件以及 Docker 流程,请参见
|
||||
[测试](/zh-CN/help/testing)。本页介绍**实时**(涉及网络)的测试
|
||||
套件:模型矩阵、CLI 后端、ACP 和媒体提供商实时测试,以及
|
||||
凭证处理。
|
||||
|
||||
## 实时:本地 profile 冒烟命令
|
||||
|
||||
在进行临时实时检查之前,先 source `~/.profile`,这样提供商密钥和本地工具
|
||||
在进行临时实时检查之前先加载 `~/.profile`,这样提供商密钥和本地工具
|
||||
路径就会与你的 shell 保持一致:
|
||||
|
||||
```bash
|
||||
@ -37,104 +37,104 @@ pnpm openclaw infer tts convert --local --json \
|
||||
--output /tmp/openclaw-live-smoke.mp3
|
||||
```
|
||||
|
||||
安全的语音呼叫就绪冒烟测试:
|
||||
安全的语音通话就绪性冒烟测试:
|
||||
|
||||
```bash
|
||||
pnpm openclaw voicecall setup --json
|
||||
pnpm openclaw voicecall smoke --to "+15555550123"
|
||||
```
|
||||
|
||||
`voicecall smoke` 是一次干运行,除非同时提供 `--yes`。仅当
|
||||
你明确想要发起一次真实的通知呼叫时才使用 `--yes`。对于 Twilio、Telnyx 和
|
||||
Plivo,成功的就绪检查需要一个公开的 webhook URL;仅限本地的
|
||||
loopback/私有回退方案会按设计被拒绝。
|
||||
`voicecall smoke` 默认是一次演练,除非同时提供 `--yes`。仅当你确实
|
||||
想要发起真实的通知呼叫时才使用 `--yes`。对于 Twilio、Telnyx 和
|
||||
Plivo,成功的就绪性检查需要一个公开的 webhook URL;仅本地的
|
||||
loopback/私有回退方案按设计会被拒绝。
|
||||
|
||||
## 实时:Android 节点能力全面检查
|
||||
|
||||
- 测试:`src/gateway/android-node.capabilities.live.test.ts`
|
||||
- 脚本:`pnpm android:test:integration`
|
||||
- 目标:调用已连接 Android 节点当前**声明的每一条命令**,并断言命令契约行为。
|
||||
- 目标:调用已连接 Android 节点当前**宣告的每一条命令**,并断言命令契约行为。
|
||||
- 范围:
|
||||
- 预置条件/手动设置(该测试套件不会安装/运行/配对应用)。
|
||||
- 针对所选 Android 节点逐条执行 Gateway 网关 `node.invoke` 验证。
|
||||
- 需要满足前置条件/手动设置(该测试套件不会安装/运行/配对应用)。
|
||||
- 针对所选 Android 节点逐条验证 Gateway 网关 `node.invoke`。
|
||||
- 必需的预先设置:
|
||||
- Android 应用已连接并已与 Gateway 网关配对。
|
||||
- Android 应用已连接并与 Gateway 网关完成配对。
|
||||
- 应用保持在前台运行。
|
||||
- 为你希望通过的能力授予所需权限/采集同意。
|
||||
- 已授予你期望通过的能力所需的权限/采集同意。
|
||||
- 可选目标覆盖:
|
||||
- `OPENCLAW_ANDROID_NODE_ID` 或 `OPENCLAW_ANDROID_NODE_NAME`。
|
||||
- `OPENCLAW_ANDROID_GATEWAY_URL` / `OPENCLAW_ANDROID_GATEWAY_TOKEN` / `OPENCLAW_ANDROID_GATEWAY_PASSWORD`。
|
||||
- 完整 Android 设置详情:[Android App](/zh-CN/platforms/android)
|
||||
- 完整的 Android 设置详情: [Android App](/zh-CN/platforms/android)
|
||||
|
||||
## 实时:模型冒烟测试(profile 密钥)
|
||||
## 实时:模型冒烟测试(profile keys)
|
||||
|
||||
实时测试分为两层,这样我们可以隔离故障:
|
||||
实时测试分为两层,以便我们隔离故障:
|
||||
|
||||
- “直接模型”告诉我们,在给定密钥的情况下,提供商/模型是否至少能够响应。
|
||||
- “Gateway 网关冒烟测试”告诉我们,该模型的完整 Gateway 网关 + 智能体流水线是否正常工作(会话、历史、工具、沙箱策略等)。
|
||||
- “Direct model” 用于确认在给定密钥下提供商/模型本身是否能够响应。
|
||||
- “Gateway smoke” 用于确认该模型的完整 Gateway 网关 + 智能体流水线是否正常工作(会话、历史、工具、沙箱策略等)。
|
||||
|
||||
### 第 1 层:直接模型补全(无 Gateway 网关)
|
||||
|
||||
- 测试:`src/agents/models.profiles.live.test.ts`
|
||||
- 目标:
|
||||
- 枚举已发现的模型
|
||||
- 使用 `getApiKeyForModel` 选择你具备凭证的模型
|
||||
- 对每个模型运行一次小型补全(并在需要时运行有针对性的回归测试)
|
||||
- 使用 `getApiKeyForModel` 选择你拥有凭证的模型
|
||||
- 为每个模型运行一个小型补全测试(并在需要时执行定向回归测试)
|
||||
- 启用方式:
|
||||
- `pnpm test:live`(或者如果你直接调用 Vitest,则设置 `OPENCLAW_LIVE_TEST=1`)
|
||||
- 设置 `OPENCLAW_LIVE_MODELS=modern`(或 `all`,即 modern 的别名)才能真正运行该套件;否则它会跳过,以便让 `pnpm test:live` 专注于 Gateway 网关冒烟测试
|
||||
- 选择模型的方法:
|
||||
- `OPENCLAW_LIVE_MODELS=modern` 运行 modern 允许列表(Opus/Sonnet 4.6+、GPT-5.2 + Codex、Gemini 3、DeepSeek V4、GLM 4.7、MiniMax M2.7、Grok 4)
|
||||
- `OPENCLAW_LIVE_MODELS=all` 是 modern 允许列表的别名
|
||||
- 或 `OPENCLAW_LIVE_MODELS="openai/gpt-5.2,openai-codex/gpt-5.2,anthropic/claude-opus-4-6,..."`(逗号分隔的允许列表)
|
||||
- modern/all 全量检查默认使用精心挑选的高信号上限;将 `OPENCLAW_LIVE_MAX_MODELS=0` 设为 exhaustive 的 modern 全量检查,或设为正数以使用更小的上限。
|
||||
- exhaustive 全量检查会将 `OPENCLAW_LIVE_TEST_TIMEOUT_MS` 用作整个直接模型测试的超时时间。默认值:60 分钟。
|
||||
- 直接模型探测默认以 20 路并行运行;设置 `OPENCLAW_LIVE_MODEL_CONCURRENCY` 可覆盖此值。
|
||||
- 选择提供商的方法:
|
||||
- `OPENCLAW_LIVE_PROVIDERS="google,google-antigravity,google-gemini-cli"`(逗号分隔的允许列表)
|
||||
- 设置 `OPENCLAW_LIVE_MODELS=modern`(或 `all`,即 `modern` 的别名)才会真正运行该套件;否则它会跳过,以便让 `pnpm test:live` 专注于 Gateway 网关冒烟测试
|
||||
- 选择模型的方式:
|
||||
- `OPENCLAW_LIVE_MODELS=modern` 运行现代 allowlist(Opus/Sonnet 4.6+、GPT-5.2 + Codex、Gemini 3、DeepSeek V4、GLM 4.7、MiniMax M2.7、Grok 4)
|
||||
- `OPENCLAW_LIVE_MODELS=all` 是现代 allowlist 的别名
|
||||
- 或者 `OPENCLAW_LIVE_MODELS="openai/gpt-5.2,openai-codex/gpt-5.2,anthropic/claude-opus-4-6,..."`(逗号分隔的 allowlist)
|
||||
- `modern/all` 全量检查默认使用精心挑选的高信号上限;将 `OPENCLAW_LIVE_MAX_MODELS=0` 设为现代模型的完整扫描,或设为正数以使用较小的上限。
|
||||
- 完整扫描会将 `OPENCLAW_LIVE_TEST_TIMEOUT_MS` 用作整个直接模型测试的超时时间。默认值:60 分钟。
|
||||
- 直接模型探测默认使用 20 路并行;可设置 `OPENCLAW_LIVE_MODEL_CONCURRENCY` 进行覆盖。
|
||||
- 选择提供商的方式:
|
||||
- `OPENCLAW_LIVE_PROVIDERS="google,google-antigravity,google-gemini-cli"`(逗号分隔的 allowlist)
|
||||
- 密钥来源:
|
||||
- 默认:profile 存储和环境变量回退
|
||||
- 设置 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 可强制**仅**使用 profile 存储
|
||||
- 设置 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 可强制**仅使用 profile 存储**
|
||||
- 存在原因:
|
||||
- 将“提供商 API 出错 / 密钥无效”与“Gateway 网关智能体流水线出错”区分开来
|
||||
- 容纳小型、隔离的回归测试(例如:OpenAI Responses/Codex Responses 推理重放 + 工具调用流程)
|
||||
- 将“提供商 API 已损坏 / 密钥无效”与“Gateway 网关智能体流水线已损坏”分离开来
|
||||
- 包含小型、隔离的回归测试(例如:OpenAI Responses/Codex Responses 推理回放 + 工具调用流程)
|
||||
|
||||
### 第 2 层:Gateway 网关 + dev 智能体冒烟测试(也就是 “@openclaw” 实际执行的内容)
|
||||
|
||||
- 测试:`src/gateway/gateway-models.profiles.live.test.ts`
|
||||
- 目标:
|
||||
- 启动一个进程内 Gateway 网关
|
||||
- 创建/修补一个 `agent:dev:*` 会话(每次运行时覆盖模型)
|
||||
- 遍历带密钥的模型并断言:
|
||||
- “有意义”的响应(无工具)
|
||||
- 真实的工具调用可正常工作(read 探测)
|
||||
- 可选的额外工具探测(exec+read 探测)
|
||||
- OpenAI 回归路径(仅工具调用 → 后续跟进)持续正常
|
||||
- 探测细节(这样你就能快速解释故障):
|
||||
- `read` 探测:测试会在工作区中写入一个 nonce 文件,并要求智能体 `read` 该文件并回显 nonce。
|
||||
- `exec+read` 探测:测试会要求智能体通过 `exec` 将 nonce 写入一个临时文件,然后再 `read` 回来。
|
||||
- 图像探测:测试会附加一个生成的 PNG(猫 + 随机代码),并期望模型返回 `cat <CODE>`。
|
||||
- 创建/修补一个 `agent:dev:*` 会话(每次运行按模型覆盖)
|
||||
- 遍历带有密钥的模型并断言:
|
||||
- 有“有意义”的响应(不使用工具)
|
||||
- 真实的工具调用可以正常工作(读取探针)
|
||||
- 可选的额外工具探针(exec+read 探针)
|
||||
- OpenAI 回归路径(仅工具调用 → 后续跟进)继续正常工作
|
||||
- 探针细节(这样你就能快速解释故障):
|
||||
- `read` 探针:测试会在工作区写入一个 nonce 文件,并要求智能体 `read` 该文件并回显 nonce。
|
||||
- `exec+read` 探针:测试要求智能体通过 `exec` 将 nonce 写入临时文件,然后再 `read` 回来。
|
||||
- 图像探针:测试会附加一个生成的 PNG(猫 + 随机代码),并期望模型返回 `cat <CODE>`。
|
||||
- 实现参考:`src/gateway/gateway-models.profiles.live.test.ts` 和 `src/gateway/live-image-probe.ts`。
|
||||
- 启用方式:
|
||||
- `pnpm test:live`(或者如果你直接调用 Vitest,则设置 `OPENCLAW_LIVE_TEST=1`)
|
||||
- 选择模型的方法:
|
||||
- 默认:modern 允许列表(Opus/Sonnet 4.6+、GPT-5.2 + Codex、Gemini 3、DeepSeek V4、GLM 4.7、MiniMax M2.7、Grok 4)
|
||||
- `OPENCLAW_LIVE_GATEWAY_MODELS=all` 是 modern 允许列表的别名
|
||||
- 选择模型的方式:
|
||||
- 默认:现代 allowlist(Opus/Sonnet 4.6+、GPT-5.2 + Codex、Gemini 3、DeepSeek V4、GLM 4.7、MiniMax M2.7、Grok 4)
|
||||
- `OPENCLAW_LIVE_GATEWAY_MODELS=all` 是现代 allowlist 的别名
|
||||
- 或设置 `OPENCLAW_LIVE_GATEWAY_MODELS="provider/model"`(或逗号分隔列表)以缩小范围
|
||||
- modern/all Gateway 网关全量检查默认使用精心挑选的高信号上限;将 `OPENCLAW_LIVE_GATEWAY_MAX_MODELS=0` 设为 exhaustive 的 modern 全量检查,或设为正数以使用更小的上限。
|
||||
- 选择提供商的方法(避免 “OpenRouter 一切都测”):
|
||||
- `OPENCLAW_LIVE_GATEWAY_PROVIDERS="google,google-antigravity,google-gemini-cli,openai,anthropic,zai,minimax"`(逗号分隔的允许列表)
|
||||
- 工具 + 图像探测在此实时测试中始终启用:
|
||||
- `read` 探测 + `exec+read` 探测(工具压力测试)
|
||||
- 当模型声明支持图像输入时,会运行图像探测
|
||||
- 流程(高层级):
|
||||
- `modern/all` Gateway 网关扫描默认使用精心挑选的高信号上限;将 `OPENCLAW_LIVE_GATEWAY_MAX_MODELS=0` 设为现代模型的完整扫描,或设为正数以使用较小的上限。
|
||||
- 选择提供商的方式(避免“OpenRouter 全都测”):
|
||||
- `OPENCLAW_LIVE_GATEWAY_PROVIDERS="google,google-antigravity,google-gemini-cli,openai,anthropic,zai,minimax"`(逗号分隔的 allowlist)
|
||||
- 该实时测试始终启用工具 + 图像探针:
|
||||
- `read` 探针 + `exec+read` 探针(工具压力测试)
|
||||
- 当模型声明支持图像输入时运行图像探针
|
||||
- 流程(高级概览):
|
||||
- 测试生成一个带有 “CAT” + 随机代码的小型 PNG(`src/gateway/live-image-probe.ts`)
|
||||
- 通过 `agent` `attachments: [{ mimeType: "image/png", content: "<base64>" }]` 发送
|
||||
- Gateway 网关将附件解析为 `images[]`(`src/gateway/server-methods/agent.ts` + `src/gateway/chat-attachments.ts`)
|
||||
- 嵌入式智能体将多模态用户消息转发给模型
|
||||
- 断言:回复包含 `cat` + 该代码(OCR 容错:允许少量错误)
|
||||
- 断言:回复包含 `cat` + 该代码(OCR 容错:允许轻微错误)
|
||||
|
||||
提示:若要查看你的机器上可以测试什么(以及准确的 `provider/model` id),请运行:
|
||||
提示:要查看你的机器上可以测试哪些内容(以及精确的 `provider/model` id),请运行:
|
||||
|
||||
```bash
|
||||
openclaw models list
|
||||
@ -144,8 +144,8 @@ openclaw models list --json
|
||||
## 实时:CLI 后端冒烟测试(Claude、Codex、Gemini 或其他本地 CLI)
|
||||
|
||||
- 测试:`src/gateway/gateway-cli-backend.live.test.ts`
|
||||
- 目标:使用本地 CLI 后端验证 Gateway 网关 + 智能体流水线,而不触碰你的默认配置。
|
||||
- 后端专用的默认冒烟设置位于所属扩展的 `cli-backend.ts` 定义中。
|
||||
- 目标:在不触碰你的默认配置的情况下,使用本地 CLI 后端验证 Gateway 网关 + 智能体流水线。
|
||||
- 特定后端的默认冒烟设置位于所属扩展的 `cli-backend.ts` 定义中。
|
||||
- 启用:
|
||||
- `pnpm test:live`(或者如果你直接调用 Vitest,则设置 `OPENCLAW_LIVE_TEST=1`)
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND=1`
|
||||
@ -156,12 +156,12 @@ openclaw models list --json
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_MODEL="codex-cli/gpt-5.2"`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_COMMAND="/full/path/to/codex"`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_ARGS='["exec","--json","--color","never","--sandbox","read-only","--skip-git-repo-check"]'`
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_PROBE=1` 发送真实图像附件(路径会注入到提示中)。Docker 配方默认关闭此项,除非明确请求。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_ARG="--image"` 将图像文件路径作为 CLI 参数传递,而不是注入到提示中。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_MODE="repeat"`(或 `"list"`)在设置 `IMAGE_ARG` 时控制图像参数的传递方式。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_RESUME_PROBE=1` 发送第二轮消息并验证恢复流程。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_MODEL_SWITCH_PROBE=1` 在所选模型支持切换目标时,选择加入 Claude Sonnet -> Opus 的同一会话连续性探测。Docker 配方默认关闭此项,以提升整体可靠性。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_MCP_PROBE=1` 选择加入 MCP/工具 local loopback 探测。Docker 配方默认关闭此项,除非明确请求。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_PROBE=1` 发送真实图像附件(路径会注入到提示中)。除非明确请求,否则 Docker 配方默认关闭此项。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_ARG="--image"` 通过 CLI 参数传递图像文件路径,而不是通过提示注入。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_MODE="repeat"`(或 `"list"`)用于控制在设置 `IMAGE_ARG` 时图像参数的传递方式。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_RESUME_PROBE=1` 发送第二轮对话并验证恢复流程。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_MODEL_SWITCH_PROBE=1` 选择启用 Claude Sonnet -> Opus 的同会话连续性探针,前提是所选模型支持切换目标。为了整体可靠性,Docker 配方默认关闭此项。
|
||||
- `OPENCLAW_LIVE_CLI_BACKEND_MCP_PROBE=1` 选择启用 MCP/工具 loopback 探针。除非明确请求,否则 Docker 配方默认关闭此项。
|
||||
|
||||
示例:
|
||||
|
||||
@ -171,16 +171,17 @@ OPENCLAW_LIVE_CLI_BACKEND=1 \
|
||||
pnpm test:live src/gateway/gateway-cli-backend.live.test.ts
|
||||
```
|
||||
|
||||
低成本的 Gemini MCP 配置冒烟测试:
|
||||
低成本 Gemini MCP 配置冒烟测试:
|
||||
|
||||
```bash
|
||||
OPENCLAW_LIVE_TEST=1 \
|
||||
pnpm test:live src/agents/cli-runner/bundle-mcp.gemini.live.test.ts
|
||||
```
|
||||
|
||||
这不会要求 Gemini 生成响应。它会写入与 OpenClaw 提供给 Gemini 相同的系统
|
||||
设置,然后运行 `gemini --debug mcp list` 来证明一个已保存的
|
||||
`transport: "streamable-http"` 服务器会被规范化为 Gemini 的 HTTP MCP 形态,并且能够连接到本地 streamable-HTTP MCP 服务器。
|
||||
这不会要求 Gemini 生成响应。它会写入 OpenClaw 提供给 Gemini 的同一组系统
|
||||
设置,然后运行 `gemini --debug mcp list`,以证明已保存的
|
||||
`transport: "streamable-http"` 服务器会被标准化为 Gemini 的 HTTP MCP
|
||||
格式,并且能够连接到本地的 streamable-HTTP MCP 服务器。
|
||||
|
||||
Docker 配方:
|
||||
|
||||
@ -201,30 +202,31 @@ pnpm test:docker:live-cli-backend:gemini
|
||||
|
||||
- Docker 运行器位于 `scripts/test-live-cli-backend-docker.sh`。
|
||||
- 它会在仓库 Docker 镜像内以非 root 的 `node` 用户身份运行实时 CLI 后端冒烟测试。
|
||||
- 它会从所属扩展解析 CLI 冒烟元数据,然后将匹配的 Linux CLI 包(`@anthropic-ai/claude-code`、`@openai/codex` 或 `@google/gemini-cli`)安装到 `OPENCLAW_DOCKER_CLI_TOOLS_DIR` 指定的可写缓存前缀中(默认值:`~/.cache/openclaw/docker-cli-tools`)。
|
||||
- `pnpm test:docker:live-cli-backend:claude-subscription` 需要可移植的 Claude Code 订阅 OAuth,可通过 `~/.claude/.credentials.json` 中带有 `claudeAiOauth.subscriptionType` 的配置,或通过 `claude setup-token` 得到的 `CLAUDE_CODE_OAUTH_TOKEN` 提供。它会先在 Docker 中验证直接 `claude -p`,然后在不保留 Anthropic API-key 环境变量的情况下运行两轮 Gateway 网关 CLI 后端测试。此订阅通道默认禁用 Claude MCP/工具和图像探测,因为 Claude 当前会将第三方应用用量路由到额外用量计费,而不是普通订阅套餐限制。
|
||||
- 实时 CLI 后端冒烟测试现在会对 Claude、Codex 和 Gemini 运行相同的端到端流程:文本轮次、图像分类轮次,然后是通过 Gateway 网关 CLI 验证的 MCP `cron` 工具调用。
|
||||
- Claude 的默认冒烟测试还会将会话从 Sonnet 修补切换到 Opus,并验证恢复后的会话仍然记得先前的备注。
|
||||
- 它会从所属扩展解析 CLI 冒烟元数据,然后把匹配的 Linux CLI 包(`@anthropic-ai/claude-code`、`@openai/codex` 或 `@google/gemini-cli`)安装到 `OPENCLAW_DOCKER_CLI_TOOLS_DIR` 指定的可写缓存前缀中(默认:`~/.cache/openclaw/docker-cli-tools`)。
|
||||
- `pnpm test:docker:live-cli-backend:claude-subscription` 需要便携式 Claude Code 订阅 OAuth,可通过带有 `claudeAiOauth.subscriptionType` 的 `~/.claude/.credentials.json`,或者来自 `claude setup-token` 的 `CLAUDE_CODE_OAUTH_TOKEN` 提供。它会先证明 Docker 中的直接 `claude -p` 可用,然后在不保留 Anthropic API 密钥环境变量的情况下运行两轮 Gateway 网关 CLI 后端交互。这个订阅通道默认禁用 Claude MCP/工具和图像探针,因为 Claude 目前会通过额外用量计费来处理第三方应用使用,而不是走普通订阅计划额度。
|
||||
- 现在,实时 CLI 后端冒烟测试会对 Claude、Codex 和 Gemini 执行相同的端到端流程:文本轮次、图像分类轮次,然后通过 Gateway 网关 CLI 验证 MCP `cron` 工具调用。
|
||||
- Claude 的默认冒烟测试还会将会话从 Sonnet 修补为 Opus,并验证恢复后的会话仍然记得之前的一条备注。
|
||||
|
||||
## 实时:ACP 绑定冒烟测试(`/acp spawn ... --bind here`)
|
||||
|
||||
- 测试:`src/gateway/gateway-acp-bind.live.test.ts`
|
||||
- 目标:使用一个实时 ACP 智能体验证真实的 ACP 会话绑定流程:
|
||||
- 目标:使用实时 ACP 智能体验证真实的 ACP 会话绑定流程:
|
||||
- 发送 `/acp spawn <agent> --bind here`
|
||||
- 就地绑定一个合成的消息渠道会话
|
||||
- 在同一个会话上发送一次普通后续消息
|
||||
- 验证该后续消息进入了已绑定 ACP 会话的转录记录
|
||||
- 在同一个会话上发送一次普通的后续消息
|
||||
- 验证该后续消息落入已绑定 ACP 会话的转录中
|
||||
- 启用:
|
||||
- `pnpm test:live src/gateway/gateway-acp-bind.live.test.ts`
|
||||
- `OPENCLAW_LIVE_ACP_BIND=1`
|
||||
- 默认值:
|
||||
- Docker 中的 ACP 智能体:`claude,codex,gemini`
|
||||
- 直接 `pnpm test:live ...` 使用的 ACP 智能体:`claude`
|
||||
- 直接运行 `pnpm test:live ...` 时使用的 ACP 智能体:`claude`
|
||||
- 合成渠道:Slack 私信风格的会话上下文
|
||||
- ACP 后端:`acpx`
|
||||
- 覆盖项:
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT=claude`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT=codex`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT=droid`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT=gemini`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENT=opencode`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_AGENTS=claude,codex,gemini`
|
||||
@ -234,8 +236,8 @@ pnpm test:docker:live-cli-backend:gemini
|
||||
- `OPENCLAW_LIVE_ACP_BIND_REQUIRE_TRANSCRIPT=1`
|
||||
- `OPENCLAW_LIVE_ACP_BIND_PARENT_MODEL=openai/gpt-5.2`
|
||||
- 说明:
|
||||
- 该通道使用 Gateway 网关 `chat.send` 接口,并配合仅管理员可用的合成 originating-route 字段,这样测试就可以附加消息渠道上下文,而无需假装进行了外部投递。
|
||||
- 当未设置 `OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND` 时,测试会使用内嵌 `acpx` 插件内置的智能体注册表来选择所需的 ACP harness 智能体。
|
||||
- 该通道使用 Gateway 网关 `chat.send` 接口,并带有仅管理员可用的合成 originating-route 字段,这样测试就能附加消息渠道上下文,而无需伪装成外部投递。
|
||||
- 当 `OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND` 未设置时,测试会对所选 ACP harness 智能体使用内嵌 `acpx` 插件的内置智能体注册表。
|
||||
|
||||
示例:
|
||||
|
||||
@ -256,6 +258,7 @@ pnpm test:docker:live-acp-bind
|
||||
```bash
|
||||
pnpm test:docker:live-acp-bind:claude
|
||||
pnpm test:docker:live-acp-bind:codex
|
||||
pnpm test:docker:live-acp-bind:droid
|
||||
pnpm test:docker:live-acp-bind:gemini
|
||||
pnpm test:docker:live-acp-bind:opencode
|
||||
```
|
||||
@ -264,35 +267,37 @@ Docker 说明:
|
||||
|
||||
- Docker 运行器位于 `scripts/test-live-acp-bind-docker.sh`。
|
||||
- 默认情况下,它会按顺序对聚合的实时 CLI 智能体运行 ACP 绑定冒烟测试:`claude`、`codex`,然后是 `gemini`。
|
||||
- 使用 `OPENCLAW_LIVE_ACP_BIND_AGENTS=claude`、`OPENCLAW_LIVE_ACP_BIND_AGENTS=codex`、`OPENCLAW_LIVE_ACP_BIND_AGENTS=gemini` 或 `OPENCLAW_LIVE_ACP_BIND_AGENTS=opencode` 可以缩小矩阵范围。
|
||||
- 它会 source `~/.profile`,将匹配的 CLI 认证材料暂存到容器中,然后在缺失时安装所请求的实时 CLI(`@anthropic-ai/claude-code`、`@openai/codex`、`@google/gemini-cli` 或 `opencode-ai`)。ACP 后端本身则是来自 `acpx` 插件的内置嵌入式 `acpx/runtime` 包。
|
||||
- OpenCode Docker 变体是一个严格的单智能体回归通道。它会在 source `~/.profile` 之后,根据 `OPENCLAW_LIVE_ACP_BIND_OPENCODE_MODEL`(默认值为 `opencode/kimi-k2.6`)写入一个临时 `OPENCODE_CONFIG_CONTENT` 默认模型,并且 `pnpm test:docker:live-acp-bind:opencode` 要求必须存在已绑定的助手转录记录,而不是接受通用的绑定后跳过结果。
|
||||
- 直接调用 `acpx` CLI 仅作为手动/变通路径,用于比较 Gateway 网关之外的行为。Docker ACP 绑定冒烟测试验证的是 OpenClaw 内嵌的 `acpx` 运行时后端。
|
||||
- 使用 `OPENCLAW_LIVE_ACP_BIND_AGENTS=claude`、`OPENCLAW_LIVE_ACP_BIND_AGENTS=codex`、`OPENCLAW_LIVE_ACP_BIND_AGENTS=droid`、`OPENCLAW_LIVE_ACP_BIND_AGENTS=gemini` 或 `OPENCLAW_LIVE_ACP_BIND_AGENTS=opencode` 可缩小矩阵范围。
|
||||
- 它会加载 `~/.profile`,将匹配的 CLI 认证材料暂存到容器中,然后在缺失时安装请求的实时 CLI(`@anthropic-ai/claude-code`、`@openai/codex`、通过 `https://app.factory.ai/cli` 安装的 Factory Droid、`@google/gemini-cli` 或 `opencode-ai`)。ACP 后端本身是来自 `acpx` 插件的内置嵌入式 `acpx/runtime` 包。
|
||||
- Droid Docker 变体会为设置暂存 `~/.factory`,转发 `FACTORY_API_KEY`,并且要求提供该 API key,因为本地 Factory OAuth/keyring 认证无法可移植地带入容器。它使用 ACPX 内置的 `droid exec --output-format acp` 注册表项。
|
||||
- OpenCode Docker 变体是一个严格的单智能体回归通道。在加载 `~/.profile` 后,它会根据 `OPENCLAW_LIVE_ACP_BIND_OPENCODE_MODEL`(默认 `opencode/kimi-k2.6`)写入临时的 `OPENCODE_CONFIG_CONTENT` 默认模型,而 `pnpm test:docker:live-acp-bind:opencode` 要求绑定的助手转录存在,而不是接受通用的绑定后跳过结果。
|
||||
- 直接调用 `acpx` CLI 仅用于在 Gateway 网关外对比行为的手动/变通路径。Docker ACP 绑定冒烟测试运行的是 OpenClaw 的内嵌 `acpx` 运行时后端。
|
||||
|
||||
## 实时:Codex app-server harness 冒烟测试
|
||||
|
||||
- 目标:通过常规 Gateway 网关
|
||||
`agent` 方法验证由插件拥有的 Codex harness:
|
||||
- 目标:通过正常的 Gateway 网关
|
||||
`agent` 方法验证插件自有的 Codex harness:
|
||||
- 加载内置的 `codex` 插件
|
||||
- 选择 `OPENCLAW_AGENT_RUNTIME=codex`
|
||||
- 在强制使用 Codex harness 的情况下,向 `openai/gpt-5.2` 发送第一轮 Gateway 网关智能体消息
|
||||
- 在强制使用 Codex harness 的情况下,向 `openai/gpt-5.2` 发送第一个 Gateway 网关智能体轮次
|
||||
- 向同一个 OpenClaw 会话发送第二轮消息,并验证 app-server
|
||||
线程可以恢复
|
||||
线程能够恢复
|
||||
- 通过相同的 Gateway 网关命令
|
||||
路径运行 `/codex status` 和 `/codex models`
|
||||
- 可选运行两个经 Guardian 审核的提权 shell 探测:一个应被批准的无害
|
||||
命令,以及一个应被拒绝的伪造密钥上传操作,以便智能体回问
|
||||
- 可选运行两个经过 Guardian 审核的提权 shell 探针:一个应被批准的无害
|
||||
命令,以及一个应被拒绝的伪造密钥上传操作,
|
||||
从而让智能体回头询问
|
||||
- 测试:`src/gateway/gateway-codex-harness.live.test.ts`
|
||||
- 启用:`OPENCLAW_LIVE_CODEX_HARNESS=1`
|
||||
- 默认模型:`openai/gpt-5.2`
|
||||
- 可选图像探测:`OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=1`
|
||||
- 可选 MCP/工具探测:`OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE=1`
|
||||
- 可选 Guardian 探测:`OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE=1`
|
||||
- 该冒烟测试会设置 `OPENCLAW_AGENT_HARNESS_FALLBACK=none`,这样损坏的 Codex
|
||||
harness 就无法通过静默回退到 Pi 来蒙混过关。
|
||||
- 可选图像探针:`OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=1`
|
||||
- 可选 MCP/工具探针:`OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE=1`
|
||||
- 可选 Guardian 探针:`OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE=1`
|
||||
- 该冒烟测试会设置 `OPENCLAW_AGENT_HARNESS_FALLBACK=none`,以便损坏的 Codex
|
||||
harness 不会通过静默回退到 PI 而误判通过。
|
||||
- 认证:Codex app-server 认证来自本地 Codex 订阅登录。Docker
|
||||
冒烟测试在适用时也可以提供 `OPENAI_API_KEY` 用于非 Codex 探测,
|
||||
外加可选复制的 `~/.codex/auth.json` 和 `~/.codex/config.toml`。
|
||||
冒烟测试在适用时也可为非 Codex 探针提供 `OPENAI_API_KEY`,
|
||||
以及可选复制的 `~/.codex/auth.json` 和 `~/.codex/config.toml`。
|
||||
|
||||
本地配方:
|
||||
|
||||
@ -316,22 +321,22 @@ pnpm test:docker:live-codex-harness
|
||||
Docker 说明:
|
||||
|
||||
- Docker 运行器位于 `scripts/test-live-codex-harness-docker.sh`。
|
||||
- 它会 source 挂载的 `~/.profile`,传递 `OPENAI_API_KEY`,在存在时复制 Codex CLI
|
||||
认证文件,将 `@openai/codex` 安装到一个可写的挂载 npm
|
||||
前缀中,暂存源代码树,然后只运行 Codex-harness 实时测试。
|
||||
- Docker 默认启用图像、MCP/工具和 Guardian 探测。需要更窄的调试
|
||||
运行时,可设置 `OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=0` 或
|
||||
- 它会加载挂载的 `~/.profile`,传递 `OPENAI_API_KEY`,在存在时复制 Codex CLI
|
||||
认证文件,将 `@openai/codex` 安装到一个可写的已挂载 npm
|
||||
前缀中,暂存源码树,然后仅运行 Codex harness 实时测试。
|
||||
- Docker 默认启用图像、MCP/工具和 Guardian 探针。如需更窄的调试
|
||||
运行,可设置 `OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=0` 或
|
||||
`OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE=0` 或
|
||||
`OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE=0`。
|
||||
- Docker 还会导出 `OPENCLAW_AGENT_HARNESS_FALLBACK=none`,与实时
|
||||
测试配置保持一致,这样旧别名或回退到 Pi 的行为就无法掩盖 Codex harness
|
||||
测试配置保持一致,这样旧别名或 PI 回退就无法掩盖 Codex harness
|
||||
回归问题。
|
||||
|
||||
### 推荐的实时测试配方
|
||||
|
||||
范围小且明确的允许列表最快、也最不容易出错:
|
||||
范围窄、明确的 allowlist 最快且最不容易出现不稳定情况:
|
||||
|
||||
- 单模型,直接调用(无 Gateway 网关):
|
||||
- 单模型,直接模式(无 Gateway 网关):
|
||||
- `OPENCLAW_LIVE_MODELS="openai/gpt-5.2" pnpm test:live src/agents/models.profiles.live.test.ts`
|
||||
|
||||
- 单模型,Gateway 网关冒烟测试:
|
||||
@ -340,32 +345,31 @@ Docker 说明:
|
||||
- 跨多个提供商的工具调用:
|
||||
- `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.2,openai-codex/gpt-5.2,anthropic/claude-opus-4-6,google/gemini-3-flash-preview,deepseek/deepseek-v4-flash,zai/glm-4.7,minimax/MiniMax-M2.7" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
|
||||
|
||||
- 以 Google 为重点(Gemini API 密钥 + Antigravity):
|
||||
- Gemini(API 密钥):`OPENCLAW_LIVE_GATEWAY_MODELS="google/gemini-3-flash-preview" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
|
||||
- 聚焦 Google(Gemini API key + Antigravity):
|
||||
- Gemini(API key):`OPENCLAW_LIVE_GATEWAY_MODELS="google/gemini-3-flash-preview" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
|
||||
- Antigravity(OAuth):`OPENCLAW_LIVE_GATEWAY_MODELS="google-antigravity/claude-opus-4-6-thinking,google-antigravity/gemini-3-pro-high" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
|
||||
|
||||
- Google 自适应思维冒烟测试:
|
||||
- 如果本地密钥保存在 shell profile 中:`source ~/.profile`
|
||||
- Google 自适应思考冒烟测试:
|
||||
- 如果本地密钥存放在 shell profile 中:`source ~/.profile`
|
||||
- Gemini 3 动态默认值:`pnpm openclaw qa manual --provider-mode live-frontier --model google/gemini-3.1-pro-preview --alt-model google/gemini-3.1-pro-preview --message '/think adaptive Reply exactly: GEMINI_ADAPTIVE_OK' --timeout-ms 180000`
|
||||
- Gemini 2.5 动态预算:`pnpm openclaw qa manual --provider-mode live-frontier --model google/gemini-2.5-flash --alt-model google/gemini-2.5-flash --message '/think adaptive Reply exactly: GEMINI25_ADAPTIVE_OK' --timeout-ms 180000`
|
||||
|
||||
说明:
|
||||
|
||||
- `google/...` 使用 Gemini API(API 密钥)。
|
||||
- `google-antigravity/...` 使用 Antigravity OAuth bridge(类似 Cloud Code Assist 风格的智能体端点)。
|
||||
- `google-gemini-cli/...` 使用你机器上的本地 Gemini CLI(独立认证 + 工具行为差异)。
|
||||
- `google/...` 使用 Gemini API(API key)。
|
||||
- `google-antigravity/...` 使用 Antigravity OAuth 桥接(Cloud Code Assist 风格的智能体端点)。
|
||||
- `google-gemini-cli/...` 使用你机器上的本地 Gemini CLI(单独的认证 + 工具链特殊情况)。
|
||||
- Gemini API 与 Gemini CLI:
|
||||
- API:OpenClaw 通过 HTTP 调用 Google 托管的 Gemini API(API 密钥 / profile 认证);这也是大多数用户所说的 “Gemini”。
|
||||
- CLI:OpenClaw 调用本地 `gemini` 二进制;它有自己的认证方式,并且行为可能不同(流式传输/工具支持/版本偏差)。
|
||||
- API:OpenClaw 通过 HTTP 调用 Google 托管的 Gemini API(API key / profile 认证);这也是大多数用户提到 “Gemini” 时的含义。
|
||||
- CLI:OpenClaw 会调用本地 `gemini` 二进制;它有自己的认证机制,而且行为可能不同(流式传输/工具支持/版本偏差)。
|
||||
|
||||
## 实时:模型矩阵(我们的覆盖范围)
|
||||
|
||||
没有固定的 “CI 模型列表”(实时测试是选择加入的),但以下是**推荐**
|
||||
在开发机器上定期覆盖的模型,前提是你有相应密钥。
|
||||
没有固定的“CI 模型列表”(实时测试是可选启用的),但这些是在带有密钥的开发机器上建议定期覆盖的**推荐**模型。
|
||||
|
||||
### 现代冒烟集(工具调用 + 图像)
|
||||
### 现代冒烟测试集(工具调用 + 图像)
|
||||
|
||||
这是我们预期应持续正常工作的 “常见模型” 运行集:
|
||||
这是我们期望持续保持可用的“常见模型”运行集:
|
||||
|
||||
- OpenAI(非 Codex):`openai/gpt-5.2`
|
||||
- OpenAI Codex OAuth:`openai-codex/gpt-5.2`
|
||||
@ -381,7 +385,7 @@ Docker 说明:
|
||||
|
||||
### 基线:工具调用(Read + 可选 Exec)
|
||||
|
||||
每个提供商系列至少选择一个:
|
||||
每个提供商家族至少选一个:
|
||||
|
||||
- OpenAI:`openai/gpt-5.2`
|
||||
- Anthropic:`anthropic/claude-opus-4-6`(或 `anthropic/claude-sonnet-4-6`)
|
||||
@ -390,44 +394,44 @@ Docker 说明:
|
||||
- Z.AI(GLM):`zai/glm-4.7`
|
||||
- MiniMax:`minimax/MiniMax-M2.7`
|
||||
|
||||
可选附加覆盖(有更好,没有也行):
|
||||
可选附加覆盖(有更好,没有也可以):
|
||||
|
||||
- xAI:`xai/grok-4`(或最新可用版本)
|
||||
- Mistral:`mistral/`…(选择一个你已启用、支持 “tools” 的模型)
|
||||
- Cerebras:`cerebras/`…(如果你有访问权限)
|
||||
- LM Studio:`lmstudio/`…(本地;工具调用取决于 API 模式)
|
||||
|
||||
### 视觉:图像发送(附件 → 多模态消息)
|
||||
### Vision:图像发送(附件 → 多模态消息)
|
||||
|
||||
在 `OPENCLAW_LIVE_GATEWAY_MODELS` 中至少包含一个支持图像的模型(Claude/Gemini/OpenAI 支持视觉的变体等),以便执行图像探测。
|
||||
在 `OPENCLAW_LIVE_GATEWAY_MODELS` 中至少包含一个支持图像的模型(Claude/Gemini/OpenAI 支持视觉的变体等),以覆盖图像探针。
|
||||
|
||||
### 聚合器 / 替代 Gateway 网关
|
||||
|
||||
如果你已启用相应密钥,我们也支持通过以下方式测试:
|
||||
如果你已启用相关密钥,我们也支持通过以下方式测试:
|
||||
|
||||
- OpenRouter:`openrouter/...`(数百个模型;使用 `openclaw models scan` 查找支持工具 + 图像的候选项)
|
||||
- OpenCode:Zen 使用 `opencode/...`,Go 使用 `opencode-go/...`(通过 `OPENCODE_API_KEY` / `OPENCODE_ZEN_API_KEY` 认证)
|
||||
- OpenCode:`opencode/...` 用于 Zen,`opencode-go/...` 用于 Go(通过 `OPENCODE_API_KEY` / `OPENCODE_ZEN_API_KEY` 认证)
|
||||
|
||||
你还可以将更多提供商纳入实时矩阵(前提是你有相应凭证/配置):
|
||||
你还可以在实时矩阵中加入更多提供商(如果你有凭证/配置):
|
||||
|
||||
- 内置:`openai`、`openai-codex`、`anthropic`、`google`、`google-vertex`、`google-antigravity`、`google-gemini-cli`、`zai`、`openrouter`、`opencode`、`opencode-go`、`xai`、`groq`、`cerebras`、`mistral`、`github-copilot`
|
||||
- 通过 `models.providers`(自定义端点):`minimax`(云/API),以及任何兼容 OpenAI/Anthropic 的代理(LM Studio、vLLM、LiteLLM 等)
|
||||
|
||||
提示:不要尝试在文档中硬编码 “所有模型”。权威列表应以你机器上 `discoverModels(...)` 的返回结果 + 当前可用密钥为准。
|
||||
提示:不要试图在文档中硬编码 “所有模型”。权威列表是你的机器上 `discoverModels(...)` 返回的内容,加上当前可用的密钥。
|
||||
|
||||
## 凭证(切勿提交)
|
||||
## 凭证(绝不要提交)
|
||||
|
||||
实时测试发现凭证的方式与 CLI 完全相同。实际含义如下:
|
||||
|
||||
- 如果 CLI 能正常工作,实时测试通常也应能找到相同的密钥。
|
||||
- 如果某个实时测试提示 “no creds”,请用与调试 `openclaw models list` / 模型选择相同的方法进行排查。
|
||||
- 如果 CLI 可以工作,实时测试通常也应能找到相同的密钥。
|
||||
- 如果实时测试提示 “no creds”,就像调试 `openclaw models list` / 模型选择那样去调试。
|
||||
|
||||
- 按智能体划分的认证 profile:`~/.openclaw/agents/<agentId>/agent/auth-profiles.json`(这就是实时测试中 “profile keys” 的含义)
|
||||
- 每个智能体的认证 profile:`~/.openclaw/agents/<agentId>/agent/auth-profiles.json`(这就是实时测试中 “profile keys” 的含义)
|
||||
- 配置:`~/.openclaw/openclaw.json`(或 `OPENCLAW_CONFIG_PATH`)
|
||||
- 旧版状态目录:`~/.openclaw/credentials/`(存在时会复制到暂存的实时 home 中,但这不是主要的 profile-key 存储)
|
||||
- 默认情况下,本地实时运行会将当前活动配置、按智能体划分的 `auth-profiles.json` 文件、旧版 `credentials/` 以及受支持的外部 CLI 认证目录复制到一个临时测试 home 中;暂存的实时 home 会跳过 `workspace/` 和 `sandboxes/`,并移除 `agents.*.workspace` / `agentDir` 路径覆盖,这样探测就不会落到你真实主机的工作区中。
|
||||
- 旧版状态目录:`~/.openclaw/credentials/`(存在时会复制到暂存的实时 home 中,但它不是主要的 profile-key 存储)
|
||||
- 默认情况下,本地实时运行会将当前配置、每个智能体的 `auth-profiles.json` 文件、旧版 `credentials/` 以及受支持的外部 CLI 认证目录复制到临时测试 home 中;暂存的实时 home 会跳过 `workspace/` 和 `sandboxes/`,并且会剥离 `agents.*.workspace` / `agentDir` 路径覆盖,这样探针就不会落到你真实主机的工作区上。
|
||||
|
||||
如果你想依赖环境变量密钥(例如从你的 `~/.profile` 导出的密钥),请在执行本地测试前运行 `source ~/.profile`,或者使用下面的 Docker 运行器(它们可以将 `~/.profile` 挂载到容器中)。
|
||||
如果你想依赖环境变量密钥(例如导出在你的 `~/.profile` 中),请在执行本地测试前先运行 `source ~/.profile`,或者使用下面的 Docker 运行器(它们可以将 `~/.profile` 挂载到容器中)。
|
||||
|
||||
## Deepgram 实时测试(音频转录)
|
||||
|
||||
@ -440,14 +444,14 @@ Docker 说明:
|
||||
- 启用:`BYTEPLUS_API_KEY=... BYTEPLUS_LIVE_TEST=1 pnpm test:live extensions/byteplus/live.test.ts`
|
||||
- 可选模型覆盖:`BYTEPLUS_CODING_MODEL=ark-code-latest`
|
||||
|
||||
## ComfyUI 工作流媒体实时测试
|
||||
## ComfyUI workflow 媒体实时测试
|
||||
|
||||
- 测试:`extensions/comfy/comfy.live.test.ts`
|
||||
- 启用:`OPENCLAW_LIVE_TEST=1 COMFY_LIVE_TEST=1 pnpm test:live -- extensions/comfy/comfy.live.test.ts`
|
||||
- 范围:
|
||||
- 覆盖内置 comfy 图像、视频和 `music_generate` 路径
|
||||
- 除非已配置 `plugins.entries.comfy.config.<capability>`,否则会跳过各项能力
|
||||
- 在修改 comfy 工作流提交、轮询、下载或插件注册后很有用
|
||||
- 适用于在修改 comfy workflow 提交、轮询、下载或插件注册后进行验证
|
||||
|
||||
## 图像生成实时测试
|
||||
|
||||
@ -455,14 +459,14 @@ Docker 说明:
|
||||
- 命令:`pnpm test:live test/image-generation.runtime.live.test.ts`
|
||||
- Harness:`pnpm test:live:media image`
|
||||
- 范围:
|
||||
- 枚举每一个已注册的图像生成提供商插件
|
||||
- 枚举每个已注册的图像生成提供商插件
|
||||
- 在探测前从你的登录 shell(`~/.profile`)加载缺失的提供商环境变量
|
||||
- 默认优先使用实时/环境变量 API 密钥,而不是已存储的认证 profile,因此 `auth-profiles.json` 中过期的测试密钥不会掩盖真实的 shell 凭证
|
||||
- 默认优先使用实时/环境变量 API key,而不是已存储的认证 profile,这样 `auth-profiles.json` 中过期的测试密钥就不会掩盖真实的 shell 凭证
|
||||
- 跳过没有可用认证/profile/模型的提供商
|
||||
- 通过共享图像生成运行时运行每个已配置提供商:
|
||||
- 通过共享图像生成运行时运行每个已配置的提供商:
|
||||
- `<provider>:generate`
|
||||
- 当提供商声明支持编辑时运行 `<provider>:edit`
|
||||
- 当前覆盖的内置提供商:
|
||||
- 当前已覆盖的内置提供商:
|
||||
- `fal`
|
||||
- `google`
|
||||
- `minimax`
|
||||
@ -470,15 +474,15 @@ Docker 说明:
|
||||
- `openrouter`
|
||||
- `vydra`
|
||||
- `xai`
|
||||
- 可选范围缩小:
|
||||
- 可选缩小范围:
|
||||
- `OPENCLAW_LIVE_IMAGE_GENERATION_PROVIDERS="openai,google,openrouter,xai"`
|
||||
- `OPENCLAW_LIVE_IMAGE_GENERATION_MODELS="openai/gpt-image-2,google/gemini-3.1-flash-image-preview,openrouter/google/gemini-3.1-flash-image-preview,xai/grok-imagine-image"`
|
||||
- `OPENCLAW_LIVE_IMAGE_GENERATION_CASES="google:flash-generate,google:pro-edit,openrouter:generate,xai:default-generate,xai:default-edit"`
|
||||
- 可选认证行为:
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 强制使用 profile 存储认证,并忽略仅环境变量覆盖
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 强制使用 profile 存储认证并忽略仅环境变量的覆盖
|
||||
|
||||
对于已发布的 CLI 路径,在提供商/运行时实时
|
||||
测试通过后,再添加一个 `infer` 冒烟测试:
|
||||
测试通过后,添加一个 `infer` 冒烟测试:
|
||||
|
||||
```bash
|
||||
OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_INFER_CLI_TEST=1 pnpm test:live -- test/image-generation.infer-cli.live.test.ts
|
||||
@ -490,9 +494,9 @@ openclaw infer image generate \
|
||||
--json
|
||||
```
|
||||
|
||||
这涵盖 CLI 参数解析、配置/默认智能体解析、内置
|
||||
这覆盖了 CLI 参数解析、配置/默认智能体解析、内置
|
||||
插件激活、按需修复内置运行时依赖、共享
|
||||
图像生成运行时,以及实时提供商请求。
|
||||
图像生成运行时以及实时提供商请求。
|
||||
|
||||
## 音乐生成实时测试
|
||||
|
||||
@ -503,20 +507,20 @@ openclaw infer image generate \
|
||||
- 覆盖共享的内置音乐生成提供商路径
|
||||
- 当前覆盖 Google 和 MiniMax
|
||||
- 在探测前从你的登录 shell(`~/.profile`)加载提供商环境变量
|
||||
- 默认优先使用实时/环境变量 API 密钥,而不是已存储的认证 profile,因此 `auth-profiles.json` 中过期的测试密钥不会掩盖真实的 shell 凭证
|
||||
- 默认优先使用实时/环境变量 API key,而不是已存储的认证 profile,这样 `auth-profiles.json` 中过期的测试密钥就不会掩盖真实的 shell 凭证
|
||||
- 跳过没有可用认证/profile/模型的提供商
|
||||
- 在可用时同时运行两种已声明的运行时模式:
|
||||
- 使用仅提示词输入的 `generate`
|
||||
- 在可用时运行两种已声明的运行时模式:
|
||||
- 使用仅 prompt 输入的 `generate`
|
||||
- 当提供商声明 `capabilities.edit.enabled` 时运行 `edit`
|
||||
- 当前共享通道覆盖:
|
||||
- `google`:`generate`、`edit`
|
||||
- `minimax`:`generate`
|
||||
- `comfy`:单独的 Comfy 实时文件,不属于此次共享全面检查
|
||||
- 可选范围缩小:
|
||||
- `comfy`:单独的 Comfy 实时文件,不在这个共享扫描中
|
||||
- 可选缩小范围:
|
||||
- `OPENCLAW_LIVE_MUSIC_GENERATION_PROVIDERS="google,minimax"`
|
||||
- `OPENCLAW_LIVE_MUSIC_GENERATION_MODELS="google/lyria-3-clip-preview,minimax/music-2.6"`
|
||||
- 可选认证行为:
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 强制使用 profile 存储认证,并忽略仅环境变量覆盖
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 强制使用 profile 存储认证并忽略仅环境变量的覆盖
|
||||
|
||||
## 视频生成实时测试
|
||||
|
||||
@ -525,38 +529,38 @@ openclaw infer image generate \
|
||||
- Harness:`pnpm test:live:media video`
|
||||
- 范围:
|
||||
- 覆盖共享的内置视频生成提供商路径
|
||||
- 默认使用发布安全的冒烟路径:非 FAL 提供商、每个提供商一个 text-to-video 请求、1 秒龙虾提示词,并且每个提供商的操作上限由 `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS` 控制(默认 `180000`)
|
||||
- 默认跳过 FAL,因为提供商侧队列延迟可能主导发布时间;传入 `--video-providers fal` 或 `OPENCLAW_LIVE_VIDEO_GENERATION_PROVIDERS="fal"` 可显式运行
|
||||
- 默认使用发布安全的冒烟路径:非 FAL 提供商、每个提供商一次 text-to-video 请求、时长一秒的龙虾 prompt,并为每个提供商设置来自 `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS` 的操作上限(默认 `180000`)
|
||||
- 默认跳过 FAL,因为提供商侧队列延迟可能主导发布时间;传入 `--video-providers fal` 或 `OPENCLAW_LIVE_VIDEO_GENERATION_PROVIDERS="fal"` 可显式运行它
|
||||
- 在探测前从你的登录 shell(`~/.profile`)加载提供商环境变量
|
||||
- 默认优先使用实时/环境变量 API 密钥,而不是已存储的认证 profile,因此 `auth-profiles.json` 中过期的测试密钥不会掩盖真实的 shell 凭证
|
||||
- 默认优先使用实时/环境变量 API key,而不是已存储的认证 profile,这样 `auth-profiles.json` 中过期的测试密钥就不会掩盖真实的 shell 凭证
|
||||
- 跳过没有可用认证/profile/模型的提供商
|
||||
- 默认仅运行 `generate`
|
||||
- 设置 `OPENCLAW_LIVE_VIDEO_GENERATION_FULL_MODES=1` 也会在可用时运行已声明的转换模式:
|
||||
- 当提供商声明 `capabilities.imageToVideo.enabled`,且所选提供商/模型在共享全面检查中接受基于 buffer 的本地图像输入时,运行 `imageToVideo`
|
||||
- 当提供商声明 `capabilities.videoToVideo.enabled`,且所选提供商/模型在共享全面检查中接受基于 buffer 的本地视频输入时,运行 `videoToVideo`
|
||||
- 当前在共享全面检查中已声明但跳过的 `imageToVideo` 提供商:
|
||||
- 设置 `OPENCLAW_LIVE_VIDEO_GENERATION_FULL_MODES=1` 时,也会在可用时运行已声明的转换模式:
|
||||
- 当提供商声明 `capabilities.imageToVideo.enabled`,且所选提供商/模型在共享扫描中接受基于 buffer 的本地图像输入时,运行 `imageToVideo`
|
||||
- 当提供商声明 `capabilities.videoToVideo.enabled`,且所选提供商/模型在共享扫描中接受基于 buffer 的本地视频输入时,运行 `videoToVideo`
|
||||
- 当前在共享扫描中已声明但跳过的 `imageToVideo` 提供商:
|
||||
- `vydra`,因为内置 `veo3` 仅支持文本,而内置 `kling` 需要远程图像 URL
|
||||
- 提供商专用的 Vydra 覆盖:
|
||||
- `OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_VYDRA_VIDEO=1 pnpm test:live -- extensions/vydra/vydra.live.test.ts`
|
||||
- 该文件运行 `veo3` text-to-video,并且默认运行一个使用远程图像 URL fixture 的 `kling` 通道
|
||||
- 该文件运行 `veo3` text-to-video,以及默认使用远程图像 URL fixture 的 `kling` 通道
|
||||
- 当前 `videoToVideo` 实时覆盖:
|
||||
- 仅 `runway`,且所选模型为 `runway/gen4_aleph`
|
||||
- 当前在共享全面检查中已声明但跳过的 `videoToVideo` 提供商:
|
||||
- 仅 `runway`,并且所选模型为 `runway/gen4_aleph`
|
||||
- 当前在共享扫描中已声明但跳过的 `videoToVideo` 提供商:
|
||||
- `alibaba`、`qwen`、`xai`,因为这些路径当前需要远程 `http(s)` / MP4 参考 URL
|
||||
- `google`,因为当前共享 Gemini/Veo 通道使用本地基于 buffer 的输入,而该路径在共享全面检查中不被接受
|
||||
- `openai`,因为当前共享通道缺少组织专用的视频修补/混编访问保证
|
||||
- 可选范围缩小:
|
||||
- `google`,因为当前共享的 Gemini/Veo 通道使用基于本地 buffer 的输入,而该路径在共享扫描中不被接受
|
||||
- `openai`,因为当前共享通道缺乏组织专用视频修补/混剪访问保证
|
||||
- 可选缩小范围:
|
||||
- `OPENCLAW_LIVE_VIDEO_GENERATION_PROVIDERS="google,openai,runway"`
|
||||
- `OPENCLAW_LIVE_VIDEO_GENERATION_MODELS="google/veo-3.1-fast-generate-preview,openai/sora-2,runway/gen4_aleph"`
|
||||
- `OPENCLAW_LIVE_VIDEO_GENERATION_SKIP_PROVIDERS=""` 将所有提供商都纳入默认全面检查,包括 FAL
|
||||
- `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS=60000` 降低每个提供商操作上限,以便进行更激进的冒烟运行
|
||||
- `OPENCLAW_LIVE_VIDEO_GENERATION_SKIP_PROVIDERS=""` 以在默认扫描中包含所有提供商,包括 FAL
|
||||
- `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS=60000` 以降低每个提供商的操作上限,适用于激进的冒烟测试运行
|
||||
- 可选认证行为:
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 强制使用 profile 存储认证,并忽略仅环境变量覆盖
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 强制使用 profile 存储认证并忽略仅环境变量的覆盖
|
||||
|
||||
## 媒体实时 harness
|
||||
## 媒体实时 Harness
|
||||
|
||||
- 命令:`pnpm test:live:media`
|
||||
- 用途:
|
||||
- 目的:
|
||||
- 通过一个仓库原生入口点运行共享的图像、音乐和视频实时测试套件
|
||||
- 自动从 `~/.profile` 加载缺失的提供商环境变量
|
||||
- 默认自动将每个测试套件缩小到当前具有可用认证的提供商
|
||||
|
||||
@ -3,160 +3,158 @@ read_when:
|
||||
- 在本地或 CI 中运行测试
|
||||
- 为模型 / 提供商缺陷添加回归测试
|
||||
- 调试 Gateway 网关 + 智能体行为
|
||||
summary: 测试工具包:单元 / e2e / live 测试套件、Docker 运行器,以及每项测试所覆盖的内容
|
||||
summary: 测试工具包:单元 / e2e / 实时测试套件、Docker 运行器,以及每项测试所涵盖的内容
|
||||
title: 测试
|
||||
x-i18n:
|
||||
generated_at: "2026-04-26T00:04:52Z"
|
||||
generated_at: "2026-04-26T00:39:35Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 106fd042e6b065595df0b7c7667cb0e07aceff28d88bb2cd59752c0946ba2edd
|
||||
source_hash: c235193ed82e20739522cc9571408de804382c38d1b97b22c5aa70153cc23054
|
||||
source_path: help/testing.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
OpenClaw 有三个 Vitest 测试套件(单元 / 集成、e2e、live)以及一小组 Docker 运行器。本文档是一份“我们如何测试”的指南:
|
||||
OpenClaw 有三个 Vitest 测试套件(单元 / 集成、e2e、实时),以及一小组 Docker 运行器。本文档是一份“我们如何测试”的指南:
|
||||
|
||||
- 每个测试套件覆盖什么内容(以及它明确 _不_ 覆盖什么)。
|
||||
- 每个测试套件涵盖什么内容(以及它刻意**不**涵盖的内容)。
|
||||
- 常见工作流应运行哪些命令(本地、推送前、调试)。
|
||||
- live 测试如何发现凭证并选择模型 / 提供商。
|
||||
- 实时测试如何发现凭证并选择模型 / 提供商。
|
||||
- 如何为真实世界中的模型 / 提供商问题添加回归测试。
|
||||
|
||||
## 快速开始
|
||||
|
||||
大多数时候:
|
||||
大多数情况下:
|
||||
|
||||
- 完整门禁(预期在推送前执行):`pnpm build && pnpm check && pnpm check:test-types && pnpm test`
|
||||
- 完整门禁(预期在推送前运行):`pnpm build && pnpm check && pnpm check:test-types && pnpm test`
|
||||
- 在配置充足的机器上更快地运行本地完整测试套件:`pnpm test:max`
|
||||
- 直接使用 Vitest 观察模式循环:`pnpm test:watch`
|
||||
- 现在直接按文件定位也会路由到扩展 / 渠道路径:`pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`
|
||||
- 当你在迭代处理单个失败用例时,优先使用定向运行。
|
||||
- 直接进入 Vitest 监听循环:`pnpm test:watch`
|
||||
- 现在直接针对文件的运行也支持 extension / channel 路径:`pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`
|
||||
- 当你在迭代修复单个失败用例时,优先使用定向运行。
|
||||
- 基于 Docker 的 QA 站点:`pnpm qa:lab:up`
|
||||
- 基于 Linux VM 的 QA lane:`pnpm openclaw qa suite --runner multipass --scenario channel-chat-baseline`
|
||||
- 基于 Linux VM 的 QA 通道:`pnpm openclaw qa suite --runner multipass --scenario channel-chat-baseline`
|
||||
|
||||
当你修改了测试,或想获得更多信心时:
|
||||
当你修改了测试或希望获得更高信心时:
|
||||
|
||||
- 覆盖率门禁:`pnpm test:coverage`
|
||||
- E2E 测试套件:`pnpm test:e2e`
|
||||
|
||||
当你在调试真实提供商 / 模型时(需要真实凭证):
|
||||
|
||||
- live 测试套件(模型 + Gateway 网关 工具 / 图像探测):`pnpm test:live`
|
||||
- 安静地只运行一个 live 文件:`pnpm test:live -- src/agents/models.profiles.live.test.ts`
|
||||
- Docker live 模型扫描:`pnpm test:docker:live-models`
|
||||
- 现在每个选定模型都会运行一次文本轮次加上一个小型的类文件读取探测。元数据声明支持 `image` 输入的模型还会运行一个微型图像轮次。在隔离提供商故障时,可使用 `OPENCLAW_LIVE_MODEL_FILE_PROBE=0` 或 `OPENCLAW_LIVE_MODEL_IMAGE_PROBE=0` 禁用额外探测。
|
||||
- CI 覆盖范围:每日的 `OpenClaw Scheduled Live And E2E Checks` 和手动触发的 `OpenClaw Release Checks` 都会调用可复用的 live/E2E 工作流,并设置 `include_live_suites: true`,其中包括按提供商分片的独立 Docker live 模型矩阵作业。
|
||||
- 若要有针对性地重跑 CI,可派发 `OpenClaw Live And E2E Checks (Reusable)` 并设置 `include_live_suites: true` 与 `live_models_only: true`。
|
||||
- 将新的高信号提供商密钥添加到 `scripts/ci-hydrate-live-auth.sh`,以及 `.github/workflows/openclaw-live-and-e2e-checks-reusable.yml` 和它的定时 / 发布调用方中。
|
||||
- 实时测试套件(模型 + Gateway 网关工具 / 图像探测):`pnpm test:live`
|
||||
- 安静地只运行一个实时测试文件:`pnpm test:live -- src/agents/models.profiles.live.test.ts`
|
||||
- Docker 实时模型扫描:`pnpm test:docker:live-models`
|
||||
- 现在每个选定模型都会运行一次文本轮次以及一次小型文件读取式探测。元数据声明支持 `image` 输入的模型还会运行一次小型图像轮次。在隔离提供商故障时,可使用 `OPENCLAW_LIVE_MODEL_FILE_PROBE=0` 或 `OPENCLAW_LIVE_MODEL_IMAGE_PROBE=0` 禁用这些额外探测。
|
||||
- CI 覆盖:每日的 `OpenClaw Scheduled Live And E2E Checks` 和手动触发的 `OpenClaw Release Checks` 都会调用可复用的实时 / E2E 工作流,并设置 `include_live_suites: true`,其中包含按提供商分片的独立 Docker 实时模型矩阵任务。
|
||||
- 若需定向重跑 CI,可派发 `OpenClaw Live And E2E Checks (Reusable)`,并设置 `include_live_suites: true` 与 `live_models_only: true`。
|
||||
- 将新的高信号提供商密钥添加到 `scripts/ci-hydrate-live-auth.sh`、`.github/workflows/openclaw-live-and-e2e-checks-reusable.yml` 及其定时 / 发布调用工作流中。
|
||||
- 原生 Codex 绑定聊天冒烟测试:`pnpm test:docker:live-codex-bind`
|
||||
- 在 Docker live lane 中针对 Codex app-server 路径运行,将一个合成的 Slack 私信通过 `/codex bind` 绑定,执行 `/codex fast` 和 `/codex permissions`,然后验证普通回复和图像附件是通过原生插件绑定路由,而不是 ACP。
|
||||
- 在 Docker 中针对 Codex app-server 路径运行一个实时测试通道,使用 `/codex bind` 绑定一个合成的 Slack 私信,执行 `/codex fast` 和 `/codex permissions`,然后验证普通回复和图像附件均通过原生插件绑定路由,而不是 ACP。
|
||||
- Crestodian 救援命令冒烟测试:`pnpm test:live:crestodian-rescue-channel`
|
||||
- 针对消息渠道救援命令表面的可选双重保险检查。它会执行 `/crestodian status`,排队一个持久化模型变更,回复 `/crestodian yes`,并验证审计 / 配置写入路径。
|
||||
- 这是针对消息渠道救援命令表面的可选双重保险检查。它会执行 `/crestodian status`,排队一个持久模型变更,回复 `/crestodian yes`,并验证审计 / 配置写入路径。
|
||||
- Crestodian planner Docker 冒烟测试:`pnpm test:docker:crestodian-planner`
|
||||
- 在无配置容器中运行 Crestodian,并在 `PATH` 上放置一个假的 Claude CLI,验证模糊 planner 回退会转换为经过审计的类型化配置写入。
|
||||
- 在无配置容器中运行 Crestodian,并在 `PATH` 上提供一个伪造的 Claude CLI,验证模糊 planner 回退会转换为带审计的类型化配置写入。
|
||||
- Crestodian 首次运行 Docker 冒烟测试:`pnpm test:docker:crestodian-first-run`
|
||||
- 从空的 OpenClaw 状态目录启动,将裸 `openclaw` 路由到 Crestodian,应用 setup / model / agent / Discord plugin + SecretRef 写入,验证配置,并检查审计条目。相同的 Ring 0 设置路径也会在 QA Lab 中通过 `pnpm openclaw qa suite --scenario crestodian-ring-zero-setup` 覆盖。
|
||||
- Moonshot / Kimi 成本冒烟测试:设置 `MOONSHOT_API_KEY` 后,运行 `openclaw models list --provider moonshot --json`,然后针对 `moonshot/kimi-k2.6` 运行独立命令 `openclaw agent --local --session-id live-kimi-cost --message 'Reply exactly: KIMI_LIVE_OK' --thinking off --json`
|
||||
。验证 JSON 报告的是 Moonshot / K2.6,并且智能体转录中存储了标准化的 `usage.cost`。
|
||||
- 从一个空的 OpenClaw 状态目录启动,将裸 `openclaw` 路由到 Crestodian,应用 setup / model / agent / Discord 插件 + SecretRef 写入,验证配置,并检查审计条目。相同的 Ring 0 设置路径也会在 QA Lab 中通过 `pnpm openclaw qa suite --scenario crestodian-ring-zero-setup` 覆盖。
|
||||
- Moonshot / Kimi 成本冒烟测试:设置 `MOONSHOT_API_KEY` 后,运行 `openclaw models list --provider moonshot --json`,然后针对 `moonshot/kimi-k2.6` 运行独立命令 `openclaw agent --local --session-id live-kimi-cost --message 'Reply exactly: KIMI_LIVE_OK' --thinking off --json`。验证 JSON 报告的是 Moonshot / K2.6,并且助手转录存储了标准化的 `usage.cost`。
|
||||
|
||||
提示:如果你只需要一个失败用例,优先通过下文描述的 allowlist 环境变量来缩小 live 测试范围。
|
||||
提示:当你只需要处理一个失败用例时,优先使用下文描述的 allowlist 环境变量来缩小实时测试范围。
|
||||
|
||||
## QA 专用运行器
|
||||
|
||||
当你需要 QA-lab 级别的真实度时,这些命令与主要测试套件并列使用:
|
||||
当你需要 QA-lab 级别的真实性时,这些命令与主测试套件并列使用:
|
||||
|
||||
CI 会在专用工作流中运行 QA Lab。`Parity gate` 会在匹配的 PR 上运行,也可通过手动触发使用模拟提供商运行。`QA-Lab - All Lanes` 会在 `main` 上每晚运行,也可通过手动触发并行运行模拟 parity gate、live Matrix lane 和 Convex 管理的 live Telegram lane。`OpenClaw Release Checks` 会在发布批准前运行相同的 lanes。
|
||||
CI 会在专用工作流中运行 QA Lab。`Parity gate` 会在匹配的 PR 上运行,也可通过手动派发并使用模拟提供商运行。`QA-Lab - All Lanes` 会在 `main` 上每晚运行,也可手动派发,其中并行运行模拟 parity gate、实时 Matrix 通道,以及由 Convex 管理的实时 Telegram 通道。`OpenClaw Release Checks` 会在发布审批前运行相同通道。
|
||||
|
||||
- `pnpm openclaw qa suite`
|
||||
- 直接在主机上运行基于仓库的 QA 场景。
|
||||
- 默认会使用隔离的 Gateway 网关 worker 并行运行多个选定场景。`qa-channel` 默认并发数为 4(受所选场景数量限制)。使用 `--concurrency <count>` 调整 worker 数量,或使用 `--concurrency 1` 启用较旧的串行 lane。
|
||||
- 只要任一场景失败,就会以非零状态退出。若你希望保留产物但不以失败退出码结束,可使用 `--allow-failures`。
|
||||
- 支持提供商模式 `live-frontier`、`mock-openai` 和 `aimock`。`aimock` 会启动一个本地的 AIMock 支持的提供商服务器,用于实验性 fixture 和协议模拟覆盖,而不替代具备场景感知能力的 `mock-openai` lane。
|
||||
- 直接在宿主机上运行基于仓库的 QA 场景。
|
||||
- 默认情况下,会使用隔离的 Gateway 网关工作进程并行运行多个选定场景。`qa-channel` 默认并发度为 4(受所选场景数量限制)。使用 `--concurrency <count>` 调整工作进程数,或使用 `--concurrency 1` 切换为旧的串行通道。
|
||||
- 任一场景失败时,以非零状态码退出。若你希望生成产物但不以失败退出码结束,请使用 `--allow-failures`。
|
||||
- 支持提供商模式 `live-frontier`、`mock-openai` 和 `aimock`。`aimock` 会启动一个本地的 AIMock 支持的提供商服务器,用于实验性 fixture 和协议模拟覆盖,而不会替代具备场景感知能力的 `mock-openai` 通道。
|
||||
- `pnpm openclaw qa suite --runner multipass`
|
||||
- 在一次性的 Multipass Linux VM 中运行相同的 QA 测试套件。
|
||||
- 保持与主机上 `qa suite` 相同的场景选择行为。
|
||||
- 在一次性 Multipass Linux VM 中运行相同的 QA 测试套件。
|
||||
- 保持与宿主机上 `qa suite` 相同的场景选择行为。
|
||||
- 复用与 `qa suite` 相同的提供商 / 模型选择标志。
|
||||
- live 运行会转发适合来宾环境的受支持 QA 凭证输入:基于环境变量的提供商密钥、QA live 提供商配置路径,以及存在时的 `CODEX_HOME`。
|
||||
- 输出目录必须保持在仓库根目录下,以便来宾环境可通过挂载的工作区回写。
|
||||
- 会将常规 QA 报告 + 摘要以及 Multipass 日志写入 `.artifacts/qa-e2e/...`。
|
||||
- 实时运行会转发适合访客机使用的受支持 QA 凭证输入:基于环境变量的提供商密钥、QA 实时提供商配置路径,以及存在时的 `CODEX_HOME`。
|
||||
- 输出目录必须保持在仓库根目录下,以便访客机可以通过挂载的工作区写回。
|
||||
- 会在 `.artifacts/qa-e2e/...` 下写入常规 QA 报告、摘要以及 Multipass 日志。
|
||||
- `pnpm qa:lab:up`
|
||||
- 启动基于 Docker 的 QA 站点,用于偏操作员风格的 QA 工作。
|
||||
- 启动基于 Docker 的 QA 站点,用于面向操作人员风格的 QA 工作。
|
||||
- `pnpm test:docker:npm-onboard-channel-agent`
|
||||
- 从当前检出构建 npm tarball,在 Docker 中全局安装,运行非交互式 OpenAI API 密钥新手引导,默认配置 Telegram,验证启用插件会按需安装运行时依赖,运行 doctor,并针对模拟的 OpenAI 端点执行一次本地智能体轮次。
|
||||
- 使用 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord` 可在 Discord 上运行相同的打包安装 lane。
|
||||
- 从当前检出构建一个 npm tarball,在 Docker 中全局安装,运行非交互式 OpenAI API key 新手引导,默认配置 Telegram,验证启用插件时会按需安装运行时依赖,运行 doctor,并针对模拟的 OpenAI 端点执行一次本地智能体轮次。
|
||||
- 使用 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord` 可在 Discord 上运行同一条打包安装通道。
|
||||
- `pnpm test:docker:session-runtime-context`
|
||||
- 为嵌入式运行时上下文转录运行一个确定性的已构建应用 Docker 冒烟测试。它会验证隐藏的 OpenClaw 运行时上下文被持久化为非展示型自定义消息,而不是泄漏到可见用户轮次中;随后注入一个受影响的损坏会话 JSONL,并验证 `openclaw doctor --fix` 会将其重写到当前分支并保留备份。
|
||||
- 在 Docker 中运行一个针对嵌入式运行时上下文转录的确定性已构建应用冒烟测试。它会验证隐藏的 OpenClaw 运行时上下文被持久化为不可显示的自定义消息,而不会泄漏到可见的用户轮次中;然后植入一个受影响的损坏会话 JSONL,并验证 `openclaw doctor --fix` 会将其重写到当前分支并保留备份。
|
||||
- `pnpm test:docker:npm-telegram-live`
|
||||
- 在 Docker 中安装一个已发布的 OpenClaw 包,运行已安装包的新手引导,通过已安装的 CLI 配置 Telegram,然后复用 live Telegram QA lane,并将该已安装包作为被测 Gateway 网关。
|
||||
- 默认为 `OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC=openclaw@beta`。
|
||||
- 使用与 `pnpm openclaw qa telegram` 相同的 Telegram 环境变量凭证或 Convex 凭证来源。对于 CI / 发布自动化,设置 `OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE=convex`,再加上 `OPENCLAW_QA_CONVEX_SITE_URL` 和角色密钥。如果在 CI 中存在 `OPENCLAW_QA_CONVEX_SITE_URL` 和 Convex 角色密钥,Docker 包装器会自动选择 Convex。
|
||||
- `OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE=ci|maintainer` 会仅为此 lane 覆盖共享的 `OPENCLAW_QA_CREDENTIAL_ROLE`。
|
||||
- GitHub Actions 将此 lane 暴露为手动维护者工作流 `NPM Telegram Beta E2E`。它不会在合并时运行。该工作流使用 `qa-live-shared` 环境和 Convex CI 凭证租约。
|
||||
- 在 Docker 中安装一个已发布的 OpenClaw 包,运行已安装包的新手引导,通过已安装的 CLI 配置 Telegram,然后复用实时 Telegram QA 通道,并将该已安装包作为被测 Gateway 网关。
|
||||
- 默认使用 `OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC=openclaw@beta`。
|
||||
- 使用与 `pnpm openclaw qa telegram` 相同的 Telegram 环境变量凭证或 Convex 凭证来源。对于 CI / 发布自动化,设置 `OPENCLAW_NPM_TELEGRAM_CREDENTIAL_SOURCE=convex`,以及 `OPENCLAW_QA_CONVEX_SITE_URL` 和角色密钥。如果在 CI 中存在 `OPENCLAW_QA_CONVEX_SITE_URL` 和 Convex 角色密钥,Docker 包装器会自动选择 Convex。
|
||||
- `OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE=ci|maintainer` 仅为此通道覆盖共享的 `OPENCLAW_QA_CREDENTIAL_ROLE`。
|
||||
- GitHub Actions 将此通道公开为手动维护者工作流 `NPM Telegram Beta E2E`。它不会在合并时运行。该工作流使用 `qa-live-shared` 环境和 Convex CI 凭证租约。
|
||||
- `pnpm test:docker:bundled-channel-deps`
|
||||
- 在 Docker 中打包并安装当前 OpenClaw 构建,使用已配置的 OpenAI 启动 Gateway 网关,然后通过编辑配置启用内置的渠道 / 插件。
|
||||
- 验证设置发现阶段会让未配置插件的运行时依赖保持缺失状态;首次配置后的 Gateway 网关 或 doctor 运行会按需安装每个内置插件的运行时依赖;第二次重启不会重新安装已经激活的依赖。
|
||||
- 还会安装一个已知较旧的 npm 基线版本,在运行 `openclaw update --tag <candidate>` 前启用 Telegram,并验证候选版本的更新后 doctor 会修复内置渠道运行时依赖,而无需 harness 侧的 postinstall 修复。
|
||||
- 在 Docker 中打包并安装当前 OpenClaw 构建,在已配置 OpenAI 的情况下启动 Gateway 网关,然后通过配置编辑启用内置 channel / plugins。
|
||||
- 验证设置发现流程会让未配置插件的运行时依赖保持缺失状态,第一次配置后的 Gateway 网关或 doctor 运行会按需安装每个内置插件的运行时依赖,而第二次重启不会重新安装已激活的依赖。
|
||||
- 还会安装一个已知的较旧 npm 基线版本,在运行 `openclaw update --tag <candidate>` 前启用 Telegram,并验证候选版本的更新后 doctor 会修复内置 channel 的运行时依赖,而无需 harness 侧的 postinstall 修复。
|
||||
- `pnpm test:parallels:npm-update`
|
||||
- 在 Parallels 来宾环境中运行原生打包安装更新冒烟测试。每个选定平台都会先安装指定的基线包,然后在同一来宾中运行已安装的 `openclaw update` 命令,并验证已安装版本、更新状态、Gateway 网关 就绪情况,以及一次本地智能体轮次。
|
||||
- 在迭代单个来宾时,使用 `--platform macos`、`--platform windows` 或 `--platform linux`。使用 `--json` 获取摘要产物路径和每个 lane 的状态。
|
||||
- 用主机超时包装较长的本地运行,以避免 Parallels 传输卡顿耗尽其余测试窗口:
|
||||
- 在 Parallels 访客机中运行原生打包安装更新冒烟测试。每个选定平台会先安装请求的基线包,然后在同一访客机中运行已安装的 `openclaw update` 命令,并验证已安装版本、更新状态、Gateway 网关就绪情况,以及一次本地智能体轮次。
|
||||
- 在迭代单个访客机时,使用 `--platform macos`、`--platform windows` 或 `--platform linux`。使用 `--json` 获取摘要产物路径和每条通道状态。
|
||||
- 使用宿主机超时包装长时间本地运行,以防 Parallels 传输卡住而耗尽剩余测试窗口:
|
||||
|
||||
```bash
|
||||
timeout --foreground 150m pnpm test:parallels:npm-update -- --json
|
||||
timeout --foreground 90m pnpm test:parallels:npm-update -- --platform windows --json
|
||||
```
|
||||
|
||||
- 该脚本会将嵌套 lane 日志写入 `/tmp/openclaw-parallels-npm-update.*`。在认定外层包装器卡住之前,请检查 `windows-update.log`、`macos-update.log` 或 `linux-update.log`。
|
||||
- 在冷启动来宾环境中,Windows 更新可能会在更新后的 doctor / 运行时依赖修复阶段花费 10 到 15 分钟;只要嵌套的 npm 调试日志仍在推进,这仍然是健康状态。
|
||||
- 不要将这个聚合包装器与单独的 Parallels macOS、Windows 或 Linux 冒烟 lane 并行运行。它们共享 VM 状态,可能会在快照恢复、包服务或来宾 Gateway 网关 状态上发生冲突。
|
||||
- 更新后验证会运行常规的内置插件表面,因为诸如语音、图像生成和媒体理解等能力门面都是通过内置运行时 API 加载的,即使智能体轮次本身只检查简单的文本响应也是如此。
|
||||
- 该脚本会将嵌套通道日志写入 `/tmp/openclaw-parallels-npm-update.*`。在认定外层包装器卡住之前,请先检查 `windows-update.log`、`macos-update.log` 或 `linux-update.log`。
|
||||
- 在冷启动访客机上,Windows 更新可能会在更新后的 doctor / 运行时依赖修复阶段耗时 10 到 15 分钟;只要嵌套的 npm 调试日志仍在推进,这仍然属于健康状态。
|
||||
- 不要将这个聚合包装器与单独的 Parallels macOS、Windows 或 Linux 冒烟通道并行运行。它们共享 VM 状态,可能在快照恢复、包服务或访客机 Gateway 网关状态上发生冲突。
|
||||
- 更新后的验证会运行常规的内置插件表面,因为语音、图像生成和媒体理解等能力门面是通过内置运行时 API 加载的,即使智能体轮次本身只检查简单的文本响应。
|
||||
|
||||
- `pnpm openclaw qa aimock`
|
||||
- 仅启动本地 AIMock 提供商服务器,用于直接协议冒烟测试。
|
||||
- `pnpm openclaw qa matrix`
|
||||
- 针对一次性的、基于 Docker 的 Tuwunel homeserver 运行 Matrix live QA lane。
|
||||
- 这个 QA 主机目前仅用于仓库 / 开发环境。打包后的 OpenClaw 安装不包含 `qa-lab`,因此也不会暴露 `openclaw qa`。
|
||||
- 针对一次性 Docker 支持的 Tuwunel homeserver 运行 Matrix 实时 QA 通道。
|
||||
- 这个 QA 宿主目前仅用于仓库 / 开发环境。打包后的 OpenClaw 安装不包含 `qa-lab`,因此不会暴露 `openclaw qa`。
|
||||
- 仓库检出会直接加载内置运行器;不需要单独的插件安装步骤。
|
||||
- 预配三个临时 Matrix 用户(`driver`、`sut`、`observer`)以及一个私有房间,然后启动一个 QA Gateway 网关 子进程,并使用真实的 Matrix 插件作为 SUT 传输层。
|
||||
- 默认使用固定的稳定版 Tuwunel 镜像 `ghcr.io/matrix-construct/tuwunel:v1.5.1`。当你需要测试其他镜像时,可使用 `OPENCLAW_QA_MATRIX_TUWUNEL_IMAGE` 覆盖。
|
||||
- Matrix 不暴露共享凭证来源标志,因为该 lane 会在本地预配一次性用户。
|
||||
- 会将 Matrix QA 报告、摘要、observed-events 产物,以及合并后的 stdout / stderr 输出日志写入 `.artifacts/qa-e2e/...`。
|
||||
- 默认会输出进度,并通过 `OPENCLAW_QA_MATRIX_TIMEOUT_MS` 强制执行硬性运行超时(默认 30 分钟)。清理由 `OPENCLAW_QA_MATRIX_CLEANUP_TIMEOUT_MS` 限定,失败信息中会包含用于恢复的 `docker compose ... down --remove-orphans` 命令。
|
||||
- 预配三个临时 Matrix 用户(`driver`、`sut`、`observer`)以及一个私有房间,然后启动一个以真实 Matrix 插件作为 SUT 传输层的 QA gateway 子进程。
|
||||
- 默认使用固定稳定版 Tuwunel 镜像 `ghcr.io/matrix-construct/tuwunel:v1.5.1`。当你需要测试不同镜像时,可使用 `OPENCLAW_QA_MATRIX_TUWUNEL_IMAGE` 覆盖。
|
||||
- Matrix 不暴露共享凭证来源标志,因为该通道会在本地预配一次性用户。
|
||||
- 会在 `.artifacts/qa-e2e/...` 下写入 Matrix QA 报告、摘要、observed-events 产物,以及合并后的 stdout / stderr 输出日志。
|
||||
- 默认会输出进度,并通过 `OPENCLAW_QA_MATRIX_TIMEOUT_MS` 强制执行硬性运行超时(默认 30 分钟)。清理由 `OPENCLAW_QA_MATRIX_CLEANUP_TIMEOUT_MS` 限制,失败信息中会包含恢复命令 `docker compose ... down --remove-orphans`。
|
||||
- `pnpm openclaw qa telegram`
|
||||
- 使用环境变量中的 driver 和 SUT bot token,针对真实私有群组运行 Telegram live QA lane。
|
||||
- 使用环境变量中的 driver 和 SUT 机器人令牌,针对真实私有群组运行 Telegram 实时 QA 通道。
|
||||
- 需要 `OPENCLAW_QA_TELEGRAM_GROUP_ID`、`OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN` 和 `OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`。群组 id 必须是 Telegram 聊天的数字 id。
|
||||
- 支持 `--credential-source convex` 以使用共享池化凭证。默认使用环境变量模式,或设置 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex` 以启用池化租约。
|
||||
- 只要任一场景失败,就会以非零状态退出。若你希望保留产物但不以失败退出码结束,可使用 `--allow-failures`。
|
||||
- 需要两个不同的 bot 位于同一个私有群组中,并且 SUT bot 需要暴露 Telegram 用户名。
|
||||
- 为了实现稳定的 bot 对 bot 观察,请在 `@BotFather` 中为两个 bot 都启用 Bot-to-Bot Communication Mode,并确保 driver bot 能观察群组中的 bot 流量。
|
||||
- 会将 Telegram QA 报告、摘要和 observed-messages 产物写入 `.artifacts/qa-e2e/...`。回复类场景会包含从 driver 发送请求到观察到 SUT 回复之间的 RTT。
|
||||
- 任一场景失败时,以非零状态码退出。若你希望生成产物但不以失败退出码结束,请使用 `--allow-failures`。
|
||||
- 需要同一私有群组中的两个不同机器人,且 SUT 机器人需要公开 Telegram 用户名。
|
||||
- 为了稳定地观察机器人之间的通信,请在 `@BotFather` 中为两个机器人都启用 Bot-to-Bot Communication Mode,并确保 driver 机器人可以观察群组中的机器人流量。
|
||||
- 会在 `.artifacts/qa-e2e/...` 下写入 Telegram QA 报告、摘要和 observed-messages 产物。回复类场景还包含从 driver 发送请求到观察到 SUT 回复的 RTT。
|
||||
|
||||
live 传输 lane 共享一份标准契约,以避免新传输出现漂移:
|
||||
实时传输通道共享同一个标准契约,因此新增传输协议时不会发生漂移:
|
||||
|
||||
`qa-channel` 仍然是覆盖面更广的合成 QA 测试套件,不属于 live
|
||||
传输覆盖矩阵的一部分。
|
||||
`qa-channel` 仍然是广泛的合成 QA 测试套件,不属于实时传输覆盖矩阵的一部分。
|
||||
|
||||
| Lane | Canary | Mention gating | Allowlist block | Top-level reply | Restart resume | Thread follow-up | Thread isolation | Reaction observation | Help command |
|
||||
| 通道 | Canary | Mention gating | Allowlist block | 顶层回复 | 重启恢复 | 线程后续跟进 | 线程隔离 | 反应观察 | 帮助命令 |
|
||||
| -------- | ------ | -------------- | --------------- | --------------- | -------------- | ---------------- | ---------------- | -------------------- | ------------ |
|
||||
| Matrix | x | x | x | x | x | x | x | x | |
|
||||
| Telegram | x | | | | | | | | x |
|
||||
|
||||
### 通过 Convex 共享 Telegram 凭证(v1)
|
||||
|
||||
当为 `openclaw qa telegram` 启用 `--credential-source convex`(或 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`)时,QA lab 会从一个由 Convex 支持的池中获取独占租约,在 lane 运行期间持续发送该租约的心跳,并在关闭时释放租约。
|
||||
当为 `openclaw qa telegram` 启用 `--credential-source convex`(或 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`)时,QA lab 会从 Convex 支持的凭证池中获取一个独占租约,在通道运行期间对该租约发送心跳,并在关闭时释放该租约。
|
||||
|
||||
参考的 Convex 项目脚手架:
|
||||
|
||||
- `qa/convex-credential-broker/`
|
||||
|
||||
必需的环境变量:
|
||||
所需环境变量:
|
||||
|
||||
- `OPENCLAW_QA_CONVEX_SITE_URL`(例如 `https://your-deployment.convex.site`)
|
||||
- 针对所选角色的一个密钥:
|
||||
- `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER` 用于 `maintainer`
|
||||
- `OPENCLAW_QA_CONVEX_SECRET_CI` 用于 `ci`
|
||||
- `maintainer` 使用 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`
|
||||
- `ci` 使用 `OPENCLAW_QA_CONVEX_SECRET_CI`
|
||||
- 凭证角色选择:
|
||||
- CLI:`--credential-role maintainer|ci`
|
||||
- 环境变量默认值:`OPENCLAW_QA_CREDENTIAL_ROLE`(在 CI 中默认 `ci`,否则默认 `maintainer`)
|
||||
- 环境变量默认值:`OPENCLAW_QA_CREDENTIAL_ROLE`(在 CI 中默认为 `ci`,否则默认为 `maintainer`)
|
||||
|
||||
可选环境变量:
|
||||
|
||||
@ -165,15 +163,14 @@ live 传输 lane 共享一份标准契约,以避免新传输出现漂移:
|
||||
- `OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS`(默认 `90000`)
|
||||
- `OPENCLAW_QA_CREDENTIAL_HTTP_TIMEOUT_MS`(默认 `15000`)
|
||||
- `OPENCLAW_QA_CONVEX_ENDPOINT_PREFIX`(默认 `/qa-credentials/v1`)
|
||||
- `OPENCLAW_QA_CREDENTIAL_OWNER_ID`(可选的跟踪 id)
|
||||
- `OPENCLAW_QA_CREDENTIAL_OWNER_ID`(可选的追踪 id)
|
||||
- `OPENCLAW_QA_ALLOW_INSECURE_HTTP=1` 允许在仅限本地开发时使用 loopback `http://` Convex URL。
|
||||
|
||||
正常运行时,`OPENCLAW_QA_CONVEX_SITE_URL` 应使用 `https://`。
|
||||
在正常运行中,`OPENCLAW_QA_CONVEX_SITE_URL` 应使用 `https://`。
|
||||
|
||||
维护者管理命令(池添加 / 删除 / 列表)必须明确使用
|
||||
`OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`。
|
||||
维护者管理命令(池添加 / 删除 / 列表)必须明确使用 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`。
|
||||
|
||||
供维护者使用的 CLI 辅助命令:
|
||||
面向维护者的 CLI 辅助命令:
|
||||
|
||||
```bash
|
||||
pnpm openclaw qa credentials doctor
|
||||
@ -182,15 +179,14 @@ pnpm openclaw qa credentials list --kind telegram
|
||||
pnpm openclaw qa credentials remove --credential-id <credential-id>
|
||||
```
|
||||
|
||||
在 live 运行前使用 `doctor` 检查 Convex 站点 URL、broker 密钥、
|
||||
端点前缀、HTTP 超时以及管理 / 列表可达性,且不会打印密钥值。在脚本和 CI 工具中使用 `--json` 获取机器可读输出。
|
||||
在实时运行前使用 `doctor`,以检查 Convex site URL、broker 密钥、端点前缀、HTTP 超时,以及 admin / list 可达性,同时不打印密钥值。在脚本和 CI 工具中使用 `--json` 可获得机器可读输出。
|
||||
|
||||
默认端点契约(`OPENCLAW_QA_CONVEX_SITE_URL` + `/qa-credentials/v1`):
|
||||
|
||||
- `POST /acquire`
|
||||
- 请求:`{ kind, ownerId, actorRole, leaseTtlMs, heartbeatIntervalMs }`
|
||||
- 成功:`{ status: "ok", credentialId, leaseToken, payload, leaseTtlMs?, heartbeatIntervalMs? }`
|
||||
- 已耗尽 / 可重试:`{ status: "error", code: "POOL_EXHAUSTED" | "NO_CREDENTIAL_AVAILABLE", ... }`
|
||||
- 耗尽 / 可重试:`{ status: "error", code: "POOL_EXHAUSTED" | "NO_CREDENTIAL_AVAILABLE", ... }`
|
||||
- `POST /heartbeat`
|
||||
- 请求:`{ kind, ownerId, actorRole, credentialId, leaseToken, leaseTtlMs }`
|
||||
- 成功:`{ status: "ok" }`(或空的 `2xx`)
|
||||
@ -211,23 +207,23 @@ pnpm openclaw qa credentials remove --credential-id <credential-id>
|
||||
Telegram 类型的负载结构:
|
||||
|
||||
- `{ groupId: string, driverToken: string, sutToken: string }`
|
||||
- `groupId` 必须是 Telegram 聊天数字 id 的字符串。
|
||||
- `admin/add` 会在 `kind: "telegram"` 时验证此结构,并拒绝格式错误的负载。
|
||||
- `groupId` 必须是 Telegram 聊天数字 id 的字符串形式。
|
||||
- `admin/add` 会对 `kind: "telegram"` 校验该结构,并拒绝格式错误的负载。
|
||||
|
||||
### 向 QA 添加一个渠道
|
||||
|
||||
向 markdown QA 系统添加一个渠道只需要两样东西:
|
||||
要将一个渠道加入基于 Markdown 的 QA 系统,必须且只需要两样东西:
|
||||
|
||||
1. 该渠道的传输适配器。
|
||||
2. 一个用于验证该渠道契约的场景包。
|
||||
2. 一个用于验证渠道契约的场景包。
|
||||
|
||||
当共享 `qa-lab` 主机可以负责流程时,不要新增顶层 QA 命令根。
|
||||
当共享的 `qa-lab` 宿主可以承载流程时,不要新增顶层 QA 命令根。
|
||||
|
||||
`qa-lab` 负责共享主机机制:
|
||||
`qa-lab` 负责共享宿主机制:
|
||||
|
||||
- `openclaw qa` 命令根
|
||||
- 测试套件启动与拆除
|
||||
- worker 并发
|
||||
- 测试套件启动与清理
|
||||
- 工作进程并发
|
||||
- 产物写入
|
||||
- 报告生成
|
||||
- 场景执行
|
||||
@ -235,35 +231,34 @@ Telegram 类型的负载结构:
|
||||
|
||||
运行器插件负责传输契约:
|
||||
|
||||
- 如何将 `openclaw qa <runner>` 挂载到共享 `qa` 根命令下
|
||||
- 如何为该传输配置 Gateway 网关
|
||||
- `openclaw qa <runner>` 如何挂载到共享的 `qa` 根命令之下
|
||||
- 如何为该传输协议配置 gateway
|
||||
- 如何检查就绪状态
|
||||
- 如何注入入站事件
|
||||
- 如何观察出站消息
|
||||
- 如何暴露转录和标准化后的传输状态
|
||||
- 如何执行基于传输的操作
|
||||
- 如何处理传输特定的重置或清理
|
||||
- 如何暴露转录和标准化的传输状态
|
||||
- 如何执行由传输支持的操作
|
||||
- 如何处理传输专属的重置或清理
|
||||
|
||||
新渠道的最低接入门槛是:
|
||||
|
||||
1. 保持 `qa-lab` 作为共享 `qa` 根命令的所有者。
|
||||
2. 在共享的 `qa-lab` 主机接口上实现传输运行器。
|
||||
3. 将传输特定机制保留在运行器插件或渠道 harness 内部。
|
||||
4. 将运行器挂载为 `openclaw qa <runner>`,而不是注册一个竞争性的根命令。
|
||||
运行器插件应在 `openclaw.plugin.json` 中声明 `qaRunners`,并在 `runtime-api.ts` 中导出匹配的 `qaRunnerCliRegistrations` 数组。
|
||||
保持 `runtime-api.ts` 轻量;延迟加载的 CLI 和运行器执行应保留在单独的入口点之后。
|
||||
5. 在带主题的 `qa/scenarios/` 目录下编写或改造 markdown 场景。
|
||||
6. 对新场景使用通用场景辅助函数。
|
||||
7. 除非仓库正在进行有意的迁移,否则要保持现有兼容别名继续工作。
|
||||
2. 在共享的 `qa-lab` 宿主接缝上实现传输运行器。
|
||||
3. 将传输专属机制保留在运行器插件或渠道 harness 内部。
|
||||
4. 将运行器挂载为 `openclaw qa <runner>`,而不是注册一个竞争性的根命令。
|
||||
运行器插件应在 `openclaw.plugin.json` 中声明 `qaRunners`,并从 `runtime-api.ts` 导出匹配的 `qaRunnerCliRegistrations` 数组。保持 `runtime-api.ts` 轻量;延迟 CLI 和运行器执行应保留在单独的入口点之后。
|
||||
5. 在按主题划分的 `qa/scenarios/` 目录下编写或改造 Markdown 场景。
|
||||
6. 为新场景使用通用场景辅助函数。
|
||||
7. 除非仓库正在进行有意迁移,否则保持现有兼容别名继续可用。
|
||||
|
||||
决策规则很严格:
|
||||
决策规则非常严格:
|
||||
|
||||
- 如果某个行为可以在 `qa-lab` 中表达一次,就放进 `qa-lab`。
|
||||
- 如果某个行为依赖于单一渠道传输,就将其保留在该运行器插件或插件 harness 中。
|
||||
- 如果某个场景需要一个以上渠道都能使用的新能力,就添加一个通用辅助函数,而不是在 `suite.ts` 中加入渠道特定分支。
|
||||
- 如果某个行为只对一种传输有意义,就让该场景保持传输特定,并在场景契约中明确说明。
|
||||
- 如果某种行为可以在 `qa-lab` 中统一表达一次,就把它放在 `qa-lab`。
|
||||
- 如果某种行为依赖单一渠道传输,就将它保留在该运行器插件或插件 harness 中。
|
||||
- 如果某个场景需要多个渠道都能使用的新能力,应添加通用辅助函数,而不是在 `suite.ts` 中加入渠道专属分支。
|
||||
- 如果某种行为只对单一传输有意义,就让场景保持传输专属,并在场景契约中明确说明。
|
||||
|
||||
新场景推荐使用的通用辅助函数名称是:
|
||||
新场景推荐使用的通用辅助函数名称为:
|
||||
|
||||
- `waitForTransportReady`
|
||||
- `waitForChannelReady`
|
||||
@ -286,304 +281,300 @@ Telegram 类型的负载结构:
|
||||
- `formatConversationTranscript`
|
||||
- `resetBus`
|
||||
|
||||
新的渠道工作应使用通用辅助函数名称。
|
||||
兼容别名的存在是为了避免一次性强制迁移,而不是为了成为
|
||||
新场景编写的范式。
|
||||
新的渠道工作应使用通用辅助函数名称。
|
||||
兼容别名的存在是为了避免一次性迁移,不应作为新场景编写的范式。
|
||||
|
||||
## 测试套件(哪些内容在哪里运行)
|
||||
## 测试套件(各自在哪里运行)
|
||||
|
||||
可以将这些测试套件理解为“真实度递增”(同时不稳定性 / 成本也递增):
|
||||
可以把这些测试套件理解为“真实性逐步提升”(同时波动性 / 成本也逐步升高):
|
||||
|
||||
### 单元 / 集成(默认)
|
||||
|
||||
- 命令:`pnpm test`
|
||||
- 配置:未定向的运行使用 `vitest.full-*.config.ts` 分片集合,并且可能会将多项目分片展开为按项目划分的配置,以便并行调度
|
||||
- 文件:核心 / 单元清单位于 `src/**/*.test.ts`、`packages/**/*.test.ts`、`test/**/*.test.ts`,以及由 `vitest.unit.config.ts` 覆盖的白名单 `ui` Node 测试
|
||||
- 配置:非定向运行使用 `vitest.full-*.config.ts` 分片集合,并且为了并行调度,可能会将多项目分片展开为按项目划分的配置
|
||||
- 文件:核心 / 单元测试清单位于 `src/**/*.test.ts`、`packages/**/*.test.ts`、`test/**/*.test.ts`,以及 `vitest.unit.config.ts` 覆盖的白名单 `ui` Node 测试
|
||||
- 范围:
|
||||
- 纯单元测试
|
||||
- 进程内集成测试(Gateway 网关 认证、路由、工具、解析、配置)
|
||||
- 针对已知缺陷的确定性回归测试
|
||||
- 进程内集成测试(gateway 凭证、路由、工具、解析、配置)
|
||||
- 已知缺陷的确定性回归测试
|
||||
- 预期:
|
||||
- 在 CI 中运行
|
||||
- 不需要真实密钥
|
||||
- 应该快速且稳定
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="项目、分片和定向 lanes">
|
||||
<Accordion title="项目、分片和定向通道">
|
||||
|
||||
- 未定向的 `pnpm test` 会运行 12 个较小的分片配置(`core-unit-fast`、`core-unit-src`、`core-unit-security`、`core-unit-ui`、`core-unit-support`、`core-support-boundary`、`core-contracts`、`core-bundled`、`core-runtime`、`agentic`、`auto-reply`、`extensions`),而不是一个巨大的原生根项目进程。这样可以降低高负载机器上的峰值 RSS,并避免 auto-reply / 扩展工作拖累无关测试套件。
|
||||
- `pnpm test --watch` 仍然使用原生根级 `vitest.config.ts` 项目图,因为多分片的观察循环并不现实。
|
||||
- `pnpm test`、`pnpm test:watch` 和 `pnpm test:perf:imports` 会先通过定向 lane 路由显式文件 / 目录目标,因此 `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` 无需付出完整根项目启动的代价。
|
||||
- 当 diff 只涉及可路由的源码 / 测试文件时,`pnpm test:changed` 会将变更过的 git 路径展开为相同的定向 lane;配置 / setup 编辑仍会回退到更广泛的根项目重跑。
|
||||
- `pnpm check:changed` 是窄范围工作时的常规智能本地门禁。它会将 diff 分类到 core、core tests、extensions、extension tests、apps、docs、发布元数据和工具中,然后运行匹配的类型检查 / lint / 测试 lane。公开的插件 SDK 和插件契约变更会额外包含一次扩展验证,因为扩展依赖这些核心契约。仅发布元数据的版本号变更会运行定向的版本 / 配置 / 根依赖检查,而不是完整测试套件,并带有一个保护机制:拒绝顶层版本字段之外的包变更。
|
||||
- 来自 agents、commands、plugins、auto-reply 辅助函数、`plugin-sdk` 以及类似纯工具区域的轻量导入单元测试会通过 `unit-fast` lane 路由,该 lane 会跳过 `test/setup-openclaw-runtime.ts`;有状态 / 运行时较重的文件则保持在现有 lanes 上。
|
||||
- 某些选定的 `plugin-sdk` 和 `commands` 辅助源码文件也会将 changed 模式运行映射到这些轻量 lanes 中的显式同级测试,因此辅助函数编辑无需为该目录重跑完整的重型测试套件。
|
||||
- `auto-reply` 拥有专门的桶,用于顶层 core 辅助函数、顶层 `reply.*` 集成测试,以及 `src/auto-reply/reply/**` 子树。CI 还会进一步将 reply 子树拆分为 agent-runner、dispatch 和 commands / state-routing 分片,这样一个导入较重的桶就不会独占整个 Node 尾部时间。
|
||||
- 非定向的 `pnpm test` 会运行 12 个较小的分片配置(`core-unit-fast`、`core-unit-src`、`core-unit-security`、`core-unit-ui`、`core-unit-support`、`core-support-boundary`、`core-contracts`、`core-bundled`、`core-runtime`、`agentic`、`auto-reply`、`extensions`),而不是一个巨大的原生根项目进程。这样可以降低高负载机器上的峰值 RSS,并避免 auto-reply / extension 工作拖慢无关测试套件。
|
||||
- `pnpm test --watch` 仍然使用原生根 `vitest.config.ts` 项目图,因为多分片监听循环并不现实。
|
||||
- `pnpm test`、`pnpm test:watch` 和 `pnpm test:perf:imports` 会先通过定向通道处理显式文件 / 目录目标,因此 `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` 可以避免承担完整根项目启动开销。
|
||||
- 当差异只涉及可路由的源码 / 测试文件时,`pnpm test:changed` 会将变更的 git 路径展开到相同的定向通道中;配置 / setup 编辑仍会回退到广泛的根项目重跑。
|
||||
- `pnpm check:changed` 是窄范围工作时常规的智能本地门禁。它会将差异分类为 core、core tests、extensions、extension tests、apps、docs、release metadata 和 tooling,然后运行匹配的 typecheck / lint / test 通道。公开 Plugin SDK 和插件契约变更会额外包含一次 extension 校验,因为 extensions 依赖这些核心契约。仅发布元数据的版本提升会运行定向的版本 / 配置 / 根依赖检查,而不是完整测试套件,并带有一个防护,拒绝除顶层版本字段之外的 package 更改。
|
||||
- 来自 agents、commands、plugins、auto-reply 辅助函数、`plugin-sdk` 以及类似纯工具区域的轻导入单元测试,会路由到 `unit-fast` 通道,该通道会跳过 `test/setup-openclaw-runtime.ts`;有状态 / 运行时较重的文件则保留在现有通道中。
|
||||
- 部分选定的 `plugin-sdk` 和 `commands` 辅助源码文件,也会在 changed 模式运行时映射到这些轻量通道中的显式同级测试,因此辅助函数编辑不需要为该目录重跑完整的重型测试套件。
|
||||
- `auto-reply` 为顶层 core 辅助函数、顶层 `reply.*` 集成测试以及 `src/auto-reply/reply/**` 子树提供了专用分桶。CI 还会将 reply 子树进一步拆分为 agent-runner、dispatch 以及 commands / state-routing 分片,这样某一个导入较重的分桶就不会独占整个 Node 收尾阶段。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="嵌入式运行器覆盖范围">
|
||||
|
||||
- 当你修改消息工具发现输入或压缩运行时上下文时,要同时保持这两个层级的覆盖。
|
||||
- 为纯路由和标准化边界添加有针对性的辅助函数回归测试。
|
||||
- 当你修改消息工具发现输入或压缩运行时上下文时,请同时保留两个层级的覆盖。
|
||||
- 为纯路由和标准化边界添加聚焦的辅助函数回归测试。
|
||||
- 保持嵌入式运行器集成测试套件健康:
|
||||
`src/agents/pi-embedded-runner/compact.hooks.test.ts`、
|
||||
`src/agents/pi-embedded-runner/run.overflow-compaction.test.ts` 和
|
||||
`src/agents/pi-embedded-runner/run.overflow-compaction.loop.test.ts`。
|
||||
- 这些测试套件会验证带作用域的 id 和压缩行为仍然会流经真实的 `run.ts` / `compact.ts` 路径;仅辅助函数测试不足以替代这些集成路径。
|
||||
- 这些测试套件会验证作用域 id 和压缩行为仍然流经真实的 `run.ts` / `compact.ts` 路径;仅有辅助函数测试并不能充分替代这些集成路径。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Vitest 池和隔离默认值">
|
||||
|
||||
- 基础 Vitest 配置默认使用 `threads`。
|
||||
- 共享 Vitest 配置将 `isolate: false` 固定下来,并在根项目、e2e 和 live 配置中使用非隔离运行器。
|
||||
- 根 UI lane 保留其 `jsdom` setup 和优化器,但也运行在共享的非隔离运行器上。
|
||||
- 每个 `pnpm test` 分片都会从共享 Vitest 配置继承相同的 `threads` + `isolate: false` 默认值。
|
||||
- 共享 Vitest 配置固定设置 `isolate: false`,并在根项目、e2e 和实时配置中使用非隔离运行器。
|
||||
- 根 UI 通道保留其 `jsdom` setup 和优化器,但也运行在共享的非隔离运行器上。
|
||||
- 每个 `pnpm test` 分片都从共享 Vitest 配置继承相同的 `threads` + `isolate: false` 默认值。
|
||||
- `scripts/run-vitest.mjs` 默认会为 Vitest 子 Node 进程添加 `--no-maglev`,以减少大型本地运行期间的 V8 编译抖动。设置 `OPENCLAW_VITEST_ENABLE_MAGLEV=1` 可与原生 V8 行为进行对比。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="快速本地迭代">
|
||||
|
||||
- `pnpm changed:lanes` 会显示某个 diff 会触发哪些架构 lanes。
|
||||
- pre-commit 钩子仅负责格式化。它会重新暂存已格式化文件,不会运行 lint、类型检查或测试。
|
||||
- 在交接或推送前,如果你需要智能本地门禁,请显式运行 `pnpm check:changed`。公开的插件 SDK 和插件契约变更会额外包含一次扩展验证。
|
||||
- 当变更路径可以清晰映射到更小的测试套件时,`pnpm test:changed` 会通过定向 lanes 路由。
|
||||
- `pnpm changed:lanes` 会显示某个差异触发了哪些架构通道。
|
||||
- pre-commit 钩子仅负责格式化。它会重新暂存格式化后的文件,不会运行 lint、typecheck 或测试。
|
||||
- 在交接或推送前,当你需要智能本地门禁时,请显式运行 `pnpm check:changed`。公开 Plugin SDK 和插件契约变更会包含一次 extension 校验。
|
||||
- 当变更路径可以清晰映射到更小的测试套件时,`pnpm test:changed` 会通过定向通道进行路由。
|
||||
- `pnpm test:max` 和 `pnpm test:changed:max` 保持相同的路由行为,只是使用更高的 worker 上限。
|
||||
- 本地 worker 自动扩缩容是有意保守的,当主机负载平均值已经很高时会主动回退,因此默认情况下多个并发的 Vitest 运行造成的影响会更小。
|
||||
- 基础 Vitest 配置会将项目 / 配置文件标记为 `forceRerunTriggers`,以便测试接线发生变化时,changed 模式重跑仍然正确。
|
||||
- 配置会在受支持主机上保持 `OPENCLAW_VITEST_FS_MODULE_CACHE` 启用;如果你希望为直接分析指定一个明确的缓存位置,可设置 `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/abs/path`。
|
||||
- 本地 worker 自动缩放有意保持保守;当宿主机负载平均值已经较高时会主动回退,因此默认情况下多个并发 Vitest 运行造成的影响更小。
|
||||
- 基础 Vitest 配置会将项目 / 配置文件标记为 `forceRerunTriggers`,从而保证测试接线变更时 changed 模式重跑仍然正确。
|
||||
- 配置会在受支持宿主机上保持启用 `OPENCLAW_VITEST_FS_MODULE_CACHE`;如果你希望为直接性能分析指定一个明确缓存位置,可设置 `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/abs/path`。
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="性能调试">
|
||||
|
||||
- `pnpm test:perf:imports` 会启用 Vitest 导入耗时报告以及导入拆解输出。
|
||||
- `pnpm test:perf:imports:changed` 会将相同的分析视图限制到自 `origin/main` 以来发生变化的文件。
|
||||
- 分片耗时数据会写入 `.artifacts/vitest-shard-timings.json`。
|
||||
整个配置运行使用配置路径作为键;带 include 模式的 CI 分片会追加分片名称,以便单独跟踪经过筛选的分片。
|
||||
- 当某个热点测试仍将大部分时间花在启动导入上时,应将重依赖隐藏在狭窄的本地 `*.runtime.ts` 接口之后,并直接 mock 该接口,而不是仅为了传给 `vi.mock(...)` 就深度导入运行时辅助函数。
|
||||
- `pnpm test:perf:changed:bench -- --ref <git-ref>` 会针对该已提交 diff,将已路由的 `test:changed` 与原生根项目路径进行对比,并输出墙钟时间与 macOS 最大 RSS。
|
||||
- `pnpm test:perf:changed:bench -- --worktree` 会通过 `scripts/test-projects.mjs` 和根 Vitest 配置路由当前未提交工作树中的变更文件列表,并对其进行基准测试。
|
||||
- `pnpm test:perf:profile:main` 会为 Vitest / Vite 启动和转换开销写入主线程 CPU profile。
|
||||
- `pnpm test:perf:profile:runner` 会在禁用文件并行的情况下,为单元测试套件写入 runner CPU + 堆 profile。
|
||||
- `pnpm test:perf:imports:changed` 会将相同的性能分析视图限定到自 `origin/main` 以来变更的文件。
|
||||
- 分片耗时数据会写入 `.artifacts/vitest-shard-timings.json`。整套配置运行使用配置路径作为键;include-pattern CI 分片会附加分片名称,以便单独跟踪过滤后的分片。
|
||||
- 当某个热点测试的大部分时间仍耗费在启动导入上时,请将重型依赖放在狭窄的本地 `*.runtime.ts` 接缝之后,并直接 mock 该接缝,而不是为了传给 `vi.mock(...)` 就深度导入运行时辅助函数。
|
||||
- `pnpm test:perf:changed:bench -- --ref <git-ref>` 会将定向的 `test:changed` 与该已提交差异的原生根项目路径进行比较,并输出总耗时以及 macOS 最大 RSS。
|
||||
- `pnpm test:perf:changed:bench -- --worktree` 会通过 `scripts/test-projects.mjs` 和根 Vitest 配置,将当前未提交工作树的变更文件列表进行路由并做基准测试。
|
||||
- `pnpm test:perf:profile:main` 会为 Vitest / Vite 启动与转换开销写出主线程 CPU profile。
|
||||
- `pnpm test:perf:profile:runner` 会在禁用文件并行的情况下,为单元测试套件写出运行器 CPU + 堆 profile。
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### 稳定性(Gateway 网关)
|
||||
### 稳定性(gateway)
|
||||
|
||||
- 命令:`pnpm test:stability:gateway`
|
||||
- 配置:`vitest.gateway.config.ts`,强制单 worker
|
||||
- 范围:
|
||||
- 启动一个默认启用诊断的真实 loopback Gateway 网关
|
||||
- 通过诊断事件路径驱动合成的 Gateway 网关 消息、内存和大负载抖动
|
||||
- 启动一个默认启用诊断功能的真实 loopback Gateway 网关
|
||||
- 通过诊断事件路径驱动合成的 gateway 消息、memory 和大负载抖动
|
||||
- 通过 Gateway 网关 WS RPC 查询 `diagnostics.stability`
|
||||
- 覆盖诊断稳定性捆绑持久化辅助函数
|
||||
- 断言记录器保持有界,合成 RSS 样本保持在压力预算之下,并且每个会话队列深度最终回落到零
|
||||
- 覆盖诊断稳定性包持久化辅助函数
|
||||
- 断言记录器保持有界、合成 RSS 采样保持在压力预算之下,并且每个会话的队列深度会回落到零
|
||||
- 预期:
|
||||
- 对 CI 安全且无需密钥
|
||||
- 是用于稳定性回归跟进的窄 lane,不可替代完整的 Gateway 网关 测试套件
|
||||
- CI 安全且无需密钥
|
||||
- 这是用于稳定性回归跟进的窄范围通道,不可替代完整 Gateway 网关测试套件
|
||||
|
||||
### E2E(Gateway 网关 冒烟测试)
|
||||
### E2E(gateway 冒烟)
|
||||
|
||||
- 命令:`pnpm test:e2e`
|
||||
- 配置:`vitest.e2e.config.ts`
|
||||
- 文件:`src/**/*.e2e.test.ts`、`test/**/*.e2e.test.ts`,以及位于 `extensions/` 下的内置插件 E2E 测试
|
||||
- 文件:`src/**/*.e2e.test.ts`、`test/**/*.e2e.test.ts`,以及 `extensions/` 下的内置插件 E2E 测试
|
||||
- 运行时默认值:
|
||||
- 使用带 `isolate: false` 的 Vitest `threads`,与仓库其他部分保持一致。
|
||||
- 使用 Vitest `threads` 且 `isolate: false`,与仓库其余部分保持一致。
|
||||
- 使用自适应 worker(CI:最多 2 个,本地:默认 1 个)。
|
||||
- 默认以 silent 模式运行,以减少控制台 I/O 开销。
|
||||
- 默认以静默模式运行,以减少控制台 I/O 开销。
|
||||
- 常用覆盖项:
|
||||
- `OPENCLAW_E2E_WORKERS=<n>` 用于强制指定 worker 数量(上限为 16)。
|
||||
- `OPENCLAW_E2E_VERBOSE=1` 用于重新启用详细控制台输出。
|
||||
- 范围:
|
||||
- 多实例 Gateway 网关 端到端行为
|
||||
- WebSocket / HTTP 表面、节点配对和更重的网络交互
|
||||
- 多实例 gateway 端到端行为
|
||||
- WebSocket / HTTP 表面、节点配对以及更重的网络交互
|
||||
- 预期:
|
||||
- 在 CI 中运行(当流水线启用时)
|
||||
- 会在 CI 中运行(当流水线启用时)
|
||||
- 不需要真实密钥
|
||||
- 比单元测试有更多活动部件(可能更慢)
|
||||
|
||||
### E2E:OpenShell 后端冒烟测试
|
||||
### E2E:OpenShell 后端冒烟
|
||||
|
||||
- 命令:`pnpm test:e2e:openshell`
|
||||
- 文件:`extensions/openshell/src/backend.e2e.test.ts`
|
||||
- 范围:
|
||||
- 通过 Docker 在主机上启动一个隔离的 OpenShell Gateway 网关
|
||||
- 从一个临时本地 Dockerfile 创建一个沙箱
|
||||
- 通过真实的 `sandbox ssh-config` + SSH exec 执行 OpenClaw 的 OpenShell 后端
|
||||
- 通过沙箱文件系统桥验证远端规范文件系统行为
|
||||
- 通过 Docker 在宿主机上启动一个隔离的 OpenShell gateway
|
||||
- 从一个临时本地 Dockerfile 创建沙箱
|
||||
- 通过真实的 `sandbox ssh-config` + SSH exec 运行 OpenClaw 的 OpenShell 后端
|
||||
- 通过沙箱 fs bridge 验证远端规范化的文件系统行为
|
||||
- 预期:
|
||||
- 仅限显式启用;不属于默认 `pnpm test:e2e` 运行的一部分
|
||||
- 需要本地 `openshell` CLI 和可用的 Docker daemon
|
||||
- 使用隔离的 `HOME` / `XDG_CONFIG_HOME`,然后销毁测试 Gateway 网关 和沙箱
|
||||
- 仅按需启用;不属于默认 `pnpm test:e2e` 运行的一部分
|
||||
- 需要本地 `openshell` CLI 以及可用的 Docker daemon
|
||||
- 使用隔离的 `HOME` / `XDG_CONFIG_HOME`,然后销毁测试 gateway 和沙箱
|
||||
- 常用覆盖项:
|
||||
- `OPENCLAW_E2E_OPENSHELL=1`:在手动运行更广泛的 e2e 测试套件时启用该测试
|
||||
- `OPENCLAW_E2E_OPENSHELL=1`:手动运行更广泛的 e2e 测试套件时启用该测试
|
||||
- `OPENCLAW_E2E_OPENSHELL_COMMAND=/path/to/openshell`:指向非默认 CLI 二进制或包装脚本
|
||||
|
||||
### live(真实提供商 + 真实模型)
|
||||
### 实时(真实提供商 + 真实模型)
|
||||
|
||||
- 命令:`pnpm test:live`
|
||||
- 配置:`vitest.live.config.ts`
|
||||
- 文件:`src/**/*.live.test.ts`、`test/**/*.live.test.ts`,以及位于 `extensions/` 下的内置插件 live 测试
|
||||
- 默认:由 `pnpm test:live` **启用**(设置 `OPENCLAW_LIVE_TEST=1`)
|
||||
- 文件:`src/**/*.live.test.ts`、`test/**/*.live.test.ts`,以及 `extensions/` 下的内置插件实时测试
|
||||
- 默认值:由 `pnpm test:live` **启用**(设置 `OPENCLAW_LIVE_TEST=1`)
|
||||
- 范围:
|
||||
- “这个提供商 / 模型 _今天_ 在真实凭证下是否真的可用?”
|
||||
- 捕获提供商格式变化、工具调用怪癖、认证问题和速率限制行为
|
||||
- “这个提供商 / 模型在今天配合真实凭证是否真的可用?”
|
||||
- 捕获提供商格式变更、工具调用怪异行为、认证问题和限流行为
|
||||
- 预期:
|
||||
- 按设计不具备 CI 稳定性(真实网络、真实提供商策略、配额、故障)
|
||||
- 会产生费用 / 消耗速率限制
|
||||
- 应优先运行缩小范围的子集,而不是“全部”
|
||||
- live 运行会读取 `~/.profile` 以获取缺失的 API 密钥。
|
||||
- 默认情况下,live 运行仍会隔离 `HOME`,并将配置 / 认证材料复制到临时测试 home 中,以免单元测试 fixture 修改你的真实 `~/.openclaw`。
|
||||
- 仅当你明确需要让 live 测试使用真实 home 目录时,才设置 `OPENCLAW_LIVE_USE_REAL_HOME=1`。
|
||||
- `pnpm test:live` 现在默认使用更安静的模式:它会保留 `[live] ...` 进度输出,但会抑制额外的 `~/.profile` 提示,并静音 Gateway 网关 启动日志 / Bonjour 杂音。如果你想恢复完整启动日志,可设置 `OPENCLAW_LIVE_TEST_QUIET=0`。
|
||||
- API 密钥轮换(按提供商区分):设置逗号 / 分号格式的 `*_API_KEYS` 或 `*_API_KEY_1`、`*_API_KEY_2`(例如 `OPENAI_API_KEYS`、`ANTHROPIC_API_KEYS`、`GEMINI_API_KEYS`),或通过 `OPENCLAW_LIVE_*_KEY` 为 live 运行单独覆盖;测试会在收到速率限制响应时重试。
|
||||
- 按设计并非 CI 稳定(真实网络、真实提供商策略、配额、故障)
|
||||
- 会花钱 / 消耗速率限制
|
||||
- 优先运行缩小范围后的子集,而不是“全部”
|
||||
- 实时运行会加载 `~/.profile`,以补齐缺失的 API key。
|
||||
- 默认情况下,实时运行仍会隔离 `HOME`,并将配置 / 认证材料复制到临时测试 home 中,这样单元测试夹具就不会修改你真实的 `~/.openclaw`。
|
||||
- 只有在你有意让实时测试使用真实 home 目录时,才设置 `OPENCLAW_LIVE_USE_REAL_HOME=1`。
|
||||
- `pnpm test:live` 现在默认使用更安静的模式:会保留 `[live] ...` 进度输出,但会抑制额外的 `~/.profile` 提示,并静默 gateway 启动日志 / Bonjour 杂讯。如果你想恢复完整启动日志,请设置 `OPENCLAW_LIVE_TEST_QUIET=0`。
|
||||
- API key 轮换(按提供商区分):设置逗号 / 分号格式的 `*_API_KEYS`,或设置 `*_API_KEY_1`、`*_API_KEY_2`(例如 `OPENAI_API_KEYS`、`ANTHROPIC_API_KEYS`、`GEMINI_API_KEYS`),也可通过 `OPENCLAW_LIVE_*_KEY` 进行每次实时运行覆盖;测试会在收到限流响应时重试。
|
||||
- 进度 / 心跳输出:
|
||||
- live 测试套件现在会将进度行输出到 stderr,这样即使 Vitest 控制台捕获较安静,长时间的提供商调用也能看出仍在活动。
|
||||
- `vitest.live.config.ts` 会禁用 Vitest 控制台拦截,因此提供商 / Gateway 网关 进度行会在 live 运行期间立即流式输出。
|
||||
- 实时测试套件现在会将进度行输出到 stderr,因此即使 Vitest 控制台捕获较安静,长时间的提供商调用也能明显显示仍在活动。
|
||||
- `vitest.live.config.ts` 会禁用 Vitest 控制台拦截,因此提供商 / gateway 进度行在实时运行期间会立即流式输出。
|
||||
- 使用 `OPENCLAW_LIVE_HEARTBEAT_MS` 调整直接模型心跳。
|
||||
- 使用 `OPENCLAW_LIVE_GATEWAY_HEARTBEAT_MS` 调整 Gateway 网关 / 探测心跳。
|
||||
- 使用 `OPENCLAW_LIVE_GATEWAY_HEARTBEAT_MS` 调整 gateway / 探测心跳。
|
||||
|
||||
## 我应该运行哪个测试套件?
|
||||
|
||||
使用下面这个决策表:
|
||||
使用下表进行判断:
|
||||
|
||||
- 编辑逻辑 / 测试:运行 `pnpm test`(如果你改动很多,再运行 `pnpm test:coverage`)
|
||||
- 修改 Gateway 网关 网络 / WS 协议 / 配对:额外运行 `pnpm test:e2e`
|
||||
- 调试“我的 bot 挂了” / 提供商特定故障 / 工具调用:运行缩小范围的 `pnpm test:live`
|
||||
- 编辑逻辑 / 测试:运行 `pnpm test`(如果改动很多,再加上 `pnpm test:coverage`)
|
||||
- 触及 gateway 网络 / WS 协议 / 配对:额外运行 `pnpm test:e2e`
|
||||
- 调试“我的机器人挂了” / 提供商专属故障 / 工具调用:运行缩小范围的 `pnpm test:live`
|
||||
|
||||
## live(触网)测试
|
||||
## 实时(会触网)测试
|
||||
|
||||
关于 live 模型矩阵、CLI 后端冒烟测试、ACP 冒烟测试、Codex app-server
|
||||
harness,以及所有媒体提供商 live 测试(Deepgram、BytePlus(国际版)、ComfyUI、图像、
|
||||
音乐、视频、媒体 harness)——再加上 live 运行的凭证处理——请参阅
|
||||
[测试——live 测试套件](/zh-CN/help/testing-live)。
|
||||
关于实时模型矩阵、CLI 后端冒烟、ACP 冒烟、Codex app-server
|
||||
harness,以及所有媒体提供商实时测试(Deepgram、BytePlus、ComfyUI、图像、
|
||||
音乐、视频、媒体 harness)——以及实时运行的凭证处理——请参见
|
||||
[测试 —— 实时测试套件](/zh-CN/help/testing-live)。
|
||||
|
||||
## Docker 运行器(可选的“在 Linux 中可工作”检查)
|
||||
## Docker 运行器(可选的“在 Linux 中可用”检查)
|
||||
|
||||
这些 Docker 运行器分为两类:
|
||||
|
||||
- live 模型运行器:`test:docker:live-models` 和 `test:docker:live-gateway` 只会在仓库 Docker 镜像中运行各自匹配的 profile-key live 文件(`src/agents/models.profiles.live.test.ts` 和 `src/gateway/gateway-models.profiles.live.test.ts`),并挂载你的本地配置目录和工作区(如果已挂载,还会读取 `~/.profile`)。对应的本地入口点是 `test:live:models-profiles` 和 `test:live:gateway-profiles`。
|
||||
- Docker live 运行器默认采用更小的冒烟上限,以便完整的 Docker 扫描仍然可行:
|
||||
`test:docker:live-models` 默认设置 `OPENCLAW_LIVE_MAX_MODELS=12`,而
|
||||
`test:docker:live-gateway` 默认设置 `OPENCLAW_LIVE_GATEWAY_SMOKE=1`、
|
||||
`OPENCLAW_LIVE_GATEWAY_MAX_MODELS=8`、
|
||||
`OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=45000` 和
|
||||
`OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`。当你明确希望执行更大的穷尽式扫描时,可覆盖这些环境变量。
|
||||
- `test:docker:all` 会先通过 `test:docker:live-build` 构建一次 live Docker 镜像,然后在 live Docker lanes 中复用它。它还会通过 `test:docker:e2e-build` 构建一个共享的 `scripts/e2e/Dockerfile` 镜像,并在执行已构建应用的 E2E 容器冒烟运行器中复用它。这个聚合运行器使用加权本地调度器:`OPENCLAW_DOCKER_ALL_PARALLELISM` 控制进程槽位数,而资源上限会防止重型 live、npm 安装和多服务 lanes 同时启动。默认值为 10 个槽位、`OPENCLAW_DOCKER_ALL_LIVE_LIMIT=6`、`OPENCLAW_DOCKER_ALL_NPM_LIMIT=8` 和 `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`;只有当 Docker 主机拥有更多余量时,才调整 `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` 或 `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT`。该运行器默认执行 Docker 预检、移除陈旧的 OpenClaw E2E 容器、每 30 秒打印一次状态、将成功 lane 的耗时保存到 `.artifacts/docker-tests/lane-timings.json`,并在后续运行中根据这些耗时优先启动更长的 lanes。使用 `OPENCLAW_DOCKER_ALL_DRY_RUN=1` 可在不构建或运行 Docker 的情况下打印加权 lane 清单。
|
||||
- 实时模型运行器:`test:docker:live-models` 和 `test:docker:live-gateway` 仅在仓库 Docker 镜像中运行与其匹配的 profile-key 实时测试文件(`src/agents/models.profiles.live.test.ts` 和 `src/gateway/gateway-models.profiles.live.test.ts`),会挂载你的本地配置目录和工作区(如果已挂载,也会加载 `~/.profile`)。对应的本地入口点是 `test:live:models-profiles` 和 `test:live:gateway-profiles`。
|
||||
- Docker 实时运行器默认使用较小的冒烟上限,以便完整的 Docker 扫描仍然可行:
|
||||
`test:docker:live-models` 默认使用 `OPENCLAW_LIVE_MAX_MODELS=12`,而
|
||||
`test:docker:live-gateway` 默认使用 `OPENCLAW_LIVE_GATEWAY_SMOKE=1`、
|
||||
`OPENCLAW_LIVE_GATEWAY_MAX_MODELS=8`、
|
||||
`OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=45000` 和
|
||||
`OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`。当你明确想执行更大范围的穷尽扫描时,可覆盖这些环境变量。
|
||||
- `test:docker:all` 会先通过 `test:docker:live-build` 构建一次实时 Docker 镜像,然后在后续实时 Docker 通道中复用它。它还会通过 `test:docker:e2e-build` 构建一个共享的 `scripts/e2e/Dockerfile` 镜像,并在运行已构建应用的 E2E 容器冒烟运行器中复用。这个聚合器使用带权重的本地调度器:`OPENCLAW_DOCKER_ALL_PARALLELISM` 控制进程槽位,而资源上限会防止重型实时、npm 安装和多服务通道同时全部启动。默认值为 10 个槽位、`OPENCLAW_DOCKER_ALL_LIVE_LIMIT=6`、`OPENCLAW_DOCKER_ALL_NPM_LIMIT=8` 和 `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`;仅当 Docker 宿主有更多余量时,才调整 `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` 或 `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT`。该运行器默认会执行 Docker 预检,删除陈旧的 OpenClaw E2E 容器,每 30 秒打印一次状态,将成功通道的耗时存储到 `.artifacts/docker-tests/lane-timings.json`,并在后续运行中利用这些耗时优先启动更长的通道。使用 `OPENCLAW_DOCKER_ALL_DRY_RUN=1` 可仅打印带权重的通道清单,而不构建或运行 Docker。
|
||||
- 容器冒烟运行器:`test:docker:openwebui`、`test:docker:onboard`、`test:docker:npm-onboard-channel-agent`、`test:docker:session-runtime-context`、`test:docker:agents-delete-shared-workspace`、`test:docker:gateway-network`、`test:docker:mcp-channels`、`test:docker:pi-bundle-mcp-tools`、`test:docker:cron-mcp-cleanup`、`test:docker:plugins`、`test:docker:plugin-update` 和 `test:docker:config-reload` 会启动一个或多个真实容器,并验证更高层级的集成路径。
|
||||
|
||||
live 模型 Docker 运行器还会只绑定挂载所需的 CLI 认证 home(如果运行未缩小范围,则挂载所有受支持的),然后在运行前将它们复制到容器 home 中,这样外部 CLI OAuth 就可以刷新令牌,而不会修改主机认证存储:
|
||||
实时模型 Docker 运行器还会仅对所需的 CLI 认证 home 目录执行 bind-mount(如果运行未缩小范围,则挂载所有支持的目录),然后在运行前将它们复制到容器 home 中,这样外部 CLI OAuth 就可以刷新令牌,而不会修改宿主机认证存储:
|
||||
|
||||
- 直接模型:`pnpm test:docker:live-models`(脚本:`scripts/test-live-models-docker.sh`)
|
||||
- ACP 绑定冒烟测试:`pnpm test:docker:live-acp-bind`(脚本:`scripts/test-live-acp-bind-docker.sh`;默认覆盖 Claude、Codex 和 Gemini,若需严格的 OpenCode 覆盖则使用 `pnpm test:docker:live-acp-bind:opencode`)
|
||||
- CLI 后端冒烟测试:`pnpm test:docker:live-cli-backend`(脚本:`scripts/test-live-cli-backend-docker.sh`)
|
||||
- Codex app-server harness 冒烟测试:`pnpm test:docker:live-codex-harness`(脚本:`scripts/test-live-codex-harness-docker.sh`)
|
||||
- 直连模型:`pnpm test:docker:live-models`(脚本:`scripts/test-live-models-docker.sh`)
|
||||
- ACP 绑定冒烟:`pnpm test:docker:live-acp-bind`(脚本:`scripts/test-live-acp-bind-docker.sh`;默认覆盖 Claude、Codex 和 Gemini,更严格的 Droid/OpenCode 覆盖可通过 `pnpm test:docker:live-acp-bind:droid` 和 `pnpm test:docker:live-acp-bind:opencode` 运行)
|
||||
- CLI 后端冒烟:`pnpm test:docker:live-cli-backend`(脚本:`scripts/test-live-cli-backend-docker.sh`)
|
||||
- Codex app-server harness 冒烟:`pnpm test:docker:live-codex-harness`(脚本:`scripts/test-live-codex-harness-docker.sh`)
|
||||
- Gateway 网关 + dev 智能体:`pnpm test:docker:live-gateway`(脚本:`scripts/test-live-gateway-models-docker.sh`)
|
||||
- Open WebUI live 冒烟测试:`pnpm test:docker:openwebui`(脚本:`scripts/e2e/openwebui-docker.sh`)
|
||||
- Open WebUI 实时冒烟:`pnpm test:docker:openwebui`(脚本:`scripts/e2e/openwebui-docker.sh`)
|
||||
- 新手引导向导(TTY,完整脚手架):`pnpm test:docker:onboard`(脚本:`scripts/e2e/onboard-docker.sh`)
|
||||
- npm tarball 新手引导 / 渠道 / 智能体冒烟测试:`pnpm test:docker:npm-onboard-channel-agent` 会在 Docker 中全局安装打包后的 OpenClaw tarball,通过 env-ref 新手引导配置 OpenAI,并默认配置 Telegram,验证 doctor 会修复已激活插件的运行时依赖,然后运行一次模拟 OpenAI 智能体轮次。使用 `OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ=/path/to/openclaw-*.tgz` 复用预构建 tarball,使用 `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0` 跳过主机构建,或通过 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord` 切换渠道。
|
||||
- 会话运行时上下文冒烟测试:`pnpm test:docker:session-runtime-context` 会验证隐藏运行时上下文转录的持久化,以及 doctor 对受影响的重复 prompt-rewrite 分支的修复。
|
||||
- Bun 全局安装冒烟测试:`bash scripts/e2e/bun-global-install-smoke.sh` 会打包当前树,在隔离的 home 中使用 `bun install -g` 安装,并验证 `openclaw infer image providers --json` 返回的是内置图像提供商,而不是挂起。使用 `OPENCLAW_BUN_GLOBAL_SMOKE_PACKAGE_TGZ=/path/to/openclaw-*.tgz` 复用预构建 tarball,使用 `OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0` 跳过主机构建,或使用 `OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE=openclaw-dockerfile-smoke:local` 从已构建的 Docker 镜像复制 `dist/`。
|
||||
- 安装器 Docker 冒烟测试:`bash scripts/test-install-sh-docker.sh` 会在其 root、update 和 direct-npm 容器之间共享一个 npm 缓存。更新冒烟测试默认以 npm `latest` 作为稳定基线,然后再升级到候选 tarball。非 root 安装器检查会保持独立的 npm 缓存,这样 root 拥有的缓存条目就不会掩盖用户本地安装行为。设置 `OPENCLAW_INSTALL_SMOKE_NPM_CACHE_DIR=/path/to/cache` 可在本地重跑之间复用 root / update / direct-npm 缓存。
|
||||
- Install Smoke CI 会通过 `OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL=1` 跳过重复的 direct-npm 全局更新;当需要覆盖直接 `npm install -g` 时,请在本地运行该脚本且不要设置此环境变量。
|
||||
- Agents 删除共享工作区 CLI 冒烟测试:`pnpm test:docker:agents-delete-shared-workspace`(脚本:`scripts/e2e/agents-delete-shared-workspace-docker.sh`)默认会构建根 Dockerfile 镜像,在隔离的容器 home 中为两个智能体预置一个工作区,运行 `agents delete --json`,并验证 JSON 有效且工作区保留行为正确。可使用 `OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE=openclaw-dockerfile-smoke:local OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_SKIP_BUILD=1` 复用 install-smoke 镜像。
|
||||
- Gateway 网关 网络(两个容器,WS 认证 + 健康检查):`pnpm test:docker:gateway-network`(脚本:`scripts/e2e/gateway-network-docker.sh`)
|
||||
- OpenAI Responses `web_search` 最小推理回归测试:`pnpm test:docker:openai-web-search-minimal`(脚本:`scripts/e2e/openai-web-search-minimal-docker.sh`)会让一个模拟的 OpenAI 服务器通过 Gateway 网关 运行,验证 `web_search` 会将 `reasoning.effort` 从 `minimal` 提升为 `low`,然后强制提供商 schema 拒绝,并检查原始细节是否出现在 Gateway 网关 日志中。
|
||||
- MCP 渠道桥接(预置的 Gateway 网关 + stdio bridge + 原始 Claude 通知帧冒烟测试):`pnpm test:docker:mcp-channels`(脚本:`scripts/e2e/mcp-channels-docker.sh`)
|
||||
- Pi 内置 MCP 工具(真实 stdio MCP 服务器 + 嵌入式 Pi 配置文件 allow / deny 冒烟测试):`pnpm test:docker:pi-bundle-mcp-tools`(脚本:`scripts/e2e/pi-bundle-mcp-tools-docker.sh`)
|
||||
- Cron / subagent MCP 清理(真实 Gateway 网关 + 在隔离的 cron 和一次性 subagent 运行后拆除 stdio MCP 子进程):`pnpm test:docker:cron-mcp-cleanup`(脚本:`scripts/e2e/cron-mcp-cleanup-docker.sh`)
|
||||
- 插件(安装冒烟测试 + `/plugin` 别名 + Claude 内置包重启语义):`pnpm test:docker:plugins`(脚本:`scripts/e2e/plugins-docker.sh`)
|
||||
- 插件更新未变更冒烟测试:`pnpm test:docker:plugin-update`(脚本:`scripts/e2e/plugin-update-unchanged-docker.sh`)
|
||||
- 配置热重载元数据冒烟测试:`pnpm test:docker:config-reload`(脚本:`scripts/e2e/config-reload-source-docker.sh`)
|
||||
- 内置插件运行时依赖:`pnpm test:docker:bundled-channel-deps` 默认会构建一个小型 Docker 运行器镜像,在主机上构建并打包一次 OpenClaw,然后将该 tarball 挂载到每个 Linux 安装场景中。可使用 `OPENCLAW_SKIP_DOCKER_BUILD=1` 复用镜像,使用 `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0` 在完成一次新的本地构建后跳过主机重构建,或使用 `OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ=/path/to/openclaw-*.tgz` 指向现有 tarball。完整 Docker 聚合会预先打包一次该 tarball,然后将内置渠道检查分片为独立 lanes,其中包括 Telegram、Discord、Slack、Feishu、memory-lancedb 和 ACPX 的独立更新 lanes。直接运行该内置 lane 时,可使用 `OPENCLAW_BUNDLED_CHANNELS=telegram,slack` 缩小渠道矩阵,或使用 `OPENCLAW_BUNDLED_CHANNEL_UPDATE_TARGETS=telegram,acpx` 缩小更新场景。该 lane 还会验证 `channels.<id>.enabled=false` 和 `plugins.entries.<id>.enabled=false` 会抑制 doctor / 运行时依赖修复。
|
||||
- 在迭代时,如需缩小内置插件运行时依赖范围,可禁用无关场景,例如:
|
||||
`OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 pnpm test:docker:bundled-channel-deps`。
|
||||
- npm tarball 新手引导 / 渠道 / 智能体冒烟:`pnpm test:docker:npm-onboard-channel-agent` 会在 Docker 中全局安装打包好的 OpenClaw tarball,通过 env-ref 新手引导配置 OpenAI,并默认配置 Telegram,验证 doctor 会修复已激活插件的运行时依赖,然后运行一次模拟的 OpenAI 智能体轮次。可使用 `OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ=/path/to/openclaw-*.tgz` 复用预构建 tarball,使用 `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0` 跳过宿主机构建,或使用 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord` 切换渠道。
|
||||
- 会话运行时上下文冒烟:`pnpm test:docker:session-runtime-context` 会验证隐藏运行时上下文转录的持久化,以及对受影响的重复 prompt-rewrite 分支执行 doctor 修复。
|
||||
- Bun 全局安装冒烟:`bash scripts/e2e/bun-global-install-smoke.sh` 会打包当前工作树,在隔离的 home 中使用 `bun install -g` 进行安装,并验证 `openclaw infer image providers --json` 返回的是内置图像提供商而不是卡住。可使用 `OPENCLAW_BUN_GLOBAL_SMOKE_PACKAGE_TGZ=/path/to/openclaw-*.tgz` 复用预构建 tarball,使用 `OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0` 跳过宿主机构建,或使用 `OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE=openclaw-dockerfile-smoke:local` 从已构建的 Docker 镜像中复制 `dist/`。
|
||||
- 安装器 Docker 冒烟:`bash scripts/test-install-sh-docker.sh` 会在 root、update 和 direct-npm 容器之间共享一个 npm 缓存。更新冒烟默认使用 npm `latest` 作为稳定基线,然后再升级到候选 tarball。非 root 安装器检查会保留独立的 npm 缓存,这样 root 拥有的缓存条目就不会掩盖用户本地安装行为。设置 `OPENCLAW_INSTALL_SMOKE_NPM_CACHE_DIR=/path/to/cache` 可在本地重跑时复用 root / update / direct-npm 缓存。
|
||||
- Install Smoke CI 会通过 `OPENCLAW_INSTALL_SMOKE_SKIP_NPM_GLOBAL=1` 跳过重复的 direct-npm 全局更新;如果需要覆盖直接 `npm install -g`,请在本地不设置该环境变量运行脚本。
|
||||
- 智能体删除共享工作区 CLI 冒烟:`pnpm test:docker:agents-delete-shared-workspace`(脚本:`scripts/e2e/agents-delete-shared-workspace-docker.sh`)默认会构建根 Dockerfile 镜像,在隔离的容器 home 中植入两个共享同一工作区的智能体,运行 `agents delete --json`,并验证 JSON 有效且共享工作区被保留。可使用 `OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_IMAGE=openclaw-dockerfile-smoke:local OPENCLAW_AGENTS_DELETE_SHARED_WORKSPACE_E2E_SKIP_BUILD=1` 复用 install-smoke 镜像。
|
||||
- Gateway 网关网络(两个容器,WS 认证 + 健康检查):`pnpm test:docker:gateway-network`(脚本:`scripts/e2e/gateway-network-docker.sh`)
|
||||
- OpenAI Responses `web_search` 最小推理回归:`pnpm test:docker:openai-web-search-minimal`(脚本:`scripts/e2e/openai-web-search-minimal-docker.sh`)会通过 Gateway 网关运行一个模拟的 OpenAI 服务器,验证 `web_search` 会将 `reasoning.effort` 从 `minimal` 提升到 `low`,然后强制触发提供商 schema 拒绝,并检查原始详情是否出现在 Gateway 网关日志中。
|
||||
- MCP 渠道桥接(已植入的 Gateway 网关 + stdio bridge + 原始 Claude notification-frame 冒烟):`pnpm test:docker:mcp-channels`(脚本:`scripts/e2e/mcp-channels-docker.sh`)
|
||||
- Pi 内置 MCP 工具(真实 stdio MCP 服务器 + 嵌入式 Pi profile allow / deny 冒烟):`pnpm test:docker:pi-bundle-mcp-tools`(脚本:`scripts/e2e/pi-bundle-mcp-tools-docker.sh`)
|
||||
- Cron / subagent MCP 清理(真实 Gateway 网关 + 在隔离的 cron 和一次性 subagent 运行后清理 stdio MCP 子进程):`pnpm test:docker:cron-mcp-cleanup`(脚本:`scripts/e2e/cron-mcp-cleanup-docker.sh`)
|
||||
- 插件(安装冒烟 + `/plugin` 别名 + Claude bundle 重启语义):`pnpm test:docker:plugins`(脚本:`scripts/e2e/plugins-docker.sh`)
|
||||
- 插件更新未变更冒烟:`pnpm test:docker:plugin-update`(脚本:`scripts/e2e/plugin-update-unchanged-docker.sh`)
|
||||
- 配置热重载元数据冒烟:`pnpm test:docker:config-reload`(脚本:`scripts/e2e/config-reload-source-docker.sh`)
|
||||
- 内置插件运行时依赖:`pnpm test:docker:bundled-channel-deps` 默认会构建一个小型 Docker 运行器镜像,在宿主机上构建并打包一次 OpenClaw,然后将该 tarball 挂载到每个 Linux 安装场景中。可使用 `OPENCLAW_SKIP_DOCKER_BUILD=1` 复用镜像,在完成一次新的本地构建后使用 `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0` 跳过宿主机重建,或使用 `OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ=/path/to/openclaw-*.tgz` 指向现有 tarball。完整 Docker 聚合器会先预打包一次该 tarball,然后将内置渠道检查切分为独立通道,包括 Telegram、Discord、Slack、Feishu、memory-lancedb 和 ACPX 的独立更新通道。直接运行该内置通道时,可使用 `OPENCLAW_BUNDLED_CHANNELS=telegram,slack` 缩小渠道矩阵,或使用 `OPENCLAW_BUNDLED_CHANNEL_UPDATE_TARGETS=telegram,acpx` 缩小更新场景。该通道还会验证 `channels.<id>.enabled=false` 和 `plugins.entries.<id>.enabled=false` 会抑制 doctor / 运行时依赖修复。
|
||||
- 在迭代时如需缩小内置插件运行时依赖范围,可禁用无关场景,例如:
|
||||
`OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 pnpm test:docker:bundled-channel-deps`
|
||||
|
||||
如需手动预构建并复用共享的 built-app 镜像:
|
||||
如需手动预构建并复用共享的已构建应用镜像:
|
||||
|
||||
```bash
|
||||
OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e:local pnpm test:docker:e2e-build
|
||||
OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e:local OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels
|
||||
```
|
||||
|
||||
设置后,诸如 `OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE` 之类的测试套件专用镜像覆盖项仍然优先。当 `OPENCLAW_SKIP_DOCKER_BUILD=1` 指向一个远程共享镜像时,如果该镜像尚未存在于本地,脚本会先拉取它。QR 和安装器 Docker 测试保留各自的 Dockerfile,因为它们验证的是包 / 安装行为,而不是共享的 built-app 运行时。
|
||||
设置后,诸如 `OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE` 之类的测试套件专用镜像覆盖项仍然优先生效。当 `OPENCLAW_SKIP_DOCKER_BUILD=1` 指向远程共享镜像时,如果该镜像尚未存在于本地,脚本会先拉取它。QR 和安装器 Docker 测试保留各自独立的 Dockerfile,因为它们验证的是打包 / 安装行为,而不是共享的已构建应用运行时。
|
||||
|
||||
live 模型 Docker 运行器还会以只读方式绑定挂载当前检出,并将其暂存到容器内的临时工作目录中。这样既能保持运行时镜像精简,又能让 Vitest 针对你本地的精确源码 / 配置运行。暂存步骤会跳过大型仅本地缓存和应用构建输出,例如 `.pnpm-store`、`.worktrees`、`__openclaw_vitest__`,以及应用本地的 `.build` 或 Gradle 输出目录,因此 Docker live 运行不会花上数分钟复制与机器相关的产物。
|
||||
它们还会设置 `OPENCLAW_SKIP_CHANNELS=1`,这样 Gateway 网关 live 探测就不会在容器内启动真实的 Telegram / Discord / 等渠道 worker。
|
||||
`test:docker:live-models` 仍然会运行 `pnpm test:live`,因此当你需要缩小或排除该 Docker lane 中的 Gateway 网关 live 覆盖时,也要一并传入 `OPENCLAW_LIVE_GATEWAY_*`。
|
||||
`test:docker:openwebui` 是更高层级的兼容性冒烟测试:它会启动一个启用了 OpenAI 兼容 HTTP 端点的 OpenClaw Gateway 网关 容器,启动一个固定版本的 Open WebUI 容器并将其指向该 Gateway 网关,通过 Open WebUI 登录,验证 `/api/models` 会暴露 `openclaw/default`,然后通过 Open WebUI 的 `/api/chat/completions` 代理发送一条真实聊天请求。
|
||||
首次运行可能会明显更慢,因为 Docker 可能需要拉取 Open WebUI 镜像,而 Open WebUI 也可能需要完成自己的冷启动设置。
|
||||
这个 lane 需要可用的 live 模型密钥,而 `OPENCLAW_PROFILE_FILE`
|
||||
(默认为 `~/.profile`)是在 Docker 化运行中提供该密钥的主要方式。
|
||||
成功运行会打印一个小型 JSON 负载,例如 `{ "ok": true, "model":
|
||||
"openclaw/default", ... }`。
|
||||
`test:docker:mcp-channels` 是有意设计为确定性的,不需要真实的 Telegram、Discord 或 iMessage 账号。它会启动一个已预置的 Gateway 网关 容器,再启动第二个容器来生成 `openclaw mcp serve`,然后验证已路由会话发现、转录读取、附件元数据、live 事件队列行为、出站发送路由,以及通过真实 stdio MCP bridge 传输的 Claude 风格渠道 + 权限通知。通知检查会直接检查原始 stdio MCP 帧,因此该冒烟测试验证的是 bridge 实际发出的内容,而不仅仅是某个特定客户端 SDK 恰好暴露出来的内容。
|
||||
`test:docker:pi-bundle-mcp-tools` 是确定性的,不需要 live 模型密钥。它会构建仓库 Docker 镜像,在容器内启动一个真实的 stdio MCP 探测服务器,通过嵌入式 Pi 内置 MCP 运行时将该服务器实体化,执行工具,然后验证 `coding` 和 `messaging` 会保留 `bundle-mcp` 工具,而 `minimal` 和 `tools.deny: ["bundle-mcp"]` 会将其过滤掉。
|
||||
`test:docker:cron-mcp-cleanup` 是确定性的,不需要 live 模型密钥。它会启动一个已预置的 Gateway 网关 和真实的 stdio MCP 探测服务器,运行一次隔离的 cron 轮次和一次 `/subagents spawn` 一次性子智能体轮次,然后验证每次运行后 MCP 子进程都会退出。
|
||||
实时模型 Docker 运行器还会以只读方式 bind-mount 当前检出,并在容器内将其暂存到一个临时工作目录中。这样既能保持运行时镜像精简,又仍然能基于你本地精确的源码 / 配置运行 Vitest。暂存步骤会跳过大型仅限本地的缓存和应用构建输出,例如 `.pnpm-store`、`.worktrees`、`__openclaw_vitest__`,以及应用本地的 `.build` 或 Gradle 输出目录,这样 Docker 实时运行就不会花上几分钟去复制机器专属产物。
|
||||
它们还会设置 `OPENCLAW_SKIP_CHANNELS=1`,这样 Gateway 网关实时探测就不会在容器内启动真实的 Telegram / Discord / 等渠道工作进程。
|
||||
`test:docker:live-models` 仍然会运行 `pnpm test:live`,因此当你需要缩小或排除该 Docker 通道中的 gateway 实时覆盖范围时,也要一并传入 `OPENCLAW_LIVE_GATEWAY_*`。
|
||||
`test:docker:openwebui` 是一个更高层级的兼容性冒烟测试:它会启动一个启用了 OpenAI 兼容 HTTP 端点的 OpenClaw gateway 容器,针对该 gateway 启动一个固定版本的 Open WebUI 容器,通过 Open WebUI 完成登录,验证 `/api/models` 暴露了 `openclaw/default`,然后通过 Open WebUI 的 `/api/chat/completions` 代理发送一次真实聊天请求。
|
||||
第一次运行可能会明显更慢,因为 Docker 可能需要拉取 Open WebUI 镜像,而 Open WebUI 也可能需要完成自身的冷启动设置。
|
||||
这个通道需要一个可用的实时模型密钥,而 `OPENCLAW_PROFILE_FILE`(默认值为 `~/.profile`)是在 Docker 化运行中提供它的主要方式。
|
||||
成功运行会打印一个小型 JSON 负载,例如 `{ "ok": true, "model": "openclaw/default", ... }`。
|
||||
`test:docker:mcp-channels` 刻意保持确定性,不需要真实的 Telegram、Discord 或 iMessage 账号。它会启动一个已植入的 Gateway 网关容器,启动第二个容器来生成 `openclaw mcp serve`,然后通过真实的 stdio MCP bridge 验证路由后的会话发现、转录读取、附件元数据、实时事件队列行为、出站发送路由,以及 Claude 风格的 channel + permission 通知。通知检查会直接检查原始 stdio MCP 帧,因此这个冒烟测试验证的是 bridge 实际发出的内容,而不仅仅是某个特定客户端 SDK 恰好暴露出来的内容。
|
||||
`test:docker:pi-bundle-mcp-tools` 具有确定性,不需要实时模型密钥。它会构建仓库 Docker 镜像,在容器内启动一个真实的 stdio MCP probe 服务器,通过嵌入式 Pi bundle MCP 运行时将该服务器实体化,执行该工具,然后验证 `coding` 和 `messaging` 会保留 `bundle-mcp` 工具,而 `minimal` 和 `tools.deny: ["bundle-mcp"]` 会将其过滤掉。
|
||||
`test:docker:cron-mcp-cleanup` 具有确定性,不需要实时模型密钥。它会启动一个带真实 stdio MCP probe 服务器的已植入 Gateway 网关,运行一次隔离的 cron 轮次和一次 `/subagents spawn` 单次子智能体轮次,然后验证 MCP 子进程会在每次运行后退出。
|
||||
|
||||
手动 ACP 纯文本线程冒烟测试(不在 CI 中运行):
|
||||
手动 ACP 自然语言线程冒烟测试(不在 CI 中运行):
|
||||
|
||||
- `bun scripts/dev/discord-acp-plain-language-smoke.ts --channel <discord-channel-id> ...`
|
||||
- 请保留这个脚本用于回归 / 调试工作流。它未来可能还会再次用于 ACP 线程路由验证,因此不要删除。
|
||||
- 保留这个脚本用于回归 / 调试工作流。它未来可能还会再次用于 ACP 线程路由校验,因此不要删除它。
|
||||
|
||||
常用环境变量:
|
||||
有用的环境变量:
|
||||
|
||||
- `OPENCLAW_CONFIG_DIR=...`(默认:`~/.openclaw`)挂载到 `/home/node/.openclaw`
|
||||
- `OPENCLAW_WORKSPACE_DIR=...`(默认:`~/.openclaw/workspace`)挂载到 `/home/node/.openclaw/workspace`
|
||||
- `OPENCLAW_PROFILE_FILE=...`(默认:`~/.profile`)挂载到 `/home/node/.profile`,并在运行测试前读取
|
||||
- `OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1` 用于仅验证从 `OPENCLAW_PROFILE_FILE` 读取的环境变量,使用临时配置 / 工作区目录且不挂载外部 CLI 认证
|
||||
- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...`(默认:`~/.cache/openclaw/docker-cli-tools`)挂载到 `/home/node/.npm-global`,用于 Docker 内缓存 CLI 安装
|
||||
- `OPENCLAW_CONFIG_DIR=...`(默认值:`~/.openclaw`)会挂载到 `/home/node/.openclaw`
|
||||
- `OPENCLAW_WORKSPACE_DIR=...`(默认值:`~/.openclaw/workspace`)会挂载到 `/home/node/.openclaw/workspace`
|
||||
- `OPENCLAW_PROFILE_FILE=...`(默认值:`~/.profile`)会挂载到 `/home/node/.profile` 并在运行测试前加载
|
||||
- `OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1` 用于只校验从 `OPENCLAW_PROFILE_FILE` 加载的环境变量,使用临时 config / workspace 目录且不挂载外部 CLI 认证目录
|
||||
- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...`(默认值:`~/.cache/openclaw/docker-cli-tools`)会挂载到 `/home/node/.npm-global`,用于缓存 Docker 内部的 CLI 安装
|
||||
- 位于 `$HOME` 下的外部 CLI 认证目录 / 文件会以只读方式挂载到 `/host-auth...` 下,然后在测试开始前复制到 `/home/node/...`
|
||||
- 默认目录:`.minimax`
|
||||
- 默认文件:`~/.codex/auth.json`、`~/.codex/config.toml`、`.claude.json`、`~/.claude/.credentials.json`、`~/.claude/settings.json`、`~/.claude/settings.local.json`
|
||||
- 缩小范围的提供商运行只会挂载根据 `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS` 推断出的所需目录 / 文件
|
||||
- 可通过 `OPENCLAW_DOCKER_AUTH_DIRS=all`、`OPENCLAW_DOCKER_AUTH_DIRS=none` 或逗号列表(例如 `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex`)手动覆盖
|
||||
- 缩小范围后的提供商运行只会挂载从 `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS` 推断出的所需目录 / 文件
|
||||
- 也可通过 `OPENCLAW_DOCKER_AUTH_DIRS=all`、`OPENCLAW_DOCKER_AUTH_DIRS=none` 或逗号列表手动覆盖,例如 `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex`
|
||||
- `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...` 用于缩小运行范围
|
||||
- `OPENCLAW_LIVE_GATEWAY_PROVIDERS=...` / `OPENCLAW_LIVE_PROVIDERS=...` 用于在容器内过滤提供商
|
||||
- `OPENCLAW_SKIP_DOCKER_BUILD=1` 用于复用已有的 `openclaw:local-live` 镜像,以便在不需要重建的重跑中使用
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 用于确保凭证来自 profile 存储,而不是环境变量
|
||||
- `OPENCLAW_OPENWEBUI_MODEL=...` 用于选择由 Gateway 网关 暴露给 Open WebUI 冒烟测试的模型
|
||||
- `OPENCLAW_SKIP_DOCKER_BUILD=1` 用于复用现有的 `openclaw:local-live` 镜像,以便在无需重建时重跑
|
||||
- `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 用于确保凭证来自 profile 存储而不是环境变量
|
||||
- `OPENCLAW_OPENWEBUI_MODEL=...` 用于选择 Gateway 网关为 Open WebUI 冒烟测试暴露的模型
|
||||
- `OPENCLAW_OPENWEBUI_PROMPT=...` 用于覆盖 Open WebUI 冒烟测试使用的 nonce 检查提示词
|
||||
- `OPENWEBUI_IMAGE=...` 用于覆盖固定的 Open WebUI 镜像标签
|
||||
|
||||
## 文档完整性检查
|
||||
|
||||
修改文档后运行文档检查:`pnpm check:docs`。
|
||||
当你还需要页内标题检查时,运行完整的 Mintlify 锚点校验:`pnpm docs:check-links:anchors`。
|
||||
编辑文档后运行文档检查:`pnpm check:docs`。
|
||||
当你还需要检查页内标题锚点时,运行完整的 Mintlify 锚点校验:`pnpm docs:check-links:anchors`。
|
||||
|
||||
## 离线回归测试(对 CI 安全)
|
||||
## 离线回归(CI 安全)
|
||||
|
||||
这些是在没有真实提供商时的“真实流水线”回归测试:
|
||||
这些是不依赖真实提供商的“真实流水线”回归测试:
|
||||
|
||||
- Gateway 网关 工具调用(模拟 OpenAI,真实 Gateway 网关 + Agent loop):`src/gateway/gateway.test.ts`(用例:“runs a mock OpenAI tool call end-to-end via gateway agent loop”)
|
||||
- Gateway 网关 向导(WS `wizard.start` / `wizard.next`,强制写入配置 + 认证):`src/gateway/gateway.test.ts`(用例:“runs wizard over ws and writes auth token config”)
|
||||
- Gateway 网关工具调用(模拟 OpenAI,真实 gateway + Agent loop):`src/gateway/gateway.test.ts`(用例:“runs a mock OpenAI tool call end-to-end via gateway agent loop”)
|
||||
- Gateway 网关向导(WS `wizard.start` / `wizard.next`,强制写入 config + auth):`src/gateway/gateway.test.ts`(用例:“runs wizard over ws and writes auth token config”)
|
||||
|
||||
## 智能体可靠性评估(Skills)
|
||||
|
||||
我们已经有一些对 CI 安全的测试,其行为类似于“智能体可靠性评估”:
|
||||
我们已经有一些 CI 安全的测试,行为上类似“智能体可靠性评估”:
|
||||
|
||||
- 通过真实的 Gateway 网关 + Agent loop 进行模拟工具调用(`src/gateway/gateway.test.ts`)。
|
||||
- 通过真实 gateway + Agent loop 执行模拟工具调用(`src/gateway/gateway.test.ts`)。
|
||||
- 验证会话接线和配置效果的端到端向导流程(`src/gateway/gateway.test.ts`)。
|
||||
|
||||
对于 Skills(见 [Skills](/zh-CN/tools/skills)),目前仍然缺少的是:
|
||||
对于 Skills(参见 [Skills](/zh-CN/tools/skills)),目前仍然缺少的内容:
|
||||
|
||||
- **决策能力:** 当提示词中列出了 Skills 时,智能体是否会选择正确的 Skill(或者避免选择无关的 Skill)?
|
||||
- **合规性:** 智能体在使用前是否会读取 `SKILL.md`,并遵循要求的步骤 / 参数?
|
||||
- **工作流契约:** 断言工具顺序、会话历史延续和沙箱边界的多轮场景。
|
||||
- **决策能力:** 当提示词中列出了 Skills 时,智能体是否会选择正确的 Skill(或避免无关 Skill)?
|
||||
- **合规性:** 智能体在使用前是否会读取 `SKILL.md` 并遵循所需步骤 / 参数?
|
||||
- **工作流契约:** 用于断言工具顺序、会话历史延续以及沙箱边界的多轮场景。
|
||||
|
||||
未来的评估应首先保持确定性:
|
||||
未来的评估应优先保持确定性:
|
||||
|
||||
- 一个使用模拟提供商的场景运行器,用于断言工具调用 + 顺序、Skill 文件读取和会话接线。
|
||||
- 一个小型的 Skill 聚焦场景套件(使用 vs 避免、门控、提示注入)。
|
||||
- 只有在对 CI 安全的测试套件到位之后,才添加可选的 live 评估(显式启用、受环境变量控制)。
|
||||
- 使用模拟提供商的场景运行器,用于断言工具调用 + 顺序、Skill 文件读取和会话接线。
|
||||
- 一小组聚焦 Skill 的场景(使用与避免、门控、提示注入)。
|
||||
- 只有在 CI 安全测试套件就位后,才添加可选的实时评估(显式启用、由环境变量控制)。
|
||||
|
||||
## 契约测试(插件和渠道形状)
|
||||
|
||||
契约测试用于验证每个已注册插件和渠道都符合其接口契约。它们会遍历所有已发现的插件,并运行一组形状与行为断言。默认的 `pnpm test` 单元 lane 会有意跳过这些共享接口和冒烟文件;当你修改共享渠道或提供商表面时,请显式运行契约命令。
|
||||
契约测试用于验证每个已注册插件和渠道都符合其接口契约。它们会遍历所有已发现的插件,并运行一组形状和行为断言。默认的 `pnpm test` 单元通道会刻意跳过这些共享接缝和冒烟文件;当你修改共享渠道或提供商表面时,请显式运行契约命令。
|
||||
|
||||
### 命令
|
||||
|
||||
@ -600,10 +591,10 @@ live 模型 Docker 运行器还会以只读方式绑定挂载当前检出,并
|
||||
- **session-binding** - 会话绑定行为
|
||||
- **outbound-payload** - 消息负载结构
|
||||
- **inbound** - 入站消息处理
|
||||
- **actions** - 渠道动作处理器
|
||||
- **actions** - 渠道操作处理器
|
||||
- **threading** - 线程 ID 处理
|
||||
- **directory** - 目录 / roster API
|
||||
- **group-policy** - 群组策略执行
|
||||
- **group-policy** - 群组策略强制执行
|
||||
|
||||
### 提供商 Status 契约
|
||||
|
||||
@ -616,8 +607,8 @@ live 模型 Docker 运行器还会以只读方式绑定挂载当前检出,并
|
||||
|
||||
位于 `src/plugins/contracts/*.contract.test.ts`:
|
||||
|
||||
- **auth** - 认证流契约
|
||||
- **auth-choice** - 认证选项 / 选择
|
||||
- **auth** - Auth 流程契约
|
||||
- **auth-choice** - Auth 选项 / 选择
|
||||
- **catalog** - 模型目录 API
|
||||
- **discovery** - 插件发现
|
||||
- **loader** - 插件加载
|
||||
@ -627,24 +618,24 @@ live 模型 Docker 运行器还会以只读方式绑定挂载当前检出,并
|
||||
|
||||
### 何时运行
|
||||
|
||||
- 修改插件 SDK 导出或子路径之后
|
||||
- 添加或修改渠道或提供商插件之后
|
||||
- 重构插件注册或发现逻辑之后
|
||||
- 修改 plugin-sdk 导出或子路径后
|
||||
- 添加或修改渠道或提供商插件后
|
||||
- 重构插件注册或发现逻辑后
|
||||
|
||||
契约测试会在 CI 中运行,且不需要真实 API 密钥。
|
||||
契约测试会在 CI 中运行,并且不需要真实 API key。
|
||||
|
||||
## 添加回归测试(指导)
|
||||
|
||||
当你修复了在 live 中发现的提供商 / 模型问题时:
|
||||
当你修复了在实时环境中发现的提供商 / 模型问题时:
|
||||
|
||||
- 如果可能,添加一个对 CI 安全的回归测试(模拟 / 存根提供商,或捕获精确的请求形状转换)
|
||||
- 如果问题本质上只能在 live 中复现(速率限制、认证策略),就让 live 测试保持窄范围,并通过环境变量显式启用
|
||||
- 优先定位到能捕获该缺陷的最小层级:
|
||||
- 提供商请求转换 / 重放缺陷 → 直接模型测试
|
||||
- Gateway 网关 会话 / 历史 / 工具管线缺陷 → Gateway 网关 live 冒烟测试或对 CI 安全的 Gateway 网关 模拟测试
|
||||
- SecretRef 遍历防护栏:
|
||||
- `src/secrets/exec-secret-ref-id-parity.test.ts` 会从注册表元数据(`listSecretTargetRegistryEntries()`)中为每个 SecretRef 类派生一个采样目标,然后断言遍历片段 exec id 会被拒绝。
|
||||
- 如果你在 `src/secrets/target-registry-data.ts` 中新增了一个 `includeInPlan` SecretRef 目标家族,请更新该测试中的 `classifyTargetClass`。该测试会有意对未分类目标 id 失败,以确保新的类别不会被静默跳过。
|
||||
- 如果可能,添加一个 CI 安全的回归测试(模拟 / stub 提供商,或捕获精确的请求形状转换)
|
||||
- 如果问题本质上只能在实时环境中出现(限流、认证策略),就让实时测试保持狭窄范围,并通过环境变量显式启用
|
||||
- 优先定位到能够捕获该缺陷的最小层:
|
||||
- 提供商请求转换 / 重放缺陷 → 直连模型测试
|
||||
- gateway 会话 / 历史 / 工具管线缺陷 → gateway 实时冒烟测试或 CI 安全的 gateway 模拟测试
|
||||
- SecretRef 遍历防护:
|
||||
- `src/secrets/exec-secret-ref-id-parity.test.ts` 会从注册表元数据(`listSecretTargetRegistryEntries()`)中为每个 SecretRef 类推导一个采样目标,然后断言遍历片段 exec id 会被拒绝。
|
||||
- 如果你在 `src/secrets/target-registry-data.ts` 中添加了新的 `includeInPlan` SecretRef 目标族,请更新该测试中的 `classifyTargetClass`。该测试会有意在遇到未分类目标 id 时失败,从而避免新类别被静默跳过。
|
||||
|
||||
## 相关内容
|
||||
|
||||
|
||||
@ -2,34 +2,34 @@
|
||||
read_when:
|
||||
- 在 Fly.io 上部署 OpenClaw
|
||||
- 设置 Fly 卷、密钥和首次运行配置
|
||||
summary: OpenClaw 的 Fly.io 分步部署指南,包含持久化存储和 HTTPS
|
||||
summary: OpenClaw 在 Fly.io 上部署的分步指南,包含持久化存储和 HTTPS
|
||||
title: Fly.io
|
||||
x-i18n:
|
||||
generated_at: "2026-04-24T03:17:28Z"
|
||||
generated_at: "2026-04-26T00:39:24Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 8913b6917c23de69865c57ec6a455f3e615bc65b09334edec0a3fe8ff69cf503
|
||||
source_hash: 1fe13cb60aff6ee2159e1008d2af660b689d819d38893e9758c23e1edaf32e22
|
||||
source_path: install/fly.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Fly.io 部署
|
||||
|
||||
**目标:** 让 OpenClaw Gateway 网关运行在 [Fly.io](https://fly.io) 机器上,并具备持久化存储、自动 HTTPS 和 Discord / 渠道访问能力。
|
||||
**目标:** 让 OpenClaw Gateway 网关运行在 [Fly.io](https://fly.io) 的机器上,并具备持久化存储、自动 HTTPS,以及 Discord/渠道访问能力。
|
||||
|
||||
## 你需要准备
|
||||
|
||||
- 已安装 [flyctl CLI](https://fly.io/docs/hands-on/install-flyctl/)
|
||||
- Fly.io 账户(免费层即可)
|
||||
- 模型认证:你所选模型提供商的 API 密钥
|
||||
- 渠道凭据:Discord 机器人令牌、Telegram 令牌等
|
||||
- Fly.io 账号(免费层即可)
|
||||
- 模型凭证:你所选模型提供商的 API 密钥
|
||||
- 渠道凭证:Discord 机器人令牌、Telegram 令牌等
|
||||
|
||||
## 面向初学者的快速路径
|
||||
|
||||
1. 克隆仓库 → 自定义 `fly.toml`
|
||||
2. 创建应用 + 卷 → 设置密钥
|
||||
3. 使用 `fly deploy` 部署
|
||||
4. 通过 SSH 登录创建配置,或使用 Control UI
|
||||
4. SSH 登录后创建配置,或使用 Control UI
|
||||
|
||||
<Steps>
|
||||
<Step title="创建 Fly 应用">
|
||||
@ -38,10 +38,10 @@ x-i18n:
|
||||
git clone https://github.com/openclaw/openclaw.git
|
||||
cd openclaw
|
||||
|
||||
# 创建一个新的 Fly 应用(请自行选择名称)
|
||||
# 创建一个新的 Fly 应用(自己选择名称)
|
||||
fly apps create my-openclaw
|
||||
|
||||
# 创建一个持久化卷(通常 1 GB 就够)
|
||||
# 创建一个持久卷(通常 1GB 就够)
|
||||
fly volumes create openclaw_data --size 1 --region iad
|
||||
```
|
||||
|
||||
@ -50,9 +50,9 @@ x-i18n:
|
||||
</Step>
|
||||
|
||||
<Step title="配置 fly.toml">
|
||||
编辑 `fly.toml` 以匹配你的应用名称和需求。
|
||||
编辑 `fly.toml`,使其与你的应用名称和需求匹配。
|
||||
|
||||
**安全说明:** 默认配置会暴露一个公开 URL。若需无公网 IP 的加固部署,请参见 [Private Deployment](#private-deployment-hardened),或使用 `fly.private.toml`。
|
||||
**安全说明:** 默认配置会暴露一个公开 URL。如需无公网 IP 的加固部署,请参见 [私有部署](#private-deployment-hardened),或使用 `fly.private.toml`。
|
||||
|
||||
```toml
|
||||
app = "my-openclaw" # 你的应用名称
|
||||
@ -89,13 +89,13 @@ x-i18n:
|
||||
|
||||
**关键设置:**
|
||||
|
||||
| Setting | Why |
|
||||
| ------------------------------ | ------------------------------------------------------------------ |
|
||||
| `--bind lan` | 绑定到 `0.0.0.0`,这样 Fly 的代理才能访问 Gateway 网关 |
|
||||
| `--allow-unconfigured` | 在没有配置文件的情况下启动(之后你会创建一个) |
|
||||
| `internal_port = 3000` | 必须与 `--port 3000`(或 `OPENCLAW_GATEWAY_PORT`)一致,供 Fly 健康检查使用 |
|
||||
| `memory = "2048mb"` | 512 MB 太小;建议使用 2 GB |
|
||||
| `OPENCLAW_STATE_DIR = "/data"` | 将状态持久化到卷中 |
|
||||
| 设置 | 原因 |
|
||||
| ------------------------------ | --------------------------------------------------------------------------- |
|
||||
| `--bind lan` | 绑定到 `0.0.0.0`,这样 Fly 的代理才能访问 Gateway 网关 |
|
||||
| `--allow-unconfigured` | 允许在没有配置文件的情况下启动(你稍后再创建) |
|
||||
| `internal_port = 3000` | 必须与 `--port 3000`(或 `OPENCLAW_GATEWAY_PORT`)一致,以满足 Fly 健康检查 |
|
||||
| `memory = "2048mb"` | 512MB 太小;建议使用 2GB |
|
||||
| `OPENCLAW_STATE_DIR = "/data"` | 将状态持久化到卷上 |
|
||||
|
||||
</Step>
|
||||
|
||||
@ -117,9 +117,9 @@ x-i18n:
|
||||
|
||||
**说明:**
|
||||
|
||||
- 非 loopback 绑定(`--bind lan`)需要一个有效的 Gateway 网关认证路径。本 Fly.io 示例使用 `OPENCLAW_GATEWAY_TOKEN`,但 `gateway.auth.password` 或正确配置的非 loopback `trusted-proxy` 部署同样满足这一要求。
|
||||
- 请像对待密码一样对待这些令牌。
|
||||
- **所有 API 密钥和令牌都优先使用环境变量,而不是配置文件。** 这样可以避免把密钥放进 `openclaw.json`,从而降低意外暴露或记录到日志中的风险。
|
||||
- 非 loopback 绑定(`--bind lan`)要求存在有效的 Gateway 网关认证路径。这个 Fly.io 示例使用 `OPENCLAW_GATEWAY_TOKEN`,但 `gateway.auth.password` 或配置正确的非 loopback `trusted-proxy` 部署也能满足该要求。
|
||||
- 请像对待密码一样保护这些令牌。
|
||||
- 对所有 API 密钥和令牌,**优先使用环境变量而不是配置文件**。这样可以避免密钥出现在 `openclaw.json` 中,从而减少意外暴露或被记录到日志中的风险。
|
||||
|
||||
</Step>
|
||||
|
||||
@ -137,7 +137,7 @@ x-i18n:
|
||||
fly logs
|
||||
```
|
||||
|
||||
你应该会看到:
|
||||
你应该看到:
|
||||
|
||||
```
|
||||
[gateway] listening on ws://0.0.0.0:3000 (PID xxx)
|
||||
@ -147,7 +147,7 @@ x-i18n:
|
||||
</Step>
|
||||
|
||||
<Step title="创建配置文件">
|
||||
通过 SSH 登录到机器以创建正式配置:
|
||||
通过 SSH 进入机器来创建正式配置:
|
||||
|
||||
```bash
|
||||
fly ssh console
|
||||
@ -200,23 +200,32 @@ x-i18n:
|
||||
},
|
||||
"gateway": {
|
||||
"mode": "local",
|
||||
"bind": "auto"
|
||||
"bind": "auto",
|
||||
"controlUi": {
|
||||
"allowedOrigins": [
|
||||
"https://my-openclaw.fly.dev",
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000"
|
||||
]
|
||||
}
|
||||
},
|
||||
"meta": {}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**注意:** 使用 `OPENCLAW_STATE_DIR=/data` 时,配置路径是 `/data/openclaw.json`。
|
||||
**注意:** 设置 `OPENCLAW_STATE_DIR=/data` 后,配置路径就是 `/data/openclaw.json`。
|
||||
|
||||
**注意:** Discord 令牌可来自以下任一位置:
|
||||
**注意:** 请将 `https://my-openclaw.fly.dev` 替换为你真实的 Fly 应用来源地址。Gateway 网关启动时会根据运行时的 `--bind` 和 `--port` 值预填充本地 Control UI 来源,因此首次启动可以在配置尚不存在时继续进行;但通过 Fly 在浏览器中访问时,仍然需要在 `gateway.controlUi.allowedOrigins` 中列出准确的 HTTPS 来源。
|
||||
|
||||
**注意:** Discord 令牌可以来自以下任一位置:
|
||||
|
||||
- 环境变量:`DISCORD_BOT_TOKEN`(推荐用于密钥)
|
||||
- 配置文件:`channels.discord.token`
|
||||
|
||||
如果使用环境变量,就不需要把令牌加入配置。Gateway 网关会自动读取 `DISCORD_BOT_TOKEN`。
|
||||
如果使用环境变量,则无需把令牌加入配置。Gateway 网关会自动读取 `DISCORD_BOT_TOKEN`。
|
||||
|
||||
重启以应用更改:
|
||||
重启以应用配置:
|
||||
|
||||
```bash
|
||||
exit
|
||||
@ -236,7 +245,7 @@ x-i18n:
|
||||
|
||||
或访问 `https://my-openclaw.fly.dev/`
|
||||
|
||||
使用已配置的共享密钥进行认证。本指南使用来自 `OPENCLAW_GATEWAY_TOKEN` 的 Gateway 网关令牌;如果你改用了密码认证,请改用该密码。
|
||||
使用你配置的共享密钥进行认证。本指南使用的是来自 `OPENCLAW_GATEWAY_TOKEN` 的 Gateway 网关令牌;如果你改用了密码认证,请改用对应密码。
|
||||
|
||||
### 日志
|
||||
|
||||
@ -260,19 +269,19 @@ x-i18n:
|
||||
|
||||
Gateway 网关绑定到了 `127.0.0.1`,而不是 `0.0.0.0`。
|
||||
|
||||
**修复:** 在 `fly.toml` 的进程命令中加入 `--bind lan`。
|
||||
**修复方法:** 在 `fly.toml` 的进程命令中添加 `--bind lan`。
|
||||
|
||||
### 健康检查失败 / connection refused
|
||||
|
||||
Fly 无法通过配置的端口访问 Gateway 网关。
|
||||
|
||||
**修复:** 确保 `internal_port` 与 Gateway 网关端口一致(设置 `--port 3000` 或 `OPENCLAW_GATEWAY_PORT=3000`)。
|
||||
**修复方法:** 确保 `internal_port` 与 Gateway 网关端口一致(设置 `--port 3000` 或 `OPENCLAW_GATEWAY_PORT=3000`)。
|
||||
|
||||
### OOM / 内存问题
|
||||
|
||||
容器持续重启或被杀死。迹象包括:`SIGABRT`、`v8::internal::Runtime_AllocateInYoungGeneration`,或静默重启。
|
||||
容器持续重启或被杀掉。常见迹象包括:`SIGABRT`、`v8::internal::Runtime_AllocateInYoungGeneration`,或无提示重启。
|
||||
|
||||
**修复:** 在 `fly.toml` 中增加内存:
|
||||
**修复方法:** 增加 `fly.toml` 中的内存:
|
||||
|
||||
```toml
|
||||
[[vm]]
|
||||
@ -285,15 +294,15 @@ Fly 无法通过配置的端口访问 Gateway 网关。
|
||||
fly machine update <machine-id> --vm-memory 2048 -y
|
||||
```
|
||||
|
||||
**注意:** 512 MB 太小。1 GB 可能可用,但在有负载或启用详细日志时可能 OOM。**推荐使用 2 GB。**
|
||||
**注意:** 512MB 太小。1GB 可能可用,但在有负载或日志较多时仍可能 OOM。**推荐使用 2GB。**
|
||||
|
||||
### Gateway 网关锁问题
|
||||
### Gateway 网关锁文件问题
|
||||
|
||||
Gateway 网关因为“already running”错误而拒绝启动。
|
||||
Gateway 网关启动时报 “already running” 错误并拒绝启动。
|
||||
|
||||
这通常发生在容器重启后,但 PID 锁文件仍保留在卷中。
|
||||
这通常发生在容器重启后,PID 锁文件仍保留在卷中。
|
||||
|
||||
**修复:** 删除锁文件:
|
||||
**修复方法:** 删除锁文件:
|
||||
|
||||
```bash
|
||||
fly ssh console --command "rm -f /data/gateway.*.lock"
|
||||
@ -304,7 +313,7 @@ fly machine restart <machine-id>
|
||||
|
||||
### 未读取配置
|
||||
|
||||
`--allow-unconfigured` 只会绕过启动保护。它不会创建或修复 `/data/openclaw.json`,因此请确保你的实际配置文件存在,并且在你希望正常以本地 Gateway 网关启动时包含 `gateway.mode="local"`。
|
||||
`--allow-unconfigured` 只会跳过启动保护,不会创建或修复 `/data/openclaw.json`,因此请确保你的正式配置文件已存在,并且在你希望以普通本地 Gateway 网关方式启动时,包含 `gateway.mode="local"`。
|
||||
|
||||
验证配置是否存在:
|
||||
|
||||
@ -314,10 +323,10 @@ fly ssh console --command "cat /data/openclaw.json"
|
||||
|
||||
### 通过 SSH 写入配置
|
||||
|
||||
`fly ssh console -C` 命令不支持 shell 重定向。若要写入配置文件:
|
||||
`fly ssh console -C` 命令不支持 shell 重定向。要写入配置文件:
|
||||
|
||||
```bash
|
||||
# 使用 echo + tee(从本地通过管道发送到远程)
|
||||
# 使用 echo + tee(从本地管道传到远程)
|
||||
echo '{"your":"config"}' | fly ssh console -C "tee /data/openclaw.json"
|
||||
|
||||
# 或使用 sftp
|
||||
@ -325,7 +334,7 @@ fly sftp shell
|
||||
> put /local/path/config.json /data/openclaw.json
|
||||
```
|
||||
|
||||
**注意:** 如果文件已存在,`fly sftp` 可能会失败。请先删除:
|
||||
**注意:** 如果目标文件已存在,`fly sftp` 可能会失败。请先删除:
|
||||
|
||||
```bash
|
||||
fly ssh console --command "rm /data/openclaw.json"
|
||||
@ -333,14 +342,14 @@ fly ssh console --command "rm /data/openclaw.json"
|
||||
|
||||
### 状态未持久化
|
||||
|
||||
如果你在重启后丢失 auth 配置文件、渠道 / 提供商状态或会话,说明状态目录被写到了容器文件系统中。
|
||||
如果你在重启后丢失了 auth 配置、渠道/提供商状态或会话,说明状态目录被写到了容器文件系统中。
|
||||
|
||||
**修复:** 确保在 `fly.toml` 中设置了 `OPENCLAW_STATE_DIR=/data`,然后重新部署。
|
||||
**修复方法:** 确保在 `fly.toml` 中设置了 `OPENCLAW_STATE_DIR=/data`,然后重新部署。
|
||||
|
||||
## 更新
|
||||
|
||||
```bash
|
||||
# 拉取最新变更
|
||||
# 拉取最新更改
|
||||
git pull
|
||||
|
||||
# 重新部署
|
||||
@ -353,10 +362,10 @@ fly logs
|
||||
|
||||
### 更新机器命令
|
||||
|
||||
如果你需要在不完整重新部署的情况下更改启动命令:
|
||||
如果你需要在不完整重新部署的情况下修改启动命令:
|
||||
|
||||
```bash
|
||||
# 获取 machine ID
|
||||
# 获取机器 ID
|
||||
fly machines list
|
||||
|
||||
# 更新命令
|
||||
@ -366,31 +375,31 @@ fly machine update <machine-id> --command "node dist/index.js gateway --port 300
|
||||
fly machine update <machine-id> --vm-memory 2048 --command "node dist/index.js gateway --port 3000 --bind lan" -y
|
||||
```
|
||||
|
||||
**注意:** 执行 `fly deploy` 后,机器命令可能会重置为 `fly.toml` 中的内容。如果你做过手动更改,请在部署后重新应用。
|
||||
**注意:** 执行 `fly deploy` 后,机器命令可能会重置为 `fly.toml` 中的内容。如果你做了手动更改,请在部署后重新应用。
|
||||
|
||||
## Private Deployment(加固版)
|
||||
## 私有部署(加固)
|
||||
|
||||
默认情况下,Fly 会分配公网 IP,因此你的 Gateway 网关可通过 `https://your-app.fly.dev` 访问。这很方便,但也意味着你的部署会被互联网扫描器(Shodan、Censys 等)发现。
|
||||
默认情况下,Fly 会分配公网 IP,这会让你的 Gateway 网关可通过 `https://your-app.fly.dev` 访问。这很方便,但也意味着你的部署可被互联网扫描器(Shodan、Censys 等)发现。
|
||||
|
||||
如果你希望进行**完全无公网暴露**的加固部署,请使用私有模板。
|
||||
如果你需要**完全不对公网暴露**的加固部署,请使用私有模板。
|
||||
|
||||
### 何时使用私有部署
|
||||
|
||||
- 你只进行**出站**调用 / 消息(没有入站 webhook)
|
||||
- 你对 webhook 回调使用 **ngrok 或 Tailscale** 隧道
|
||||
- 你只会发起**出站**调用/消息(没有入站 webhook)
|
||||
- 你使用 **ngrok 或 Tailscale** 隧道处理 webhook 回调
|
||||
- 你通过 **SSH、代理或 WireGuard** 访问 Gateway 网关,而不是浏览器
|
||||
- 你希望部署**不被互联网扫描器发现**
|
||||
|
||||
### 设置
|
||||
|
||||
使用 `fly.private.toml`,而不是标准配置:
|
||||
使用 `fly.private.toml` 代替标准配置:
|
||||
|
||||
```bash
|
||||
# 使用私有配置部署
|
||||
fly deploy -c fly.private.toml
|
||||
```
|
||||
|
||||
或将现有部署转换为私有部署:
|
||||
或者将现有部署转换为私有部署:
|
||||
|
||||
```bash
|
||||
# 列出当前 IP
|
||||
@ -400,15 +409,15 @@ fly ips list -a my-openclaw
|
||||
fly ips release <public-ipv4> -a my-openclaw
|
||||
fly ips release <public-ipv6> -a my-openclaw
|
||||
|
||||
# 切换到私有配置,以便后续部署不再重新分配公网 IP
|
||||
# (删除 [http_service],或使用私有模板部署)
|
||||
# 切换到私有配置,避免后续部署再次分配公网 IP
|
||||
# (删除 [http_service] 或使用私有模板部署)
|
||||
fly deploy -c fly.private.toml
|
||||
|
||||
# 分配仅私有 IPv6
|
||||
# 分配仅私有的 IPv6
|
||||
fly ips allocate-v6 --private -a my-openclaw
|
||||
```
|
||||
|
||||
完成后,`fly ips list` 应只显示一个 `private` 类型 IP:
|
||||
完成后,`fly ips list` 应只显示 `private` 类型的 IP:
|
||||
|
||||
```
|
||||
VERSION IP TYPE REGION
|
||||
@ -435,7 +444,7 @@ fly proxy 3000:3000 -a my-openclaw
|
||||
fly wireguard create
|
||||
|
||||
# 导入到 WireGuard 客户端,然后通过内部 IPv6 访问
|
||||
# 例如:http://[fdaa:x:x:x:x::x]:3000
|
||||
# 示例:http://[fdaa:x:x:x:x::x]:3000
|
||||
```
|
||||
|
||||
**选项 3:仅 SSH**
|
||||
@ -444,15 +453,15 @@ fly wireguard create
|
||||
fly ssh console -a my-openclaw
|
||||
```
|
||||
|
||||
### 私有部署中的 Webhook
|
||||
### 使用私有部署处理 webhook
|
||||
|
||||
如果你在不公开暴露的情况下仍需要 webhook 回调(Twilio、Telnyx 等):
|
||||
如果你需要 webhook 回调(Twilio、Telnyx 等),但又不想对公网暴露:
|
||||
|
||||
1. **ngrok 隧道** —— 在容器内或作为 sidecar 运行 ngrok
|
||||
2. **Tailscale Funnel** —— 通过 Tailscale 暴露特定路径
|
||||
3. **仅出站** —— 某些提供商(如 Twilio)在无 webhook 的情况下也可正常用于出站通话
|
||||
1. **ngrok 隧道** - 在容器内运行 ngrok,或作为 sidecar 运行
|
||||
2. **Tailscale Funnel** - 通过 Tailscale 暴露特定路径
|
||||
3. **仅出站** - 某些提供商(如 Twilio)即使没有 webhook 也能正常处理出站呼叫
|
||||
|
||||
使用 ngrok 的示例 voice-call 配置:
|
||||
使用 ngrok 的语音通话配置示例:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -473,37 +482,37 @@ fly ssh console -a my-openclaw
|
||||
}
|
||||
```
|
||||
|
||||
ngrok 隧道在容器内运行,并提供一个公开的 webhook URL,而无需暴露 Fly 应用本身。将 `webhookSecurity.allowedHosts` 设置为公开隧道主机名,这样转发后的 host 标头才会被接受。
|
||||
ngrok 隧道在容器内运行,可提供一个公开的 webhook URL,而无需暴露 Fly 应用本身。请将 `webhookSecurity.allowedHosts` 设置为公开隧道的主机名,这样转发过来的 Host 头才会被接受。
|
||||
|
||||
### 安全收益
|
||||
|
||||
| Aspect | Public | Private |
|
||||
| ----------------- | ------ | --------- |
|
||||
| Internet scanners | 可发现 | 隐藏 |
|
||||
| Direct attacks | 可能 | 被阻止 |
|
||||
| Control UI access | 浏览器 | 代理 / VPN |
|
||||
| Webhook delivery | 直连 | 通过隧道 |
|
||||
| 方面 | 公开部署 | 私有部署 |
|
||||
| ---------------- | -------- | ------------ |
|
||||
| 互联网扫描器发现 | 可发现 | 隐藏 |
|
||||
| 直接攻击 | 可能 | 被阻止 |
|
||||
| Control UI 访问 | 浏览器 | 代理 / VPN |
|
||||
| webhook 投递 | 直接 | 通过隧道 |
|
||||
|
||||
## 说明
|
||||
|
||||
- Fly.io 使用的是 **x86 架构**(不是 ARM)
|
||||
- Dockerfile 兼容这两种架构
|
||||
- 对于 WhatsApp / Telegram 新手引导,请使用 `fly ssh console`
|
||||
- Fly.io 使用 **x86 架构**(不是 ARM)
|
||||
- Dockerfile 同时兼容这两种架构
|
||||
- 对于 WhatsApp/Telegram 新手引导,请使用 `fly ssh console`
|
||||
- 持久化数据存放在卷上的 `/data`
|
||||
- Signal 需要 Java + `signal-cli`;请使用自定义镜像,并将内存保持在 2 GB 以上。
|
||||
- Signal 需要 Java + `signal-cli`;请使用自定义镜像,并将内存保持在 2GB 以上。
|
||||
|
||||
## 成本
|
||||
## 费用
|
||||
|
||||
使用推荐配置(`shared-cpu-2x`、2 GB RAM)时:
|
||||
使用推荐配置(`shared-cpu-2x`,2GB RAM)时:
|
||||
|
||||
- 约 $10–15 / 月,具体取决于使用情况
|
||||
- 约 $10–15/月,具体取决于使用情况
|
||||
- 免费层包含一定额度
|
||||
|
||||
详情请参见 [Fly.io 定价](https://fly.io/docs/about/pricing/)。
|
||||
|
||||
## 后续步骤
|
||||
## 下一步
|
||||
|
||||
- 设置消息渠道:[渠道](/zh-CN/channels)
|
||||
- 设置消息渠道:[Channels](/zh-CN/channels)
|
||||
- 配置 Gateway 网关:[Gateway 网关配置](/zh-CN/gateway/configuration)
|
||||
- 保持 OpenClaw 为最新版本:[更新](/zh-CN/install/updating)
|
||||
|
||||
|
||||
@ -1,167 +1,174 @@
|
||||
---
|
||||
read_when:
|
||||
- 通过 ACP 运行编码 harness
|
||||
- 在消息渠道上设置与对话绑定的 ACP 会话
|
||||
- 将消息渠道中的会话绑定到持久化的 ACP 会话
|
||||
- ACP 后端和插件连接的故障排除
|
||||
- 调试 ACP 完成结果传递或智能体到智能体循环
|
||||
- 从聊天中操作 `/acp` 命令
|
||||
summary: 对 Claude Code、Cursor、Gemini CLI 使用 ACP 运行时会话,显式的 Codex ACP 回退、OpenClaw ACP,以及其他 harness 智能体也同样如此
|
||||
- 在消息渠道上设置与会话绑定的 ACP 会话
|
||||
- 将消息渠道会话绑定到持久化 ACP 会话
|
||||
- 排查 ACP 后端和插件接线问题
|
||||
- 调试 ACP 完成结果传递或智能体到智能体循环问题
|
||||
- 在聊天中使用 `/acp` 命令
|
||||
summary: 对 Claude Code、Cursor、Gemini CLI 使用 ACP 运行时会话,并为 Codex ACP 显式回退、OpenClaw ACP 以及其他 harness 智能体使用 ACP 运行时会话
|
||||
title: ACP 智能体
|
||||
x-i18n:
|
||||
generated_at: "2026-04-26T00:28:54Z"
|
||||
generated_at: "2026-04-26T00:39:28Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 6fbf36aa46bbd555881333368c858ec80271ce188d01fb0e71ef12a13d2d626e
|
||||
source_hash: a83f8a9a90569fd5d6e7bff4edbf15831e01179964a2d758fd1b70578ca378a0
|
||||
source_path: tools/acp-agents.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
[Agent Client Protocol(ACP)](https://agentclientprotocol.com/) 会话让 OpenClaw 能够通过 ACP 后端插件运行外部编码 harness(例如 Pi、Claude Code、Cursor、Copilot、OpenClaw ACP、OpenCode、Gemini CLI,以及其他受支持的 ACPX harness)。
|
||||
[Agent Client Protocol(ACP)](https://agentclientprotocol.com/) 会话让 OpenClaw 能够通过 ACP 后端插件运行外部编码 harness(例如 Pi、Claude Code、Cursor、Copilot、Droid、OpenClaw ACP、OpenCode、Gemini CLI 以及其他受支持的 ACPX harness)。
|
||||
|
||||
如果你用自然语言请求 OpenClaw 在当前会话中绑定或控制 Codex,并且内置的 `codex` 插件已启用,OpenClaw 应使用原生 Codex 应用服务器插件(`/codex bind`、`/codex threads`、`/codex resume`、`/codex steer`、`/codex stop`),而不是 ACP。如果你明确请求 `/acp`、ACP、acpx 或 ACP 适配器测试,OpenClaw 仍然可以通过 ACP 路由 Codex。每次 ACP 会话生成都会作为一个[后台任务](/zh-CN/automation/tasks)进行跟踪。
|
||||
如果你用自然语言要求 OpenClaw 在当前会话中绑定或控制 Codex,并且内置的 `codex` 插件已启用,OpenClaw 应使用原生 Codex app-server 插件(`/codex bind`、`/codex threads`、`/codex resume`、`/codex steer`、`/codex stop`),而不是 ACP。如果你明确要求 `/acp`、ACP、acpx,或 ACP 适配器测试,OpenClaw 仍然可以通过 ACP 路由 Codex。每次 ACP 会话启动都会被记录为一个[后台任务](/zh-CN/automation/tasks)。
|
||||
|
||||
如果你用自然语言请求 OpenClaw “在一个线程中启动 Claude Code” 或使用其他外部 harness,OpenClaw 应将该请求路由到 ACP 运行时(而不是原生子智能体运行时)。
|
||||
如果你用自然语言要求 OpenClaw “在一个线程中启动 Claude Code” 或使用其他外部 harness,OpenClaw 应将该请求路由到 ACP 运行时(而不是原生子智能体运行时)。
|
||||
|
||||
如果你想让 Codex 或 Claude Code 作为外部 MCP 客户端直接连接到现有的 OpenClaw 渠道会话,请使用 [`openclaw mcp serve`](/zh-CN/cli/mcp) 而不是 ACP。
|
||||
如果你希望让 Codex 或 Claude Code 作为外部 MCP 客户端直接连接到现有的 OpenClaw 渠道会话,
|
||||
请使用 [`openclaw mcp serve`](/zh-CN/cli/mcp),而不是 ACP。
|
||||
|
||||
## 我想看哪个页面?
|
||||
## 我想看哪一页?
|
||||
|
||||
这里有三个相邻但很容易混淆的入口:
|
||||
这里有三个彼此相近、很容易混淆的功能入口:
|
||||
|
||||
| 你想要…… | 使用这个 | 说明 |
|
||||
| ----------------------------------------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 在当前会话中绑定或控制 Codex | `/codex bind`、`/codex threads` | 当 `codex` 插件启用时使用原生 Codex 应用服务器路径;包括绑定聊天回复、图片转发、model/fast/permissions、停止和 steer 控制。ACP 是显式回退路径 |
|
||||
| 通过 OpenClaw 运行 Claude Code、Gemini CLI、显式 Codex ACP,或其他外部 harness | 本页:ACP 智能体 | 绑定聊天的会话、`/acp spawn`、`sessions_spawn({ runtime: "acp" })`、后台任务、运行时控制 |
|
||||
| 将一个 OpenClaw Gateway 网关会话作为 ACP 服务器暴露给编辑器或客户端 | [`openclaw acp`](/zh-CN/cli/acp) | 桥接模式。IDE/客户端 通过 stdio/WebSocket 使用 ACP 与 OpenClaw 通信 |
|
||||
| 复用本地 AI CLI 作为纯文本回退模型 | [CLI 后端](/zh-CN/gateway/cli-backends) | 不是 ACP。没有 OpenClaw 工具、没有 ACP 控制、没有 harness 运行时 |
|
||||
| 在当前会话中绑定或控制 Codex | `/codex bind`、`/codex threads` | 当 `codex` 插件启用时,使用原生 Codex app-server 路径;包括绑定聊天回复、图片转发、模型 / 快速 / 权限、停止和引导控制。ACP 是显式回退方案 |
|
||||
| 通过 OpenClaw 运行 Claude Code、Gemini CLI、显式 Codex ACP,或其他外部 harness | 本页:ACP 智能体 | 聊天绑定会话、`/acp spawn`、`sessions_spawn({ runtime: "acp" })`、后台任务、运行时控制 |
|
||||
| 将一个 OpenClaw Gateway 网关会话作为 ACP 服务器暴露给编辑器或客户端 | [`openclaw acp`](/zh-CN/cli/acp) | 桥接模式。IDE / 客户端通过 stdio/WebSocket 使用 ACP 与 OpenClaw 通信 |
|
||||
| 将本地 AI CLI 复用为纯文本回退模型 | [CLI 后端](/zh-CN/gateway/cli-backends) | 不是 ACP。没有 OpenClaw 工具、没有 ACP 控制、没有 harness 运行时 |
|
||||
|
||||
## 这是开箱即用的吗?
|
||||
|
||||
通常是的。全新安装默认会启用内置的 `acpx` 运行时插件,并带有一个插件本地固定版本的 `acpx` 二进制文件,OpenClaw 会在启动时探测并自我修复。运行 `/acp doctor` 可进行就绪状态检查。
|
||||
通常是。全新安装会默认启用内置的 `acpx` 运行时插件,并带有一个插件本地固定版本的 `acpx` 二进制文件,OpenClaw 会在启动时探测并自我修复。运行 `/acp doctor` 可进行就绪性检查。
|
||||
|
||||
首次运行的常见问题:
|
||||
首次运行的注意事项:
|
||||
|
||||
- 如果设置了 `plugins.allow`,它就是一个限制性的插件清单,必须包含 `acpx`;否则内置默认项会被有意阻止,且 `/acp doctor` 会报告缺少 allowlist 条目。
|
||||
- 如果设置了 `plugins.allow`,它就是一个限制性的插件清单,必须包含 `acpx`;否则内置默认项会被有意阻止,且 `/acp doctor` 会报告缺失的 allowlist 条目。
|
||||
- 目标 harness 适配器(Codex、Claude 等)可能会在你首次使用时通过 `npx` 按需获取。
|
||||
- 该 harness 依赖的厂商认证仍然必须已存在于主机上。
|
||||
- 如果主机没有 npm 或网络访问能力,首次适配器获取会失败,直到缓存预热完成或以其他方式安装适配器。
|
||||
- 该 harness 对应的厂商认证仍然必须已存在于主机上。
|
||||
- 如果主机没有 npm 或网络访问能力,首次运行的适配器获取会失败,直到缓存被预热或通过其他方式安装该适配器。
|
||||
|
||||
## 操作手册
|
||||
|
||||
从聊天中快速执行 `/acp` 流程:
|
||||
聊天中的快速 `/acp` 流程:
|
||||
|
||||
1. **生成** — `/acp spawn claude --bind here`、`/acp spawn gemini --mode persistent --thread auto`,或显式使用 `/acp spawn codex --bind here`
|
||||
2. 在绑定的会话或线程中**工作**(或者显式指定会话键)。
|
||||
1. **启动** — `/acp spawn claude --bind here`、`/acp spawn gemini --mode persistent --thread auto`,或显式使用 `/acp spawn codex --bind here`
|
||||
2. 在已绑定的会话或线程中**工作**(或者显式指定会话键)。
|
||||
3. **检查状态** — `/acp status`
|
||||
4. **调优** — `/acp model <provider/model>`、`/acp permissions <profile>`、`/acp timeout <seconds>`
|
||||
5. **引导**而不替换上下文 — `/acp steer tighten logging and continue`
|
||||
5. 在不替换上下文的情况下**引导** — `/acp steer tighten logging and continue`
|
||||
6. **停止** — `/acp cancel`(当前轮次)或 `/acp close`(会话 + 绑定)
|
||||
|
||||
当原生 Codex 插件启用时,以下自然语言触发词应路由到原生 Codex 插件:
|
||||
当启用了原生 Codex 插件时,应路由到该插件的自然语言触发方式:
|
||||
|
||||
- “把这个 Discord 渠道绑定到 Codex。”
|
||||
- “将这个 Discord 渠道绑定到 Codex。”
|
||||
- “把这个聊天附加到 Codex 线程 `<id>`。”
|
||||
- “显示 Codex 线程,然后绑定这个。”
|
||||
- “显示 Codex 线程,然后绑定当前这个。”
|
||||
|
||||
原生 Codex 会话绑定是默认的聊天控制路径。OpenClaw 动态工具仍通过 OpenClaw 执行,而 Codex 原生工具(如 shell/apply-patch)则在 Codex 内执行。对于 Codex 原生工具事件,OpenClaw 会注入一个按轮次生效的原生 hook 中继,以便插件钩子可以拦截 `before_tool_call`、观察 `after_tool_call`,并通过 OpenClaw 审批机制路由 Codex `PermissionRequest` 事件。Codex 的 `Stop` 钩子会被中继到 OpenClaw 的 `before_agent_finalize`,此时插件可以在 Codex 最终输出答案前请求再进行一次模型调用。该中继刻意保持保守:它不会修改 Codex 原生工具参数,也不会重写 Codex 线程记录。只有当你想使用 ACP 运行时/会话模型时,才应显式使用 ACP。嵌入式 Codex 支持边界记录在 [Codex harness v1 支持契约](/zh-CN/plugins/codex-harness#v1-support-contract) 中。
|
||||
原生 Codex 会话绑定是默认的聊天控制路径。OpenClaw 动态工具仍然通过 OpenClaw 执行,而 Codex 原生工具(如 shell / apply-patch)则在 Codex 内部执行。对于 Codex 原生工具事件,OpenClaw 会按轮次注入一个原生 hook relay,以便插件钩子能够阻止
|
||||
`before_tool_call`、观察 `after_tool_call`,并通过 OpenClaw 审批流程路由 Codex 的
|
||||
`PermissionRequest` 事件。Codex 的 `Stop` 钩子会被转发到 OpenClaw 的 `before_agent_finalize`,在那里插件可以在 Codex 最终生成答案之前请求再进行一次模型调用。这个 relay 保持刻意保守:它不会修改 Codex 原生工具参数,也不会重写 Codex 线程记录。只有当你明确想要 ACP 运行时 / 会话模型时,才使用显式 ACP。嵌入式 Codex 支持边界记录在
|
||||
[Codex harness v1 支持约定](/zh-CN/plugins/codex-harness#v1-support-contract) 中。
|
||||
|
||||
以下自然语言触发词应路由到 ACP 运行时:
|
||||
应路由到 ACP 运行时的自然语言触发方式:
|
||||
|
||||
- “把这个任务作为一次性的 Claude Code ACP 会话运行,并总结结果。”
|
||||
- “这个任务使用 Gemini CLI 在线程中执行,然后后续继续在同一个线程里进行。”
|
||||
- “通过 ACP 在线程后台运行 Codex。”
|
||||
- “在一个线程里用 Gemini CLI 处理这个任务,然后把后续内容继续保留在同一个线程中。”
|
||||
- “通过 ACP 在后台线程中运行 Codex。”
|
||||
|
||||
OpenClaw 会选择 `runtime: "acp"`,解析 harness 的 `agentId`,在支持时绑定到当前会话或线程,并将后续消息路由到该会话,直到关闭或过期。只有在明确请求 ACP/acpx,或所请求操作无法使用原生 Codex 插件时,Codex 才会走这条路径。
|
||||
OpenClaw 会选择 `runtime: "acp"`,解析 harness `agentId`,在支持的情况下绑定到当前会话或线程,并将后续消息路由到该会话,直到关闭 / 过期。只有在明确要求 ACP / acpx,或请求的操作无法使用原生 Codex 插件时,Codex 才会走这条路径。
|
||||
|
||||
对于 `sessions_spawn`,只有在 ACP 已启用、请求方未处于沙箱隔离状态,并且 ACP 运行时后端已加载时,`runtime: "acp"` 才会被公布。它面向 `codex`、`claude`、`gemini` 或 `opencode` 等 ACP harness id。除非 `agents_list` 中的某个普通 OpenClaw 配置智能体条目明确设置了 `agents.list[].runtime.type="acp"`,否则不要传入它的 id;在其他情况下,请使用默认的子智能体运行时。当某个 OpenClaw 智能体配置了 `runtime.type="acp"` 时,OpenClaw 会使用 `runtime.acp.agent` 作为底层 harness id。
|
||||
对于 `sessions_spawn`,只有在 ACP 已启用、
|
||||
请求方未处于沙箱隔离状态,且 ACP 运行时后端已加载时,才会公布 `runtime: "acp"`。它面向
|
||||
ACP harness id,例如 `codex`、`claude`、`droid`、`gemini` 或 `opencode`。不要传入来自 `agents_list` 的普通 OpenClaw 配置智能体 id,除非该条目已显式配置了 `agents.list[].runtime.type="acp"`;否则应使用默认子智能体运行时。当一个 OpenClaw 智能体配置了
|
||||
`runtime.type="acp"` 时,OpenClaw 会使用 `runtime.acp.agent` 作为底层 harness id。
|
||||
|
||||
## ACP 与子智能体
|
||||
|
||||
当你想使用外部 harness 运行时时,请使用 ACP。当 `codex` 插件启用并需要 Codex 会话绑定/控制时,请使用原生 Codex 应用服务器。当你想使用 OpenClaw 原生委派运行时,请使用子智能体。
|
||||
当你需要外部 harness 运行时时,使用 ACP。当 `codex` 插件启用且你要进行 Codex 会话绑定 / 控制时,使用原生 Codex app-server。若你希望执行 OpenClaw 原生委派运行,则使用子智能体。
|
||||
|
||||
| 区域 | ACP 会话 | 子智能体运行 |
|
||||
| ------------- | ------------------------------------- | ---------------------------------- |
|
||||
| 运行时 | ACP 后端插件(例如 acpx) | OpenClaw 原生子智能体运行时 |
|
||||
| 会话键 | `agent:<agentId>:acp:<uuid>` | `agent:<agentId>:subagent:<uuid>` |
|
||||
| 主要命令 | `/acp ...` | `/subagents ...` |
|
||||
| 生成工具 | `sessions_spawn`,配合 `runtime:"acp"` | `sessions_spawn`(默认运行时) |
|
||||
| 启动工具 | `sessions_spawn` 配合 `runtime:"acp"` | `sessions_spawn`(默认运行时) |
|
||||
|
||||
另见[子智能体](/zh-CN/tools/subagents)。
|
||||
另请参阅 [子智能体](/zh-CN/tools/subagents)。
|
||||
|
||||
## ACP 如何运行 Claude Code
|
||||
|
||||
对于通过 ACP 运行的 Claude Code,堆栈如下:
|
||||
对于通过 ACP 运行的 Claude Code,其栈结构如下:
|
||||
|
||||
1. OpenClaw ACP 会话控制平面
|
||||
2. 内置 `acpx` 运行时插件
|
||||
3. Claude ACP 适配器
|
||||
4. Claude 侧运行时/会话机制
|
||||
4. Claude 侧运行时 / 会话机制
|
||||
|
||||
重要区别:
|
||||
|
||||
- ACP Claude 是一个 harness 会话,具备 ACP 控制、会话恢复、后台任务跟踪,以及可选的会话/线程绑定。
|
||||
- ACP Claude 是一种 harness 会话,具备 ACP 控制、会话恢复、后台任务跟踪,以及可选的会话 / 线程绑定。
|
||||
- CLI 后端是独立的纯文本本地回退运行时。参见 [CLI 后端](/zh-CN/gateway/cli-backends)。
|
||||
|
||||
对于操作人员,实用规则是:
|
||||
对操作人员来说,实用规则是:
|
||||
|
||||
- 想要 `/acp spawn`、可绑定会话、运行时控制或持久化 harness 工作:使用 ACP
|
||||
- 想要通过原始 CLI 获得简单的本地文本回退:使用 CLI 后端
|
||||
- 想要 `/acp spawn`、可绑定会话、运行时控制,或持久化 harness 工作:使用 ACP
|
||||
- 想要通过原始 CLI 进行简单的本地文本回退:使用 CLI 后端
|
||||
|
||||
## 绑定会话
|
||||
|
||||
### 当前会话绑定
|
||||
|
||||
`/acp spawn <harness> --bind here` 会将当前会话固定绑定到已生成的 ACP 会话 —— 不创建子线程,保留同一个聊天界面。OpenClaw 继续负责传输、认证、安全和消息投递;该会话中的后续消息会路由到同一个会话;`/new` 和 `/reset` 会原地重置会话;`/acp close` 会移除绑定。
|
||||
`/acp spawn <harness> --bind here` 会将当前会话固定到已启动的 ACP 会话 —— 不创建子线程,仍在同一个聊天界面中。OpenClaw 继续负责传输、认证、安全和消息投递;该会话中的后续消息会路由到同一个会话;`/new` 和 `/reset` 会原地重置该会话;`/acp close` 会移除该绑定。
|
||||
|
||||
理解模型:
|
||||
心智模型:
|
||||
|
||||
- **聊天界面** —— 人们持续交流的地方(Discord 渠道、Telegram 话题、iMessage 聊天)。
|
||||
- **ACP 会话** —— OpenClaw 路由到的持久化 Codex/Claude/Gemini 运行时状态。
|
||||
- **子线程/话题** —— 仅在使用 `--thread ...` 时创建的可选附加消息界面。
|
||||
- **运行时工作区** —— harness 运行所在的文件系统位置(`cwd`、仓库检出目录、后端工作区)。它独立于聊天界面。
|
||||
- **ACP 会话** —— OpenClaw 路由到的持久化 Codex / Claude / Gemini 运行时状态。
|
||||
- **子线程 / 话题** —— 仅在使用 `--thread ...` 时才会创建的可选额外消息界面。
|
||||
- **运行时工作区** —— harness 运行的文件系统位置(`cwd`、仓库检出目录、后端工作区)。它独立于聊天界面。
|
||||
|
||||
示例:
|
||||
|
||||
- `/codex bind` —— 保留当前聊天,生成或附加原生 Codex 应用服务器,并将未来消息路由到这里。
|
||||
- `/codex model gpt-5.4`、`/codex fast on`、`/codex permissions yolo` —— 直接从聊天中调优绑定的原生 Codex 线程。
|
||||
- `/codex bind` —— 保留当前聊天,启动或附加原生 Codex app-server,并将未来消息路由到这里。
|
||||
- `/codex model gpt-5.4`、`/codex fast on`、`/codex permissions yolo` —— 直接在聊天中调优已绑定的原生 Codex 线程。
|
||||
- `/codex stop` 或 `/codex steer focus on the failing tests first` —— 控制当前活跃的原生 Codex 轮次。
|
||||
- `/acp spawn codex --bind here` —— 对 Codex 使用显式 ACP 回退。
|
||||
- `/acp spawn codex --thread auto` —— OpenClaw 可能创建一个子线程/话题并在那里绑定。
|
||||
- `/acp spawn codex --bind here --cwd /workspace/repo` —— 保持当前聊天绑定,Codex 在 `/workspace/repo` 中运行。
|
||||
- `/acp spawn codex --bind here` —— Codex 的显式 ACP 回退方案。
|
||||
- `/acp spawn codex --thread auto` —— OpenClaw 可能会创建一个子线程 / 话题并绑定到那里。
|
||||
- `/acp spawn codex --bind here --cwd /workspace/repo` —— 保持当前聊天绑定,但 Codex 在 `/workspace/repo` 中运行。
|
||||
|
||||
说明:
|
||||
|
||||
- `--bind here` 和 `--thread ...` 互斥。
|
||||
- `--bind here` 仅适用于声明支持当前会话绑定的渠道;否则 OpenClaw 会返回明确的不支持提示。绑定在 Gateway 网关重启后仍会保留。
|
||||
- 在 Discord 上,只有当 OpenClaw 需要为 `--thread auto|here` 创建子线程时,才需要 `spawnAcpSessions` —— 对于 `--bind here` 则不需要。
|
||||
- 如果你在没有 `--cwd` 的情况下生成到不同的 ACP 智能体,OpenClaw 默认会继承**目标智能体的**工作区。若继承路径缺失(`ENOENT`/`ENOTDIR`),则回退到后端默认值;其他访问错误(例如 `EACCES`)会直接作为生成错误显示。
|
||||
- `--bind here` 仅适用于声明支持当前会话绑定的渠道;否则 OpenClaw 会返回清晰的不支持提示。绑定会在 Gateway 网关重启后继续保留。
|
||||
- 在 Discord 上,仅当 OpenClaw 需要为 `--thread auto|here` 创建子线程时,才需要 `spawnAcpSessions` —— 对于 `--bind here` 则不需要。
|
||||
- 如果你启动到不同的 ACP 智能体且没有指定 `--cwd`,OpenClaw 默认会继承**目标智能体的**工作区。若继承的路径缺失(`ENOENT` / `ENOTDIR`),则会回退到后端默认值;其他访问错误(例如 `EACCES`)则会作为启动错误直接暴露。
|
||||
|
||||
### 线程绑定会话
|
||||
|
||||
当某个渠道适配器启用了线程绑定时,ACP 会话可以绑定到线程:
|
||||
|
||||
- OpenClaw 将一个线程绑定到目标 ACP 会话。
|
||||
- 该线程中的后续消息会路由到绑定的 ACP 会话。
|
||||
- ACP 输出会回传到同一个线程。
|
||||
- 取消聚焦、关闭、归档、空闲超时或达到最大存活时间后,绑定会被移除。
|
||||
- OpenClaw 将某个线程绑定到目标 ACP 会话。
|
||||
- 该线程中的后续消息会路由到已绑定的 ACP 会话。
|
||||
- ACP 输出会被投递回同一个线程。
|
||||
- 取消聚焦 / 关闭 / 归档 / 空闲超时,或最大存活时间到期,都会移除该绑定。
|
||||
|
||||
线程绑定支持是适配器特定的。如果当前渠道适配器不支持线程绑定,OpenClaw 会返回明确的不支持/不可用提示。
|
||||
线程绑定支持取决于适配器。如果当前渠道适配器不支持线程绑定,OpenClaw 会返回清晰的不支持 / 不可用提示。
|
||||
|
||||
线程绑定 ACP 所需的功能开关:
|
||||
线程绑定 ACP 所需的功能标志:
|
||||
|
||||
- `acp.enabled=true`
|
||||
- `acp.dispatch.enabled` 默认开启(设为 `false` 可暂停 ACP 分发)
|
||||
- 启用渠道适配器的 ACP 线程生成开关(适配器特定)
|
||||
- `acp.dispatch.enabled` 默认开启(设置为 `false` 可暂停 ACP 分发)
|
||||
- 渠道适配器的 ACP 线程启动标志已启用(适配器特定)
|
||||
- Discord:`channels.discord.threadBindings.spawnAcpSessions=true`
|
||||
- Telegram:`channels.telegram.threadBindings.spawnAcpSessions=true`
|
||||
|
||||
### 支持线程的渠道
|
||||
|
||||
- 任何暴露会话/线程绑定能力的渠道适配器。
|
||||
- 任何暴露会话 / 线程绑定能力的渠道适配器。
|
||||
- 当前内置支持:
|
||||
- Discord 线程/渠道
|
||||
- Telegram 话题(群组/超级群组中的论坛话题,以及私信话题)
|
||||
- Discord 线程 / 渠道
|
||||
- Telegram 话题(群组 / 超级群组中的论坛话题,以及私信话题)
|
||||
- 插件渠道也可以通过相同的绑定接口添加支持。
|
||||
|
||||
## 渠道特定设置
|
||||
@ -170,15 +177,15 @@ OpenClaw 会选择 `runtime: "acp"`,解析 harness 的 `agentId`,在支持
|
||||
|
||||
### 绑定模型
|
||||
|
||||
- `bindings[].type="acp"` 表示一个持久化的 ACP 会话绑定。
|
||||
- `bindings[].type="acp"` 标记一个持久化 ACP 会话绑定。
|
||||
- `bindings[].match` 用于标识目标会话:
|
||||
- Discord 渠道或线程:`match.channel="discord"` + `match.peer.id="<channelOrThreadId>"`
|
||||
- Telegram 论坛话题:`match.channel="telegram"` + `match.peer.id="<chatId>:topic:<topicId>"`
|
||||
- BlueBubbles 私信/群聊:`match.channel="bluebubbles"` + `match.peer.id="<handle|chat_id:*|chat_guid:*|chat_identifier:*>"`
|
||||
- BlueBubbles 私信 / 群聊:`match.channel="bluebubbles"` + `match.peer.id="<handle|chat_id:*|chat_guid:*|chat_identifier:*>"`
|
||||
对于稳定的群组绑定,优先使用 `chat_id:*` 或 `chat_identifier:*`。
|
||||
- iMessage 私信/群聊:`match.channel="imessage"` + `match.peer.id="<handle|chat_id:*|chat_guid:*|chat_identifier:*>"`
|
||||
- iMessage 私信 / 群聊:`match.channel="imessage"` + `match.peer.id="<handle|chat_id:*|chat_guid:*|chat_identifier:*>"`
|
||||
对于稳定的群组绑定,优先使用 `chat_id:*`。
|
||||
- `bindings[].agentId` 是拥有该绑定的 OpenClaw 智能体 id。
|
||||
- `bindings[].agentId` 是所属的 OpenClaw 智能体 id。
|
||||
- 可选的 ACP 覆盖项位于 `bindings[].acp` 下:
|
||||
- `mode`(`persistent` 或 `oneshot`)
|
||||
- `label`
|
||||
@ -283,18 +290,18 @@ ACP 绑定会话的覆盖优先级:
|
||||
|
||||
行为:
|
||||
|
||||
- OpenClaw 会确保配置的 ACP 会话在使用前已存在。
|
||||
- 该渠道或话题中的消息会路由到配置好的 ACP 会话。
|
||||
- OpenClaw 会在使用前确保已配置的 ACP 会话存在。
|
||||
- 该渠道或话题中的消息会路由到已配置的 ACP 会话。
|
||||
- 在已绑定的会话中,`/new` 和 `/reset` 会原地重置同一个 ACP 会话键。
|
||||
- 临时运行时绑定(例如由线程聚焦流程创建的绑定)在存在时仍然生效。
|
||||
- 对于未显式指定 `cwd` 的跨智能体 ACP 生成,OpenClaw 会从智能体配置继承目标智能体的工作区。
|
||||
- 缺失的继承工作区路径会回退到后端默认 `cwd`;非缺失类访问失败会作为生成错误返回。
|
||||
- 临时运行时绑定(例如由线程聚焦流程创建的绑定)在存在时仍会生效。
|
||||
- 对于未显式指定 `cwd` 的跨智能体 ACP 启动,OpenClaw 会从智能体配置中继承目标智能体的工作区。
|
||||
- 缺失的继承工作区路径会回退到后端默认 `cwd`;非缺失类访问失败则会作为启动错误暴露出来。
|
||||
|
||||
## 启动 ACP 会话(接口)
|
||||
|
||||
### 从 `sessions_spawn` 启动
|
||||
### 从 `sessions_spawn`
|
||||
|
||||
使用 `runtime: "acp"` 从智能体轮次或工具调用中启动一个 ACP 会话。
|
||||
使用 `runtime: "acp"` 从一个智能体轮次或工具调用中启动 ACP 会话。
|
||||
|
||||
```json
|
||||
{
|
||||
@ -308,28 +315,28 @@ ACP 绑定会话的覆盖优先级:
|
||||
|
||||
说明:
|
||||
|
||||
- `runtime` 默认值是 `subagent`,因此对于 ACP 会话,需要显式设置 `runtime: "acp"`。
|
||||
- `runtime` 默认为 `subagent`,因此对于 ACP 会话需要显式设置 `runtime: "acp"`。
|
||||
- 如果省略 `agentId`,OpenClaw 会在已配置时使用 `acp.defaultAgent`。
|
||||
- `mode: "session"` 需要 `thread: true` 才能保持持久化的绑定会话。
|
||||
- `mode: "session"` 需要 `thread: true` 才能保持持久化绑定会话。
|
||||
|
||||
接口细节:
|
||||
|
||||
- `task`(必填):发送给 ACP 会话的初始提示。
|
||||
- `task`(必填):发送到 ACP 会话的初始提示。
|
||||
- `runtime`(ACP 必填):必须为 `"acp"`。
|
||||
- `agentId`(可选):ACP 目标 harness id。如果已设置,则回退到 `acp.defaultAgent`。
|
||||
- `thread`(可选,默认 `false`):在支持时请求线程绑定流程。
|
||||
- `mode`(可选):`run`(一次性)或 `session`(持久化)。
|
||||
- 默认值是 `run`
|
||||
- 如果 `thread: true` 且省略 `mode`,OpenClaw 可能根据运行时路径默认采用持久化行为
|
||||
- 默认值为 `run`
|
||||
- 如果 `thread: true` 且省略 `mode`,OpenClaw 可能会根据运行时路径默认采用持久化行为
|
||||
- `mode: "session"` 需要 `thread: true`
|
||||
- `cwd`(可选):请求的运行时工作目录(由后端/运行时策略校验)。如果省略,ACP 生成会在已配置时继承目标智能体工作区;缺失的继承路径会回退到后端默认值,而真实访问错误会被返回。
|
||||
- `label`(可选):在会话/banner 文本中使用的面向操作员的标签。
|
||||
- `cwd`(可选):请求的运行时工作目录(由后端 / 运行时策略校验)。如果省略,ACP 启动会在已配置时继承目标智能体工作区;缺失的继承路径会回退到后端默认值,而真实访问错误会被直接返回。
|
||||
- `label`(可选):面向操作人员的标签,用于会话 / 横幅文本。
|
||||
- `resumeSessionId`(可选):恢复现有 ACP 会话,而不是创建新会话。智能体会通过 `session/load` 重放其会话历史。需要 `runtime: "acp"`。
|
||||
- `streamTo`(可选):`"parent"` 会将初始 ACP 运行进度摘要作为系统事件流式传回请求方会话。
|
||||
- 可用时,返回结果中可能包含 `streamLogPath`,指向一个按会话范围生成的 JSONL 日志(`<sessionId>.acp-stream.jsonl`),你可以对其执行 tail 以查看完整中继历史。
|
||||
- `runTimeoutSeconds`(可选):在 N 秒后中止 ACP 子轮次。`0` 会让该轮次走 Gateway 网关的无超时路径。同样的值会同时应用到 Gateway 网关运行和 ACP 运行时,避免卡住或额度耗尽的 harness 无限期占用父智能体通道。
|
||||
- `model`(可选):为 ACP 子会话显式覆盖模型。Codex ACP 生成会在 `session/new` 之前,将 OpenClaw 的 Codex 引用(例如 `openai-codex/gpt-5.4`)规范化为 Codex ACP 启动配置;带斜杠的形式(例如 `openai-codex/gpt-5.4/high`)还会设置 Codex ACP 的推理强度。其他 harness 必须声明 ACP `models` 并支持 `session/set_model`;否则 OpenClaw/acpx 会清晰失败,而不会静默回退到目标智能体默认值。
|
||||
- `thinking`(可选):为 ACP 子会话显式设置 thinking/推理强度。对于 Codex ACP,`minimal` 映射为低强度,`low`/`medium`/`high`/`xhigh` 直接映射,而 `off` 会省略推理强度启动覆盖。
|
||||
- `streamTo`(可选):`"parent"` 会将初始 ACP 运行进度摘要作为系统事件流式返回给请求方会话。
|
||||
- 在可用时,接受的响应中会包含 `streamLogPath`,指向一个会话范围的 JSONL 日志(`<sessionId>.acp-stream.jsonl`),你可以对其执行 tail 以查看完整 relay 历史。
|
||||
- `runTimeoutSeconds`(可选):在 N 秒后中止 ACP 子轮次。`0` 会让该轮次走 Gateway 网关的无超时路径。相同的值会同时应用到 Gateway 网关运行和 ACP 运行时,这样卡住或额度耗尽的 harness 就不会无限期占用父智能体通道。
|
||||
- `model`(可选):ACP 子会话的显式模型覆盖。Codex ACP 启动会在 `session/new` 之前,将 OpenClaw Codex 引用(例如 `openai-codex/gpt-5.4`)规范化为 Codex ACP 启动配置;像 `openai-codex/gpt-5.4/high` 这样的斜杠形式还会设置 Codex ACP 推理强度。其他 harness 必须声明 ACP `models` 并支持 `session/set_model`;否则 OpenClaw / acpx 会清晰地失败,而不是静默回退到目标智能体默认值。
|
||||
- `thinking`(可选):ACP 子会话的显式思考 / 推理强度。对于 Codex ACP,`minimal` 映射为低强度,`low` / `medium` / `high` / `xhigh` 直接映射,而 `off` 会省略推理强度启动覆盖。
|
||||
|
||||
## 交付模型
|
||||
|
||||
@ -337,42 +344,42 @@ ACP 会话既可以是交互式工作区,也可以是由父级拥有的后台
|
||||
|
||||
### 交互式 ACP 会话
|
||||
|
||||
交互式会话用于在可见聊天界面中持续交流:
|
||||
交互式会话用于在可见的聊天界面上持续对话:
|
||||
|
||||
- `/acp spawn ... --bind here` 将当前会话绑定到 ACP 会话。
|
||||
- `/acp spawn ... --thread ...` 将某个渠道线程/话题绑定到 ACP 会话。
|
||||
- `/acp spawn ... --thread ...` 将渠道线程 / 话题绑定到 ACP 会话。
|
||||
- 持久化配置的 `bindings[].type="acp"` 会将匹配的会话路由到同一个 ACP 会话。
|
||||
|
||||
在已绑定会话中的后续消息会直接路由到 ACP 会话,而 ACP 输出会回传到同一个渠道/线程/话题。
|
||||
已绑定会话中的后续消息会直接路由到 ACP 会话,而 ACP 输出也会投递回同一个渠道 / 线程 / 话题。
|
||||
|
||||
### 父级拥有的一次性 ACP 会话
|
||||
|
||||
由另一个智能体运行生成的一次性 ACP 会话是后台子级,类似子智能体:
|
||||
由另一个智能体运行启动的一次性 ACP 会话属于后台子任务,类似子智能体:
|
||||
|
||||
- 父级通过 `sessions_spawn({ runtime: "acp", mode: "run" })` 请求工作。
|
||||
- 子级在自己的 ACP harness 会话中运行。
|
||||
- 子级轮次运行在与原生子智能体生成相同的后台通道上,因此较慢的 ACP harness 不会阻塞无关的主会话工作。
|
||||
- 完成结果会通过内部任务完成通知路径回报。
|
||||
- 当需要面向用户的回复时,父级会用正常 assistant 语气重写子级结果。
|
||||
- 子级在它自己的 ACP harness 会话中运行。
|
||||
- 子级轮次运行在与原生子智能体启动相同的后台通道上,因此缓慢的 ACP harness 不会阻塞不相关的主会话工作。
|
||||
- 完成结果会通过内部任务完成通知路径回传。
|
||||
- 当需要面向用户的回复时,父级会用普通助手语气重写子级结果。
|
||||
|
||||
不要把这一路径视为父子之间的点对点聊天。子级已经有一个回传给父级的完成通道。
|
||||
不要将这条路径视为父子之间的点对点聊天。子级已经有一个返回给父级的完成通道。
|
||||
|
||||
### `sessions_send` 与 A2A 交付
|
||||
|
||||
`sessions_send` 可以在生成后将目标指向另一个会话。对于普通对等会话,在注入消息后,OpenClaw 会使用智能体到智能体(A2A)后续路径:
|
||||
`sessions_send` 可以在启动后将目标指向另一个会话。对于普通对等会话,OpenClaw 会在注入消息后使用智能体到智能体(A2A)的后续路径:
|
||||
|
||||
- 等待目标会话的回复
|
||||
- 可选地让请求方和目标交换有限轮数的后续消息
|
||||
- 要求目标生成一条 announce 消息
|
||||
- 将该 announce 投递到可见渠道或线程
|
||||
- 可选地让请求方和目标方进行有限次数的后续往返
|
||||
- 要求目标生成一条通知消息
|
||||
- 将该通知投递到可见的渠道或线程
|
||||
|
||||
该 A2A 路径是用于对等发送的一种回退机制,适用于发送方需要一个可见后续回复的情况。当一个无关会话在较宽泛的 `tools.sessions.visibility` 设置下可以看到并向 ACP 目标发送消息时,该机制仍保持启用。
|
||||
这条 A2A 路径是对等发送场景下的回退方案,适用于发送方需要可见后续结果时。它在某个无关会话可以查看并向 ACP 目标发消息时仍会保持启用,例如在较宽松的 `tools.sessions.visibility` 设置下。
|
||||
|
||||
仅当请求方是其自己所拥有的、父级拥有的一次性 ACP 子级的父级时,OpenClaw 才会跳过 A2A 后续流程。在这种情况下,如果在任务完成之上再运行 A2A,可能会用子级结果唤醒父级,再把父级回复转发回子级,从而形成父子回声循环。对于这种自有子级情况,`sessions_send` 结果会报告 `delivery.status="skipped"`,因为完成路径已经负责处理结果。
|
||||
只有当请求方是其自有的一次性 ACP 子会话的父级时,OpenClaw 才会跳过 A2A 后续流程。在这种情况下,如果在任务完成之上再运行 A2A,可能会用子级结果唤醒父级,再把父级回复转发回子级,从而形成父 / 子回声循环。对于这种已拥有子级的情况,`sessions_send` 结果会报告 `delivery.status="skipped"`,因为完成路径已经负责处理结果了。
|
||||
|
||||
### 恢复现有会话
|
||||
|
||||
使用 `resumeSessionId` 继续之前的 ACP 会话,而不是重新开始。智能体会通过 `session/load` 重放其会话历史,因此会带着此前完整上下文继续执行。
|
||||
使用 `resumeSessionId` 来继续之前的 ACP 会话,而不是重新开始。智能体会通过 `session/load` 重放其会话历史,因此它能够带着之前的完整上下文继续工作。
|
||||
|
||||
```json
|
||||
{
|
||||
@ -383,49 +390,49 @@ ACP 会话既可以是交互式工作区,也可以是由父级拥有的后台
|
||||
}
|
||||
```
|
||||
|
||||
常见用例:
|
||||
常见使用场景:
|
||||
|
||||
- 将 Codex 会话从你的笔记本电脑切换到手机 —— 告诉你的智能体从你上次中断的地方继续
|
||||
- 继续一个你在 CLI 中交互式启动、现在想通过智能体以无头方式继续的编码会话
|
||||
- 接续因 Gateway 网关重启或空闲超时而中断的工作
|
||||
- 将一个 Codex 会话从你的笔记本电脑交接到手机 —— 告诉你的智能体从你上次中断的地方继续
|
||||
- 继续一个你之前在 CLI 中以交互方式启动、现在想通过智能体以无头方式继续的编码会话
|
||||
- 继续因 Gateway 网关重启或空闲超时而中断的工作
|
||||
|
||||
说明:
|
||||
|
||||
- `resumeSessionId` 需要 `runtime: "acp"` —— 如果与子智能体运行时一起使用,会返回错误。
|
||||
- `resumeSessionId` 会恢复上游 ACP 会话历史;你正在创建的新 OpenClaw 会话仍会正常应用 `thread` 和 `mode`,因此 `mode: "session"` 仍然需要 `thread: true`。
|
||||
- 目标智能体必须支持 `session/load`(Codex 和 Claude Code 支持)。
|
||||
- 如果找不到该会话 ID,生成会以明确错误失败 —— 不会静默回退到新会话。
|
||||
- 目标智能体必须支持 `session/load`(Codex 和 Claude Code 都支持)。
|
||||
- 如果找不到该会话 id,启动会以清晰的错误失败 —— 不会静默回退到新会话。
|
||||
|
||||
<Accordion title="部署后冒烟测试">
|
||||
<Accordion title="部署后的冒烟测试">
|
||||
|
||||
在 Gateway 网关部署后,执行一次真实的端到端检查,而不是只依赖单元测试:
|
||||
在 Gateway 网关部署后,运行一次真实的端到端检查,而不是只依赖单元测试:
|
||||
|
||||
1. 验证目标主机上的已部署 Gateway 网关版本和提交记录。
|
||||
2. 打开一个到在线智能体的临时 ACPX 桥接会话。
|
||||
3. 要求该智能体调用 `sessions_spawn`,并设置 `runtime: "acp"`、`agentId: "codex"`、`mode: "run"`,任务内容为 `Reply with exactly LIVE-ACP-SPAWN-OK`。
|
||||
4. 验证 `accepted=yes`、存在真实的 `childSessionKey`,并且没有校验器错误。
|
||||
5. 清理这个临时桥接会话。
|
||||
1. 验证目标主机上已部署的 Gateway 网关版本和提交。
|
||||
2. 打开一个到在线智能体的临时 ACPX bridge 会话。
|
||||
3. 要求该智能体调用 `sessions_spawn`,参数为 `runtime: "acp"`、`agentId: "codex"`、`mode: "run"`,任务内容为 `Reply with exactly LIVE-ACP-SPAWN-OK`。
|
||||
4. 验证 `accepted=yes`、存在真实的 `childSessionKey`,且没有校验器错误。
|
||||
5. 清理临时 bridge 会话。
|
||||
|
||||
将该检查保持在 `mode: "run"`,并跳过 `streamTo: "parent"` —— 线程绑定的 `mode: "session"` 和流式中继路径属于另外更丰富的集成测试阶段。
|
||||
将检查门槛保持在 `mode: "run"`,并跳过 `streamTo: "parent"` —— 线程绑定的 `mode: "session"` 和流 relay 路径属于独立且更丰富的集成验证。
|
||||
|
||||
</Accordion>
|
||||
|
||||
## 沙箱兼容性
|
||||
|
||||
ACP 会话当前运行在主机运行时上,而不是 OpenClaw 沙箱内部。
|
||||
ACP 会话当前运行在主机运行时上,而不是 OpenClaw 沙箱内。
|
||||
|
||||
当前限制:
|
||||
|
||||
- 如果请求方会话处于沙箱隔离状态,则 `sessions_spawn({ runtime: "acp" })` 和 `/acp spawn` 都会阻止 ACP 生成。
|
||||
- 如果请求方会话处于沙箱隔离状态,则 `sessions_spawn({ runtime: "acp" })` 和 `/acp spawn` 都会阻止 ACP 启动。
|
||||
- 错误:`Sandboxed sessions cannot spawn ACP sessions because runtime="acp" runs on the host. Use runtime="subagent" from sandboxed sessions.`
|
||||
- `sessions_spawn` 配合 `runtime: "acp"` 时不支持 `sandbox: "require"`。
|
||||
- 使用 `runtime: "acp"` 的 `sessions_spawn` 不支持 `sandbox: "require"`。
|
||||
- 错误:`sessions_spawn sandbox="require" is unsupported for runtime="acp" because ACP sessions run outside the sandbox. Use runtime="subagent" or sandbox="inherit".`
|
||||
|
||||
当你需要由沙箱强制执行时,请使用 `runtime: "subagent"`。
|
||||
当你需要强制使用沙箱执行时,请使用 `runtime: "subagent"`。
|
||||
|
||||
### 从 `/acp` 命令启动
|
||||
### 从 `/acp` 命令
|
||||
|
||||
在需要时,使用 `/acp spawn` 从聊天中进行显式操作员控制。
|
||||
在需要时,使用 `/acp spawn` 从聊天中进行显式操作控制。
|
||||
|
||||
```text
|
||||
/acp spawn codex --mode persistent --thread auto
|
||||
@ -451,113 +458,114 @@ ACP 会话当前运行在主机运行时上,而不是 OpenClaw 沙箱内部。
|
||||
解析顺序:
|
||||
|
||||
1. 显式目标参数(或 `/acp steer` 的 `--session`)
|
||||
- 先尝试 key
|
||||
- 然后尝试 UUID 形态的 session id
|
||||
- 最后尝试 label
|
||||
2. 当前线程绑定(如果当前会话/线程绑定到了某个 ACP 会话)
|
||||
- 先尝试键名
|
||||
- 然后尝试 UUID 形状的会话 id
|
||||
- 最后尝试标签
|
||||
2. 当前线程绑定(如果当前会话 / 线程已绑定到某个 ACP 会话)
|
||||
3. 当前请求方会话回退
|
||||
|
||||
当前会话绑定和线程绑定都会参与第 2 步。
|
||||
|
||||
如果无法解析出目标,OpenClaw 会返回明确错误(`Unable to resolve session target: ...`)。
|
||||
如果无法解析出目标,OpenClaw 会返回清晰的错误(`Unable to resolve session target: ...`)。
|
||||
|
||||
## 生成绑定模式
|
||||
## 启动绑定模式
|
||||
|
||||
`/acp spawn` 支持 `--bind here|off`。
|
||||
|
||||
| 模式 | 行为 |
|
||||
| ------ | ---------------------------------------------------------------------- |
|
||||
| `here` | 原地绑定当前活跃会话;如果当前没有活跃会话则失败。 |
|
||||
| `off` | 不创建当前会话绑定。 |
|
||||
| `off` | 不创建当前会话绑定。 |
|
||||
|
||||
说明:
|
||||
|
||||
- `--bind here` 是“让这个渠道或聊天由 Codex 提供支持”的最简单操作路径。
|
||||
- `--bind here` 是“让这个渠道或聊天由 Codex 提供支持”这类操作最简单的操作路径。
|
||||
- `--bind here` 不会创建子线程。
|
||||
- `--bind here` 仅适用于暴露当前会话绑定支持的渠道。
|
||||
- `--bind` 和 `--thread` 不能在同一次 `/acp spawn` 调用中组合使用。
|
||||
- `--bind` 和 `--thread` 不能在同一个 `/acp spawn` 调用中组合使用。
|
||||
|
||||
## 生成线程模式
|
||||
## 启动线程模式
|
||||
|
||||
`/acp spawn` 支持 `--thread auto|here|off`。
|
||||
|
||||
| 模式 | 行为 |
|
||||
| ------ | --------------------------------------------------------------------------------------------------- |
|
||||
| `auto` | 在活跃线程中:绑定该线程。在非线程环境中:如果支持,则创建并绑定一个子线程。 |
|
||||
| `here` | 要求当前处于活跃线程中;如果不是则失败。 |
|
||||
| `off` | 不绑定。会话以未绑定状态启动。 |
|
||||
| `auto` | 如果当前在活跃线程中:绑定该线程。如果当前不在线程中:在支持时创建 / 绑定一个子线程。 |
|
||||
| `here` | 要求当前必须处于活跃线程中;否则失败。 |
|
||||
| `off` | 不进行绑定。会话以未绑定状态启动。 |
|
||||
|
||||
说明:
|
||||
|
||||
- 在不支持线程绑定的界面上,默认行为实际上等同于 `off`。
|
||||
- 线程绑定生成需要渠道策略支持:
|
||||
- 线程绑定启动需要渠道策略支持:
|
||||
- Discord:`channels.discord.threadBindings.spawnAcpSessions=true`
|
||||
- Telegram:`channels.telegram.threadBindings.spawnAcpSessions=true`
|
||||
- 如果你想固定当前会话而不创建子线程,请使用 `--bind here`。
|
||||
- 当你想固定当前会话而不创建子线程时,请使用 `--bind here`。
|
||||
|
||||
## ACP 控制
|
||||
|
||||
| 命令 | 作用 | 示例 |
|
||||
| -------------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| `/acp spawn` | 创建 ACP 会话;可选当前绑定或线程绑定。 | `/acp spawn codex --bind here --cwd /repo` |
|
||||
| `/acp cancel` | 取消目标会话中正在进行的轮次。 | `/acp cancel agent:codex:acp:<uuid>` |
|
||||
| `/acp steer` | 向运行中的会话发送 steer 指令。 | `/acp steer --session support inbox prioritize failing tests` |
|
||||
| `/acp close` | 关闭会话并解除线程目标绑定。 | `/acp close` |
|
||||
| `/acp cancel` | 取消目标会话的进行中轮次。 | `/acp cancel agent:codex:acp:<uuid>` |
|
||||
| `/acp steer` | 向运行中的会话发送引导指令。 | `/acp steer --session support inbox prioritize failing tests` |
|
||||
| `/acp close` | 关闭会话并解绑线程目标。 | `/acp close` |
|
||||
| `/acp status` | 显示后端、模式、状态、运行时选项、能力。 | `/acp status` |
|
||||
| `/acp set-mode` | 为目标会话设置运行时模式。 | `/acp set-mode plan` |
|
||||
| `/acp set` | 通用运行时配置项写入。 | `/acp set model openai/gpt-5.4` |
|
||||
| `/acp set` | 写入通用运行时配置选项。 | `/acp set model openai/gpt-5.4` |
|
||||
| `/acp cwd` | 设置运行时工作目录覆盖值。 | `/acp cwd /Users/user/Projects/repo` |
|
||||
| `/acp permissions` | 设置审批策略配置文件。 | `/acp permissions strict` |
|
||||
| `/acp timeout` | 设置运行时超时(秒)。 | `/acp timeout 120` |
|
||||
| `/acp model` | 设置运行时模型覆盖值。 | `/acp model anthropic/claude-opus-4-6` |
|
||||
| `/acp reset-options` | 移除会话运行时选项覆盖值。 | `/acp reset-options` |
|
||||
| `/acp sessions` | 从存储中列出最近的 ACP 会话。 | `/acp sessions` |
|
||||
| `/acp doctor` | 后端健康状态、能力、可执行修复。 | `/acp doctor` |
|
||||
| `/acp install` | 输出确定性的安装和启用步骤。 | `/acp install` |
|
||||
| `/acp doctor` | 后端健康状态、能力、可执行修复措施。 | `/acp doctor` |
|
||||
| `/acp install` | 打印确定性的安装和启用步骤。 | `/acp install` |
|
||||
|
||||
`/acp status` 会显示生效中的运行时选项,以及运行时级和后端级的会话标识符。当后端缺少某项能力时,不支持控制的错误会被明确显示。`/acp sessions` 会读取当前绑定会话或请求方会话对应的存储;目标令牌(`session-key`、`session-id` 或 `session-label`)会通过 Gateway 网关会话发现机制解析,包括每个智能体自定义的 `session.store` 根目录。
|
||||
`/acp status` 会显示生效中的运行时选项,以及运行时级别和后端级别的会话标识符。当某个后端缺少某项能力时,不支持控制的错误会被清晰地显示出来。`/acp sessions` 会读取当前已绑定会话或请求方会话的存储;目标令牌(`session-key`、`session-id` 或 `session-label`)会通过 Gateway 网关会话发现机制解析,包括自定义的每智能体 `session.store` 根目录。
|
||||
|
||||
## 运行时选项映射
|
||||
|
||||
`/acp` 提供了便捷命令和一个通用 setter。
|
||||
`/acp` 提供便捷命令和通用 setter。
|
||||
|
||||
等价操作:
|
||||
|
||||
- `/acp model <id>` 映射到运行时配置键 `model`。对于 Codex ACP,OpenClaw 会将 `openai-codex/<model>` 规范化为适配器模型 id,并将类似 `openai-codex/gpt-5.4/high` 这样的斜杠推理后缀映射为 Codex ACP 的 `reasoning_effort`。对于其他 harness,模型控制取决于适配器是否支持 ACP `models` 和 `session/set_model`。
|
||||
- `/acp set thinking <level>` 映射到运行时配置键 `thinking`。对于 Codex ACP,在适配器支持时,OpenClaw 会发送相应的 `reasoning_effort`。
|
||||
- `/acp model <id>` 映射到运行时配置键 `model`。对于 Codex ACP,OpenClaw 会将 `openai-codex/<model>` 规范化为适配器模型 id,并将斜杠推理后缀(例如 `openai-codex/gpt-5.4/high`)映射为 Codex ACP 的 `reasoning_effort`。对于其他 harness,模型控制取决于适配器是否支持 ACP `models` 和 `session/set_model`。
|
||||
- `/acp set thinking <level>` 映射到运行时配置键 `thinking`。对于 Codex ACP,在适配器支持的情况下,OpenClaw 会发送对应的 `reasoning_effort`。
|
||||
- `/acp permissions <profile>` 映射到运行时配置键 `approval_policy`。
|
||||
- `/acp timeout <seconds>` 映射到运行时配置键 `timeout`。
|
||||
- `/acp cwd <path>` 直接更新运行时 `cwd` 覆盖值。
|
||||
- `/acp set <key> <value>` 是通用路径。
|
||||
- 特殊情况:`key=cwd` 使用 `cwd` 覆盖路径。
|
||||
- 特殊情况:`key=cwd` 会使用 `cwd` 覆盖路径。
|
||||
- `/acp reset-options` 会清除目标会话的所有运行时覆盖值。
|
||||
|
||||
## acpx harness、插件设置和权限
|
||||
|
||||
关于 acpx harness 配置(Claude Code / Codex / Gemini CLI 别名)、plugin-tools 和 OpenClaw-tools MCP 桥接,以及 ACP 权限模式,请参见
|
||||
关于 acpx harness 配置(Claude Code / Codex / Gemini CLI 别名)、
|
||||
plugin-tools 和 OpenClaw-tools MCP bridge,以及 ACP 权限模式,请参阅
|
||||
[ACP 智能体 — 设置](/zh-CN/tools/acp-agents-setup)。
|
||||
|
||||
## 故障排除
|
||||
|
||||
| 症状 | 可能原因 | 修复方法 |
|
||||
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `ACP runtime backend is not configured` | 后端插件缺失、被禁用,或被 `plugins.allow` 阻止。 | 安装并启用后端插件;如果设置了 `plugins.allow` 允许列表,请将 `acpx` 包含进去;然后运行 `/acp doctor`。 |
|
||||
| `ACP is disabled by policy (acp.enabled=false)` | ACP 被全局禁用。 | 设置 `acp.enabled=true`。 |
|
||||
| `ACP runtime backend is not configured` | 后端插件缺失、被禁用,或被 `plugins.allow` 阻止。 | 安装并启用后端插件;如果设置了该 allowlist,请将 `acpx` 包含在 `plugins.allow` 中,然后运行 `/acp doctor`。 |
|
||||
| `ACP is disabled by policy (acp.enabled=false)` | ACP 在全局范围内被禁用。 | 设置 `acp.enabled=true`。 |
|
||||
| `ACP dispatch is disabled by policy (acp.dispatch.enabled=false)` | 来自普通线程消息的分发被禁用。 | 设置 `acp.dispatch.enabled=true`。 |
|
||||
| `ACP agent "<id>" is not allowed by policy` | 智能体不在允许列表中。 | 使用被允许的 `agentId`,或更新 `acp.allowedAgents`。 |
|
||||
| `Unable to resolve session target: ...` | key/id/label 令牌错误。 | 运行 `/acp sessions`,复制精确的 key/label 后重试。 |
|
||||
| `--bind here requires running /acp spawn inside an active ... conversation` | 在没有活动可绑定会话的情况下使用了 `--bind here`。 | 移动到目标聊天/渠道后重试,或使用不绑定的生成方式。 |
|
||||
| `ACP agent "<id>" is not allowed by policy` | 智能体不在 allowlist 中。 | 使用被允许的 `agentId`,或更新 `acp.allowedAgents`。 |
|
||||
| `Unable to resolve session target: ...` | 键 / id / 标签令牌错误。 | 运行 `/acp sessions`,复制精确的键或标签后重试。 |
|
||||
| `--bind here requires running /acp spawn inside an active ... conversation` | 在没有活跃可绑定会话的情况下使用了 `--bind here`。 | 移动到目标聊天 / 渠道后重试,或使用未绑定启动。 |
|
||||
| `Conversation bindings are unavailable for <channel>.` | 适配器缺少当前会话 ACP 绑定能力。 | 在支持时使用 `/acp spawn ... --thread ...`,配置顶层 `bindings[]`,或切换到受支持的渠道。 |
|
||||
| `--thread here requires running /acp spawn inside an active ... thread` | 在线程上下文之外使用了 `--thread here`。 | 移动到目标线程,或使用 `--thread auto`/`off`。 |
|
||||
| `Only <user-id> can rebind this channel/conversation/thread.` | 另一个用户拥有当前活动绑定目标。 | 由绑定所有者重新绑定,或使用不同的会话或线程。 |
|
||||
| `Thread bindings are unavailable for <channel>.` | 适配器缺少线程绑定能力。 | 使用 `--thread off`,或切换到受支持的适配器/渠道。 |
|
||||
| `Sandboxed sessions cannot spawn ACP sessions ...` | ACP 运行时位于主机侧;请求方会话处于沙箱隔离状态。 | 在沙箱隔离会话中使用 `runtime="subagent"`,或从非沙箱隔离会话中执行 ACP 生成。 |
|
||||
| `sessions_spawn sandbox="require" is unsupported for runtime="acp" ...` | 对 ACP 运行时请求了 `sandbox="require"`。 | 对需要强制沙箱隔离的情况使用 `runtime="subagent"`,或在非沙箱隔离会话中配合 `sandbox="inherit"` 使用 ACP。 |
|
||||
| `Cannot apply --model ... did not advertise model support` | 目标 harness 未暴露通用 ACP 模型切换能力。 | 使用声明支持 ACP `models`/`session/set_model` 的 harness,使用 Codex ACP 模型引用,或如果该 harness 有自己的启动参数,则直接在 harness 中配置模型。 |
|
||||
| Missing ACP metadata for bound session | ACP 会话元数据已过期或被删除。 | 使用 `/acp spawn` 重新创建,然后重新绑定/聚焦线程。 |
|
||||
| `AcpRuntimeError: Permission prompt unavailable in non-interactive mode` | `permissionMode` 在非交互式 ACP 会话中阻止写入/执行。 | 将 `plugins.entries.acpx.config.permissionMode` 设置为 `approve-all` 并重启 Gateway 网关。参见[权限配置](/zh-CN/tools/acp-agents-setup#permission-configuration)。 |
|
||||
| ACP session fails early with little output | 权限提示被 `permissionMode`/`nonInteractivePermissions` 阻止。 | 检查 Gateway 网关日志中的 `AcpRuntimeError`。如需完全权限,设置 `permissionMode=approve-all`;如需优雅降级,设置 `nonInteractivePermissions=deny`。 |
|
||||
| ACP session stalls indefinitely after completing work | harness 进程已结束,但 ACP 会话未报告完成。 | 使用 `ps aux \| grep acpx` 监控;手动终止陈旧进程。 |
|
||||
| `--thread here requires running /acp spawn inside an active ... thread` | 在线程上下文之外使用了 `--thread here`。 | 移动到目标线程,或使用 `--thread auto` / `off`。 |
|
||||
| `Only <user-id> can rebind this channel/conversation/thread.` | 当前活跃绑定目标归另一位用户所有。 | 由所有者重新绑定,或使用其他会话或线程。 |
|
||||
| `Thread bindings are unavailable for <channel>.` | 适配器缺少线程绑定能力。 | 使用 `--thread off`,或切换到受支持的适配器 / 渠道。 |
|
||||
| `Sandboxed sessions cannot spawn ACP sessions ...` | ACP 运行时在主机侧运行;请求方会话处于沙箱隔离状态。 | 在沙箱隔离会话中使用 `runtime="subagent"`,或从非沙箱隔离会话启动 ACP。 |
|
||||
| `sessions_spawn sandbox="require" is unsupported for runtime="acp" ...` | 为 ACP 运行时请求了 `sandbox="require"`。 | 对必须启用沙箱的场景使用 `runtime="subagent"`,或从非沙箱隔离会话中将 ACP 与 `sandbox="inherit"` 一起使用。 |
|
||||
| `Cannot apply --model ... did not advertise model support` | 目标 harness 未暴露通用 ACP 模型切换支持。 | 使用声明了 ACP `models` / `session/set_model` 的 harness,使用 Codex ACP 模型引用,或如果该 harness 有自己的启动标志,则直接在 harness 中配置模型。 |
|
||||
| Missing ACP metadata for bound session | ACP 会话元数据已过期或被删除。 | 使用 `/acp spawn` 重新创建,然后重新绑定 / 聚焦线程。 |
|
||||
| `AcpRuntimeError: Permission prompt unavailable in non-interactive mode` | `permissionMode` 在非交互式 ACP 会话中阻止写入 / 执行。 | 将 `plugins.entries.acpx.config.permissionMode` 设置为 `approve-all` 并重启 Gateway 网关。参见[权限配置](/zh-CN/tools/acp-agents-setup#permission-configuration)。 |
|
||||
| ACP session fails early with little output | 权限提示被 `permissionMode` / `nonInteractivePermissions` 阻止。 | 检查 Gateway 网关日志中的 `AcpRuntimeError`。若要授予完整权限,请设置 `permissionMode=approve-all`;若要优雅降级,请设置 `nonInteractivePermissions=deny`。 |
|
||||
| ACP session stalls indefinitely after completing work | harness 进程已结束,但 ACP 会话未报告完成。 | 使用 `ps aux \| grep acpx` 监控;手动杀掉陈旧进程。 |
|
||||
|
||||
## 相关内容
|
||||
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
---
|
||||
read_when:
|
||||
- 你希望通过智能体进行后台/并行工作
|
||||
- 你想通过智能体进行后台 / 并行工作
|
||||
- 你正在更改 `sessions_spawn` 或子智能体工具策略
|
||||
- 你正在实现或排查受线程绑定的子智能体会话
|
||||
summary: 子智能体:启动隔离的智能体运行,并将结果通知回请求者聊天界面
|
||||
- 你正在实现或排查线程绑定的子智能体会话
|
||||
summary: 子智能体:生成隔离的智能体运行,并将结果通告回请求者聊天界面
|
||||
title: 子智能体
|
||||
x-i18n:
|
||||
generated_at: "2026-04-26T00:04:52Z"
|
||||
generated_at: "2026-04-26T00:39:26Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 190f2dd3b5465fc2f9026769a60d03862fc2dbadb1cb66bf1fe846a7148e1f3f
|
||||
source_hash: ae5c51e89541e3dd48646afb86a49eea21b778d816f8edb7c4b35e93b62bb64f
|
||||
source_path: tools/subagents.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
子智能体是从现有智能体运行中派生出的后台智能体运行。它们在自己的会话(`agent:<agentId>:subagent:<uuid>`)中运行,并在完成后将结果**通知**回请求者聊天渠道。每次子智能体运行都会被跟踪为一个[后台任务](/zh-CN/automation/tasks)。
|
||||
子智能体是从现有智能体运行中生成的后台智能体运行。它们在各自独立的会话中运行(`agent:<agentId>:subagent:<uuid>`),并在完成后将其结果**通告**回请求者聊天渠道。每个子智能体运行都会作为一个[后台任务](/zh-CN/automation/tasks)进行跟踪。
|
||||
|
||||
## 斜杠命令
|
||||
|
||||
使用 `/subagents` 来查看或控制**当前会话**的子智能体运行:
|
||||
使用 `/subagents` 可检查或控制**当前会话**的子智能体运行:
|
||||
|
||||
- `/subagents list`
|
||||
- `/subagents kill <id|#|all>`
|
||||
@ -38,140 +38,140 @@ x-i18n:
|
||||
- `/session idle <duration|off>`
|
||||
- `/session max-age <duration|off>`
|
||||
|
||||
`/subagents info` 会显示运行元数据(Status、时间戳、会话 id、转录路径、清理信息)。
|
||||
使用 `sessions_history` 获取有边界且经过安全过滤的回顾视图;当你需要原始完整转录时,请检查磁盘上的转录路径。
|
||||
`/subagents info` 会显示运行元数据(状态、时间戳、会话 id、转录路径、清理设置)。
|
||||
使用 `sessions_history` 获取有边界且经过安全过滤的回溯视图;当你需要原始完整转录时,请检查磁盘上的转录路径。
|
||||
|
||||
### 启动行为
|
||||
### 生成行为
|
||||
|
||||
`/subagents spawn` 会以用户命令而非内部中继的方式启动一个后台子智能体,并在运行结束时向请求者聊天发送一条最终完成更新。
|
||||
`/subagents spawn` 以用户命令而非内部中继的方式启动一个后台子智能体,并在运行结束时向请求者聊天发送一条最终完成更新。
|
||||
|
||||
- 启动命令是非阻塞的;它会立即返回一个运行 id。
|
||||
- 完成时,子智能体会向请求者聊天渠道通知一条摘要/结果消息。
|
||||
- 完成采用基于推送的方式。启动后,不要仅仅为了等待其完成而循环轮询 `/subagents list`、`sessions_list` 或 `sessions_history`;只有在调试或干预时按需查看 Status。
|
||||
- 完成时,OpenClaw 会尽最大努力在通知清理流程继续之前,关闭该子智能体会话打开并被跟踪的浏览器标签页/进程。
|
||||
- 对于手动启动,投递具有弹性:
|
||||
- OpenClaw 会先尝试使用稳定的幂等键进行直接 `agent` 投递。
|
||||
- 生成命令是非阻塞的;它会立即返回一个运行 id。
|
||||
- 完成后,子智能体会向请求者聊天渠道通告一条摘要 / 结果消息。
|
||||
- 完成投递是基于推送的。生成后,不要仅为了等待其完成而循环轮询 `/subagents list`、`sessions_list` 或 `sessions_history`;仅在调试或干预时按需检查状态。
|
||||
- 完成时,OpenClaw 会尽最大努力在通告清理流程继续之前关闭该子智能体会话打开的已跟踪浏览器标签页 / 进程。
|
||||
- 对于手动生成,投递具有弹性:
|
||||
- OpenClaw 会先使用稳定的幂等键尝试直接 `agent` 投递。
|
||||
- 如果直接投递失败,则回退到队列路由。
|
||||
- 如果队列路由仍不可用,则在最终放弃前,通知会以较短的指数退避方式重试。
|
||||
- 如果队列路由仍不可用,则会以短暂的指数退避重试通告,之后才最终放弃。
|
||||
- 完成投递会保留已解析的请求者路由:
|
||||
- 可用时,线程绑定或会话绑定的完成路由优先生效
|
||||
- 如果完成来源只提供渠道,OpenClaw 会从请求者会话的已解析路由(`lastChannel` / `lastTo` / `lastAccountId`)中补全缺失的 target/account,以便直接投递仍能工作
|
||||
- 向请求者会话移交完成结果时,使用的是运行时生成的内部上下文(不是用户编写的文本),其中包括:
|
||||
- `Result`(最新可见的 `assistant` 回复文本;否则为已清理的最新 tool/toolResult 文本;终态失败运行不会复用已捕获的回复文本)
|
||||
- 可用时,线程绑定或会话绑定的完成路由优先
|
||||
- 如果完成来源只提供渠道,OpenClaw 会从请求者会话的已解析路由(`lastChannel` / `lastTo` / `lastAccountId`)中补全缺失的目标 / 账号,从而让直接投递仍然可用
|
||||
- 向请求者会话移交完成结果时,使用的是运行时生成的内部上下文(不是用户撰写的文本),其中包括:
|
||||
- `Result`(最新可见的 `assistant` 回复文本;否则为已净化的最新 `tool` / `toolResult` 文本;终态失败的运行不会复用已捕获的回复文本)
|
||||
- `Status`(`completed successfully` / `failed` / `timed out` / `unknown`)
|
||||
- 紧凑的运行时/token 统计信息
|
||||
- 一条投递说明,告诉请求者智能体用正常的助手语气重写,而不是转发原始内部元数据
|
||||
- 紧凑的运行时 / token 统计信息
|
||||
- 一条投递指令,告知请求者智能体用正常的 assistant 语气重写,而不是转发原始内部元数据
|
||||
- `--model` 和 `--thinking` 会覆盖该次特定运行的默认值。
|
||||
- 完成后,使用 `info`/`log` 查看详细信息和输出。
|
||||
- `/subagents spawn` 是一次性模式(`mode: "run"`)。对于持久的线程绑定会话,请使用带有 `thread: true` 和 `mode: "session"` 的 `sessions_spawn`。
|
||||
- 对于 ACP harness 会话(Claude Code、Gemini CLI、OpenCode 或显式的 Codex ACP/acpx),当工具声明该运行时可用时,请使用带有 `runtime: "acp"` 的 `sessions_spawn`,并参见 [ACP 智能体](/zh-CN/tools/acp-agents),尤其是在排查完成投递或智能体到智能体循环时的 [ACP 投递模型](/zh-CN/tools/acp-agents#delivery-model)。当启用 `codex` 插件时,Codex 聊天/线程控制应优先使用 `/codex ...` 而不是 ACP,除非用户明确要求 ACP/acpx。OpenClaw 会在 ACP 已启用、请求者不在沙箱中且已加载如 `acpx` 之类的后端插件之前隐藏 `runtime: "acp"`。`runtime: "acp"` 需要一个外部 ACP harness id,或一个带有 `runtime.type="acp"` 的 `agents.list[]` 条目;对于来自 `agents_list` 的普通 OpenClaw 配置智能体,请使用默认的子智能体运行时。
|
||||
- 完成后,使用 `info` / `log` 检查详细信息和输出。
|
||||
- `/subagents spawn` 是一次性模式(`mode: "run"`)。对于持久线程绑定会话,请使用带有 `thread: true` 和 `mode: "session"` 的 `sessions_spawn`。
|
||||
- 对于 ACP harness 会话(Claude Code、Gemini CLI、OpenCode,或显式的 Codex ACP / acpx),当工具声明了该运行时,请使用 `runtime: "acp"` 的 `sessions_spawn`,并参见 [ACP Agents](/zh-CN/tools/acp-agents),尤其是在调试完成回传或智能体到智能体循环时的 [ACP delivery model](/zh-CN/tools/acp-agents#delivery-model)。启用 `codex` 插件后,Codex 聊天 / 线程控制应优先使用 `/codex ...` 而不是 ACP,除非用户明确要求 ACP / acpx。只有在 ACP 已启用、请求者未处于沙箱隔离状态,并且已加载 `acpx` 等后端插件时,OpenClaw 才会显示 `runtime: "acp"`。`runtime: "acp"` 需要一个外部 ACP harness id,或 `agents.list[]` 中 `runtime.type="acp"` 的条目;对于来自 `agents_list` 的普通 OpenClaw 配置智能体,请使用默认子智能体运行时。
|
||||
|
||||
主要目标:
|
||||
|
||||
- 将“研究 / 长任务 / 慢工具”工作并行化,而不会阻塞主运行。
|
||||
- 将“研究 / 长任务 / 慢工具”工作并行化,而不阻塞主运行。
|
||||
- 默认保持子智能体隔离(会话分离 + 可选沙箱隔离)。
|
||||
- 让工具表面难以误用:默认情况下,子智能体**不会**获得会话工具。
|
||||
- 支持可配置的嵌套深度,以实现编排器模式。
|
||||
- 保持工具表面不易被误用:子智能体默认**不会**获得会话工具。
|
||||
- 支持可配置的嵌套深度,以适配编排器模式。
|
||||
|
||||
成本说明:默认情况下,每个子智能体都有其**自己的**上下文和 token 用量。对于高负载或重复性任务,请为子智能体设置更便宜的模型,并让主智能体保留使用质量更高的模型。你可以通过 `agents.defaults.subagents.model` 或按智能体覆盖来配置这一点。当子级确实需要请求者当前的转录时,智能体可以在该次启动时请求 `context: "fork"`。
|
||||
成本说明:默认情况下,每个子智能体都有其**自己的**上下文和 token 使用量。对于高负载或重复性任务,请为子智能体设置更便宜的模型,并让你的主智能体继续使用质量更高的模型。你可以通过 `agents.defaults.subagents.model` 或按智能体覆盖来配置这一点。当子级确实需要请求者当前转录时,智能体可以在该次生成中请求 `context: "fork"`。
|
||||
|
||||
## 上下文模式
|
||||
|
||||
原生子智能体默认以隔离方式启动,除非调用方明确要求 fork 当前转录。
|
||||
原生子智能体默认以隔离方式启动,除非调用方明确要求分叉当前转录。
|
||||
|
||||
| 模式 | 何时使用 | 行为 |
|
||||
| 模式 | 适用场景 | 行为 |
|
||||
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| `isolated` | 全新的研究、独立实现、慢工具工作,或任何可以在任务文本中简要说明的内容 | 创建一个干净的子级转录。这是默认方式,并能保持较低的 token 用量。 |
|
||||
| `fork` | 工作依赖当前对话、先前工具结果,或请求者转录中已有的细微指令 | 在子级启动前,将请求者转录分支到子级会话中。 |
|
||||
| `isolated` | 全新的研究、独立实现、慢工具工作,或任何可以在任务文本中简要说明的事项 | 创建一个干净的子级转录。这是默认模式,并且会保持较低的 token 使用量。 |
|
||||
| `fork` | 依赖当前对话、先前工具结果,或请求者转录中已存在的细致指令的工作 | 在子级启动前,将请求者转录分支到子会话中。 |
|
||||
|
||||
谨慎使用 `fork`。它适用于对上下文敏感的委派,而不是用来替代编写清晰的任务提示。
|
||||
谨慎使用 `fork`。它用于对上下文敏感的委派,而不是替代清晰编写任务提示。
|
||||
|
||||
## 工具
|
||||
|
||||
使用 `sessions_spawn`:
|
||||
|
||||
- 启动一个子智能体运行(`deliver: false`,全局 lane:`subagent`)
|
||||
- 然后执行一个通知步骤,并将通知回复发布到请求者聊天渠道
|
||||
- 默认模型:继承调用方,除非你设置了 `agents.defaults.subagents.model`(或按智能体设置 `agents.list[].subagents.model`);显式指定的 `sessions_spawn.model` 仍然优先生效。
|
||||
- 默认 thinking:继承调用方,除非你设置了 `agents.defaults.subagents.thinking`(或按智能体设置 `agents.list[].subagents.thinking`);显式指定的 `sessions_spawn.thinking` 仍然优先生效。
|
||||
- 然后执行一个通告步骤,并将通告回复发布到请求者聊天渠道
|
||||
- 默认模型:继承调用方,除非你设置了 `agents.defaults.subagents.model`(或按智能体设置 `agents.list[].subagents.model`);显式指定的 `sessions_spawn.model` 仍然优先。
|
||||
- 默认 thinking:继承调用方,除非你设置了 `agents.defaults.subagents.thinking`(或按智能体设置 `agents.list[].subagents.thinking`);显式指定的 `sessions_spawn.thinking` 仍然优先。
|
||||
- 默认运行超时:如果省略 `sessions_spawn.runTimeoutSeconds`,OpenClaw 会在已设置时使用 `agents.defaults.subagents.runTimeoutSeconds`;否则回退到 `0`(无超时)。
|
||||
|
||||
工具参数:
|
||||
|
||||
- `task`(必需)
|
||||
- `task`(必填)
|
||||
- `label?`(可选)
|
||||
- `agentId?`(可选;如被允许,可在另一个智能体 id 下启动)
|
||||
- `runtime?`(`subagent|acp`,默认 `subagent`;`acp` 仅用于外部 ACP harness,例如 `claude`、`gemini`、`opencode`,或显式请求的 Codex ACP/acpx,或者用于 `agents.list[]` 中 `runtime.type` 为 `acp` 的条目)
|
||||
- `model?`(可选;覆盖子智能体模型;无效值会被跳过,子智能体将使用默认模型运行,并在工具结果中附带警告)
|
||||
- `agentId?`(可选;如果允许,可在另一个智能体 id 下生成)
|
||||
- `runtime?`(`subagent|acp`,默认 `subagent`;`acp` 仅用于外部 ACP harness,例如 `claude`、`droid`、`gemini`、`opencode`,或显式请求的 Codex ACP / acpx,或 `agents.list[]` 中 `runtime.type` 为 `acp` 的条目)
|
||||
- `model?`(可选;覆盖子智能体模型;无效值会被跳过,子智能体会使用默认模型运行,并在工具结果中给出警告)
|
||||
- `thinking?`(可选;覆盖子智能体运行的 thinking 级别)
|
||||
- `runTimeoutSeconds?`(在已设置时默认取 `agents.defaults.subagents.runTimeoutSeconds`,否则为 `0`;设置后,子智能体运行会在 N 秒后中止)
|
||||
- `thread?`(默认 `false`;当为 `true` 时,请求为该子智能体会话启用渠道线程绑定)
|
||||
- `runTimeoutSeconds?`(默认在已设置时使用 `agents.defaults.subagents.runTimeoutSeconds`,否则为 `0`;设置后,子智能体运行会在 N 秒后中止)
|
||||
- `thread?`(默认 `false`;为 `true` 时,请求为该子智能体会话启用渠道线程绑定)
|
||||
- `mode?`(`run|session`)
|
||||
- 默认是 `run`
|
||||
- 默认值为 `run`
|
||||
- 如果 `thread: true` 且省略 `mode`,默认变为 `session`
|
||||
- `mode: "session"` 需要 `thread: true`
|
||||
- `cleanup?`(`delete|keep`,默认 `keep`)
|
||||
- `sandbox?`(`inherit|require`,默认 `inherit`;`require` 会在目标子级运行时未处于沙箱隔离时拒绝启动)
|
||||
- `sandbox?`(`inherit|require`,默认 `inherit`;`require` 会在目标子级运行时未处于沙箱隔离时拒绝生成)
|
||||
- `context?`(`isolated|fork`,默认 `isolated`;仅适用于原生子智能体)
|
||||
- `isolated` 会创建一个干净的子级转录,并且是默认方式。
|
||||
- `fork` 会将请求者当前转录分支到子级会话中,使子级以相同的对话上下文启动。
|
||||
- 仅当子级需要当前转录时才使用 `fork`。对于有范围的工作,可省略 `context`。
|
||||
- `sessions_spawn` **不**接受渠道投递参数(`target`、`channel`、`to`、`threadId`、`replyTo`、`transport`)。对于投递,请从已启动的运行中使用 `message`/`sessions_send`。
|
||||
- `isolated` 会创建一个干净的子级转录,并且是默认值。
|
||||
- `fork` 会将请求者当前转录分支到子会话中,使子级以相同的对话上下文启动。
|
||||
- 仅当子级需要当前转录时才使用 `fork`。对于范围明确的工作,请省略 `context`。
|
||||
- `sessions_spawn` **不**接受渠道投递参数(`target`、`channel`、`to`、`threadId`、`replyTo`、`transport`)。如需投递,请从已生成的运行中使用 `message` / `sessions_send`。
|
||||
|
||||
## 线程绑定会话
|
||||
|
||||
当某个渠道启用了线程绑定时,子智能体可以持续绑定到某个线程,因此该线程中的后续用户消息会继续路由到同一个子智能体会话。
|
||||
当某个渠道启用了线程绑定时,子智能体可以持续绑定到一个线程上,以便该线程中的后续用户消息继续路由到同一个子智能体会话。
|
||||
|
||||
### 支持线程的渠道
|
||||
|
||||
- Discord(当前唯一支持的渠道):支持持久的线程绑定子智能体会话(`sessions_spawn` 配合 `thread: true`)、手动线程控制(`/focus`、`/unfocus`、`/agents`、`/session idle`、`/session max-age`),以及适配器键 `channels.discord.threadBindings.enabled`、`channels.discord.threadBindings.idleHours`、`channels.discord.threadBindings.maxAgeHours` 和 `channels.discord.threadBindings.spawnSubagentSessions`。
|
||||
- Discord(当前唯一受支持的渠道):支持持久的线程绑定子智能体会话(使用带 `thread: true` 的 `sessions_spawn`)、手动线程控制(`/focus`、`/unfocus`、`/agents`、`/session idle`、`/session max-age`),以及适配器键 `channels.discord.threadBindings.enabled`、`channels.discord.threadBindings.idleHours`、`channels.discord.threadBindings.maxAgeHours` 和 `channels.discord.threadBindings.spawnSubagentSessions`。
|
||||
|
||||
快速流程:
|
||||
|
||||
1. 使用带有 `thread: true` 的 `sessions_spawn` 启动(也可选择 `mode: "session"`)。
|
||||
2. OpenClaw 会在当前活跃渠道中创建一个线程,或将线程绑定到该会话目标。
|
||||
3. 该线程中的回复和后续消息会路由到绑定的会话。
|
||||
4. 使用 `/session idle` 查看/更新不活动自动取消聚焦设置,并使用 `/session max-age` 控制硬性时长上限。
|
||||
1. 使用带有 `thread: true` 的 `sessions_spawn` 进行生成(可选地加上 `mode: "session"`)。
|
||||
2. OpenClaw 会在活动渠道中创建一个线程或将线程绑定到该会话目标。
|
||||
3. 该线程中的回复和后续消息都会路由到绑定的会话。
|
||||
4. 使用 `/session idle` 检查 / 更新不活动自动取消聚焦设置,使用 `/session max-age` 控制硬性上限。
|
||||
5. 使用 `/unfocus` 手动解除绑定。
|
||||
|
||||
手动控制:
|
||||
|
||||
- `/focus <target>` 将当前线程(或创建一个线程)绑定到某个子智能体/会话目标。
|
||||
- `/unfocus` 移除当前已绑定线程的绑定关系。
|
||||
- `/agents` 列出活动运行和绑定状态(`thread:<id>` 或 `unbound`)。
|
||||
- `/focus <target>` 会将当前线程(或创建一个线程)绑定到某个子智能体 / 会话目标。
|
||||
- `/unfocus` 会移除当前已绑定线程的绑定。
|
||||
- `/agents` 会列出活动运行及绑定状态(`thread:<id>` 或 `unbound`)。
|
||||
- `/session idle` 和 `/session max-age` 仅对已聚焦的绑定线程有效。
|
||||
|
||||
配置开关:
|
||||
|
||||
- 全局默认值:`session.threadBindings.enabled`、`session.threadBindings.idleHours`、`session.threadBindings.maxAgeHours`
|
||||
- 渠道覆盖和启动时自动绑定键是适配器特定的。请参见上方的**支持线程的渠道**。
|
||||
- 渠道覆盖和生成自动绑定键是特定于适配器的。请参见上方的**支持线程的渠道**。
|
||||
|
||||
当前适配器详细信息请参见 [Configuration Reference](/zh-CN/gateway/configuration-reference) 和 [斜杠命令](/zh-CN/tools/slash-commands)。
|
||||
当前适配器的详细信息请参见 [Configuration Reference](/zh-CN/gateway/configuration-reference) 和 [Slash commands](/zh-CN/tools/slash-commands)。
|
||||
|
||||
允许列表:
|
||||
|
||||
- `agents.list[].subagents.allowAgents`:可通过 `agentId` 作为目标的智能体 id 列表(`["*"]` 表示允许任意)。默认:仅请求者智能体。
|
||||
- `agents.defaults.subagents.allowAgents`:当请求者智能体未设置自己的 `subagents.allowAgents` 时使用的默认目标智能体允许列表。
|
||||
- `agents.list[].subagents.allowAgents`:可通过 `agentId` 定向的智能体 id 列表(`["*"]` 表示允许任意)。默认值:仅允许请求者智能体。
|
||||
- `agents.defaults.subagents.allowAgents`:当请求者智能体未设置自己的 `subagents.allowAgents` 时,使用的默认目标智能体允许列表。
|
||||
- 沙箱继承保护:如果请求者会话处于沙箱隔离中,`sessions_spawn` 会拒绝那些将以非沙箱方式运行的目标。
|
||||
- `agents.defaults.subagents.requireAgentId` / `agents.list[].subagents.requireAgentId`:当为 true 时,阻止省略 `agentId` 的 `sessions_spawn` 调用(强制显式选择配置文件)。默认:false。
|
||||
- `agents.defaults.subagents.requireAgentId` / `agents.list[].subagents.requireAgentId`:为 true 时,阻止省略 `agentId` 的 `sessions_spawn` 调用(强制显式选择配置文件)。默认值:false。
|
||||
|
||||
发现:
|
||||
|
||||
- 使用 `agents_list` 查看当前哪些智能体 id 被允许用于 `sessions_spawn`。响应中包含每个已列出智能体的生效模型和嵌入式运行时元数据,以便调用方区分 PI、Codex app-server 以及其他已配置的原生运行时。
|
||||
- 使用 `agents_list` 查看当前哪些智能体 id 允许用于 `sessions_spawn`。响应中包含每个列出智能体的有效模型和嵌入式运行时元数据,以便调用方区分 PI、Codex app-server 以及其他已配置的原生运行时。
|
||||
|
||||
自动归档:
|
||||
|
||||
- 子智能体会话会在 `agents.defaults.subagents.archiveAfterMinutes` 指定时间后自动归档(默认:60)。
|
||||
- 归档使用 `sessions.delete`,并将转录重命名为 `*.deleted.<timestamp>`(位于同一文件夹)。
|
||||
- `cleanup: "delete"` 会在通知后立即归档(仍会通过重命名保留转录)。
|
||||
- 自动归档尽力而为;如果 Gateway 网关重启,待处理计时器会丢失。
|
||||
- `runTimeoutSeconds` **不会**自动归档;它只会停止运行。会话会保留,直到自动归档发生。
|
||||
- 自动归档对深度 1 和深度 2 会话同样适用。
|
||||
- 浏览器清理与归档清理是分开的:当运行完成时,即使保留了转录/会话记录,也会尽最大努力关闭被跟踪的浏览器标签页/进程。
|
||||
- 子智能体会话会在 `agents.defaults.subagents.archiveAfterMinutes` 之后自动归档(默认值:60)。
|
||||
- 归档使用 `sessions.delete`,并将转录重命名为 `*.deleted.<timestamp>`(同一文件夹中)。
|
||||
- `cleanup: "delete"` 会在通告后立即归档(仍会通过重命名保留转录)。
|
||||
- 自动归档是尽力而为的;如果 Gateway 网关重启,待处理计时器会丢失。
|
||||
- `runTimeoutSeconds` **不会**自动归档;它只会停止运行。会话会一直保留到自动归档发生。
|
||||
- 自动归档对深度 1 和深度 2 会话一视同仁。
|
||||
- 浏览器清理与归档清理是分开的:当运行结束时,会尽最大努力关闭已跟踪的浏览器标签页 / 进程,即使转录 / 会话记录被保留也是如此。
|
||||
|
||||
## 嵌套子智能体
|
||||
|
||||
默认情况下,子智能体不能再启动自己的子智能体(`maxSpawnDepth: 1`)。你可以通过设置 `maxSpawnDepth: 2` 启用一层嵌套,从而支持**编排器模式**:主智能体 → 编排器子智能体 → 工作子子智能体。
|
||||
默认情况下,子智能体不能生成它们自己的子智能体(`maxSpawnDepth: 1`)。你可以通过将 `maxSpawnDepth` 设置为 `2` 来启用一层嵌套,这允许使用**编排器模式**:主智能体 → 编排器子智能体 → 工作子子智能体。
|
||||
|
||||
### 如何启用
|
||||
|
||||
@ -180,10 +180,10 @@ x-i18n:
|
||||
agents: {
|
||||
defaults: {
|
||||
subagents: {
|
||||
maxSpawnDepth: 2, // 允许子智能体启动子级(默认:1)
|
||||
maxChildrenPerAgent: 5, // 每个智能体会话的最大活动子级数(默认:5)
|
||||
maxConcurrent: 8, // 全局并发 lane 上限(默认:8)
|
||||
runTimeoutSeconds: 900, // `sessions_spawn` 在省略时的默认超时(0 = 无超时)
|
||||
maxSpawnDepth: 2, // 允许子智能体生成子级(默认值:1)
|
||||
maxChildrenPerAgent: 5, // 每个智能体会话的最大活动子级数(默认值:5)
|
||||
maxConcurrent: 8, // 全局并发 lane 上限(默认值:8)
|
||||
runTimeoutSeconds: 900, // 省略时 sessions_spawn 的默认超时(0 = 无超时)
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -192,115 +192,115 @@ x-i18n:
|
||||
|
||||
### 深度级别
|
||||
|
||||
| 深度 | 会话键形态 | 角色 | 可以启动? |
|
||||
| 深度 | 会话键形态 | 角色 | 可生成? |
|
||||
| ----- | -------------------------------------------- | --------------------------------------------- | ---------------------------- |
|
||||
| 0 | `agent:<id>:main` | 主智能体 | 始终可以 |
|
||||
| 1 | `agent:<id>:subagent:<uuid>` | 子智能体(当允许深度 2 时为编排器) | 仅当 `maxSpawnDepth >= 2` |
|
||||
| 2 | `agent:<id>:subagent:<uuid>:subagent:<uuid>` | 子子智能体(叶子工作单元) | 永远不可以 |
|
||||
| 1 | `agent:<id>:subagent:<uuid>` | 子智能体(当允许深度 2 时可作为编排器) | 仅当 `maxSpawnDepth >= 2` |
|
||||
| 2 | `agent:<id>:subagent:<uuid>:subagent:<uuid>` | 子子智能体(叶子工作单元) | 永不允许 |
|
||||
|
||||
### 通知链
|
||||
### 通告链
|
||||
|
||||
结果会沿链路向上返回:
|
||||
结果会沿链路向上回流:
|
||||
|
||||
1. 深度 2 工作单元完成 → 通知其父级(深度 1 编排器)
|
||||
2. 深度 1 编排器收到通知,综合结果,完成 → 通知主智能体
|
||||
3. 主智能体收到通知并将结果投递给用户
|
||||
1. 深度 2 工作单元完成 → 向其父级(深度 1 编排器)通告
|
||||
2. 深度 1 编排器接收通告、综合结果、完成 → 向主智能体通告
|
||||
3. 主智能体接收通告并投递给用户
|
||||
|
||||
每一层只会看到其直接子级发来的通知。
|
||||
每一层只能看到其直接子级发来的通告。
|
||||
|
||||
操作指南:
|
||||
|
||||
- 子级工作启动一次后,等待完成事件,而不是围绕 `sessions_list`、`sessions_history`、`/subagents list` 或 `exec` sleep 命令构建轮询循环。
|
||||
- `sessions_list` 和 `/subagents list` 会让子会话关系聚焦于实时工作:存活的子级会持续关联,已结束的子级会在最近的一个短时间窗口内保持可见,而仅存储中的陈旧子级链接会在其新鲜度窗口过后被忽略。这可以防止旧的 `spawnedBy` / `parentSessionKey` 元数据在重启后“复活”幽灵子级。
|
||||
- 如果某个子级完成事件在你已发送最终答案后才到达,正确的后续处理是精确的静默 token `NO_REPLY` / `no_reply`。
|
||||
- 子级工作启动一次后,等待完成事件即可,不要围绕 `sessions_list`、`sessions_history`、`/subagents list` 或 `exec` sleep 命令构建轮询循环。
|
||||
- `sessions_list` 和 `/subagents list` 会让子会话关系聚焦于实时工作:活跃子级保持附着,已结束子级会在一个较短的最近窗口内保持可见,而仅存储中的过期子级链接会在其新鲜度窗口过后被忽略。这样可以防止旧的 `spawnedBy` / `parentSessionKey` 元数据在重启后重新唤起幽灵子级。
|
||||
- 如果某个子级完成事件在你已经发送最终答案之后才到达,正确的后续处理是精确的静默 token `NO_REPLY` / `no_reply`。
|
||||
|
||||
### 按深度划分的工具策略
|
||||
|
||||
- 角色和控制范围会在启动时写入会话元数据。这可以防止扁平化或恢复后的会话键意外重新获得编排器权限。
|
||||
- **深度 1(编排器,当 `maxSpawnDepth >= 2` 时)**:获得 `sessions_spawn`、`subagents`、`sessions_list`、`sessions_history`,以便管理其子级。其他会话/系统工具仍然被拒绝。
|
||||
- **深度 1(叶子节点,当 `maxSpawnDepth == 1` 时)**:没有会话工具(当前默认行为)。
|
||||
- **深度 2(叶子工作单元)**:没有会话工具 —— 在深度 2 始终拒绝 `sessions_spawn`。不能再启动更多子级。
|
||||
- 角色和控制范围会在生成时写入会话元数据。这可以防止扁平化或已恢复的会话键意外重新获得编排器权限。
|
||||
- **深度 1(编排器,当 `maxSpawnDepth >= 2` 时)**:获得 `sessions_spawn`、`subagents`、`sessions_list`、`sessions_history`,以便管理其子级。其他会话 / 系统工具仍被拒绝。
|
||||
- **深度 1(叶子,当 `maxSpawnDepth == 1` 时)**:没有会话工具(当前默认行为)。
|
||||
- **深度 2(叶子工作单元)**:没有会话工具 —— 在深度 2 上始终拒绝 `sessions_spawn`。不能继续生成更多子级。
|
||||
|
||||
### 每个智能体的启动上限
|
||||
### 每个智能体的生成上限
|
||||
|
||||
每个智能体会话(任意深度)同时最多只能有 `maxChildrenPerAgent`(默认:5)个活动子级。这可以防止单个编排器出现失控的扇出。
|
||||
每个智能体会话(任意深度)同一时间最多可以有 `maxChildrenPerAgent`(默认值:5)个活动子级。这可以防止单个编排器发生失控扇出。
|
||||
|
||||
### 级联停止
|
||||
|
||||
停止深度 1 编排器时,会自动停止其所有深度 2 子级:
|
||||
停止一个深度 1 编排器会自动停止其所有深度 2 子级:
|
||||
|
||||
- 在主聊天中发送 `/stop` 会停止所有深度 1 智能体,并级联停止其深度 2 子级。
|
||||
- `/subagents kill <id>` 会停止指定的子智能体,并级联停止其子级。
|
||||
- `/subagents kill all` 会停止该请求者的所有子智能体,并进行级联停止。
|
||||
- 在主聊天中发送 `/stop` 会停止所有深度 1 智能体,并级联停止它们的深度 2 子级。
|
||||
- `/subagents kill <id>` 会停止指定子智能体,并级联到其子级。
|
||||
- `/subagents kill all` 会停止请求者的所有子智能体,并进行级联。
|
||||
|
||||
## 认证
|
||||
## 身份验证
|
||||
|
||||
子智能体认证按**智能体 id** 解析,而不是按会话类型:
|
||||
子智能体认证按**智能体 id**解析,而不是按会话类型:
|
||||
|
||||
- 子智能体会话键为 `agent:<agentId>:subagent:<uuid>`。
|
||||
- 子智能体会话键是 `agent:<agentId>:subagent:<uuid>`。
|
||||
- 认证存储从该智能体的 `agentDir` 加载。
|
||||
- 主智能体的认证配置文件会作为**回退**合并进来;发生冲突时,智能体配置文件会覆盖主配置文件。
|
||||
|
||||
注意:该合并是追加式的,因此主配置文件始终可作为回退使用。当前尚不支持真正按智能体完全隔离的认证。
|
||||
注意:该合并是附加式的,因此主配置文件始终可作为回退使用。当前尚不支持每个智能体完全隔离的认证。
|
||||
|
||||
## 通知
|
||||
## 通告
|
||||
|
||||
子智能体通过通知步骤回报结果:
|
||||
子智能体通过一个通告步骤回报结果:
|
||||
|
||||
- 通知步骤在子智能体会话内部运行(而不是在请求者会话中)。
|
||||
- 如果子智能体回复内容精确为 `ANNOUNCE_SKIP`,则不会发布任何内容。
|
||||
- 如果最新的助手文本是精确的静默 token `NO_REPLY` / `no_reply`,即使之前存在可见进度,也会抑制通知输出。
|
||||
- 通告步骤在子智能体会话内部运行(而不是在请求者会话中)。
|
||||
- 如果子智能体精确回复 `ANNOUNCE_SKIP`,则不会发布任何内容。
|
||||
- 如果最新的 assistant 文本是精确的静默 token `NO_REPLY` / `no_reply`,则即使之前存在可见进度,也会抑制通告输出。
|
||||
- 否则,投递取决于请求者深度:
|
||||
- 顶层请求者会话使用带外部投递的后续 `agent` 调用(`deliver=true`)
|
||||
- 嵌套的请求者子智能体会话会收到内部后续注入(`deliver=false`),以便编排器在会话内综合子级结果
|
||||
- 如果嵌套的请求者子智能体会话已不存在,OpenClaw 会在可能时回退到该会话的请求者
|
||||
- 对于顶层请求者会话,完成模式的直接投递会先解析任何已绑定的会话/线程路由和 hook 覆盖,然后从请求者会话存储的路由中补全缺失的渠道目标字段。这样即使完成来源只标识了渠道,也能确保完成消息投递到正确的聊天/话题中。
|
||||
- 在构建嵌套完成结果时,子级完成聚合限定在当前请求者运行范围内,从而防止先前运行中陈旧的子级输出泄漏到当前通知中。
|
||||
- 通知回复会在渠道适配器可用时保留线程/话题路由。
|
||||
- 通知上下文会被规范化为稳定的内部事件块:
|
||||
- 嵌套请求者子智能体会话接收内部后续注入(`deliver=false`),以便编排器可在会话内综合子级结果
|
||||
- 如果嵌套请求者子智能体会话已不存在,OpenClaw 会在可用时回退到该会话的请求者
|
||||
- 对于顶层请求者会话,完成模式的直接投递会先解析任何已绑定的会话 / 线程路由和 hook 覆盖,然后从请求者会话存储的路由中补全缺失的渠道目标字段。这样即使完成来源仅标识了渠道,也能将完成结果保持在正确的聊天 / 主题中。
|
||||
- 在构建嵌套完成发现结果时,子级完成聚合会限定在当前请求者运行范围内,从而防止过期的前一次运行子级输出泄漏到当前通告中。
|
||||
- 在渠道适配器可用时,通告回复会保留线程 / 主题路由。
|
||||
- 通告上下文会规范化为稳定的内部事件块:
|
||||
- 来源(`subagent` 或 `cron`)
|
||||
- 子级会话 key/id
|
||||
- 通知类型 + 任务标签
|
||||
- 从运行时结果派生的 Status 行(`success`、`error`、`timeout` 或 `unknown`)
|
||||
- 从最新可见助手文本中选择的结果内容;否则使用已清理的最新 tool/toolResult 文本;终态失败运行会报告失败 Status,而不会重放已捕获的回复文本
|
||||
- 子会话键 / id
|
||||
- 通告类型 + 任务标签
|
||||
- 从运行时结果推导出的状态行(`success`、`error`、`timeout` 或 `unknown`)
|
||||
- 从最新可见 assistant 文本中选取的结果内容;否则使用已净化的最新 `tool` / `toolResult` 文本;终态失败运行会报告失败状态,而不会重放已捕获的回复文本
|
||||
- 一条后续指令,说明何时回复、何时保持静默
|
||||
- `Status` 不是从模型输出推断的;它来自运行时结果信号。
|
||||
- 超时时,如果子级只执行到了工具调用阶段,通知可以将这段历史压缩为简短的部分进度摘要,而不是重放原始工具输出。
|
||||
- 超时时,如果子级只执行到了工具调用,通告可以将该历史折叠为简短的部分进度摘要,而不是重放原始工具输出。
|
||||
|
||||
通知负载末尾都会包含一行统计信息(即使被包装):
|
||||
通告载荷在末尾包含一行统计信息(即使被包装也是如此):
|
||||
|
||||
- 运行时长(例如 `runtime 5m12s`)
|
||||
- Token 用量(输入/输出/总计)
|
||||
- 当配置了模型定价时的预估成本(`models.providers.*.models[].cost`)
|
||||
- `sessionKey`、`sessionId` 和转录路径(这样主智能体可以通过 `sessions_history` 获取历史,或在磁盘上检查文件)
|
||||
- 内部元数据仅用于编排;面向用户的回复应以正常助手语气重写。
|
||||
- token 使用量(输入 / 输出 / 总计)
|
||||
- 已配置模型定价时的预估成本(`models.providers.*.models[].cost`)
|
||||
- `sessionKey`、`sessionId` 和转录路径(这样主智能体可以通过 `sessions_history` 获取历史记录,或在磁盘上检查文件)
|
||||
- 内部元数据仅用于编排;面向用户的回复应以正常 assistant 语气重写。
|
||||
|
||||
`sessions_history` 是更安全的编排路径:
|
||||
|
||||
- 助手回顾会先被规范化:
|
||||
- 会先对 assistant 回溯进行规范化:
|
||||
- 去除 thinking 标签
|
||||
- 去除 `<relevant-memories>` / `<relevant_memories>` 脚手架块
|
||||
- 去除纯文本工具调用 XML 负载块,例如 `<tool_call>...</tool_call>`、`<function_call>...</function_call>`、`<tool_calls>...</tool_calls>` 和 `<function_calls>...</function_calls>`,包括那些未正常闭合的截断负载
|
||||
- 去除降级后的工具调用/结果脚手架和历史上下文标记
|
||||
- 去除泄漏的模型控制 token,例如 `<|assistant|>`、其他 ASCII `<|...|>` token,以及全角 `<|...|>` 变体
|
||||
- 去除纯文本工具调用 XML 载荷块,例如 `<tool_call>...</tool_call>`、`<function_call>...</function_call>`、`<tool_calls>...</tool_calls>` 和 `<function_calls>...</function_calls>`,包括那些从未正常闭合的截断载荷
|
||||
- 去除降级后的工具调用 / 结果脚手架和历史上下文标记
|
||||
- 去除泄漏的模型控制 token,例如 `<|assistant|>`、其他 ASCII `<|...|>` token,以及全角变体 `<|...|>`
|
||||
- 去除格式错误的 MiniMax 工具调用 XML
|
||||
- 类似凭证/token 的文本会被脱敏
|
||||
- 类凭证 / token 文本会被脱敏
|
||||
- 长内容块可能会被截断
|
||||
- 非常大的历史可能会丢弃较旧的行,或将过大的某一行替换为 `[sessions_history omitted: message too large]`
|
||||
- 当你需要完整逐字节转录时,回退方案是在磁盘上检查原始转录
|
||||
- 非常大的历史记录可能会丢弃较早的行,或用 `[sessions_history omitted: message too large]` 替换过大的行
|
||||
- 当你需要完整、逐字节的转录时,后备方案是在磁盘上检查原始转录
|
||||
|
||||
## 工具策略(子智能体工具)
|
||||
|
||||
子智能体首先沿用父级或目标智能体相同的配置文件和工具策略流水线。之后,OpenClaw 会应用子智能体限制层。
|
||||
子智能体首先使用与父级或目标智能体相同的配置文件和工具策略流水线。之后,OpenClaw 会应用子智能体限制层。
|
||||
|
||||
在没有限制性 `tools.profile` 的情况下,子智能体会获得**除会话工具**和系统工具之外的**所有工具**:
|
||||
在没有限制性 `tools.profile` 的情况下,子智能体获得**除会话工具**和系统工具之外的**所有工具**:
|
||||
|
||||
- `sessions_list`
|
||||
- `sessions_history`
|
||||
- `sessions_send`
|
||||
- `sessions_spawn`
|
||||
|
||||
这里的 `sessions_history` 仍然是一个有边界、已清理的回顾视图;它不是原始转录转储。
|
||||
这里的 `sessions_history` 也仍然是有边界、已净化的回溯视图;它不是原始转录转储。
|
||||
|
||||
当 `maxSpawnDepth >= 2` 时,深度 1 编排器子智能体还会额外获得 `sessions_spawn`、`subagents`、`sessions_list` 和 `sessions_history`,以便管理其子级。
|
||||
|
||||
@ -318,9 +318,9 @@ x-i18n:
|
||||
tools: {
|
||||
subagents: {
|
||||
tools: {
|
||||
// deny 优先生效
|
||||
// deny 优先
|
||||
deny: ["gateway", "cron"],
|
||||
// 如果设置 allow,则变为仅 allow 模式(deny 仍然优先)
|
||||
// 如果设置了 allow,它会变为仅允许列表(deny 仍然优先)
|
||||
// allow: ["read", "exec", "process"]
|
||||
},
|
||||
},
|
||||
@ -328,7 +328,7 @@ x-i18n:
|
||||
}
|
||||
```
|
||||
|
||||
`tools.subagents.tools.allow` 是最终的仅 allow 过滤器。它可以收窄已经解析出的工具集,但不能把已被 `tools.profile` 移除的工具重新加回来。例如,`tools.profile: "coding"` 包含 `web_search`/`web_fetch`,但不包含 `browser` 工具。要让 coding 配置文件的子智能体使用浏览器自动化,请在配置文件阶段添加 browser:
|
||||
`tools.subagents.tools.allow` 是最终的仅允许过滤器。它可以缩小已解析的工具集合,但不能把已被 `tools.profile` 移除的工具重新加回来。例如,`tools.profile: "coding"` 包含 `web_search` / `web_fetch`,但不包含 `browser` 工具。若要让 coding 配置文件的子智能体使用浏览器自动化,请在配置文件阶段加入 browser:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -339,36 +339,36 @@ x-i18n:
|
||||
}
|
||||
```
|
||||
|
||||
当只有某一个智能体需要浏览器自动化时,使用按智能体配置的 `agents.list[].tools.alsoAllow: ["browser"]`。
|
||||
如果只希望某一个智能体获得浏览器自动化,请使用按智能体配置的 `agents.list[].tools.alsoAllow: ["browser"]`。
|
||||
|
||||
## 并发
|
||||
|
||||
子智能体使用一个专用的进程内队列 lane:
|
||||
|
||||
- lane 名称:`subagent`
|
||||
- 并发数:`agents.defaults.subagents.maxConcurrent`(默认 `8`)
|
||||
- 并发数:`agents.defaults.subagents.maxConcurrent`(默认值 `8`)
|
||||
|
||||
## 存活性与恢复
|
||||
|
||||
OpenClaw 不会将缺少 `endedAt` 视为子智能体仍然存活的永久性证据。超过陈旧运行窗口的未结束运行,将不再在 `/subagents list`、Status 摘要、后代完成门控以及每会话并发检查中计为活动/待处理。
|
||||
OpenClaw 不会将缺少 `endedAt` 永久视为某个子智能体仍然存活的证明。超过过期运行窗口的未结束运行将不再在 `/subagents list`、状态摘要、后代完成门控以及每会话并发检查中计为活动 / 待处理。
|
||||
|
||||
Gateway 网关重启后,陈旧的未结束恢复运行会被清理,除非其子会话被标记为 `abortedLastRun: true`。这些因重启而中止的子会话仍可通过子智能体孤儿恢复流程恢复,该流程会在清除中止标记之前发送一条合成恢复消息。
|
||||
在 Gateway 网关重启后,过期且未结束的已恢复运行会被修剪,除非其子会话被标记为 `abortedLastRun: true`。这些因重启而中止的子会话仍可通过子智能体孤儿恢复流程进行恢复,该流程会先发送一条合成恢复消息,然后再清除中止标记。
|
||||
|
||||
如果子智能体启动因 Gateway 网关 `PAIRING_REQUIRED` / `scope-upgrade` 失败,请在编辑配对状态之前先检查 RPC 调用方。内部 `sessions_spawn` 协调应通过直接 loopback 共享 token/password 认证,以 `client.id: "gateway-client"` 和 `client.mode: "backend"` 连接;该路径不依赖 CLI 的已配对设备 scope 基线。远程调用方、显式 `deviceIdentity`、显式设备 token 路径以及 browser/node 客户端仍需要正常的设备批准才能进行 scope 升级。
|
||||
如果子智能体生成因 Gateway 网关 `PAIRING_REQUIRED` / `scope-upgrade` 失败,在编辑配对状态之前,请先检查 RPC 调用方。内部 `sessions_spawn` 协调应通过 direct loopback shared-token / password auth 以 `client.id: "gateway-client"` 和 `client.mode: "backend"` 连接;该路径不依赖 CLI 的配对设备 scope 基线。远程调用方、显式 `deviceIdentity`、显式设备 token 路径以及浏览器 / node 客户端仍然需要正常的设备批准来进行 scope 升级。
|
||||
|
||||
## 停止
|
||||
|
||||
- 在请求者聊天中发送 `/stop` 会中止请求者会话,并停止由其启动的所有活动子智能体运行,同时级联到嵌套子级。
|
||||
- `/subagents kill <id>` 会停止指定子智能体,并级联停止其子级。
|
||||
- 在请求者聊天中发送 `/stop` 会中止请求者会话,并停止由其生成的任何活动子智能体运行,同时级联到嵌套子级。
|
||||
- `/subagents kill <id>` 会停止指定子智能体,并级联到其子级。
|
||||
|
||||
## 限制
|
||||
|
||||
- 子智能体通知是**尽力而为**的。如果 Gateway 网关重启,待处理的“通知回传”工作会丢失。
|
||||
- 子智能体通告是**尽力而为**的。如果 Gateway 网关重启,待处理的“回传通告”工作会丢失。
|
||||
- 子智能体仍共享同一个 Gateway 网关进程资源;请将 `maxConcurrent` 视为一个安全阀。
|
||||
- `sessions_spawn` 始终是非阻塞的:它会立即返回 `{ status: "accepted", runId, childSessionKey }`。
|
||||
- 子智能体上下文只注入 `AGENTS.md` + `TOOLS.md`(不包括 `SOUL.md`、`IDENTITY.md`、`USER.md`、`HEARTBEAT.md` 或 `BOOTSTRAP.md`)。
|
||||
- 最大嵌套深度为 5(`maxSpawnDepth` 范围:1–5)。对于大多数使用场景,推荐使用深度 2。
|
||||
- `maxChildrenPerAgent` 限制每个会话的活动子级数量(默认:5,范围:1–20)。
|
||||
- 子智能体上下文只会注入 `AGENTS.md` + `TOOLS.md`(不包含 `SOUL.md`、`IDENTITY.md`、`USER.md`、`HEARTBEAT.md` 或 `BOOTSTRAP.md`)。
|
||||
- 最大嵌套深度为 5(`maxSpawnDepth` 范围:1–5)。对于大多数用例,建议使用深度 2。
|
||||
- `maxChildrenPerAgent` 限制每个会话的活动子级数量上限(默认值:5,范围:1–20)。
|
||||
|
||||
## 相关内容
|
||||
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
---
|
||||
read_when:
|
||||
- 你想通过浏览器操作 Gateway 网关
|
||||
- 你希望在不使用 SSH 隧道的情况下获得 Tailnet 访问权限
|
||||
summary: 用于 Gateway 网关的基于浏览器的控制 UI(聊天、节点、配置)
|
||||
- 你希望在不使用 SSH 隧道的情况下通过 Tailnet 访问
|
||||
summary: 基于浏览器的 Gateway 网关控制 UI(聊天、节点、配置)
|
||||
title: 控制 UI
|
||||
x-i18n:
|
||||
generated_at: "2026-04-25T17:12:33Z"
|
||||
generated_at: "2026-04-26T00:39:24Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 29d77ae57e32abe5ad25b2c22986d9d8e67f7ac183af06e8ffc4907ae4e6c0bc
|
||||
source_hash: 6b8b92415bedb9102a42cf5a01ec3872318d713a8de1d95d3931a9d199d237ee
|
||||
source_path: web/control-ui.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
Control UI 是一个小型的 **Vite + Lit** 单页应用,由 Gateway 网关提供服务:
|
||||
Control UI 是一个由 Gateway 网关提供服务的小型 **Vite + Lit** 单页应用:
|
||||
|
||||
- 默认:`http://<host>:18789/`
|
||||
- 可选前缀:设置 `gateway.controlUi.basePath`(例如 `/openclaw`)
|
||||
@ -28,22 +28,22 @@ Control UI 是一个小型的 **Vite + Lit** 单页应用,由 Gateway 网关
|
||||
|
||||
如果页面加载失败,请先启动 Gateway 网关:`openclaw gateway`。
|
||||
|
||||
身份验证会在 WebSocket 握手期间通过以下方式提供:
|
||||
认证会在 WebSocket 握手期间通过以下方式提供:
|
||||
|
||||
- `connect.params.auth.token`
|
||||
- `connect.params.auth.password`
|
||||
- 当 `gateway.auth.allowTailscale: true` 时使用 Tailscale Serve 身份标头
|
||||
- 当 `gateway.auth.mode: "trusted-proxy"` 时使用受信任代理身份标头
|
||||
- 当 `gateway.auth.mode: "trusted-proxy"` 时使用 trusted-proxy 身份标头
|
||||
|
||||
仪表板设置面板会为当前浏览器标签页会话保存一个 token 和所选的 gateway URL;不会持久化保存密码。新手引导通常会在首次连接时为共享密钥身份验证生成一个 gateway token,但当 `gateway.auth.mode` 为 `"password"` 时,也可以使用密码身份验证。
|
||||
仪表板设置面板会为当前浏览器标签页会话保存 token 和所选的 gateway URL;不会持久化保存密码。新手引导通常会在首次连接时为共享密钥认证生成一个 gateway token,但当 `gateway.auth.mode` 为 `"password"` 时,也可以使用密码认证。
|
||||
|
||||
## 设备配对(首次连接)
|
||||
|
||||
当你从新的浏览器或设备连接到 Control UI 时,Gateway 网关会要求进行**一次性配对批准**——即使你位于同一个 Tailnet 上并设置了 `gateway.auth.allowTailscale: true` 也是如此。这是一项安全措施,用于防止未经授权的访问。
|
||||
当你从新的浏览器或设备连接到 Control UI 时,Gateway 网关会要求进行**一次性配对批准**——即使你位于同一个 Tailnet 且 `gateway.auth.allowTailscale: true` 也是如此。这是一项安全措施,用于防止未授权访问。
|
||||
|
||||
**你会看到:** “disconnected (1008): pairing required”
|
||||
|
||||
**批准该设备的方法:**
|
||||
**批准设备的方法:**
|
||||
|
||||
```bash
|
||||
# 列出待处理请求
|
||||
@ -53,129 +53,133 @@ openclaw devices list
|
||||
openclaw devices approve <requestId>
|
||||
```
|
||||
|
||||
如果浏览器在重试配对时更改了身份验证详情(角色/作用域/公钥),之前待处理的请求会被替代,并创建一个新的 `requestId`。批准前请重新运行 `openclaw devices list`。
|
||||
如果浏览器在认证详情发生变化后重试配对(角色/作用域/公钥),之前的待处理请求会被新请求取代,并创建一个新的 `requestId`。批准前请重新运行 `openclaw devices list`。
|
||||
|
||||
如果浏览器已经配对,而你将其权限从只读访问更改为写入/admin 访问,这会被视为一次批准升级,而不是静默重连。OpenClaw 会保持旧批准继续有效,阻止更高权限的重新连接,并要求你显式批准新的作用域集合。
|
||||
如果浏览器已经完成配对,而你将其从只读访问改为写入/管理员访问,这会被视为批准升级,而不是静默重连。OpenClaw 会保留旧的批准仍然有效,阻止更高权限的重连,并要求你显式批准新的作用域集合。
|
||||
|
||||
批准后,该设备会被记住,除非你使用 `openclaw devices revoke --device <id> --role <role>` 撤销它,否则不需要再次批准。有关 token 轮换和撤销,请参阅 [Devices CLI](/zh-CN/cli/devices)。
|
||||
一旦获得批准,该设备会被记住,除非你使用 `openclaw devices revoke --device <id> --role <role>` 撤销它,否则无需再次批准。有关 token 轮换和撤销,请参阅 [Devices CLI](/zh-CN/cli/devices)。
|
||||
|
||||
**注意:**
|
||||
**说明:**
|
||||
|
||||
- 直接的本地 local loopback 浏览器连接(`127.0.0.1` / `localhost`)会自动批准。
|
||||
- Tailnet 和局域网浏览器连接仍然需要显式批准,即使它们来自同一台机器。
|
||||
- 直接的本地 local loopback 浏览器连接(`127.0.0.1` / `localhost`)会自动获批。
|
||||
- Tailnet 和 LAN 浏览器连接仍然需要显式批准,即使它们来自同一台机器。
|
||||
- 每个浏览器配置文件都会生成唯一的设备 ID,因此切换浏览器或清除浏览器数据都需要重新配对。
|
||||
|
||||
## 个人身份(浏览器本地)
|
||||
|
||||
Control UI 支持按浏览器区分的个人身份(显示名称和头像),用于在共享会话中为发出的消息附加归属信息。它保存在浏览器存储中,仅限当前浏览器配置文件使用,不会同步到其他设备,也不会在服务器端持久化,除了你实际发送的消息中正常的转录作者元数据之外。清除站点数据或切换浏览器后,它会重置为空。
|
||||
Control UI 支持按浏览器设置个人身份(显示名称和头像),它会附加到发出的消息中,以便在共享会话中标注归属。它存储在浏览器存储中,仅限当前浏览器配置文件,不会同步到其他设备,也不会在服务端持久化;只有你实际发送的消息中,正常的对话记录作者元数据会保留。清除站点数据或切换浏览器后,它会重置为空。
|
||||
|
||||
同样的浏览器本地模式也适用于助手头像覆盖。上传的助手头像只会在本地浏览器中覆盖 gateway 解析出的身份,且绝不会通过 `config.patch` 往返传输。共享的 `ui.assistant.avatar` 配置字段仍可供直接写入该字段的非 UI 客户端使用(例如脚本化 gateways 或自定义仪表板)。
|
||||
同样的浏览器本地模式也适用于助手头像覆盖。上传的助手头像只会在本地浏览器中覆盖 gateway 解析出的身份,绝不会通过 `config.patch` 往返。共享的 `ui.assistant.avatar` 配置字段仍可供直接写入该字段的非 UI 客户端使用(例如脚本化 gateway 或自定义仪表板)。
|
||||
|
||||
## 运行时配置端点
|
||||
|
||||
Control UI 会从 `__/openclaw/control-ui-config.json` 获取其运行时设置。该端点受与其余 HTTP 接口相同的 gateway 身份验证保护:未通过身份验证的浏览器无法获取它;成功获取需要已有有效的 gateway token/密码、Tailscale Serve 身份,或受信任代理身份。
|
||||
Control UI 会从 `__/openclaw/control-ui-config.json` 获取其运行时设置。该端点受与其余 HTTP 接口相同的 gateway 认证保护:未认证的浏览器无法获取它,而成功获取则要求已经具有有效的 gateway token/密码、Tailscale Serve 身份或 trusted-proxy 身份之一。
|
||||
|
||||
## 语言支持
|
||||
|
||||
Control UI 可以在首次加载时根据你的浏览器语言环境自动本地化。若要稍后覆盖它,请打开 **Overview -> Gateway Access -> Language**。语言选择器位于 Gateway Access 卡片中,而不是在 Appearance 下。
|
||||
Control UI 可以在首次加载时根据你的浏览器区域设置进行本地化。若要稍后覆盖它,请打开 **Overview -> Gateway Access -> Language**。区域选择器位于 Gateway Access 卡片中,而不是在 Appearance 下。
|
||||
|
||||
- 支持的语言环境:`en`, `zh-CN`, `zh-TW`, `pt-BR`, `de`, `es`, `ja-JP`, `ko`, `fr`, `tr`, `uk`, `id`, `pl`, `th`
|
||||
- 非英语翻译会在浏览器中按需懒加载。
|
||||
- 所选语言环境会保存在浏览器存储中,并在以后访问时重复使用。
|
||||
- 支持的区域设置:`en`、`zh-CN`、`zh-TW`、`pt-BR`、`de`、`es`、`ja-JP`、`ko`、`fr`、`tr`、`uk`、`id`、`pl`、`th`
|
||||
- 非英语翻译会在浏览器中按需延迟加载。
|
||||
- 所选区域设置会保存在浏览器存储中,并在后续访问时复用。
|
||||
- 缺失的翻译键会回退到英语。
|
||||
|
||||
## 它目前能做什么
|
||||
|
||||
- 通过 Gateway WS 与模型聊天(`chat.history`, `chat.send`, `chat.abort`, `chat.inject`)
|
||||
- 通过 WebRTC 直接从浏览器连接到 OpenAI Realtime。Gateway 网关会使用 `talk.realtime.session` 签发一个短期有效的 Realtime 客户端密钥;浏览器将麦克风音频直接发送到 OpenAI,并通过 `chat.send` 将 `openclaw_agent_consult` 工具调用中继回更大、已配置的 OpenClaw 模型。
|
||||
- 在聊天中流式显示工具调用 + 实时工具输出卡片(智能体事件)
|
||||
- 渠道:内置渠道以及内置/外部插件渠道的状态、二维码登录和按渠道配置(`channels.status`, `web.login.*`, `config.patch`)
|
||||
- 通过 Gateway WS 与模型聊天(`chat.history`、`chat.send`、`chat.abort`、`chat.inject`)
|
||||
- 通过 WebRTC 直接从浏览器与 OpenAI Realtime 通话。Gateway 网关会通过 `talk.realtime.session` 签发一个短期有效的 Realtime 客户端密钥;浏览器会将麦克风音频直接发送给 OpenAI,并通过 `chat.send` 中继 `openclaw_agent_consult` 工具调用,以供配置中的更大型 OpenClaw 模型使用。
|
||||
- 在聊天中流式显示工具调用和实时工具输出卡片(智能体事件)
|
||||
- 渠道:内置以及内置/外部渠道插件的状态、QR 登录和按渠道配置(`channels.status`、`web.login.*`、`config.patch`)
|
||||
- 实例:在线列表 + 刷新(`system-presence`)
|
||||
- 会话:列表 + 按会话设置模型/思考/快速/详细/追踪/推理覆盖(`sessions.list`, `sessions.patch`)
|
||||
- Dreams:Dreaming 状态、启用/禁用开关,以及 Dream Diary 阅读器(`doctor.memory.status`, `doctor.memory.dreamDiary`, `config.patch`)
|
||||
- 会话:列表 + 按会话覆盖 model/thinking/fast/verbose/trace/reasoning(`sessions.list`、`sessions.patch`)
|
||||
- Dreams:Dreaming 状态、启用/禁用切换,以及 Dream Diary 阅读器(`doctor.memory.status`、`doctor.memory.dreamDiary`、`config.patch`)
|
||||
- Cron 作业:列出/添加/编辑/运行/启用/禁用 + 运行历史(`cron.*`)
|
||||
- Skills:状态、启用/禁用、安装、API 密钥更新(`skills.*`)
|
||||
- 节点:列表 + 能力上限(`node.list`)
|
||||
- Exec 批准:编辑 gateway 或节点允许列表 + `exec host=gateway/node` 的请求策略(`exec.approvals.*`)
|
||||
- 配置:查看/编辑 `~/.openclaw/openclaw.json`(`config.get`, `config.set`)
|
||||
- 配置:带验证地应用 + 重启(`config.apply`),并唤醒最近活跃的会话
|
||||
- 配置写入包含 base-hash 防护,以防止覆盖并发编辑
|
||||
- 配置写入(`config.set`/`config.apply`/`config.patch`)还会对提交的配置负载中的活跃 SecretRef 解析进行预检;未解析的活跃已提交引用会在写入前被拒绝
|
||||
- 配置 schema + 表单渲染(`config.schema` / `config.schema.lookup`,包括字段 `title` / `description`、匹配的 UI 提示、直接子项摘要、嵌套对象/通配符/数组/组合节点上的文档元数据,以及可用时的插件 + 渠道 schema);仅当快照可以安全进行原始往返时,才提供原始 JSON 编辑器
|
||||
- 如果某个快照无法安全地进行原始文本往返,Control UI 会强制使用表单模式,并为该快照禁用原始模式
|
||||
- 原始 JSON 编辑器中的“Reset to saved”会保留原始编写的结构(格式、注释、`$include` 布局),而不是重新渲染为扁平化快照,因此当快照可以安全往返时,外部编辑在重置后仍会保留
|
||||
- 结构化 SecretRef 对象值会在表单文本输入中以只读方式渲染,以防止意外将对象损坏为字符串
|
||||
- 调试:状态/健康检查/模型快照 + 事件日志 + 手动 RPC 调用(`status`, `health`, `models.list`)
|
||||
- 日志:gateway 文件日志的实时追踪,支持过滤/导出(`logs.tail`)
|
||||
- 更新:执行 package/git 更新 + 重启(`update.run`),并附带重启报告
|
||||
- 节点:列表 + 能力(`node.list`)
|
||||
- Exec 批准:编辑 gateway 或节点允许列表 + `exec host=gateway/node` 的询问策略(`exec.approvals.*`)
|
||||
- 配置:查看/编辑 `~/.openclaw/openclaw.json`(`config.get`、`config.set`)
|
||||
- 配置:带验证地应用并重启(`config.apply`),并唤醒最后一个活跃会话
|
||||
- 配置写入包含 base-hash 保护,以防覆盖并发编辑
|
||||
- 配置写入(`config.set` / `config.apply` / `config.patch`)也会对提交的配置负载中的 SecretRef 执行活跃预解析;未解析的活跃已提交引用会在写入前被拒绝
|
||||
- 配置 schema + 表单渲染(`config.schema` / `config.schema.lookup`,包括字段 `title` / `description`、匹配的 UI 提示、直接子项摘要、嵌套对象/通配符/数组/组合节点上的文档元数据,以及可用时的插件 + 渠道 schema);只有当快照可以安全进行原始往返时,Raw JSON 编辑器才可用
|
||||
- 如果某个快照无法安全地进行原始往返,Control UI 会强制使用表单模式,并对该快照禁用原始模式
|
||||
- Raw JSON 编辑器中的“Reset to saved”会保留原始编写形态(格式、注释、`$include` 布局),而不是重新渲染扁平化快照,因此当快照可以安全往返时,外部编辑在重置后仍能保留
|
||||
- 结构化 SecretRef 对象值会在表单文本输入中以只读方式呈现,以防对象被意外损坏并转换为字符串
|
||||
- 调试:Status/health/models 快照 + 事件日志 + 手动 RPC 调用(`status`、`health`、`models.list`)
|
||||
- 日志:实时跟踪 gateway 文件日志,并支持筛选/导出(`logs.tail`)
|
||||
- 更新:运行 package/git 更新并重启(`update.run`),附带重启报告
|
||||
|
||||
Cron 作业面板说明:
|
||||
|
||||
- 对于隔离作业,默认投递方式为公告摘要。如果你希望仅内部运行,可以切换为 none。
|
||||
- 选择 announce 时会显示渠道/目标字段。
|
||||
- 对于隔离作业,默认投递方式为发送摘要公告。如果你希望仅进行内部运行,可以切换为 none。
|
||||
- 选择 announce 后会显示渠道/目标字段。
|
||||
- Webhook 模式使用 `delivery.mode = "webhook"`,并将 `delivery.to` 设置为有效的 HTTP(S) webhook URL。
|
||||
- 对于主会话作业,可用的投递模式包括 webhook 和 none。
|
||||
- 高级编辑控件包括运行后删除、清除智能体覆盖、cron 精确/错峰选项、智能体模型/思考覆盖,以及尽力投递切换。
|
||||
- 表单验证为内联方式,并带有字段级错误;无效值会禁用保存按钮,直到修复为止。
|
||||
- 设置 `cron.webhookToken` 以发送专用 bearer token;如果省略,则 webhook 会在不带身份验证标头的情况下发送。
|
||||
- 已弃用的回退方式:存储的旧版作业如果使用 `notify: true`,在迁移前仍可使用 `cron.webhook`。
|
||||
- 高级编辑控件包括运行后删除、清除智能体覆盖、cron 精确/错峰选项、智能体 model/thinking 覆盖,以及尽力投递切换。
|
||||
- 表单验证为内联方式,并提供字段级错误;在修复前,无效值会禁用保存按钮。
|
||||
- 设置 `cron.webhookToken` 可以发送专用 bearer token;如果省略,则发送 webhook 时不会带认证标头。
|
||||
- 已弃用的回退方案:仍然存储为 `notify: true` 的旧版作业,在迁移前仍可使用 `cron.webhook`。
|
||||
|
||||
## 聊天行为
|
||||
|
||||
- `chat.send` 是**非阻塞的**:它会立即确认并返回 `{ runId, status: "started" }`,随后响应会通过 `chat` 事件流式传输。
|
||||
- `chat.send` 是**非阻塞**的:它会立即确认并返回 `{ runId, status: "started" }`,响应则通过 `chat` 事件流式传输。
|
||||
- 使用相同的 `idempotencyKey` 重新发送时,运行中会返回 `{ status: "in_flight" }`,完成后会返回 `{ status: "ok" }`。
|
||||
- 出于 UI 安全考虑,`chat.history` 响应有大小限制。当转录条目过大时,Gateway 网关可能会截断较长的文本字段、省略较重的元数据块,并用占位符替换超大消息(`[chat.history omitted: message too large]`)。
|
||||
- 助手/生成的图像会作为托管媒体引用持久化,并通过经过身份验证的 Gateway 网关媒体 URL 返回,因此重新加载不依赖于聊天历史响应中是否仍保留原始 base64 图像负载。
|
||||
- `chat.history` 还会从可见的助手文本中移除仅用于显示的内联指令标签(例如 `[[reply_to_*]]` 和 `[[audio_as_voice]]`)、纯文本工具调用 XML 负载(包括 `<tool_call>...</tool_call>`、`<function_call>...</function_call>`、`<tool_calls>...</tool_calls>`、`<function_calls>...</function_calls>` 以及被截断的工具调用块),并移除泄露的 ASCII/全角模型控制 token;同时会省略那些全部可见文本仅为精确静默 token `NO_REPLY` / `no_reply` 的助手条目。
|
||||
- 在活跃发送期间以及最终历史刷新期间,如果 `chat.history` 短暂返回较旧的快照,聊天视图会保持本地乐观用户/助手消息可见;一旦 Gateway 网关历史追上,这些本地消息就会被规范转录替换。
|
||||
- `chat.inject` 会将一条助手备注追加到会话转录中,并广播一个 `chat` 事件用于仅 UI 的更新(不运行智能体、不投递到渠道)。
|
||||
- 聊天页眉中的模型和思考选择器会通过 `sessions.patch` 立即修补当前活跃会话;它们是持久化的会话覆盖,而不是仅单轮生效的发送选项。
|
||||
- 当新的 Gateway 网关会话使用情况报告显示上下文压力较高时,聊天编辑区会显示上下文提示,并且在建议压缩级别下,会显示一个 compact 按钮来运行正常的会话压缩路径。过期的 token 快照会被隐藏,直到 Gateway 网关再次报告新的使用情况。
|
||||
- Talk 模式使用已注册的实时语音提供商,该提供商支持浏览器 WebRTC 会话。请使用 `talk.provider: "openai"` 并配置 `talk.providers.openai.apiKey`,或者复用 Voice Call 实时提供商配置。浏览器绝不会收到标准 OpenAI API 密钥;它只会收到临时的 Realtime 客户端密钥。Google Live 实时语音支持后端 Voice Call 和 Google Meet 桥接,但目前尚不支持此浏览器 WebRTC 路径。Realtime 会话提示词由 Gateway 网关组装;`talk.realtime.session` 不接受调用方提供的指令覆盖。
|
||||
- 在聊天编辑器中,Talk 控件是麦克风听写按钮旁边的波浪按钮。Talk 启动时,编辑器状态行会显示 `Connecting Talk...`,音频连接后显示 `Talk live`,而当实时工具调用通过 `chat.send` 咨询已配置的更大 OpenClaw 模型时,则显示 `Asking OpenClaw...`。
|
||||
- `chat.history` 的响应具有大小限制,以确保 UI 安全。当对话记录条目过大时,Gateway 网关可能会截断较长的文本字段、省略较重的元数据块,并用占位符替换超大消息(`[chat.history omitted: message too large]`)。
|
||||
- 助手/生成的图片会持久化为受管媒体引用,并通过经过认证的 Gateway 网关媒体 URL 返回,因此重新加载时不依赖聊天历史响应中保留原始 base64 图片负载。
|
||||
- `chat.history` 还会从可见的助手文本中移除仅用于显示的内联指令标签(例如 `[[reply_to_*]]` 和 `[[audio_as_voice]]`)、纯文本工具调用 XML 负载(包括 `<tool_call>...</tool_call>`、`<function_call>...</function_call>`、`<tool_calls>...</tool_calls>`、`<function_calls>...</function_calls>` 以及被截断的工具调用块),以及泄露的 ASCII/全角模型控制 token,并省略那些整个可见文本仅为精确静默 token `NO_REPLY` / `no_reply` 的助手条目。
|
||||
- 在主动发送期间以及最终刷新历史记录时,如果 `chat.history` 短暂返回较旧的快照,聊天视图会保持本地乐观显示的用户/助手消息可见;一旦 Gateway 网关历史记录追上,这些本地消息就会被规范对话记录替换。
|
||||
- `chat.inject` 会向会话对话记录追加一条助手注释,并广播一个 `chat` 事件,用于仅 UI 的更新(不运行智能体,不进行渠道投递)。
|
||||
- 聊天头部中的 model 和 thinking 选择器会立即通过 `sessions.patch` 更新当前活跃会话;它们是持久的会话覆盖,而不是仅单轮发送选项。
|
||||
- 当最新的 Gateway 网关会话用量报告显示上下文压力较高时,聊天输入区域会显示上下文提示,并且在建议的压缩级别下,会显示一个 compact 按钮来运行常规会话压缩路径。过期的 token 快照会被隐藏,直到 Gateway 网关再次报告最新用量。
|
||||
- Talk 模式使用支持浏览器 WebRTC 会话的已注册实时语音 provider。可通过设置 `talk.provider: "openai"` 加上 `talk.providers.openai.apiKey` 来配置 OpenAI,或复用 Voice Call 实时 provider 配置。浏览器永远不会收到标准 OpenAI API 密钥;它只会收到临时 Realtime 客户端密钥。Google Live 实时语音支持后端 Voice Call 和 Google Meet 桥接,但暂不支持此浏览器 WebRTC 路径。Realtime 会话提示由 Gateway 网关组装;`talk.realtime.session` 不接受调用方提供的指令覆盖。
|
||||
- 在聊天输入框中,Talk 控件是位于麦克风听写按钮旁边的波形按钮。当 Talk 启动时,输入框状态行会显示 `Connecting Talk...`,然后在音频连接期间显示 `Talk live`,或者在实时工具调用通过 `chat.send` 咨询已配置的大型 OpenClaw 模型时显示 `Asking OpenClaw...`。
|
||||
- 停止:
|
||||
- 点击 **Stop**(调用 `chat.abort`)
|
||||
- 当某个运行处于活跃状态时,普通后续消息会进入队列。点击排队消息上的 **Steer**,可将该后续消息注入当前运行中的轮次。
|
||||
- 输入 `/stop`(或独立的中止短语,例如 `stop`、`stop action`、`stop run`、`stop openclaw`、`please stop`)以进行带外中止
|
||||
- `chat.abort` 支持 `{ sessionKey }`(无需 `runId`)以中止该会话的所有活跃运行
|
||||
- 当某次运行处于活动状态时,普通后续消息会排队。点击已排队消息上的 **Steer**,可将该后续消息注入到当前运行中的轮次。
|
||||
- 输入 `/stop`(或独立的中止短语,如 `stop`、`stop action`、`stop run`、`stop openclaw`、`please stop`)可进行带外中止
|
||||
- `chat.abort` 支持 `{ sessionKey }`(无需 `runId`)来中止该会话的所有活动运行
|
||||
- 中止后的部分保留:
|
||||
- 当某个运行被中止时,部分助手文本仍可能显示在 UI 中
|
||||
- 当存在缓冲输出时,Gateway 网关会将被中止的部分助手文本持久化到转录历史中
|
||||
- 持久化条目会包含中止元数据,以便转录使用者区分中止产生的部分内容与正常完成输出
|
||||
- 当某次运行被中止时,部分助手文本仍可能在 UI 中显示
|
||||
- 当存在缓冲输出时,Gateway 网关会将中止时的部分助手文本持久化到对话记录历史中
|
||||
- 持久化条目会包含中止元数据,以便对话记录使用方区分中止部分与正常完成输出
|
||||
|
||||
## PWA 安装和 Web Push
|
||||
|
||||
Control UI 附带了一个 `manifest.webmanifest` 和一个 service worker,因此现代浏览器可以将其安装为独立的 PWA。Web Push 允许 Gateway 网关在标签页或浏览器窗口未打开时,通过通知唤醒已安装的 PWA。
|
||||
Control UI 内置了 `manifest.webmanifest` 和 service worker,因此现代浏览器可以将其安装为独立 PWA。Web Push 允许 Gateway 网关在标签页或浏览器窗口未打开时,通过通知唤醒已安装的 PWA。
|
||||
|
||||
| 界面 | 作用 |
|
||||
| ----------------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| `ui/public/manifest.webmanifest` | PWA 清单。浏览器一旦可以访问它,就会提供“Install app”。 |
|
||||
| `ui/public/sw.js` | 处理 `push` 事件和通知点击的 service worker。 |
|
||||
| `push/vapid-keys.json`(位于 OpenClaw 状态目录下) | 自动生成的 VAPID 密钥对,用于对 Web Push 负载签名。 |
|
||||
| `push/web-push-subscriptions.json` | 持久化保存的浏览器订阅端点。 |
|
||||
| Surface | 作用 |
|
||||
| ----------------------------------------------------- | ---- |
|
||||
| `ui/public/manifest.webmanifest` | PWA 清单。浏览器一旦可以访问它,就会提供“安装应用”选项。 |
|
||||
| `ui/public/sw.js` | 处理 `push` 事件和通知点击的 service worker。 |
|
||||
| `push/vapid-keys.json`(位于 OpenClaw 状态目录下) | 自动生成的 VAPID 密钥对,用于对 Web Push 负载进行签名。 |
|
||||
| `push/web-push-subscriptions.json` | 持久化的浏览器订阅端点。 |
|
||||
|
||||
如果你希望固定密钥对(用于多主机部署、密钥轮换或测试),可以通过 Gateway 网关进程上的环境变量覆盖 VAPID 密钥对:
|
||||
当你希望固定密钥时(例如多主机部署、密钥轮换或测试),可以通过 Gateway 网关进程上的环境变量覆盖 VAPID 密钥对:
|
||||
|
||||
- `OPENCLAW_VAPID_PUBLIC_KEY`
|
||||
- `OPENCLAW_VAPID_PRIVATE_KEY`
|
||||
- `OPENCLAW_VAPID_SUBJECT`(默认为 `mailto:openclaw@localhost`)
|
||||
|
||||
Control UI 使用以下受作用域限制的 Gateway 网关方法来注册和测试浏览器订阅:
|
||||
Control UI 使用以下这些受作用域保护的 Gateway 网关方法来注册和测试浏览器订阅:
|
||||
|
||||
- `push.web.vapidPublicKey` — 获取当前活跃的 VAPID 公钥。
|
||||
- `push.web.subscribe` — 注册一个 `endpoint` 以及 `keys.p256dh`/`keys.auth`。
|
||||
- `push.web.unsubscribe` — 移除一个已注册的端点。
|
||||
- `push.web.test` — 向调用者的订阅发送一条测试通知。
|
||||
- `push.web.vapidPublicKey` —— 获取当前活动的 VAPID 公钥。
|
||||
- `push.web.subscribe` —— 注册一个 `endpoint` 以及 `keys.p256dh` / `keys.auth`。
|
||||
- `push.web.unsubscribe` —— 删除一个已注册的端点。
|
||||
- `push.web.test` —— 向调用者的订阅发送测试通知。
|
||||
|
||||
Web Push 独立于 iOS APNS 中继路径(有关中继支持的推送,请参阅 [配置](/zh-CN/gateway/configuration))以及现有的 `push.test` 方法,后者面向原生移动端配对。
|
||||
Web Push 独立于 iOS APNS 中继路径
|
||||
(有关基于中继的推送,请参阅 [配置](/zh-CN/gateway/configuration))
|
||||
以及现有的 `push.test` 方法,后者面向原生移动端配对。
|
||||
|
||||
## 托管嵌入
|
||||
|
||||
助手消息可以通过 `[embed ...]` 短代码内联渲染托管的网页内容。iframe 沙箱策略由 `gateway.controlUi.embedSandbox` 控制:
|
||||
助手消息可以使用 `[embed ...]`
|
||||
短代码以内联方式渲染托管网页内容。iframe 沙箱策略由
|
||||
`gateway.controlUi.embedSandbox` 控制:
|
||||
|
||||
- `strict`:禁用托管嵌入中的脚本执行
|
||||
- `scripts`:允许交互式嵌入,同时保持源隔离;这是默认值,通常足以支持自包含的浏览器游戏/小部件
|
||||
- `trusted`:在 `allow-scripts` 的基础上再添加 `allow-same-origin`,用于有意需要更高权限的同站文档
|
||||
- `scripts`:允许交互式嵌入,同时保持源隔离;这是默认值,通常足以支持自包含的浏览器游戏/小组件
|
||||
- `trusted`:在 `allow-scripts` 之上额外添加 `allow-same-origin`,用于那些确实需要更高权限的同站文档
|
||||
|
||||
示例:
|
||||
|
||||
@ -191,13 +195,14 @@ Web Push 独立于 iOS APNS 中继路径(有关中继支持的推送,请参
|
||||
|
||||
仅当嵌入文档确实需要同源行为时才使用 `trusted`。对于大多数由智能体生成的游戏和交互式画布,`scripts` 是更安全的选择。
|
||||
|
||||
绝对外部 `http(s)` 嵌入 URL 默认仍会被阻止。如果你确实希望 `[embed url="https://..."]` 加载第三方页面,请设置 `gateway.controlUi.allowExternalEmbedUrls: true`。
|
||||
默认情况下,绝对外部 `http(s)` 嵌入 URL 仍会被阻止。如果你确实希望 `[embed url="https://..."]` 加载第三方页面,请设置
|
||||
`gateway.controlUi.allowExternalEmbedUrls: true`。
|
||||
|
||||
## Tailnet 访问(推荐)
|
||||
|
||||
### 集成的 Tailscale Serve(首选)
|
||||
### 集成 Tailscale Serve(首选)
|
||||
|
||||
让 Gateway 网关保持在 loopback 上,并让 Tailscale Serve 通过 HTTPS 为其做反向代理:
|
||||
让 Gateway 网关保持绑定在 loopback,并使用 Tailscale Serve 通过 HTTPS 进行代理:
|
||||
|
||||
```bash
|
||||
openclaw gateway --tailscale serve
|
||||
@ -207,9 +212,13 @@ openclaw gateway --tailscale serve
|
||||
|
||||
- `https://<magicdns>/`(或你配置的 `gateway.controlUi.basePath`)
|
||||
|
||||
默认情况下,当 `gateway.auth.allowTailscale` 为 `true` 时,Control UI/WebSocket Serve 请求可以通过 Tailscale 身份标头(`tailscale-user-login`)进行身份验证。OpenClaw 会通过使用 `tailscale whois` 解析 `x-forwarded-for` 地址并将其与标头匹配来验证该身份,并且只有当请求命中 loopback 且带有 Tailscale 的 `x-forwarded-*` 标头时才接受这些身份验证。若你希望即使对于 Serve 流量也要求显式共享密钥凭证,请设置 `gateway.auth.allowTailscale: false`。然后使用 `gateway.auth.mode: "token"` 或 `"password"`。
|
||||
对于该异步 Serve 身份路径,来自同一客户端 IP 和同一身份验证作用域的失败身份验证尝试会在写入速率限制前进行串行化。因此,同一浏览器的并发错误重试中,第二个请求可能会显示 `retry later`,而不是两个普通不匹配并行竞争。
|
||||
无 token 的 Serve 身份验证假定 gateway 主机是可信的。如果该主机上可能运行不受信任的本地代码,请要求使用 token/密码身份验证。
|
||||
默认情况下,当 `gateway.auth.allowTailscale` 为 `true` 时,Control UI/WebSocket Serve 请求可以通过 Tailscale 身份标头
|
||||
(`tailscale-user-login`)进行认证。OpenClaw
|
||||
会通过使用 `tailscale whois` 解析 `x-forwarded-for` 地址并与标头匹配来验证该身份,而且只有在请求命中 loopback 且带有 Tailscale 的 `x-forwarded-*` 标头时才会接受这些身份。若你希望即使对 Serve 流量也要求显式共享密钥凭证,请设置
|
||||
`gateway.auth.allowTailscale: false`。然后使用 `gateway.auth.mode: "token"` 或
|
||||
`"password"`。
|
||||
对于该异步 Serve 身份路径,来自同一客户端 IP 和认证作用域的失败认证尝试会在写入限速记录前被串行化。因此,来自同一浏览器的并发错误重试在第二个请求上可能会显示 `retry later`,而不是两个普通不匹配并行竞争。
|
||||
无 token 的 Serve 认证假设 gateway 主机是可信的。如果该主机上可能运行不受信任的本地代码,请要求使用 token/密码认证。
|
||||
|
||||
### 绑定到 tailnet + token
|
||||
|
||||
@ -221,16 +230,19 @@ openclaw gateway --bind tailnet --token "$(openssl rand -hex 32)"
|
||||
|
||||
- `http://<tailscale-ip>:18789/`(或你配置的 `gateway.controlUi.basePath`)
|
||||
|
||||
将匹配的共享密钥粘贴到 UI 设置中(作为 `connect.params.auth.token` 或 `connect.params.auth.password` 发送)。
|
||||
将匹配的共享密钥粘贴到 UI 设置中(作为
|
||||
`connect.params.auth.token` 或 `connect.params.auth.password` 发送)。
|
||||
|
||||
## 不安全的 HTTP
|
||||
|
||||
如果你通过纯 HTTP(`http://<lan-ip>` 或 `http://<tailscale-ip>`)打开仪表板,浏览器会在**非安全上下文**中运行,并阻止 WebCrypto。默认情况下,OpenClaw **会阻止**没有设备身份的 Control UI 连接。
|
||||
如果你通过明文 HTTP 打开仪表板(`http://<lan-ip>` 或 `http://<tailscale-ip>`),
|
||||
浏览器会运行在**非安全上下文**中,并阻止 WebCrypto。默认情况下,
|
||||
OpenClaw 会**阻止**没有设备身份的 Control UI 连接。
|
||||
|
||||
文档化的例外情况:
|
||||
|
||||
- 通过 `gateway.controlUi.allowInsecureAuth=true` 启用的仅 localhost 不安全 HTTP 兼容模式
|
||||
- 通过 `gateway.auth.mode: "trusted-proxy"` 成功进行的操作员 Control UI 身份验证
|
||||
- 仅限 localhost 的不安全 HTTP 兼容模式,使用 `gateway.controlUi.allowInsecureAuth=true`
|
||||
- 通过 `gateway.auth.mode: "trusted-proxy"` 成功进行 operator Control UI 认证
|
||||
- 紧急兜底 `gateway.controlUi.dangerouslyDisableDeviceAuth=true`
|
||||
|
||||
**推荐修复方式:** 使用 HTTPS(Tailscale Serve)或在本地打开 UI:
|
||||
@ -238,7 +250,7 @@ openclaw gateway --bind tailnet --token "$(openssl rand -hex 32)"
|
||||
- `https://<magicdns>/`(Serve)
|
||||
- `http://127.0.0.1:18789/`(在 gateway 主机上)
|
||||
|
||||
**不安全身份验证开关行为:**
|
||||
**不安全认证开关行为:**
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -252,11 +264,11 @@ openclaw gateway --bind tailnet --token "$(openssl rand -hex 32)"
|
||||
|
||||
`allowInsecureAuth` 只是一个本地兼容性开关:
|
||||
|
||||
- 它允许 localhost 上的 Control UI 会话在非安全 HTTP 上下文中在没有设备身份的情况下继续进行。
|
||||
- 它允许 localhost Control UI 会话在非安全 HTTP 上下文中在没有设备身份的情况下继续进行。
|
||||
- 它不会绕过配对检查。
|
||||
- 它不会放宽远程(非 localhost)设备身份要求。
|
||||
|
||||
**仅用于紧急兜底:**
|
||||
**仅限紧急兜底:**
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -268,42 +280,43 @@ openclaw gateway --bind tailnet --token "$(openssl rand -hex 32)"
|
||||
}
|
||||
```
|
||||
|
||||
`dangerouslyDisableDeviceAuth` 会禁用 Control UI 的设备身份检查,这是一次严重的安全降级。紧急使用后应尽快恢复。
|
||||
`dangerouslyDisableDeviceAuth` 会禁用 Control UI 设备身份检查,这是一次严重的安全降级。紧急使用后请尽快恢复。
|
||||
|
||||
受信任代理说明:
|
||||
Trusted-proxy 说明:
|
||||
|
||||
- 成功的 trusted-proxy 身份验证可以允许**操作员** Control UI 会话在没有设备身份的情况下接入
|
||||
- 这**不**适用于 node 角色的 Control UI 会话
|
||||
- 同主机 loopback 反向代理仍然不满足 trusted-proxy 身份验证;请参阅 [受信任代理身份验证](/zh-CN/gateway/trusted-proxy-auth)
|
||||
- 成功的 trusted-proxy 认证可以允许**operator** Control UI 会话在没有设备身份的情况下接入
|
||||
- 这**不**适用于 node-role Control UI 会话
|
||||
- 同主机 loopback 反向代理仍然不满足 trusted-proxy 认证;请参阅
|
||||
[Trusted proxy auth](/zh-CN/gateway/trusted-proxy-auth)
|
||||
|
||||
有关 HTTPS 设置指导,请参阅 [Tailscale](/zh-CN/gateway/tailscale)。
|
||||
|
||||
## 内容安全策略
|
||||
|
||||
Control UI 采用了严格的 `img-src` 策略:仅允许**同源**资源、`data:` URL 和本地生成的 `blob:` URL。远程 `http(s)` 和协议相对图片 URL 会被浏览器拒绝,并且不会发起网络请求。
|
||||
Control UI 内置了严格的 `img-src` 策略:仅允许**同源**资源、`data:` URL 和本地生成的 `blob:` URL。远程 `http(s)` 和协议相对图片 URL 会被浏览器拒绝,不会发起网络请求。
|
||||
|
||||
这在实际中的含义是:
|
||||
这在实践中的含义如下:
|
||||
|
||||
- 通过相对路径提供的头像和图片(例如 `/avatars/<id>`)仍然可以渲染,包括那些需要身份验证、由 UI 获取并转换为本地 `blob:` URL 的头像路由。
|
||||
- 内联 `data:image/...` URL 仍然可以渲染(这对于协议内负载很有用)。
|
||||
- 通过相对路径提供的头像和图片(例如 `/avatars/<id>`)仍然可以渲染,包括那些 UI 获取后转换为本地 `blob:` URL 的、需要认证的头像路由。
|
||||
- 内联 `data:image/...` URL 仍然可以渲染(这对协议内负载很有用)。
|
||||
- 由 Control UI 创建的本地 `blob:` URL 仍然可以渲染。
|
||||
- 由渠道元数据发出的远程头像 URL 会在 Control UI 的头像辅助逻辑中被剥离,并替换为内置 logo/badge,因此即使某个渠道被入侵或是恶意的,也无法强制操作员浏览器去抓取任意远程图片。
|
||||
- 渠道元数据输出的远程头像 URL 会在 Control UI 的头像辅助函数中被剥离,并替换为内置 logo/badge,因此即使某个渠道已被攻陷或存在恶意行为,也无法强迫 operator 浏览器发起任意远程图片请求。
|
||||
|
||||
你无需做任何更改即可获得此行为——它始终开启且不可配置。
|
||||
你无需做任何更改即可获得此行为——它始终启用,且不可配置。
|
||||
|
||||
## 头像路由身份验证
|
||||
## 头像路由认证
|
||||
|
||||
配置了 gateway 身份验证后,Control UI 头像端点要求使用与其余 API 相同的 gateway token:
|
||||
配置了 gateway 认证后,Control UI 头像端点会要求与其他 API 相同的 gateway token:
|
||||
|
||||
- `GET /avatar/<agentId>` 仅向已通过身份验证的调用方返回头像图片。`GET /avatar/<agentId>?meta=1` 会在相同规则下返回头像元数据。
|
||||
- 对这两个路由的未认证请求都会被拒绝(与相邻的助手媒体路由一致)。这可以防止头像路由在本应受保护的主机上泄露智能体身份。
|
||||
- Control UI 本身在获取头像时会将 gateway token 作为 bearer 标头转发,并使用经过身份验证的 blob URL,因此图片仍然可以在仪表板中渲染。
|
||||
- `GET /avatar/<agentId>` 仅向已认证调用方返回头像图片。`GET /avatar/<agentId>?meta=1` 也会在相同规则下返回头像元数据。
|
||||
- 对这两个路由的未认证请求都会被拒绝(与同级 assistant-media 路由保持一致)。这可以防止头像路由在其他方面受保护的主机上泄露智能体身份。
|
||||
- Control UI 自身在获取头像时会将 gateway token 作为 bearer 标头转发,并使用经过认证的 blob URL,因此图片仍然可以在仪表板中渲染。
|
||||
|
||||
如果你禁用了 gateway 身份验证(不建议在共享主机上这样做),则头像路由也会变为未认证状态,与 gateway 的其余部分保持一致。
|
||||
如果你禁用 gateway 认证(不建议在共享主机上这样做),头像路由也会像 gateway 的其他部分一样变为无需认证。
|
||||
|
||||
## 构建 UI
|
||||
|
||||
Gateway 网关会从 `dist/control-ui` 提供静态文件。使用以下命令构建它们:
|
||||
Gateway 网关从 `dist/control-ui` 提供静态文件。使用以下命令构建:
|
||||
|
||||
```bash
|
||||
pnpm ui:build
|
||||
@ -315,7 +328,7 @@ pnpm ui:build
|
||||
OPENCLAW_CONTROL_UI_BASE_PATH=/openclaw/ pnpm ui:build
|
||||
```
|
||||
|
||||
用于本地开发(独立 dev server):
|
||||
用于本地开发(独立开发服务器):
|
||||
|
||||
```bash
|
||||
pnpm ui:dev
|
||||
@ -323,36 +336,42 @@ pnpm ui:dev
|
||||
|
||||
然后将 UI 指向你的 Gateway WS URL(例如 `ws://127.0.0.1:18789`)。
|
||||
|
||||
## 调试/测试:dev server + 远程 Gateway 网关
|
||||
## 调试/测试:开发服务器 + 远程 Gateway
|
||||
|
||||
Control UI 是静态文件;WebSocket 目标是可配置的,并且可以与 HTTP 源不同。当你希望本地运行 Vite dev server,但 Gateway 网关运行在其他地方时,这会很方便。
|
||||
Control UI 是静态文件;WebSocket 目标可配置,并且可以
|
||||
不同于 HTTP 来源。当你希望在本地使用 Vite 开发服务器,而 Gateway 网关运行在别处时,这非常有用。
|
||||
|
||||
1. 启动 UI dev server:`pnpm ui:dev`
|
||||
2. 打开类似如下的 URL:
|
||||
1. 启动 UI 开发服务器:`pnpm ui:dev`
|
||||
2. 打开类似下面的 URL:
|
||||
|
||||
```text
|
||||
http://localhost:5173/?gatewayUrl=ws://<gateway-host>:18789
|
||||
```
|
||||
|
||||
可选的一次性身份验证(如有需要):
|
||||
可选的一次性认证(如果需要):
|
||||
|
||||
```text
|
||||
http://localhost:5173/?gatewayUrl=wss://<gateway-host>:18789#token=<gateway-token>
|
||||
```
|
||||
|
||||
注意:
|
||||
说明:
|
||||
|
||||
- `gatewayUrl` 会在加载后存储到 `localStorage` 中,并从 URL 中移除。
|
||||
- 应尽可能通过 URL 片段(`#token=...`)传递 `token`。片段不会发送到服务器,这可以避免请求日志和 Referer 泄露。旧版 `?token=` 查询参数仍会为了兼容性导入一次,但仅作为后备方案,并会在引导后立即被移除。
|
||||
- `password` 仅保存在内存中。
|
||||
- 设置了 `gatewayUrl` 后,UI 不会回退到配置或环境变量凭证。
|
||||
- `gatewayUrl` 会在加载后存储到 localStorage 中,并从 URL 中移除。
|
||||
- 尽可能通过 URL 片段(`#token=...`)传递 `token`。片段不会发送到服务器,这样可以避免请求日志和 Referer 泄露。旧版 `?token=` 查询参数仍会出于兼容性被一次性导入,但仅作为回退方案,并会在启动后立即移除。
|
||||
- `password` 只保存在内存中。
|
||||
- 设置了 `gatewayUrl` 后,UI 不会回退到配置或环境凭证。
|
||||
请显式提供 `token`(或 `password`)。缺少显式凭证会报错。
|
||||
- 当 Gateway 网关位于 TLS 后方时(Tailscale Serve、HTTPS 代理等),请使用 `wss://`。
|
||||
- `gatewayUrl` 仅在顶层窗口中被接受(不接受嵌入场景),以防止点击劫持。
|
||||
- 非 loopback 的 Control UI 部署必须显式设置 `gateway.controlUi.allowedOrigins`(完整 origin)。这也包括远程开发设置。
|
||||
- 除非是在严格受控的本地测试中,否则不要使用 `gateway.controlUi.allowedOrigins: ["*"]`。
|
||||
它表示允许任何浏览器 origin,而不是“匹配我当前使用的任意主机”。
|
||||
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true` 会启用 Host 标头 origin 回退模式,但这是一种危险的安全模式。
|
||||
- 当 Gateway 网关位于 TLS 后面时(Tailscale Serve、HTTPS 代理等),请使用 `wss://`。
|
||||
- `gatewayUrl` 仅在顶层窗口中接受(不能嵌入),以防止点击劫持。
|
||||
- 非 loopback 的 Control UI 部署必须显式设置 `gateway.controlUi.allowedOrigins`
|
||||
(完整 origin)。这也包括远程开发设置。
|
||||
- Gateway 网关启动时可能会根据生效的运行时 bind 和端口,自动填充本地 origin,如 `http://localhost:<port>` 和
|
||||
`http://127.0.0.1:<port>`,但远程浏览器 origin 仍然需要显式条目。
|
||||
- 除非是严格受控的本地测试,否则不要使用 `gateway.controlUi.allowedOrigins: ["*"]`。
|
||||
它表示允许任意浏览器 origin,而不是“匹配我正在
|
||||
使用的任意主机”。
|
||||
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true` 会启用
|
||||
Host 标头 origin 回退模式,但这是一种危险的安全模式。
|
||||
|
||||
示例:
|
||||
|
||||
@ -370,7 +389,7 @@ http://localhost:5173/?gatewayUrl=wss://<gateway-host>:18789#token=<gateway-toke
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [仪表板](/zh-CN/web/dashboard) — gateway 仪表板
|
||||
- [Dashboard](/zh-CN/web/dashboard) — gateway 仪表板
|
||||
- [WebChat](/zh-CN/web/webchat) — 基于浏览器的聊天界面
|
||||
- [TUI](/zh-CN/web/tui) — 终端用户界面
|
||||
- [健康检查](/zh-CN/gateway/health) — gateway 健康监控
|
||||
- [Health Checks](/zh-CN/gateway/health) — gateway 健康监控
|
||||
|
||||
Loading…
Reference in New Issue
Block a user