Files
openclaw-config/docs/openclaw-API响应性能分析与优化.md
T
yangxuan 0865090a2b fix(tools): 清除 36 条无法注册的飞书工具声明,未注册告警 20→0
根因(实测两层):
- tools.alsoAllow 声明 37 个 feishu_* 工具,仅 feishu_chat 能注册
- 其余 36 个由 @larksuite/openclaw-lark 提供,而该插件无法加载:
  require("openclaw/plugin-sdk") 在 openclaw 2026.9.4 的 exports 中不存在
  (只有 ./plugin-sdk/core 等子路径);插件版本 2026.6.10 落后一个大版本

关键对照: @openclaw/feishu 2026.9.4 已装且 loaded,提供 14 个飞书工具
(doc/wiki/drive/perm/bitable),能力无损失 —— 无需依赖 lark 插件

处置:
- tools.alsoAllow: 37 条 -> 1 条(feishu_chat)
- plugins.entries.openclaw-lark.enabled 保持 false(曾置 true 验证失败后回退)
- 重启验证: 未注册告警 0、插件加载失败 0、feishu 14 工具全在、飞书渠道正常
- 文档补充"渠道层 vs 工具层"辨析:这也解释了为何手机飞书交互一直正常
2026-09-16 16:36:40 +08:00

