OpenClaw调用某些模型缓存命中率低的原因分析

问题背景

OpenClaw 是一个开源的 AI Agent 框架,支持接入多种大模型 API。在使用过程中,我们发现一个奇怪的现象:同一个 OpenClaw 实例,调用小米 mimo-v2.5 模型时缓存命中率高达 99.3%,而调用智谱 GLM-5.2 模型时缓存命中率却是 0%

小米 mimo-v2.5 直连 api.xiaomimimo.com,智谱 GLM-5.2 走汉得 New API 代理(地址:handai-code.hand-china.com),都通过同一个 OpenClaw 实例调用。为什么缓存命中率差异如此之大?本文记录了完整的排查过程和结论。

前置知识:大模型 API 的缓存机制

主流大模型 API(OpenAI、智谱、小米等)普遍支持前缀缓存(Prefix Caching)机制:如果两次请求的 prompt 前缀相同,服务端会跳过已缓存部分的计算,直接复用,从而大幅降低延迟和成本。

缓存命中率通常在 API 响应的 usage 字段中体现:

"usage": {
    "prompt_tokens": 35000,
    "prompt_tokens_details": {
        "cached_tokens": 34500
    }
}

其中 cached_tokens 表示命中缓存的 token 数。命中率 = cached_tokens / prompt_tokens。

排查过程

第一步:确认 OpenClaw 代码层面是否有缓存控制

通过阅读 OpenClaw 源码,确认了以下事实:

  • OpenClaw 有 cacheStablePromptPrefix 机制,但这是内部 memoization 优化(避免重复构建相同 prompt),不控制 API 层面的缓存
  • OpenClaw 对第三方 API(非 api.openai.com)默认不发送 prompt_cache_key 等缓存控制参数
  • 缓存命中率的统计完全依赖 API 返回的 usage 字段,OpenClaw 自动读取,不需要额外配置
  • system prompt 通过 <!-- OPENCLAW_CACHE_BOUNDARY --> 标记分为静态前缀和动态后缀,但此机制仅对 Anthropic 模型的 cache_control 有效

结论:OpenClaw 代码本身不会主动阻止缓存命中。

第二步:用 curl 直接测试智谱 GLM-5.2 的缓存能力

绕过 OpenClaw,直接用 curl 调用汉得代理访问 GLM-5.2:

# 固定 system prompt(~6000 tokens),动态内容在末尾
# 第1次:cached=0(冷启动)
# 第2次:cached=5952,命中率 99.8%
# 第3次:cached=5952,命中率 99.8%

结论:智谱 GLM-5.2 通过汉得代理完全支持前缀缓存,命中率可达 99%+。

第三步:对比 OpenClaw 调用两个模型的缓存数据

模型 调用方式 缓存命中率 备注
小米 mimo-v2.5 OpenClaw 直连 api.xiaomimimo.com 99.3% cacheRead=115,968
智谱 GLM-5.2 OpenClaw 走汉得代理 0% cacheRead=0
智谱 GLM-5.2 curl 直接调汉得代理 99.8% cacheRead=5,952

关键发现:

  • 同一个 OpenClaw 代码,小米能命中缓存,智谱不能
  • 同一个智谱模型,curl 能命中缓存,OpenClaw 不能
  • 汉得代理本身不是瓶颈(curl 测试已证明)

第四步:排除各种可能因素

逐一测试了以下可能影响缓存的因素:

测试项 结果 结论
stream=true vs stream=false 两者都能命中 不影响
带 tools 参数 vs 不带 两者都能命中 不影响
prompt_cache_key=null vs 不带 两者都能命中 不影响
prompt_cache_key=session-1 能命中 不影响
动态内容在 prompt 末尾 能命中 不影响
动态内容在 prompt 开头 第1次不命中,后续命中 影响首次

所有测试项在 curl 环境下都能正常命中缓存。问题不在这些参数上。

第五步:分析 OpenClaw 的 system prompt 结构

通过代码分析,OpenClaw 的 system prompt 结构如下:

