书接上文

更新到 2026.4.8 / 2026.4.9 这一批版本之后,发现缓存又不触发了。跟上次 3 月那波(#49877)原因类似,但代码结构变了,之前那个改 shouldStripResponsesPromptCache(model) 单函数的补丁已经不管用了,这里写一下新版的改法。

修复前:

  • 发往代理的请求里,prompt_cache_key 被 OpenClaw 剥掉
  • 上游虽然支持缓存,但打不中
  • cache_tokens 长时间是 0

修复后:

  • 白名单代理保留 prompt_cache_key
  • OpenAI-compatible proxy 正常往上游透传缓存键
  • 缓存正常命中

适用场景:

  • 你用的是 openai-responses
  • 链路是 OpenClaw -> new-api / CPA / 自建 proxy / 其他 OpenAI-compatible proxy -> 上游 OpenAI / Codex
  • 升级之后 prompt cache 基本不触发
  • 你想做的是只对白名单代理保留 prompt_cache_key,不是全局关闭兼容逻辑

适用版本:OpenClaw 2026.4.82026.4.9 以及附近这一批。这批版本的特征是 dist 里有 provider-attribution-*.jsopenai-responses-payload-policy-*.js,跟之前不一样。


一、根因

这批新版对 openai-responses 做了更细的请求策略判断。

只要 OpenClaw 认定你走的不是官方 OpenAI 直连、不是 Azure OpenAI 直连,而是代理 / proxy-like endpoint,就会把 prompt_cache_keyprompt_cache_retention 从请求里删掉。

这个逻辑本身是为了避免某些第三方兼容接口不认识这两个字段报错。但如果你的代理实际上是转发到真实 OpenAI / Codex 的,比如本地 new-apiCPA、自建 proxy,缓存就被一起干掉了。


二、具体改哪个文件

1) 找 dist 目录

全局安装的话,通常在:

  • /usr/lib/node_modules/openclaw/dist/
  • $(npm root -g)/openclaw/dist/
if [ -d /usr/lib/node_modules/openclaw/dist ]; then
  DIST=/usr/lib/node_modules/openclaw/dist
else
  DIST="$(npm root -g)/openclaw/dist"
fi

printf 'DIST=%s\n' "$DIST"

2) 不要再只搜旧函数名

这批版本的逻辑拆成了两部分:

  • provider-attribution-*.js — 算出 shouldStripResponsesPromptCache
  • openai-responses-payload-policy-*.js — 执行 delete payloadObj.prompt_cache_key

搜索方式:

grep -RIl 'shouldStripResponsesPromptCache:' "$DIST"
grep -RIl 'delete payloadObj.prompt_cache_key' "$DIST"

或者直接:

grep -RIl 'prompt_cache_key' "$DIST"

3) 要改的是哪个

provider-attribution-*.js,就是判定逻辑所在的那个。另一个 openai-responses-payload-policy-*.js 是执行删除的,只用来确认链路,不建议直接改。

你那边 bundle 文件名和我的大概率不一样,以 grep 结果为准,不要改 .bak 备份文件。


三、具体怎么改

当前的核心判断

shouldStripResponsesPromptCache:
  api !== void 0 &&
  OPENAI_RESPONSES_APIS.has(api) &&
  policy.usesExplicitProxyLikeEndpoint

就是只要是 openai-responsesbaseUrl 被判断成代理,就剥掉 prompt_cache_key

改法

不全局关掉这个逻辑,而是加白名单。白名单里的代理保留 prompt_cache_key,其他的继续走原来的 strip。

provider-attribution-*.js 里加白名单常量:

const RESPONSES_PROMPT_CACHE_ALLOWLIST_BASE_URLS = new Set([
  "http://127.0.0.1:13000/v1",
  "http://localhost:13000/v1",
  "https://api.lp1.net/v1"
]);

const RESPONSES_PROMPT_CACHE_ALLOWLIST_HOSTS = new Set([
  "api.lp1.net"
]);

加一个判断函数:

function shouldPreserveResponsesPromptCache(baseUrl) {
  const normalizedBaseUrl = normalizeComparableBaseUrl(baseUrl);
  if (
    normalizedBaseUrl &&
    RESPONSES_PROMPT_CACHE_ALLOWLIST_BASE_URLS.has(normalizedBaseUrl)
  ) return true;

  const hostname = resolveUrlHostname(baseUrl);
  return hostname !== void 0 &&
    RESPONSES_PROMPT_CACHE_ALLOWLIST_HOSTS.has(hostname);
}

把原来的判断改成:

shouldStripResponsesPromptCache:
  api !== void 0 &&
  OPENAI_RESPONSES_APIS.has(api) &&
  policy.usesExplicitProxyLikeEndpoint &&
  !shouldPreserveResponsesPromptCache(input.baseUrl)

改动很小,只改了判定条件,不碰删除实现,对官方 OpenAI / Azure OpenAI 没有影响,没进白名单的代理也不受影响。

白名单填什么

