Show HN: Claude-thermos – 为您保持 Claude 会话活跃
Show HN: Claude-thermos keeps your Claude session warm for you

原始链接: https://github.com/izeigerman/claude-thermos

Claude Code 的提示词缓存会在五分钟无操作后失效。当子代理运行时间超过此限制时,主代理的缓存就会失效,导致返回时必须进行代价高昂的完整重新编码(通常为 20 万至 50 万 token)。这些“缓存税”重新编码可能占到你总使用成本的 20% 以上。 **Claude-thermos** 通过保持主代理的缓存活跃来解决这个问题。它充当本地代理,监控你的 Claude Code 会话。当它检测到主代理在子代理活跃时处于闲置状态,会自动发送“预热”请求(1 个 token 的非流式调用)以维持缓存。 **主要功能:** * **零阻力:** 只需在命令前加上 `uvx claude-thermos` 即可使用。 * **高性价比:** 以廉价的缓存读取(0.1 倍成本)取代昂贵的完整写入(1.25 倍成本),可实现显著的净节省。 * **透明化:** 它会创建详细的日志文件(`events.jsonl` 和 `summary.json`),以便你追踪具体节省了多少 token 和费用。 需要 Python 3.11+ 和 Claude CLI。你可以通过简单的标志(例如 `--idle`、`--interval`)自定义时间,或通过 `CLAUDE_WARMER_DISABLE=1` 禁用它。

Hacker News 的讨论围绕着一款名为“Claude-thermos”的工具展开。该工具通过定期发送请求来维持 Claude 的提示词缓存(Prompt Cache)处于“预热”状态,从而防止缓存过期(API 用户通常为 5 分钟,订阅用户为 1 小时)。这让用户能够避免重复处理大规模上下文所带来的成本和延迟。 社区反应各异: * **支持者:** 支持者认为该工具解决了实际的可用性痛点。他们主张,如果 Anthropic 等服务商不提供灵活的长期缓存功能,用户只能通过这些“旁门左道”来维持长时间代理任务的工作流连续性。 * **批评者:** 怀疑论者将其视为“公地悲剧”。他们认为,强制保持会话在显存(VRAM)中会无谓地浪费有限的基础设施资源,这可能导致服务商对所有用户实施更严格、更不利的限制政策。 * **技术争论:** 讨论的焦点在于这是否属于“滥用”。一些人认为,既然用户为缓存读取付费,那么他们只是在按预期使用服务。另一些人则指出,5 分钟的过期时间是服务商为了平衡容量与成本而刻意做出的权衡,而该工具实际上以牺牲整体系统效率为代价绕过了这一机制。
相关文章

原文

Stop paying to rebuild your Claude Code cache. When your main agent waits on a subagent for more than 5 minutes, its prompt cache silently expires, and the next turn re-encodes your entire conversation at the write rate instead of reading it back cheap. On long sessions with many subagents that's roughly 20% of your bill. claude-thermos keeps the cache warm so you never pay that tax.

Run Claude Code exactly as you normally would, but through claude-thermos with uvx:

uvx claude-thermos                     # instead of: claude
uvx claude-thermos -p "fix the bug"    # any claude args pass straight through

Requires Python 3.11+ and the claude CLI on your PATH.

That's it. Warming runs automatically in the background. To disable it for a run without changing the command, set CLAUDE_WARMER_DISABLE=1.

Tuning (all optional):

Flag Default Meaning
--idle 270 Seconds the main agent must be idle before warming kicks in
--interval 270 Seconds between warming cycles
--max-cycles 4 Max warms per idle episode (auto for unlimited)
--subagent-window 540 Seconds a subagent counts as "still active"

Why your cache keeps expiring

Claude Code's prompt cache uses a 5-minute TTL. Every turn, your whole conversation history is served from cache at 0.1x the input price instead of being re-sent at full price, as long as the cache stays alive.

The cache expires if more than 5 minutes pass between requests on the same prefix. The dominant trigger for that gap is not you thinking. It's the main agent blocked on a subagent that runs longer than 5 minutes. A subagent has a different system prompt and tool set, so its requests have a different cache prefix and never refresh the main agent's. While the subagent works, the main agent's cached history ages untouched; past 5 minutes it's gone. When the subagent returns, the main agent resumes with a byte-identical, append-only history, and finds its cache missing, forcing a full re-encode at the 1.25x write rate.

By then the history is large, so the re-encode is expensive: individual collapses re-write 200K to 500K tokens. Measured across roughly 185 local sessions, these rebuilds accounted for about 22% of the total bill, money spent re-encoding content that was already cached moments earlier.

claude-thermos launches Claude Code behind a small local reverse proxy (it points ANTHROPIC_BASE_URL at a loopback port; all traffic still goes to the real Anthropic API).

  1. Observe. The proxy watches /v1/messages traffic and groups it into sessions and lineages, a lineage being one cache prefix, keyed by model + tool set + system text. The first tool-bearing lineage is the main agent; the rest are subagents.
  2. Detect the danger window. When the main lineage goes idle and a subagent is actively running, the main prefix is at risk of expiring.
  3. Warm. On an interval under the 5-minute TTL, it replays the main agent's last real request as a warm request: identical cacheable prefix, but max_tokens: 1 and no streaming. The single token is thrown away; the point is the prefill, which reads and refreshes the full cached prefix. Warm requests go directly to the API, never through the proxy, so they can't disturb real traffic.
  4. Result. When the subagent finishes, the main agent's cache is still warm. It pays a cheap read instead of a full rewrite.

Each warm costs a cache read (0.1x); each rewrite it prevents would have cost a write (1.25x) on a much larger prefix, so the trade is heavily in your favor.

Every session writes to:

~/.claude-thermos/logs/<session_id>/
├── events.jsonl    # append-only structured event stream
└── summary.json    # rollup totals, written when the session ends

events.jsonl records each request/response's token usage plus every warming decision (warm_fired, warm_result, cap_reached, resume_detected, and so on). summary.json is the rollup you'll usually read:

Field Meaning
warms_fired Warm requests sent
cache_read_total Tokens read back by those warms
episodes Idle-with-subagent episodes that ended in a successful resume (a rewrite actually avoided)
rewrite_avoided_tokens Tokens that would have been re-written, summed across episodes
warm_cost What warming cost you: 0.1 × cache_read_total
rewrite_avoided_cost What it saved: 1.25 × rewrite_avoided_tokens
net_savings rewrite_avoided_cost − warm_cost

All three cost figures are in base-input-token units (token counts already weighted by their cache multiplier). To turn net_savings into dollars, multiply it by your model's price per input token:

dollars saved ≈ net_savings × (input token price)

For example, at an input price of $3 / 1M tokens, a net_savings of 1_200_000 is about 1_200_000 × $3 / 1_000_000 = $3.60 saved that session.

联系我们 contact @ memedata.com