[静态指令内容 - 固定不变]
[<!-- OPENCLAW_CACHE_BOUNDARY -->]
[动态项目上下文 - 可能变化]
[Runtime 信息 - 每次变化]

理论上,静态内容在前,动态内容在后,前缀缓存应该正常工作。但实际结果是 0% 命中率。

根因分析

排除汉得代理

汉得 New API 代理是透传模式,curl 测试已证明它完全支持缓存。而且其他用户通过同一代理使用 Codex、Cline、OpenCode 等工具,缓存命中率可达 10%-30%。

排除模型本身

智谱 GLM-5.2 的 curl 测试证明它支持前缀缓存,命中率 99%+。

锁定问题范围

问题锁定在:OpenClaw 发送给智谱的 API 请求,与 curl 发送的请求,在前缀部分存在差异。

具体来说,最可能的原因是:

  1. OpenClaw 在请求的前缀位置注入了动态内容(如时间戳、session 标识、或其他元数据),导致每次请求的前缀都不同
  2. 智谱 API 的前缀匹配是精确的——前缀有任何不同,整个 prompt 都不命中缓存
  3. 小米 API 的前缀匹配是宽容的——它可能识别出哪些部分是”结构性变化”(如时间戳),忽略这些变化,仍然匹配缓存前缀

为什么这个解释最合理?

这个解释完美契合所有观察到的现象:

  • OpenClaw + 小米 = 命中(小米宽容匹配,忽略动态变化)
  • OpenClaw + 智谱 = 不命中(智谱精确匹配,不认变化后的前缀)
  • curl + 智谱 = 命中(curl 没有动态变化,前缀固定)
  • Codex/Cline + 智谱 = 部分命中(这些工具的动态变化可能更少或位置不同)

如何验证?

要最终确认这个结论,需要做以下验证:

  1. 在 New API 后台对比请求日志:对比 OpenClaw 发给智谱的请求 body 和 Codex 发给智谱的请求 body,找出前缀部分的具体差异
  2. 开启 OpenClaw 的 cacheTrace:在配置中设置 cacheTrace.enabled: true,查看详细的缓存追踪数据
  3. 抓包分析:用 mitmproxy 等工具拦截 OpenClaw 发出的实际 HTTP 请求,检查前缀内容

对使用者的建议

如果智谱模型的缓存命中率对你很重要

  • 方案一:直连智谱 API,绕过汉得代理,减少中间层可能的干扰
  • 方案二:使用小米 mimo-v2.5,它的前缀匹配更宽容,对 OpenClaw 这类 Agent 更友好
  • 方案三:等待 OpenClaw 优化,如果确认是 OpenClaw 注入了不必要的动态前缀内容,可以向 OpenClaw 社区反馈

关于缓存命中率的实际影响

缓存命中率 0% 意味着:

  • 每次请求都需要完整处理 prompt(~35,000 tokens)
  • 延迟更高(首次处理 vs 缓存复用)
  • 成本更高(如果 API 按处理 token 计费)

但对于日常使用来说,如果 API 本身价格合理且延迟可接受,缓存命中率低并不影响功能正确性。它主要影响的是成本效率和响应速度

总结

项目 结论
汉得代理 不是原因,完全支持缓存
智谱 GLM-5.2 支持缓存,但前缀匹配精确
小米 mimo-v2.5 支持缓存,前缀匹配宽容
OpenClaw 发给智谱的请求前缀可能包含动态内容
根本原因 OpenClaw 请求前缀 + 智谱精确匹配 = 0% 命中

这个问题的本质是:AI Agent 框架(如 OpenClaw)在构建 API 请求时,不可避免地会加入一些动态元数据,而不同模型服务端对这些动态变化的容忍度不同。小米做得更智能,智谱做得更严格,这导致了相同的 Agent 在不同模型上表现出截然不同的缓存行为。

作者: Jack.shang

jack.shang 程序员->项目经理->技术总监->项目总监->部门总监->事业部总经理->子公司总经理->集团产品运营支持