放精确 baseUrl 和精确 host 就行,比如:

  • http://127.0.0.1:13000/v1
  • http://localhost:13000/v1
  • https://api.lp1.net/v1
  • api.lp1.net

如果想从配置里自动提取候选,可以跑:

node <<'NODE'
const fs = require("fs");
const p = `${process.env.HOME}/.openclaw/openclaw.json`;
const data = JSON.parse(fs.readFileSync(p, "utf8"));
const providers = data?.models?.providers ?? {};

for (const [name, cfg] of Object.entries(providers)) {
  if (cfg?.api !== "openai-responses") continue;
  if (typeof cfg?.baseUrl !== "string" || !cfg.baseUrl.trim()) continue;
  try {
    const u = new URL(cfg.baseUrl);
    const host = u.hostname.toLowerCase();
    if (host === "api.openai.com" || host.endsWith(".openai.azure.com")) continue;
    console.log(`${name}\t${u.toString().replace(/\/+$/, "")}\t${host}`);
  } catch {}
}
NODE

扫出来确认之后填进上面两个 Set 就行。


四、修改步骤

1) 定位文件

if [ -d /usr/lib/node_modules/openclaw/dist ]; then
  DIST=/usr/lib/node_modules/openclaw/dist
else
  DIST="$(npm root -g)/openclaw/dist"
fi

grep -RIl 'shouldStripResponsesPromptCache:' "$DIST"

一般会拿到一个 $DIST/provider-attribution-XXXXXX.js

2) 备份

cp -p "$DIST/provider-attribution-XXXXXX.js" \
  "$DIST/provider-attribution-XXXXXX.js.bak.$(date +%F-%H%M%S)"

3) 编辑

打开 grep 找到的那个文件,加 allowlist 常量、shouldPreserveResponsesPromptCache 函数、改判定条件。

4) 重启验证

openclaw gateway restart
openclaw health

再看看服务状态:

systemctl --user status openclaw-gateway.service --no-pager
journalctl --user -u openclaw-gateway.service -n 50 --no-pager

5) 确认缓存

看你的 new-api / CPA / 自建代理日志,确认重新出现 prompt_cache_keycache_tokens > 0。如果代理会打印请求体,能直接看到 prompt_cache_key 有没有保留下来。


五、懒得手动找文件的话,直接把这段发给自家 OpenClaw

你帮我修一个 OpenClaw 的本地缓存兼容问题。

目标:

- 我现在的 OpenClaw 升级后,会把发往 OpenAI-compatible proxy 的 `prompt_cache_key` 剥掉,导致 prompt cache 基本不触发。
- 请你在本机全局安装的 OpenClaw dist 文件里,找到当前版本真正负责 `shouldStripResponsesPromptCache` 的 bundle 逻辑。
- 对我指定的代理 baseUrl / host 做白名单放行,让这些代理不要再被 strip `prompt_cache_key`。

请按以下步骤做:

1. 先定位 OpenClaw 的实际 dist 目录:
   - 优先看 `/usr/lib/node_modules/openclaw/dist`
   - 如果没有,再看 `$(npm root -g)/openclaw/dist`

2. 在 dist 目录中搜索:
   - `shouldStripResponsesPromptCache:`
   - `delete payloadObj.prompt_cache_key`

3. 找到包含判断逻辑的真实 bundle 文件:
   - 优先改 `provider-attribution-*.js`
   - 不要改 `.bak` 备份文件

4. 先备份该文件。

5. 从 `~/.openclaw/openclaw.json` 读取 provider 配置,把:
   - `models.providers.*`
   - 且 `api = openai-responses`
   - 且 `baseUrl` 不是官方 OpenAI / Azure OpenAI
   的 provider baseUrl 自动整理出来,作为 allowlist 候选。

6. 做最小修改:
   - 增加 allowlist baseUrl / host 常量
   - 增加 `shouldPreserveResponsesPromptCache(baseUrl)`
   - 仅把 `shouldStripResponsesPromptCache` 的判定改成:
     `... && !shouldPreserveResponsesPromptCache(input.baseUrl)`

7. 修改完成后:
   - `openclaw gateway restart`
   - `openclaw health`

8. 最后告诉我:
   - 你改了哪个文件
   - 你放行了哪些 baseUrl / host
   - 是否重启成功
   - health 是否正常

注意事项:

- 只做最小修改,不要顺手改别的逻辑
- 目标是"对白名单代理保留 `prompt_cache_key`",不是全局关闭 strip 逻辑
- 如果 bundle 文件名和当前版本不同,以实际 grep 结果为准
- 如果找不到相关逻辑,就把搜索结果告诉我,不要瞎改

六、补充

这个补丁不是要把新版的兼容保护全关掉。做法就是保留默认逻辑,只对你自己确认支持缓存的代理加白名单放行。后续 OpenClaw 每次更新可能还会改文件结构,但思路不变:grep 找到判断 strip 的位置,加白名单,重启就行。

另外 OpenClaw 上游已经有人提了 PR(#52017),如果后面合进去了就不用再手打补丁了。

最后修改:2026 年 04 月 09 日
如果觉得我的文章对你有用,请随意赞赏~