
claude-code-cache-keepalive
Keep the Claude Code prompt cache hitting: what burns it, stable-prefix configuration, and TTL keepalive for long sessions and agents
Install with your AI
Paste into Claude Code, Cursor, or any agent — it reads the repo and wires the tool into your project.
Install and set up claude-code-cache-keepalive (git-clone project) into my current project. Found on https://claudeers.com/claude-code-cache-keepalive Repo: https://github.com/lllq-123/claude-code-cache-keepalive Homepage/docs: — Detected install method: git-clone → git clone https://github.com/lllq-123/claude-code-cache-keepalive Category: mcp-servers. Platforms: cli, api. Read the repo's README for exact setup and env vars, then install it and wire it into my project. Claudeers Health Verdict: active; community-verified: false. Confirm the source before running anything.
git clone https://github.com/lllq-123/claude-code-cache-keepalive
// compatibility
| Platforms | cli, api |
|---|---|
| Operating systems | — |
| AI compatibility | claude |
| License | MIT |
| Pricing | open-source |
| Language | — |
claude-code-cache-keepalive
让长会话和自动化 agent 的 Claude Code prompt cache 保持命中:哪些行为会烧掉缓存、怎么配置让前缀稳定、以及 TTL 到期前的轻量保活。
[!WARNING] 非官方社区笔记,不隶属于 Anthropic。数字来自 Claude Code 2.1.175 / 2.1.258 的真实 API usage;CC 版本升级后行为可能变化,请重新验证再信。 姊妹项目:claude-code-turn-anchor (已于 2026-09-02 改为每 15 个 prompt 稀疏追加,不再改写 transcript)。
先读这个:缓存崩塌是自愈的,肉眼看不见
最反直觉、也最重要的一条:砸缓存的事故会自己恢复,所以你几乎不可能 "当场看到"它。
砸掉的那一轮把整个前缀按缓存写入价(1.25× 或 2×)全量重算,下一轮就恢复 99% 命中,之后一路正常。你听说缓存出了问题、跑去看当前命中率——一切正常。 于是结论变成"没有问题"。实际上钱(或订阅额度)在那一轮已经烧掉了。
**盯当前值永远抓不到崩塌,要看历史序列。**从 session transcript 里拉:
jq -r 'select(.type=="assistant" and .message.usage) |
[.timestamp,
.message.usage.cache_read_input_tokens,
.message.usage.cache_creation_input_tokens] | @tsv' \
~/.claude/projects/<project-dir>/<session-id>.jsonl
健康的长会话里 cache_read 稳定爬升、cache_creation 只有每轮新增的一小条。
序列中间突然出现一行 read=0 / create=一大坨——那就是一次崩塌,
发生过、自愈了、被你错过了。
缓存机制一页纸
- 请求前缀按 tools → system → messages 顺序拼接;缓存是前缀逐字匹配: 从第一个不一致的字节起,后面全部作废重算。
- 计费(Anthropic API):缓存写入 1.25×(5 分钟 TTL)或 2×(1 小时 TTL), 缓存读取 0.1×。订阅(Pro/Max)用户烧的不是账单,是 5 小时窗口的额度—— 一样疼。
- TTL 是滑动的:每次命中都重置计时。
- 判读三条(排查时最有用):
read = 0、create巨大 → 最前排就变了(tools 段或 system 段), 典型:工具表变化、system prompt 变化;read = 一个中等小值、create巨大 → 最前排完好,分叉在 messages 靠前处,典型:历史被改写过、MCP 工具异步接入时序差;- 一轮的 usage 全是 0 → 大概率是被打断的轮,那是"没数据", 不是崩塌,别当病查。
原则:不变的放前面,会变的放后面
前缀按序匹配意味着:一个会变的字节,位置越靠前,烧掉的越多。
-
反例:把当前时间放进 system prompt 开头 → 每轮全量重算,缓存等于不存在。
-
一个真实案例:Claude Code 的 system reminder 里有一个
currentDate字段, 每个进程启动时计算一次,位置在 prompt 最前排。常驻交互进程没事 (进程活着日期不重算;跨天时 CC 往末尾追加一条 date-change 通知, 前缀不动——这是正确示范)。但"每轮起新进程 resume"的自动化架构会在 跨零点后的第一轮触发日期重算 → 前缀第一个字节就失配 → 整份全量重写。 同样的字段,两种架构下代价完全不同。每轮 resume 架构下这个问题有两条出路:
- 认了它:代价是每天跨零点后第一轮全量重写一次,时间点可预期。 上下文不大的话这是最省心的选择;
- 根治(风险自负的非官方手段):patch 客户端二进制,把那个启动时 计算的日期字段清空,日期信息改由轮末 hook 注入提供——这正是 "会变的放末尾"原则的实操版。代价:每次 Claude Code 升级都要重做, 且属于修改客户端行为,自己权衡。我们自己走的是这条, 所以本文的实测数据里不含跨零点重写。
-
正确做法:时间、状态、提醒这类动态内容,用
UserPromptSubmithook 的additionalContext注到轮末(token 窗口最近处),别碰前缀。 注意每轮注入会在 transcript 里累积,历史里的旧注入不要用改写文件的 方式清理——2026-09-02 起实测:改过历史的会话,下一轮必 miss。是模型 (当天换到 Fable 5.1)还是服务端导致的,我没有单独验证过,各位自己看着办。 稀疏追加示例见 turn-anchor。
什么会烧掉缓存(行为清单)
| 行为 | 失配点 | 代价 |
|---|---|---|
| 工具表任何变化:装/卸 MCP server、deferred 工具首次加载、MCP 连接抖动 | tools 段(最前) | 全量 |
| system prompt 变化:切 output style、改 system 级配置后 resume | system 段 | 全量 |
| 切换模型 | 缓存按模型隔离 | 全量重建 |
| 改写磁盘历史(transcript 手术类方案)后 resume | 第一处被改的消息 | 从那里起全部 |
/clear、/compact | 前缀整个重来 | 全量(这是功能不是事故,知道即可) |
| 闲置超过 TTL | 无失配,单纯过期 | 全量重建 |
| 每轮 resume 架构跨零点(date 类字段启动时计算) | 最前排 | 全量,每天一次 |
| 会话中改 CLAUDE.md | 当轮无失配(活进程不重读它) | 延迟爆发:每条缓存线各自的下一次重建时,从第一条消息起全烧 |
会话中改任何 skills/*/SKILL.md(≤ 2.1.175) | probe 可能暂时仍命中旧线 | 下一条真实 user 消息延迟爆发:skill 清单处在第一 cache breakpoint 前,本机实测 read=0 / create=76,047 |
会话中新增一个 skills/*/SKILL.md(2.1.258 起) | 无失配 | 零。四格实测全部精确命中,见下一节 |
会话中修改一个既有 skills/*/SKILL.md(2.1.258 起,改时有活进程在场) | 活进程当轮照常命中 | 延迟爆发,只烧 messages 段:之后任何按磁盘重建前缀的请求(旁路探针、进程重启后 resume)只读回 tools + system 那段,本机 read=19,055,见下一节 |
CC 2.1.258 totalTokensReminder | 默认在尾部追加动态 reminder | 本机官方路由未见它每轮击穿 cache,但 1500 万是 padded 任务预算、不是 context 余量;可用顶层 "totalTokensReminder":"off" 关闭,首次生效会因 system prompt 变化冷一次 |
其中最容易被忽视的是第一行:工具表在前缀最前面,它的任何变化都是 最贵的一种变化。「延迟爆发」那两行则是另一种容易误判的形状——改的当下 一切正常,账挂在之后。skill 那条还随 CC 版本翻过面、且「新增」和「修改既有」是两回事,一起放在下一节展开。
改配置要挑时机(CLAUDE.md / skills / MCP)
结论按 CC 版本分叉,三类配置各不相同:
-
会话中改 CLAUDE.md:后续轮次的请求里完全没有新内容—— 活进程根本不重读它,当轮零感知;账在下一次重建时结(见下)。
-
改
skills/*/SKILL.md:2.1.258 起要分「新增」和「修改既有」两件事说。**新增一个 skill 不烧。**四格矩阵——进程是否重启 × 有没有新装一个 skill—— 四格全部精确全量命中,每一格都恰好等于「上一轮
read+create」,一个 token 不差:同一进程 重启后 resume skill 没变 read=34,475✅read=34,814✅新装一个 skill read=34,604✅read=34,989✅**修改一个既有 skill 的正文会烧,条件是改的时候有活进程在场。**同一频道、同模型、 同探针,唯一变量换成「改既有 skill 的正文」:旁路探针
read=19,055——只剩 tools + system 那段前缀能读回来,messages 段整段重写(对照:新装一个 skill 之后同一探针read=34,686)。 先把进程掐掉、改完再拉起,则read=212,713精确命中。所以两个条件要同时成立才烧: ①改的是既有 skill;②改时活进程在场。活进程自己当轮不受影响,账在下一个按磁盘重建 前缀的请求上结——旁路探针、进程重启后的 resume——和 CLAUDE.md 那条同一个形状: 延迟爆发,messages 段从头重写,tools + system 那段仍可读回。机制没验证,两个假说并存:客户端按内容或字节数记着每个 skill,动既有的就破坏前缀; 或活进程同时攥着旧清单和改动通知,修改时多出一份旧的。别把任何一个写成定论。 实测两条自救路:逐字节改回去就恢复;改之前先把进程掐掉,改完再拉起。
边界照实说:这组数字取自
claude-fable-5-1[1m],同版本的其他模型没测。 -
同一件事在 ≤ 2.1.175 上相反:任何 SKILL.md 改动都按会烧处理。 wire 抓包曾显示正文不在可见 listing 里,但 2026-09-01 的真实时序推翻了 「正文可以随便改」这个外推——改动后旁路 probe 仍命中 75,995, 下一条真实 user 消息却
read=0 / create=76,047。 probe 看不见这类延迟失配,不能拿它为任何一侧背书。 跑老版本的读者按这条走。 -
改 MCP 配置 / server 脚本:照旧会烧,258 没有改变这一条。 它进的是请求的
tools段(前缀最前排),跟 skill 清单是两套机制。 而且没有兜底可打:让活进程自己发一轮确实能命中——它攥着启动时连上的 那份工具表——但这条缓存线的寿命只到进程死为止,下一个进程 resume 时 拉到新工具表,前缀照样从头作废。我们自己的保活链踩过这一下:补投的那轮 命中了,紧跟着的进程重启把账原样结掉。 工具表一变,那条线就已经注定要重建,再发什么都只是把账推后。 (一次实测:加一个工具read=0 / create=35,831,撤回read=35,672精确复原;加回后走真 resume,掉到read=19,176。)
CLAUDE.md 那条看起来很安全?危险恰恰在"之后"。它住在 prompt 最前排 (拼在第一条消息里),进程重建时按当前磁盘重新生成。改过之后, 每一条已有的缓存线都会在它自己的下一次重建——重开窗口、resume、 旁路保活探针——从头全量重写一次。≤ 2.1.175 的 skill 全量清单紧跟在 CLAUDE.md 后面,同理;258 起新增 skill 不再算这一类,但修改既有 skill 仍算(见上)。
三条实操:
- **挑上下文短的时候改。**爆的代价等于各窗口当时的上下文长度: 在一个 2 万 token 的新窗里改完配置,代价是 2 万;开着 30 万 token 的长会话去改,代价就是 30 万。
- **别开着一堆长会话的时候改。**每个窗口是一条独立缓存线, 改一次全局配置,开着几个窗,就在之后各爆几份。 要动配置,先把不必要的窗口收掉。
- 改完后的第一次 resume / 新窗 / 真实 user turn 全量重写是预期内的,别当故障查。 跑保活的读者额外注意:会进前缀的配置改动(258 起新增 SKILL.md 不算,修改既有的算) 同样会让旁路探针重建的前缀跟主会话对不上,探针从此开始 miss—— 改完配置,尽早让长会话自然收尾重开。
还有一个实测到的细节,影响"账在哪一轮结"的判断:CC 的常驻长进程是 懒初始化的——stream-json 进程起来后并不读盘(不跑 SessionStart hook、 不连 MCP,实测闲置 45 分钟事件缓冲区仍为空),收到第一条输入的那一刻 才按当时的磁盘状态初始化。所以改配置的账不在"进程启动时"结, 在"下一个进程被叫醒(收到首条输入)时"结——进程可能早就起来了, 但还没读过盘。
每轮 resume 的自动化架构:工具必须开局配齐
headless 自动化(每轮 claude -p --resume <session-id> 起新进程、答完即退)
是 agent 的常见形态。这个架构下前缀每轮从磁盘逐字重建,任何"运行中才
加载"的东西都是定时炸弹:
-
ENABLE_TOOL_SEARCH=false,禁用 deferred tools。 Claude Code 的 ToolSearch 机制是"启动时不发送 deferred 工具的定义、 第一次要用再塞进 tools 数组"——塞的那一刻 tools 段变了,整份缓存作废。 上下文越长这笔账越亏:30 万 token 的会话,一次动态加载 = 30 万 token 按写入价重算,省下的只是几万字节上行流量。 (Anthropic API 官方的 tool search +defer_loading是另一回事: 工具定义全量发给服务端、只是不进上下文,前缀不动、缓存无损。 CC 当前没走官方这条路,所以只能整个关掉。) -
所有 MCP server 加
"alwaysLoad": true。 防的不只是 deferred:MCP server 异步连接的时序差也会让工具表 在两轮之间不一致。 -
**上面两件必须配套做。**只关 ToolSearch、不开 alwaysLoad 反而更糟—— MCP 工具全部挤进 tools 段之后,任何一个 server 连接抖一下, 工具表就变一次,
cache_read直接归零。 -
**环境变量每轮逐字一致。**影响 prompt 拼装的 env 少一个就是另一份 前缀(实测踩过:resume 时漏带一个 env,
read=0、全量重写, 每发一次真金白银)。把主进程的启动参数和 env 原样复刻, 别凭记忆重打。 -
**cwd 一致。**工作目录嵌在 system prompt 里,换目录 = 换 system 段。
工具表瘦身(同时是省钱项)
工具表不只要稳定,还值得变小——它是每一轮都要发的固定成本:
- deny 掉用不到的内置工具:
settings.json的permissions.deny支持 直接禁用工具。我们从 29 个砍到 21 个,tools 段瘦了约 2 万字节, 每轮省约一万 token 的固定输入。 - **MCP 别挂一排。**每个 server 的每个工具的完整 JSON schema 都进前缀。
低频服务(一周用一两次的)别常驻:
- 下沉成 CLI:一个入口命令 +
--help自描述,模型要用时 Bash 调用, 工具表零占用; - 或包成 skill:说明文档按需读入,用完就走。
- 两种做法共同点:CC 的工具表零改动 = 前缀零风险 + 零固定成本。 我们常驻只留两个高频 server,四个低频服务全部走一个统一的 CLI 网关。
- 下沉成 CLI:一个入口命令 +
TTL 保活(keepalive)
什么时候需要:会话要闲置(等人回复、夜间待命),TTL(5 分钟或 1 小时) 一过缓存就凉,下次唤醒全量重建。保活 = TTL 到期前发一个最小轮, 把缓存读一遍、TTL 重置。
值不值得:一发保活 ≈ 全量读一次 = 0.1×;凉透重建 = 1.25×/2×。 上下文越长越值得:几万 token 的会话让它凉,重建也没多少; 几十万 token 的常驻 agent 会话,保活一发和冷重建差 20 倍。
原理:重建逐字相同的前缀发出去 → cache_read 全量命中 →
服务端 TTL 刷新 → 丢弃这一轮的输出。
旁路 resume 版(不打扰主会话、不污染 transcript):
# 最小可用版:另起短命进程 resume 同一个 session
timeout 30 claude -p --resume "$SESSION_ID" --no-session-persistence \
"Reply with exactly: ok" >/dev/null
--no-session-persistence:这一轮不写盘,主会话的 transcript 保持干净 (否则保活轮会追加进历史,改变下一轮真实对话的前缀);- 启动参数、env、cwd 按上一节复刻主会话,复刻的忠实度决定命中与否;
- 验证用
--output-format stream-json看返回的 usage:cache_read > 0✅ 续上了;read = 0且create > 0❌ 参数没对齐,这发在另建一条新缓存线, 白花钱还没保到活——回去 diff 参数和 env。
完整示例见 examples/keepalive.sh。
探针的"配置一致性检查"拿什么当基准:做了旁路探针的人迟早会加一道检查—— "配置改过了,探针复刻出来的前缀还跟主会话对得上吗",对不上就停发。 判断"改过没"的基准应该是主进程拉起的那一刻(严格说是它收到首条输入、 真正读盘的那一刻,见上一节;拉起时刻更容易取到,只会偏保守):改动早于它, 主进程读到的就是当前这份,探针复刻必然一致;晚于它才危险。 别拿"上次检查时记下的指纹"当基准——那可能是几天前的,会把主进程早已 吃进去的改动判成"新变更",白停保活直到 TTL 过期。我们自己 2026-09-02 就这样 凉过一次十几万 token 的线。
间隔随机化:固定间隔的请求节奏是典型的自动化指纹,谨慎起见别用。 在贴近 TTL 的区间内随机取值(如 1 小时 TTL → 50–58 分钟均匀随机), 且以最后一次真实活动的时间为基准滑动,不是按墙钟对表。 另外两条常识:主进程不在就别为了保活去冷启动它;用户活跃时段 自然有真实请求刷新 TTL,保活只该在闲置时兜底。
输出压缩属于优化项(做不做,保活本身都成立):生产环境可以把输出
上限压到 1 token(CLAUDE_CODE_MAX_OUTPUT_TOKENS=1,未收录进官方文档
但实测是合法值)。但有个必须知道的坑:
- 输出被截断(
stop_reason=max_tokens)必触发 CC 的抢救重试, 上限硬编码 3 次——一次保活会变成 4 发请求,每发都全量读一遍缓存; - 没有开关:
CLAUDE_CODE_MAX_RETRIES管的是网络层错误,对这个无效 (触发判定的源头是 stop_reason,不是网络失败); - 绕法是 stream-kill:
--output-format stream-json下,message_start事件带着完整 usage、先于响应完成流出——读到它的那一刻 服务端已完成缓存读取、TTL 已刷新,立即SIGKILL整个进程组, 重试来不及发出。实测 API 请求数 = 1; - 读数坑:
-p模式最终result.usage是全程累计,不是单发—— 4 发抢救会让cache_read显示成单发的 4 倍。误当单发读,会推出 "怎么多了十几万 token 的注入"这类幻影,按实际请求数分摊后再下结论。
验证:怎么读数
- 用开头那条
jq拉整个 session 的 usage 序列,看形状不看单点; - 判读用「机制一页纸」的三条;
- 参考值(我们的长会话实测):配齐上面各项后,常规轮命中率 90% 以上,
长会话短输入稳定在 99–100%;resume 恢复轮可完整继承上一轮建立的
全部缓存(
read恰好等于上一轮read + create,零崩塌)。 一个例外:上一轮跑过长工具链时,恢复轮会差一小段——cache 断点 (cache_control)一次请求最多 4 个,一轮里连调五次工具就会把 靠前的断点挤掉,恢复轮 / 保活探针只能命中到工具链中间某一步 (实测一次差 +3,791,read精确等于工具链第二步那一刻的值)。 这是断点数量的机制上限,不是你的参数没对齐,别当故障查。 注意:我们的环境已按「原则」节的根治法处理了跨零点日期问题; 未处理的原版环境在每轮 resume 架构下,每天会多一次可预期的全量重写, 长期平均命中率会比这组数字略低。
边界与已知事项
- OpenAI 侧规则几乎相同:工具定义同样在缓存前缀里、改名字/schema/顺序 同样作废,写 1.25× / 读 0.1×,TTL 30 分钟。换供应商躲不掉这套逻辑。
- 排查有尽头:一次崩塌把本文清单全查完仍对不上号,就别再往本地硬找。
我们遇到过一类
read=0:同条件复现不出来,且崩塌后下一轮读回的 是崩塌前建立的老缓存线——老线一直活着,只是那一次没匹配上, 疑似服务端偶发。很低频(一天 200+ 轮里 3 次),认了比错杀自己的 配置强。 - 本文数字分别在 Claude Code 2.1.175 与 2.1.258 上实测,结论按版本分叉—— skill 那条就是活例子,一个大版本把「新增」那半整个翻面,「修改既有」那半照旧会烧。版本升级后先小流量重验, 别照搬别人(包括本文)的旧结论。
- 保活是成本优化,不是必需品。短输入、规律性自动请求本身是一种 可被识别的使用形状,自己权衡;本文的立场是把随机化和"跟随真实活动" 当作默认礼貌,而不是把保活开到极限。
License
// faq
What is claude-code-cache-keepalive?
Keep the Claude Code prompt cache hitting: what burns it, stable-prefix configuration, and TTL keepalive for long sessions and agents. It is open-source on GitHub.
Is claude-code-cache-keepalive free to use?
claude-code-cache-keepalive is open-source under the MIT license, so it is free to use.
What category does claude-code-cache-keepalive belong to?
claude-code-cache-keepalive is listed under mcp-servers in the Claudeers registry of Claude-compatible tools.
// embed badge
[](https://claudeers.com/claude-code-cache-keepalive)
// retro hit counter
[](https://claudeers.com/claude-code-cache-keepalive)
// reviews
// guestbook
// related in MCP Servers
f.k.a. Awesome ChatGPT Prompts. Share, discover, and collect prompts from the community. Free and open source — self-host for your organization with complete…
A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Gemini CLI & Hermes Agent. Only official website: ccswitch.io
🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman
An open-source AI agent that brings the power of Gemini directly into your terminal.