docs(perf): P0-1 补充三种场景实测差异与包装脚本落地方案

经实测澄清(此前表述不够精确):
- 场景① 非交互 ssh 裸调 → openclaw: command not found(nvm 未加载则 openclaw 不在 PATH)
- 场景② PATH 有 openclaw 但 Node 是 22 → 报 node:sqlite 准入错误并拒跑
- 场景③ 交互式 shell(PATH 含 v26)→ 正常
- 关键机制: openclaw 入口自带运行时准入+自动重试,PATH 里存在合格 Node
  (24.16+/26.1+) 时会自动改用其重跑,仅在 PATH 无合格 Node 时才拒跑
- 补充: 交互式用 ~/.local/bin/openclaw 包装脚本;非交互场景不加载 ~/.profile
  故包装脚本不生效,须显式 source nvm.sh 或用绝对路径
This commit is contained in:
yangxuan
2026-09-16 16:29:49 +08:00
parent ae7ca0c005
commit cb9c8f24e8
@@ -141,7 +141,7 @@ openclaw 的 agent 是 ReAct 式循环:**模型判断 → 调工具 → 结果
| 模型请求 `timeoutMs` | `undefined` | 日志中 `timeoutMs=undefined`,**模型 HTTP 请求无显式超时**;挂住时只能靠 `agents.defaults.timeoutSeconds=3600`1 小时)兜底 | | 模型请求 `timeoutMs` | `undefined` | 日志中 `timeoutMs=undefined`,**模型 HTTP 请求无显式超时**;挂住时只能靠 `agents.defaults.timeoutSeconds=3600`1 小时)兜底 |
| `agents.defaults.maxConcurrent` | `2` | 单 agent 并发上限 211 个 agent 共用同一 gateway 进程) | | `agents.defaults.maxConcurrent` | `2` | 单 agent 并发上限 211 个 agent 共用同一 gateway 进程) |
| `tools.profile` | `full` | 全量工具集,工具越多模型越容易多轮试探 | | `tools.profile` | `full` | 全量工具集,工具越多模型越容易多轮试探 |
| CLI 运行时 | Node **22.23.1** | 非交互 shell 的 PATH 不含 nvm`node` 落到系统 `/usr/bin/node`(22),导致 `openclaw models list` **直接报错拒跑**(详见 §4 建议 P0-1 | | CLI 运行时 | Node **22.23.1**(系统默认) | 非交互 shell 的 PATH 不含 nvm:轻则 `openclaw: command not found`,重则因 Node 22 触发 `node:sqlite` 准入拒跑。详见 §3 的 P0-1(含包装脚本已落地方案 |
--- ---
@@ -153,27 +153,69 @@ openclaw 的 agent 是 ReAct 式循环:**模型判断 → 调工具 → 结果
**现象**`openclaw models list` **现象**`openclaw models list`
`Node 22.23.1: node:sqlite truncates TEXT at embedded NUL (nodejs/node#61954); use 24.16+/26.1+` `Node 22.23.1: node:sqlite truncates TEXT at embedded NUL (nodejs/node#61954); use 24.16+/26.1+`
**根因(2026-09-16 核实)**:系统级 `/usr/bin/node`**v22.23.1**nvm 的 26.8.2 只在 `~/.nvm/versions/node/v26.8.2/bin/`。**非交互式 SSH(或任何未加载 nvm 的 shellPATH 不含 nvm 目录**,于是 `node` 落到 `/usr/bin/node` = 22 **根因(2026-09-16 核实)**:系统级 `/usr/bin/node`**v22.23.1**nvm 的 26.8.2 只在 `~/.nvm/versions/node/v26.8.2/bin/`。**非交互式 SSH(或任何未加载 nvm 的 shellPATH 不含 nvm 目录**,于是 `node` 落到 `/usr/bin/node` = 22
``` ```
$ ssh bk02 'echo $PATH' → /home/yangxuan/.cargo/bin:/usr/local/bin:/usr/bin:/bin:... $ ssh bk02 'echo $PATH' → /home/yangxuan/.cargo/bin:/usr/local/bin:/usr/bin:/bin:... (0 处含 .nvm)
$ ssh bk02 'node -v' → v22.23.1 ← 落到系统 node $ ssh bk02 'node -v' → v22.23.1 ← 落到系统 node
$ ssh bk02 '~/.nvm/versions/node/v26.8.2/bin/node -v' → v26.8.2 $ ssh bk02 '~/.nvm/versions/node/v26.8.2/bin/node -v' → v26.8.2
``` ```
> 与 `.nvmrc` **无关**(已验证 `~/.openclaw/.nvmrc`、`~/deepseek-harness/.nvmrc` 均不存在)。已确认 `nvm alias default` 本就是 `26`——**问题只在 PATH 未加载 nvm**。 > 与 `.nvmrc` **无关**(已验证 `~/.openclaw/.nvmrc`、`~/deepseek-harness/.nvmrc` 均不存在)。已确认 `nvm alias default` 本就是 `26`——**问题只在 PATH 未加载 nvm**。
**动作**:凡脚本 / 非交互调用 openclaw,都必须先加载 nvm 或用绝对路径,不要裸调 `node` #### 三种场景的行为差异(实测,别再混为一谈)
| 场景 | 命令 | 实测结果 |
|---|---|---|
| **① 非交互 ssh 裸调** | `ssh bk02 'openclaw models list'` | **`openclaw: command not found`** —— nvm 未加载,连 openclaw 都不在 PATH(路径是 `~/.nvm/versions/node/v26.8.2/bin/openclaw` |
| **② PATH 里有 openclaw 但 Node 是 22** | `env -i PATH=/usr/bin:/bin openclaw models list` | **报上面那条 `Node 22.23.1: node:sqlite ...` 并拒跑** |
| **③ 交互式 shell**(已加载 nvmPATH 含 v26 目录) | `openclaw models list` | **正常输出** |
**关键机制**openclaw 入口自带**运行时准入 + 自动重试**。当 `#!/usr/bin/env node` 拿到不合格的 Node,而 **PATH 里还存在合格版本(24.16+/26.1+)时,它会自动改用那个 node 重跑**
```
$ openclaw: Retrying with "/home/yangxuan/.nvm/versions/node/v26.8.2/bin/node"
(PATH; current Node failed runtime admission) ← 只是警告,不失败
$ openclaw: Node 22.23.1: node:sqlite ... use 24.16+/26.1+ ← PATH 无合格 Node 时才拒跑
```
所以**判定标准是「PATH 里有没有合格的 Node」,不是「默认 node 是几」**。
#### 动作
**交互式使用**(日常最常用)——建一个包装脚本,一劳永逸,不必每次记得 `nvm use`
```bash ```bash
export NVM_DIR=$HOME/.nvm; . $NVM_DIR/nvm.sh # 交互式登录先做(系统说明 §0 已要求) mkdir -p ~/.local/bin
node -v # 期望 v26.8.2 cp ~/.openclaw/scripts/bin/openclaw ~/.local/bin/openclaw # 仓库内已留存副本
openclaw models list --provider deepseek # 期望能正常列出 chmod +x ~/.local/bin/openclaw
openclaw models list --provider deepseek # 期望正常列出
# 非交互/脚本场景:直接给绝对路径,绕开 PATH
~/.nvm/versions/node/v26.8.2/bin/node --version
``` ```
包装脚本内容(固定绝对路径,绕开 PATH 解析):
```sh
#!/bin/sh
exec "/home/yangxuan/.nvm/versions/node/v26.8.2/bin/node" \
"/home/yangxuan/.nvm/versions/node/v26.8.2/lib/node_modules/openclaw/openclaw.mjs" "$@"
```
**非交互 / 脚本 / cron / systemd**——这些场景**不加载 `~/.profile`,所以 `~/.local/bin` 也不在 PATH**,包装脚本不生效,必须显式加载 nvm 或用绝对路径:
```bash
# 方式一:先加载 nvm(推荐,之后 openclaw/node 都正确)
export NVM_DIR=$HOME/.nvm; . $NVM_DIR/nvm.sh
node -v # 期望 v26.8.2
openclaw models list --provider deepseek
# 方式二:只用绝对路径(不依赖 shell 初始化)
~/.nvm/versions/node/v26.8.2/bin/node --version
~/.nvm/versions/node/v26.8.2/bin/openclaw models list
```
> systemd 服务不受影响:`openclaw-gateway.service` 的 `ExecStart` 写的就是 nvm 26 的绝对路径。
### P0-2 减少工具循环轮次(针对 94.6%,收益最大) ### P0-2 减少工具循环轮次(针对 94.6%,收益最大)
轮次是延迟的乘数:**每减少一轮,省下「一次模型往返 + 一次工具等待」(实测中位 6 秒)**。 轮次是延迟的乘数:**每减少一轮,省下「一次模型往返 + 一次工具等待」(实测中位 6 秒)**。