396 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# openclaw API 响应性能分析与优化
> **用途**:回答「openclaw API 响应很慢」这个问题——用网关日志实测定位瓶颈,给出可执行的优化清单。
> **配套**`bk02-openclaw-系统说明.md`(接手入口)、`openclaw-升级与维护.md`2026-09-16 升级记录)
> **主机**bk02 / `xuan-asus-nj` · 分析日期 2026-09-16 · 样本 `2026-09-15 ~ 2026-09-16` 两天全量网关日志
> ⚠️ 本目录**不含明文口令**(凭据位置见系统说明 §6)。
---
## 0. 结论速览(先看这段)
| 问题 | 结论 |
|---|---|
| **deepseek-flash API 慢吗?** | **不慢**。两天 383 次真实调用:p50 **302ms**、p90 510ms、均值 353ms。直连实测首字节 73ms |
| **那为什么感觉慢?** | **94.6% 的时间没花在模型上**。419 次调用、69 段交互实测:模型耗时合计 199.8s / 总跨度 3669.1s = **5.4%** |
| **真正的瓶颈** | **串行工具循环**。一次交互会连续调用模型几十次(实测单段最多 58 次),每次调用之间等工具/IO 平均 6 秒 |
| **new-api 网关慢吗?** | **慢 5 倍**`newapi/qwen3.5-plus` p50 **1463ms**、p90 3602ms vs deepseek-flash p50 302ms(已于 2026-09-16 删除) |
| **单次简单问答快吗?** | **快**。只有 2 次调用的短交互,模型占比 13%–26%,端到端 1.82.7 秒 |
**一句话**:慢的不是 API,是 **agent 的工具循环轮次 × 每轮工具耗时**。优化方向是「减少轮次」和「降低单轮工具等待」,不是换模型。
---
## 1. 实测数据
### 1.1 模型调用延迟分布(2026-09-15 ~ 09-16,全量)
| provider / model | 调用数 | p50 | p90 | max | 均值 | status |
|---|---|---|---|---|---|---|
| `deepseek/deepseek-flash` | 383 | **302ms** | 510ms | 3930ms | 353ms | 200 |
| `newapi/qwen3.5-plus` | 36 | **1463ms** | 3602ms | 6130ms | 2114ms | 200 |
### 1.2 直连 DeepSeek 官方 API 的网络实测
| 场景 | 结果 |
|---|---|
| `GET /v1/models` | `dns=2.3ms conn=10.9ms tls=69ms ttfb=137ms` |
| 极简请求(20 token 输出) | `ttfb=69ms total=881ms` |
| 流式输出 2000 token | `ttfb=73ms total=2.0s` |
| **长输入 234KB(约 3.2 万字符)** | `ttfb=88ms total=1.55s` |
→ 网络链路与 API 侧均无问题,**长上下文也不构成延迟**。
### 1.3 时间预算(核心证据)
按「响应间隔 > 120s」把 419 次调用切成 69 段交互,统计每段的模型耗时与总跨度:
| 交互开始 | 调用次数 | 模型耗时 | 总跨度 | **模型占比** |
|---|---|---|---|---|
| 16:02:57 | 32 | 19.4s | 450.0s | **4.3%** |
| 13:23:22 | 58 | 16.7s | 338.1s | **5.0%** |
| 12:31:47 | 50 | 18.0s | 695.0s | **2.6%** |
| 13:01:08 | 42 | 14.5s | 475.5s | **3.1%** |
| 00:12:54 | 33 | 16.0s | 302.3s | **5.3%** |
| 12:00:03 | 12 | 23.2s | 147.9s | 15.7%(走 new-api |
| 11:12:08 | 8 | 15.6s | 57.3s | 27.3%(走 new-api |
| 13:32:53 | 2 | 0.5s | 1.8s | **26.0%** |
| 15:02:53 | 2 | 0.5s | 2.3s | **19.9%** |
```
★ 合计:模型 199.8s / 跨度 3669.1s = 5.4% 非模型时间 94.6%
```
**规律非常清楚**
- **轮次少的交互(2 次调用)→ 模型占比 20%–26%,端到端 2 秒左右,体验流畅**;
- **轮次多的交互(32–58 次调用)→ 模型占比跌到 2.6%–5.4%,端到端 511 分钟**。
也就是说,用户感知的「慢」几乎全部落在模型调用**之间**的等待里。
### 1.4 单次交互的详细时间线(16:02:57 那段,共 450 秒)
```
+ 0.00s START deepseek-flash
+ 0.23s RESP 200 232ms ← 模型极快
+ 72.47s START deepseek-flash ← 中间 72 秒在装技能(ClawHub 安全审计+安装)
+ 76.40s RESP 200 3930ms
+ 77.76s START deepseek-flash
+ 78.06s RESP 200 305ms
+ 82.94s START ... ← 间隔 4.9s(工具执行)
+ 89.24s START ... ← 间隔 6.1s
+ 96.25s START ... ← 间隔 6.6s
+104.36s START ... ← 间隔 8.6s
+130.29s START ... ← 间隔 25.5s
+182.33s START ... ← 间隔 37.5s
+276.53s START ... ← 间隔 50.5s
+385.69s START ... ← 间隔 39.3s
+403.61s RESP 200 422ms ← 最后一次
```
该段共 32 次模型调用,模型自身合计 19.4 秒,其余 430 秒是工具执行、技能安装、上下文装配与网关等待。
### 1.5 调用量背景(为什么"慢"会被放大)
- 09-16 单日 140 次调用中,**12 次集中在同一分钟**(如 09:35 的 12 次、12:01 的 12 次)——即一次用户提问可触发十余次模型往返。
- 每分钟调用次数分布:1 次 = 34 分钟,3–5 次 = 10 分钟,**712 次 = 7 分钟**。
---
## 2. 瓶颈定位
### 2.1 主因:串行工具循环(占 94.6%)
openclaw 的 agent 是 ReAct 式循环:**模型判断 → 调工具 → 结果回灌 → 再问模型**,全程串行。实测典型循环步骤:
```
模型(0.3s) → 工具(3-30s) → 模型(0.4s) → 工具(5-40s) → ... → 最终回答
```
单轮工具的耗时来源(按实测出现频率排序):
| 来源 | 实测耗时 | 说明 |
|---|---|---|
| ClawHub 技能审计+安装 | 13.5s / 14s | 含安全审计、下载、安装、技能优先级冲突解析 |
| 文件读写 / 记忆检索 | 数秒 | `memory_search` 一次 `toolMs=639ms`,但多轮叠加 |
| 命令执行(exec) | 数秒至数十秒 | 如 `docker exec mysql ...`、脚本执行 |
| 外网请求 | 1040s | 搜索、抓取(searxng、邮件) |
| 上下文装配 | 单次约 150ms | `context_assembled``model_call_started` 差值 |
> 注意:**「94.6%」是两天混合负载的口径**,其中含技能安装、定时任务等批处理。日常纯问答型交互模型占比约 20%–26%(见 §1.3 最后两行)。**结论方向不变:瓶颈在模型之外。**
### 2.2 次因:new-api 网关慢 5 倍(已消除)
`newapi/qwen3.5-plus``100.115.195.188:3000`p50 1463ms,是 deepseek-flash 的 4.8 倍;两段走 new-api 的交互模型占比虽高(15.7%、27.3%),但**绝对耗时明显更差**12 次调用耗 23.2s,而 deepseek 32 次才 19.4s)。该 provider 已于 2026-09-16 全部移除。
### 2.3 已记录的异常事件
| 事件 | 次数 | 详情 | 影响 |
|---|---|---|---|
| `empty-error-retry` | 3 | `agent:juaner` 的 skill-workshop 评价任务报 `Cannot read properties of undefined (reading 'trim')`,重试 attempt 1/3→3/3 | 每次失败后重发请求,**额外增加延迟**;且最终仍失败 |
| DeepSeek 服务端 503 | 2 | 03:00:16 `elapsedMs=185`、03:01:14 `elapsedMs=77`(均为 03:00 定时任务期间) | 上游短暂不可用,openclaw 自行恢复;**当前无 failover 配置**`modelPolicy.allow` 仅 1 项) |
| `liveness heartbeat delayed` | 3 | `overdue≈1.01.5s elapsed≈31s` | 网关事件循环被阻塞约 31 秒,期间必须推迟恢复决策 |
### 2.4 配置层待确认项
| 项 | 当前值 | 问题 |
|---|---|---|
| `models.providers.deepseek.models[0].contextWindow` | `1000000` | 声明 1M 上下文,**未与 DeepSeek 官方实际能力核对**;配大了会让 openclaw 少触发压缩,长会话越跑越慢 |
| 同 `maxTokens` | `384000` | 同上,疑似超出上游上限(未验证) |
| 同 `reasoning` | `false` | 实测 `deepseek-flash` **会返回 `reasoning_content`**(是推理模型),此处语义与实际不符 |
| 模型请求 `timeoutMs` | `undefined` | 日志中 `timeoutMs=undefined`,**模型 HTTP 请求无显式超时**;挂住时只能靠 `agents.defaults.timeoutSeconds=3600`1 小时)兜底 |
| `agents.defaults.maxConcurrent` | `2` | 单 agent 并发上限 211 个 agent 共用同一 gateway 进程) |
| `tools.profile` | `full` | 全量工具集,工具越多模型越容易多轮试探 |
| CLI 运行时 | Node **22.23.1**(系统默认) | 非交互 shell 的 PATH 不含 nvm:轻则 `openclaw: command not found`,重则因 Node 22 触发 `node:sqlite` 准入拒跑。详见 §3 的 P0-1(含包装脚本已落地方案) |
---
## 3. 优化建议
按「收益/成本」排序。**P0 = 立刻做;P1 = 值得做;P2 = 观察后再定。**
### P0-1 修 CLI 的 Node 版本(成本最低,先消除工具链故障)
**现象**`openclaw models list`
`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
```
$ 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 '~/.nvm/versions/node/v26.8.2/bin/node -v' → v26.8.2
```
> 与 `.nvmrc` **无关**(已验证 `~/.openclaw/.nvmrc`、`~/deepseek-harness/.nvmrc` 均不存在)。已确认 `nvm alias default` 本就是 `26`——**问题只在 PATH 未加载 nvm**。
#### 三种场景的行为差异(实测,别再混为一谈)
| 场景 | 命令 | 实测结果 |
|---|---|---|
| **① 非交互 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
mkdir -p ~/.local/bin
cp ~/.openclaw/scripts/bin/openclaw ~/.local/bin/openclaw # 仓库内已留存副本
chmod +x ~/.local/bin/openclaw
openclaw models list --provider deepseek # 期望正常列出
```
包装脚本内容(固定绝对路径,绕开 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%,收益最大)
轮次是延迟的乘数:**每减少一轮,省下「一次模型往返 + 一次工具等待」(实测中位 6 秒)**。
| 手段 | 动作 | 预期 |
|---|---|---|
| 收敛工具集 | 让高频 agent 少暴露「用不上」的工具(当前 `tools.profile=full`;本机 `tools.allow` 未设置、`tools.alsoAllow` 仅剩 `feishu_chat`——**36 条无效声明已于 16:35 清除**,见下方说明) | 模型少做无效试探,轮次下降(⚠️ 需实测,见 §5) |
| 提升单轮信息密度 | 在 agent 的 `AGENTS.md` 中要求「一次调用批量取证」:合并多次 `read`/`memory_search` 为一次 | 显著减少往返 |
| 并行取证 | 把可并行的只读操作交给 `subagents`(当前 `subagents.maxConcurrent=4` | 串行改并行 |
| 技能不必现装现用 | 需要什么技能**提前装好**,避免在对话中触发 ClawHub 审计+安装(实测单次 13.5–14s) | 消除最大单点等待 |
> ⚠️ **曾有 36 个「声明了却不存在」的工具 → 已于 2026-09-16 16:35 修复,两条告警均清零**(详见下方"处置结果")。
**问题的样子**`tools.profile (full) allowlist contains unknown entries``tools.allow allowlist contains unknown entries` 各 10 次(当天累计 20 次,最后一次 16:27:34),报的全是 `feishu_bitable_*` / `feishu_calendar_*` / `feishu_task_*` 等。
#### 根因(实测,两层)
**第一层**`tools.alsoAllow` 声明了 **37 个** `feishu_*` 工具,但只有 **1 个**`feishu_chat`,由 `@openclaw/feishu` 提供)能注册——其余 **36 个属于另一个插件** `@larksuite/openclaw-lark`
**第二层(真因)**`openclaw-lark` **根本加载不了**——它 `require("openclaw/plugin-sdk")`,而 openclaw 2026.9.4 的 `package.json#exports` 里**只有 `./plugin-sdk/core``./plugin-sdk/setup` 等子路径,没有裸的 `./plugin-sdk`**
```
$ openclaw-lark failed to load from ~/.openclaw/extensions/openclaw-lark/index.js:
Error: Cannot find module 'openclaw/plugin-sdk'
$ 插件版本: 2026.6.10(升级前的旧版;npm 上最新仅 2026.7.16
```
**这不是配置问题,是版本不兼容**——该插件用了已不存在的模块路径,且其最新版仍落后主流 `openclaw` 一个大版本。
#### 关键对照:你要的能力其实已经有了
`@openclaw/feishu` 已经装好并在跑(`status: loaded``trusted-official`),它提供 **14 个**飞书工具(`tools.profile=full` 下全部可用):
```
feishu_doc feishu_wiki feishu_drive feishu_perm feishu_chat feishu_app_scopes
feishu_bitable_get_meta feishu_bitable_list_fields feishu_bitable_list_records
feishu_bitable_get_record feishu_bitable_create_record feishu_bitable_update_record
feishu_bitable_create_app feishu_bitable_create_field
```
| | `@larksuite/openclaw-lark`(坏) | `@openclaw/feishu`(在用) |
|---|---|---|
| 版本 | 2026.6.10(最新 2026.7.16 | **2026.9.4**`peerDeps: openclaw>=2026.9.4` |
| 加载 | **失败**`openclaw/plugin-sdk` 不存在) | 正常 |
| 工具 | 38 个细粒度(calendar / task / im / oauth / sheet…) | **14 个**doc / wiki / drive / perm / bitable),用 `_create`/`_list` 动词对内部再做复合操作 |
| 适配 | 落后一个大版本 | 与 openclaw 同步 |
#### 处置结果(2026-09-16 16:35 已完成)
| 动作 | 结果 |
|---|---|
| 从 `tools.alsoAllow` 删除 36 条 lark 专属声明(保留 `feishu_chat` | 未注册告警 **20 次/天 → 0** |
| `plugins.entries.openclaw-lark.enabled` 保持 **false** | 消除 `failed to load` 报错(曾尝试置 true 验证:确认失败后回退) |
| 复查 `@openclaw/feishu` | `status: loaded`、**14 个工具全在**,能力无损失 |
| 复查飞书渠道 | WebSocket 正常连接,**手机 App 交互不受影响** |
> **两个独立层次,别混淆**(这是本次排查最值得记住的一点):
> - **渠道层**:飞书 App ⇄ gateway 的消息收发,由 `@openclaw/feishu` 的 channel 部分负责 → **一直正常**,所以手机交互没问题
> - **工具层**:模型在对话里"动手操作飞书"的能力,由插件的 `contracts.tools` 负责 → 那 36 条声明在这里无效
>
> `alsoAllow` 坏掉**不会**影响聊天交互,只会让"让 AI 去操作飞书文档/多维表格"这类动作落空。
> 复核命令(改完这两个都应为 0/不为 0 都应引起注意):
> ```bash
> grep -c "allowlist contains unknown entries" /tmp/openclaw/openclaw-$(date +%F).log
> grep -c "failed to load" /tmp/openclaw/openclaw-$(date +%F).log
> grep "http server listening" /tmp/openclaw/openclaw-$(date +%F).log | tail -1 # 看实际加载了哪些插件
> ```
### P1-3 补上模型请求超时与 failover
- **显式超时**:当前 `timeoutMs=undefined`。建议为 provider 或模型显式设置请求超时(例如 120–300s),避免单次请求无限挂起拖满 `timeoutSeconds=3600`
- **failover**`modelPolicy.allow` 现仅 `deepseek/deepseek-flash` 一项,上游 503 时无备选。实测已出现 2 次 503。可考虑把已保留的 `siliconflow`(内容不冲突)登记为降级候选,或至少确认失败时的用户可见行为。
### P1-4 核对 deepseek-flash 的上下文与推理声明
按官方 `/v1/models` 与文档核对 `contextWindow` / `maxTokens`,把 `1000000` / `384000` 改成真实值;并把 `reasoning: false` 与实测「会返回 reasoning_content」的语义对齐(两者不一致时,reasoning token 的计费与展示都可能不符合预期)。
```bash
# 核对官方模型清单
curl -s https://api.deepseek.com/v1/models -H "Authorization: Bearer $DEEPSEEK_API_KEY"
```
### P1-5 处理 `empty-error-retry` 的 trim 崩溃
`agent:juaner` 的 skill-workshop 评价任务连续 3 次撞 `Cannot read properties of undefined (reading 'trim')`。这是 **openclaw 侧错误处理缺陷**(非模型问题):失败后重发 3 次,既慢又无效。建议升级 openclaw 后复测;若仍复现,向官方报 issue(附 `runId=skill-workshop-review:70d7e357-...`)。
### P2-6 降低定时任务对交互的干扰
`liveness heartbeat delayed`(事件循环阻塞 31 秒)+ 03:00 的 503,都出现在**定时任务窗口**。若仍有交互卡顿,检查 `cron.triggers` 的具体任务,避免定时任务与用户交互抢占同一 gateway(`maxConcurrent=2`)。
---
## 4. 验收与监控(改完怎么确认有效)
**核心指标:模型的「时间占比」而非「绝对延迟」。** 目标是把它从 5.4% 抬升上去(说明等待被消除),而不是继续压低已经很快的 302ms。
```bash
# 1) 复跑本次分析(脚本:~/.openclaw/scripts/perf-analyze.py,见 §7
python3 ~/.openclaw/scripts/perf-analyze.py
# 2) 看某次调用的实际延迟
grep "model-fetch] response" /tmp/openclaw/openclaw-$(date +%F).log | tail -5
# 3) 健康检查
openclaw doctor
```
**验收标准(建议)**
| 指标 | 现状 | 目标 |
|---|---|---|
| 纯问答型交互端到端 | 1.8–2.7s | ≤ 3s(已达标,保持) |
| 多轮任务型交互端到端 | 450s / 32 次调用 | **调用次数下降 ≥ 30%** 或端到端下降 ≥ 30% |
| 模型时间占比 | 5.4% | 上升(等待被消除的直接体现) |
| `empty-error-retry` | 3 次/2 天 | 0 |
| 技能现装现用 | 13.5–14s/次 | 0(预先安装) |
---
## 5. 未验证项(诚实声明)
本文所有数字都来自 **网关日志与直连 curl 实测**,但以下为**推断,尚未实测**,落地前请按标注方法验证:
| 未验证项 | 为何未验证 | 怎么验证 |
|---|---|---|
| 收窄 `tools.profile` 能提升速度 | 会改变 agent 能力,属行为变更,未擅自改 | 复制一个 agent 做 A/B,对比同一提示词的调用次数 |
| `contextWindow=1000000` 是错的 | 未拿到 DeepSeek 官方对该模型上下文的权威说明 | 官方文档/控制台核对,或用超长输入试探边界 |
| 工具耗时的精确归因 | 网关日志**未记录工具级耗时**(仅有工具失败记录),§2.1 的外部耗时来自会话内实际动作与时间线对齐 | 开启更详细日志级别后重测 |
| `maxConcurrent=2` 是否构成瓶颈 | 无并发排队记录 | 压测:并发发起 3 个会话观察排队 |
---
## 6. 附录:本次分析用到的命令
```bash
# 定位网关日志(注意:不在 ~/.openclaw/logs,而在 /tmp
ls -la /tmp/openclaw/openclaw-$(date +%F).log
# 统计模型调用延迟
grep "model-fetch] response" /tmp/openclaw/openclaw-2026-09-16.log \
| grep -oE "model=[^ ]+|elapsedMs=[0-9]+|status=[0-9]+"
# 找慢调用
grep "model-fetch] response" /tmp/openclaw/openclaw-2026-09-16.log \
| sed -E 's/.*elapsedMs=([0-9]+).*/\1 &/' | sort -rn | head -20
# 异常事件
grep -c "empty-error-retry" /tmp/openclaw/openclaw-2026-09-16.log
grep -oE "liveness heartbeat delayed[^\"]{0,60}" /tmp/openclaw/openclaw-2026-09-16.log
grep -oE "status=50[0-9][^\"]{0,60}" /tmp/openclaw/openclaw-2026-09-16.log
```
---
## 7. 配套脚本
`~/.openclaw/scripts/perf-analyze.py` —— 复现本文 §1.3 的时间预算分析:
```bash
python3 ~/.openclaw/scripts/perf-analyze.py [日志1 日志2 ...]
# 缺省分析 /tmp/openclaw/openclaw-<前一天>.log 与 <当天>.log
```
---
## 8. 附:2026-09-16 的配置变更(本文分析期间的改动)
| 变更 | 内容 | 生效方式 |
|---|---|---|
| 主配置 | 删除 `models.providers.newapi``modelPolicy.allow` 收敛为 `deepseek/deepseek-flash``sql` agent 主模型改为 deepseek | 配置热重载(日志:`config hot reload applied` |
| agent 级 `models.json` | `finances`/`main`/`resume`/`sql`/`travel` 各自残留的 new-api provider(含指向 `192.168.2.74:3000``100.115.195.188:3000` 的条目,含**明文 key**)全部移除 | 下次 gateway 启动生效 |
| 工具声明清理 | `tools.alsoAllow` 由 37 条减为 1 条(删除 36 条由 `openclaw-lark` 提供、实际无法注册的声明) | 重启 gateway16:35),告警 20→0 |
| 插件开关 | `plugins.entries.openclaw-lark.enabled` 保持 **false**(曾置 true 实测其 `require("openclaw/plugin-sdk")` 失败,遂回退) | 重启 gateway |
| 备份 | `openclaw.json.bak-20260916-161323-pre-newapi-removal``...-163214-pre-lark-enable``...-163405-pre-alsoallow-fix``backups/agent-models-json-2026-09-16T0816/`(均在 `~/.openclaw/` 下) | — |
> `agent` 级 `models.json` 是**第二套模型定义**(被 `loadCustomModels()` 读取),与主配置 `openclaw.json` 并行生效——排查模型问题时**两处都要看**,这是本次分析的第一个教训。