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_key 和 prompt_cache_retention 两个字段。它们是告诉 API 侧“这段 prompt 可以缓存”的关键标识。一旦被删除,API 侧就无法知道应该缓存哪些 token,每次请求都需要从头处理所有 input token。
改动后的效果
enablePromptCacheStripping: false 使得 shouldStripPromptCache 始终为 false,prompt_cache_key 和 prompt_cache_retention 字段保留在请求体中。API 侧能够:
- 首次请求时缓存 system prompt 的稳定前缀(token 计费为 cache write)
- 后续请求命中缓存前缀(token 计费为 cache read,费率更低)
- 仅处理缓存未覆盖的动态部分
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/developerrole 的消息块添加cache_control: {type: "ephemeral"} - 为最后一条
user消息也添加缓存标记 - 在边界处将 system prompt 拆分为两个独立 text block,仅对稳定部分标记缓存
3. OpenAI Responses API 路径
对 openai-responses API,通过 prompt_cache_key 和 prompt_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

