這個 Cache 設計讓 Claude Code API 費用砍掉九成
Production 環境的 cache 壞掉之後,API 費用單小時暴增十倍。同一天,Anthropic 的工程師剛好解釋了原因。
昨天有一個緊急的 production 工作要處理。做到一半,prompt cache 突然壞掉了。那單一小時的 API 費用,比前三天加起來還高。
時機點有點諷刺。就在同一天晚上,在 Anthropic 建造 Claude Code 的 Thariq,以及同樣來自 Anthropic 的 Lance Martin,先後發文談 prompt caching 的設計。看完他們的說明,我才意識到,我的 cache 打從一開始設計上就很脆弱,不是偶然出錯。
以下是我從這兩篇文章整理出來的重點,並且結合昨天親身踩過的坑來過濾。
Prefix 比對讓順序變成一切
Anthropic API 的 prompt caching 機制,是從請求的開頭逐一比對 token。只要有任何一個字元和快取版本不同,從那個位置之後的所有內容都會 cache miss,沒有部分比對,也沒有跳過的機會。
Claude Code 團隊把 prompt 的排列順序視為基礎建設來對待。靜態的 system prompt 放最前面,接著是 CLAUDE.md,然後才是 session 的情境資料,對話訊息放在最後,因為每一輪都會變動。這樣的排列,讓成本高昂但穩定的前綴段落,能在整個 session 的每一次請求中被快取並重複使用。
已快取的 token 費用只有一般輸入 token 的一成,這個落差完全解釋了為什麼 cache 壞掉會感覺像是費用暴漲了十倍。
我自己犯的錯是把時間戳記嵌入 system prompt 裡面。每次請求都產生新的時間戳,代表最開頭的 token 每次都不同,後面的內容完全無法快取。只是在錯誤的地方加了一行 debug log,每次請求超過十萬個 token 全都要付全額。
Claude Code 團隊也提到,工具定義的順序若是不確定性的,同樣會造成 cache miss。如果你的工具在不同請求之間序列化的順序有所不同,即使工具本身沒有變動,從那個位置開始 cache 就會斷掉。
透過訊息傳遞更新,不要動 system prompt
當 session 中途有情境變化,例如某個檔案被修改、時間更新、模式切換,直覺反應是去更新 system prompt。但這樣做不對。每次修改 system prompt 都會讓整個快取前綴失效。
Claude Code 的做法是在第一次請求之後,就讓 system prompt 保持不動。需要更新的情境,會放進下一則使用者訊息裡,包在 system-reminder 標籤內。模型讀取的方式是一樣的,但 cache 前綴維持完整。
Plan Mode 是個好例子。切換到 Plan Mode 本來可能意味著要替換工具定義,這樣會破壞 cache。但 Claude Code 改成把它實作為一個工具呼叫 (EnterPlanMode),由模型自己決定是否呼叫。工具集合始終不變。當模型判斷遇到了困難的問題,它可以自行進入 Plan Mode,完全不需要修改 system prompt。
同樣的邏輯也適用於模型切換。在對話過程中切換模型會讓 cache 完全失效。Claude Code 的解法是把不同模型跑成獨立情境的 subagent,讓母對話的 cache 保持完好。
隱藏工具而非移除工具
MCP server 可能載入數十個工具。把所有工具定義放進每一次請求,費用很高。但在請求之間移除工具,又會因為工具定義是快取前綴的一部分而破壞 cache。
Claude Code 團隊的解法是 defer_loading。他們不放完整的工具 schema,而是插入輕量的 stub,只包含工具名稱和 defer_loading: true 旗標。這些 stub 每次都維持相同順序,讓 cache 前綴保持一致。當模型真的需要某個工具的完整 schema,它會呼叫 ToolSearch 工具按需載入。
這個模式在 Anthropic API 今天就可以使用,你可以在自己的 agent 裡實作相同的 stub 加搜尋的做法。
Manus 的 peakji 說,cache hit rate 是 production agent 最關鍵的單一指標。昨天之後,我完全同意。
Context 壓縮藏著 cache 陷阱
當一段對話把 context window 填滿,就需要壓縮,把歷史摘要後在精簡的形式下繼續。最直覺的做法是用一個摘要 prompt 呼叫 API,但如果那次摘要呼叫用了不同的 system prompt 或不同的工具定義,就不會匹配到現有的 cache。你會在費用最高的時刻,整段超過十萬 token 的對話全部重新付費處理,完全沒有 cache 的幫助。
Claude Code 的解法是,壓縮呼叫直接重用母對話的 system prompt 和工具定義,只有最後一則使用者訊息換成壓縮指令。母對話的快取前綴依然吻合,你只需要為新訊息和摘要輸出付費。
Anthropic 後來把這個模式直接整合進 API,以 compaction 功能的形式提供。他們也釋出了 auto-caching,只要在請求主體裡設定一次 cache_control,快取斷點就會自動處理。
Cache hit rate 作為營運指標
Claude Code 團隊監控 cache hit rate 的方式,就像維運團隊監控服務可用性一樣。數字下降,就當成事故在處理。
這個思維框架改變了我看待 prompt 設計的角度。每次修改 system prompt、每次重新排列工具順序、每次在 session 中途切換模型,都是潛在的事故。命中快取的 token 是最便宜的,昨天我親身學到了另一種選擇有多貴。
訂閱電子報
獲取關於我最新專案、文章以及 AI 和 Web 開發實驗的更新。