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

22 KiB
Raw Blame History

openclaw API 响应性能分析与优化

用途:回答「openclaw API 响应很慢」这个问题——用网关日志实测定位瓶颈,给出可执行的优化清单。 配套bk02-openclaw-系统说明.md(接手入口)、openclaw-升级与维护.md2026-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%,端到端 5–11 分钟

也就是说,用户感知的「慢」几乎全部落在模型调用之间的等待里。

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_assembledmodel_call_started 差值

注意:「94.6%」是两天混合负载的口径,其中含技能安装、定时任务等批处理。日常纯问答型交互模型占比约 20%–26%(见 §1.3 最后两行)。结论方向不变:瓶颈在模型之外。

2.2 次因:new-api 网关慢 5 倍(已消除)

newapi/qwen3.5-plus100.115.195.188:3000p50 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=36001 小时)兜底
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 listNode 22.23.1: node:sqlite truncates TEXT at embedded NUL (nodejs/node#61954); use 24.16+/26.1+

根因(2026-09-16 核实):系统级 /usr/bin/nodev22.23.1nvm 的 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

mkdir -p ~/.local/bin
cp ~/.openclaw/scripts/bin/openclaw ~/.local/bin/openclaw   # 仓库内已留存副本
chmod +x ~/.local/bin/openclaw
openclaw models list --provider deepseek                    # 期望正常列出

包装脚本内容(固定绝对路径,绕开 PATH 解析):

#!/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 或用绝对路径:

# 方式一:先加载 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.serviceExecStart 写的就是 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.514s) 消除最大单点等待

⚠️ 曾有 36 个「声明了却不存在」的工具 → 已于 2026-09-16 16:35 修复,两条告警均清零(详见下方"处置结果")。

问题的样子tools.profile (full) allowlist contains unknown entriestools.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: loadedtrusted-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.4peerDeps: 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: loaded14 个工具全在,能力无损失
复查飞书渠道 WebSocket 正常连接,手机 App 交互不受影响

两个独立层次,别混淆(这是本次排查最值得记住的一点):

  • 渠道层:飞书 App ⇄ gateway 的消息收发,由 @openclaw/feishu 的 channel 部分负责 → 一直正常,所以手机交互没问题
  • 工具层:模型在对话里"动手操作飞书"的能力,由插件的 contracts.tools 负责 → 那 36 条声明在这里无效

alsoAllow 坏掉不会影响聊天交互,只会让"让 AI 去操作飞书文档/多维表格"这类动作落空。

复核命令(改完这两个都应为 0/不为 0 都应引起注意):

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
  • failovermodelPolicy.allow 现仅 deepseek/deepseek-flash 一项,上游 503 时无备选。实测已出现 2 次 503。可考虑把已保留的 siliconflow(内容不冲突)登记为降级候选,或至少确认失败时的用户可见行为。

P1-4 核对 deepseek-flash 的上下文与推理声明

按官方 /v1/models 与文档核对 contextWindow / maxTokens,把 1000000 / 384000 改成真实值;并把 reasoning: false 与实测「会返回 reasoning_content」的语义对齐(两者不一致时,reasoning token 的计费与展示都可能不符合预期)。

# 核对官方模型清单
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。

# 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.82.7s ≤ 3s(已达标,保持)
多轮任务型交互端到端 450s / 32 次调用 调用次数下降 ≥ 30% 或端到端下降 ≥ 30%
模型时间占比 5.4% 上升(等待被消除的直接体现)
empty-error-retry 3 次/2 天 0
技能现装现用 13.514s/次 0(预先安装)

5. 未验证项(诚实声明)

本文所有数字都来自 网关日志与直连 curl 实测,但以下为推断,尚未实测,落地前请按标注方法验证:

未验证项 为何未验证 怎么验证
收窄 tools.profile 能提升速度 会改变 agent 能力,属行为变更,未擅自改 复制一个 agent 做 A/B,对比同一提示词的调用次数
contextWindow=1000000 是错的 未拿到 DeepSeek 官方对该模型上下文的权威说明 官方文档/控制台核对,或用超长输入试探边界
工具耗时的精确归因 网关日志未记录工具级耗时(仅有工具失败记录),§2.1 的外部耗时来自会话内实际动作与时间线对齐 开启更详细日志级别后重测
maxConcurrent=2 是否构成瓶颈 无并发排队记录 压测:并发发起 3 个会话观察排队

6. 附录:本次分析用到的命令

# 定位网关日志(注意:不在 ~/.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 的时间预算分析:

python3 ~/.openclaw/scripts/perf-analyze.py [日志1 日志2 ...]
# 缺省分析 /tmp/openclaw/openclaw-<前一天>.log 与 <当天>.log

8. 附:2026-09-16 的配置变更(本文分析期间的改动)

变更 内容 生效方式
主配置 删除 models.providers.newapimodelPolicy.allow 收敛为 deepseek/deepseek-flashsql agent 主模型改为 deepseek 配置热重载(日志:config hot reload applied
agent 级 models.json finances/main/resume/sql/travel 各自残留的 new-api provider(含指向 192.168.2.74:3000100.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-fixbackups/agent-models-json-2026-09-16T0816/(均在 ~/.openclaw/ 下)

agentmodels.json第二套模型定义(被 loadCustomModels() 读取),与主配置 openclaw.json 并行生效——排查模型问题时两处都要看,这是本次分析的第一个教训。