OpenClaw 大模型输入 Token 缓存命中率优化说明

2026/04/15
openclawprompt cachingtokenopenai responsesanthropic

OpenClaw 大模型输入 Token 缓存命中率优化说明

背景

OpenClaw 通过代理层向大模型 API 发送请求。Anthropic / OpenAI 等 API 都支持 Prompt Caching,如果连续请求的 system prompt 前缀相同,API 侧会缓存该前缀 token,后续请求直接复用缓存而无需重新处理,从而降低延迟和费用。

优化前发现:实际缓存命中率偏低,大量 input token 未能命中缓存。

核心改动

改动文件

/opt/homebrew/lib/node_modules/openclaw/dist/proxy-stream-wrappers-C_136afN.js

改动内容(仅 1 行)

- enablePromptCacheStripping: true,
+ enablePromptCacheStripping: false,

位于 createOpenAIResponsesContextManagementWrapper() 函数内:

function createOpenAIResponsesContextManagementWrapper(baseStreamFn, extraParams) {
  const underlying = baseStreamFn ?? streamSimple;
  return (model, context, options) => {
    const policy = resolveOpenAIResponsesPayloadPolicy(model, {
      extraParams,
      enablePromptCacheStripping: false, // 这里从 true 改为 false
      enableServerCompaction: true,
      storeMode: "provider-policy"
    });
    // ...
  };
}

原始文件已备份为 proxy-stream-wrappers-C_136afN.js.bak

原理说明

改动前的问题

enablePromptCacheStripping: true 时,OpenClaw 的 payload 策略解析逻辑会判断:

shouldStripPromptCache:
  options.enablePromptCacheStripping === true &&
  capabilities.shouldStripResponsesPromptCache

如果条件成立,在发送请求前会执行:

if (policy.shouldStripPromptCache) {
  delete payloadObj.prompt_cache_key;
  delete payloadObj.prompt_cache_retention;
}

主动删除请求体中的 prompt_cache_keyprompt_cache_retention 两个字段。它们是告诉 API 侧“这段 prompt 可以缓存”的关键标识。一旦被删除,API 侧就无法知道应该缓存哪些 token,每次请求都需要从头处理所有 input token。

改动后的效果

enablePromptCacheStripping: false 使得 shouldStripPromptCache 始终为 falseprompt_cache_keyprompt_cache_retention 字段保留在请求体中。API 侧能够:

  1. 首次请求时缓存 system prompt 的稳定前缀(token 计费为 cache write)
  2. 后续请求命中缓存前缀(token 计费为 cache read,费率更低)
  3. 仅处理缓存未覆盖的动态部分

OpenClaw 的 Prompt 缓存架构

OpenClaw 内部有一套完整的 prompt 缓存优化机制,此次改动让该机制不再被旁路。

1. System Prompt 缓存边界

OpenClaw 使用 <!-- OPENCLAW_CACHE_BOUNDARY --> 标记将 system prompt 分为两部分:

┌──────────────────────────────────┐
│  稳定前缀 (Stable Prefix)         │  ← cache_control: {type: "ephemeral"}
│  - soul.md                        │
│  - identity.md                    │
│  - user.md                        │
│  - tools.md                       │
│  - bootstrap.md                   │
│  - memory.md                      │
│  - 其他非动态 context files        │
├──── CACHE_BOUNDARY ──────────────┤
│  动态后缀 (Dynamic Suffix)        │  ← 不标记缓存
│  - heartbeat.md                   │
│  - 实时上下文                      │
│  - 额外 system prompt             │
└──────────────────────────────────┘

稳定前缀每次请求基本不变,标记 cache_control: {type: "ephemeral"} 后可被 API 缓存。动态后缀每次不同,不标记缓存。

2. Anthropic API 路径

anthropic-messages API,applyAnthropicEphemeralCacheControlMarkers() 会:

  • system / developer role 的消息块添加 cache_control: {type: "ephemeral"}
  • 为最后一条 user 消息也添加缓存标记
  • 在边界处将 system prompt 拆分为两个独立 text block,仅对稳定部分标记缓存

3. OpenAI Responses API 路径

openai-responses API,通过 prompt_cache_keyprompt_cache_retention 告知服务端哪些 prompt 内容可缓存。这就是被 enablePromptCacheStripping: true 误删的字段。

4. 上下文文件排序

const CONTEXT_FILE_ORDER = new Map([
  ["soul.md", 10],      // 最稳定,排最前
  ["identity.md", 20],
  ["user.md", 30],
  ["tools.md", 40],
  ["bootstrap.md", 50],
  ["memory.md", 60],
]);

OpenClaw 按固定顺序排列 context 文件,确保稳定前缀的内容和顺序在每次请求间保持一致,从而最大化缓存命中率。

效果

指标优化前优化后
prompt_cache_key 字段被删除,不随请求发送保留,API 可识别缓存
prompt_cache_retention 字段被删除保留
System prompt 稳定前缀每次重新处理首次写入缓存,后续直接命中
Input token 计费全量按标准费率缓存命中部分按 cache_read 费率(更低)
请求延迟每次处理全量 token缓存命中后显著降低

验证方式

在 API 返回的 usage 字段中观察:

{
  "usage": {
    "input_tokens": 5000,
    "cache_creation_input_tokens": 45000,
    "cache_read_input_tokens": 45000,
    "output_tokens": 1200
  }
}
  • cache_read_input_tokens > 0 说明缓存命中生效
  • 理想情况下,稳定前缀(soul/identity/tools/memory 等)的 token 在后续请求中全部走 cache read
ScholarForce

ScholarForce

OpenClaw 大模型输入 Token 缓存命中率优化说明 | 博客