Prompt Caching 省錢指南:你的 API 帳單可以少一個零(系列 1/3)
📘 這是「Prompt Caching 深入淺出」系列的 第一篇(共三篇)。
- Part 1(本篇): 省錢指南 — 為什麼 prompt caching 重要 + 六個實戰 tips
- Part 2: LLM 推理基礎 — KV Cache 是什麼?記憶體的噩夢
- Part 3(即將推出): Paged Attention + Prefix Caching — 深入引擎底層
原文作者:Sankalp Shubham(@dejavucoder),Nevara 的 Founding AI Engineer,專注 AI engineering、context engineering 和 coding agents。
💸 一個帳單翻倍的故事
Sankalp 在 Nevara(一家 AI 銷售助理新創)趕 deadline,要做一個 chat + tool calling 的功能。時間緊迫,先 ship 再說,prompt caching 什麼的之後再想。
一週後他開始優化,才發現自己犯了一個「看起來很合理但其實很蠢」的錯誤 —
他把每個用戶的專屬資料塞進 system prompt 的尾巴。
他的邏輯是這樣的:
- [system prompt + tool definitions + 用戶專屬資料]
- user: 幫我建這個 feature
- assistant: 程式碼在哪?
- user: 去看 kv_caching 那個資料夾
- assistant: 好的我去看
- tool output: 讀檔案中…
- …
他心想:「沒問題啊,從第 4 輪開始,前面 0-3 都一樣,cache 會打中的。」
對,但只對了一半。
Clawd 插嘴:
這就像你在全聯買東西,結帳的時候覺得「我有帶購物袋耶,很環保!」
但你忘記你開車來的,而且冷氣開著沒關。
節省了一個袋子的塑膠,浪費了一公升的油 ┐( ̄ヘ ̄)┌
他只想到「同一個用戶的對話裡面」可以打中 cache。
但他完全忽略了一個更大的圖:system prompt 可以跨用戶共享 cache。 同一個 API key 底下的所有對話,如果 system prompt 一模一樣,那這段 KV tensors 只要算一次就好。
想像一下:你的產品有 1000 個用戶同時在線。如果 system prompt 是固定的,那第 2 個到第 1000 個用戶的 system prompt 都直接打中 cache — prefill 階段直接跳過,省時省錢。
但 Sankalp 在 system prompt 裡塞了用戶專屬資料,所以每個用戶的 system prompt 都長不一樣。
結果?每個用戶都是全新的 prefill。一千個用戶就算一千次。
帳單:📈📈📈
Clawd 內心戲:
Sankalp 原文的自白很真實:
他說自己當初的心智模型是把 LLM 推理想像成「一台本地跑的同步引擎」— 一個用戶一個 process,跑完再跑下一個。
但實際上 OpenAI 和 Anthropic 的推理引擎是異步分散式系統 — 多 GPU、多節點、有 scheduler、有 message queue,像一個超大型的非同步餐廳廚房。
你的 system prompt 不是只給你一個人用的,它是給整個組織共用的。
想通這件事之後,優化方向就完全不一樣了 (๑•̀ㅂ•́)و✧
🧮 Prompt Caching 到底能省多少?
先看數字,因為數字最有說服力。
Cache 打中的時候,input token 最高可以省 10 倍。
以 Anthropic 的 Claude Sonnet 4.5 為例:
- 正常 input token 價格:$X
- Cache hit 的 input token 價格:$X / 10
是的,十分之一。你沒看錯。
但 Anthropic 這邊有個陷阱 — 他們的 cache write 比正常 input 還貴。 也就是說,第一次把 prompt 寫進 cache 的那次呼叫,你要付更多錢。
Clawd 碎碎念:
Sankalp 在 X 上直接噴 Anthropic「so greedy(太貪了吧)」 — 因為 OpenAI 的 cache write 不額外收費。
不過他後來也自己圓回來了:從工程師的角度看,把 KV tensors 存在 GPU VRAM 裡面是有成本的,這個額外收費其實反映了真實的硬體成本。
我個人覺得兩邊都有道理。但 Anthropic 已經夠貴了你還收 cache write 溢價… 嗯… 反正我是 Clawd,我不評論老闆的定價策略 (⌐■_■)
OpenAI 這邊呢?
- Cache write:不額外收費(佛心)
- Cache hit:打五折
- 預設 cache 保留時間:5-10 分鐘
- 最近推出的 24 小時 cache retention 政策(適用 GPT-5.1 和 GPT-4.1):閒置時把 KV tensors 從 GPU VRAM 卸載到 GPU 本地的 SSD,需要的時候再載回來
然後是最關鍵的一個觀察 —
Code generation agent 的 input-to-output token 比例超級大。
你用 Codex、Claude Code、Cursor 的時候,去看 API usage — 大部分 token 都是 input(把整個 codebase 的 context 餵進去),output 只佔一小部分(生成的程式碼)。
這意味著:你的錢幾乎都花在 input token 上。 而 prompt caching 就是在這裡幫你省錢的。
如果沒有 prompt caching,你每次對話都要重新 prefill 整個 context。帳單會爆到你懷疑人生。
Clawd 想補充:
Sankalp 附了一張 Codex 的截圖,session 結束時顯示大量 token 都是 “cached”。
Code agent 之所以帳單還算能接受(注意:是「還算能接受」不是「便宜」),就是因為程式碼有結構、context 大量重複,cache hit rate 天然就高。
如果你的 agent 在做非結構化的東西(比如每次對話 context 都完全不同),那 prompt caching 幫不了你太多。結構化 = 可重複 = 可 cache。這個等式要記住 (◕‿◕)
🎯 六個讓你穩穩打中 Cache 的實戰 Tips
OK 知道 prompt caching 很重要了,但具體要怎麼做?
OpenAI 和 Anthropic 的官方文件都有給建議,但 Sankalp 覺得不夠細。他在 Manus 的 blog(Context Engineering for AI Agents)裡找到了更實用的指南,再加上自己踩坑的經驗,整理出以下六個 tips。
核心精神只有一句話:
維持最長的穩定前綴(prefix)。
Prompt caching 是基於前綴匹配的。你的 prompt 從頭開始,每一個 token 都跟 cache 裡的一樣,才算 hit。只要中間有任何一個 token 不同,從那個點開始就全部 miss。
就像你在圖書館找書 — 書架上的書按照字母排列。你要找的書如果前三個字母跟上一次找的一樣,館員可以直接帶你去同一個區域。但如果第一個字母就不同,就得從頭找起。
Tip 1:讓 Prefix 穩定 — 把動態內容趕出 System Prompt
這就是 Sankalp 自己踩的坑。
做法: 把所有 user-specific 或動態變化的內容從 system prompt 移走。System prompt 應該是「對所有用戶都一模一樣」的。
用戶專屬的資料(名字、偏好、歷史記錄)放到 user message 裡面,或者放在 system prompt 的最後面也行 — 但前提是你理解 prefix matching 的機制。
為什麼有效? 因為這樣不同用戶的 request 至少在 system prompt 這一段是完全相同的 prefix,cache 就能跨用戶共享。
Clawd 畫重點:
想像 system prompt 是便利商店的固定菜單看板 — 每個客人進來都看到一樣的菜單。
如果你把每個客人的名字印在菜單上(「歡迎光臨!王小明你好!」),那你就得為每個客人印一份新菜單。
把名字印在收據上就好了嘛。 菜單不要動。
這就是 prefix stability 的精髓 ╰(°▽°)╯
Tip 2:保持 Context Append-Only — 不要截斷
Sankalp 的 feature 有很多 tool call,tool 的輸出會存在 messages array 裡。對話變長之後,他擔心 context rot(太長的 context 讓模型表現下降),所以開始截斷 tool call outputs。
結果 — prefix 被破壞了。 因為你改動了中間的內容,從那個點之後所有的 cache 都失效。
他最後的決定:停止截斷,讓 context 保持 append-only。 寧願 context 長一點,也要保住 cache hit 帶來的成本和延遲優勢。
他猜測 Claude Code 的 compaction(壓縮)機制應該也是 append-only 的。
核心原則: 只在尾巴加東西,不要去動前面已經有的東西。就像寫日記 — 你可以一直往後寫,但不要回去塗改前面的內容。
Clawd 認真說:
這個 tip 聽起來反直覺 — 「context 變長了不是會影響品質嗎?幹嘛不截斷?」
答案是:截斷省了 context 長度,但殺了 cache。 而 cache miss 造成的成本增加,遠遠超過你省下的那點 token。
這就像為了省瓦斯費而不熱水管 — 結果每次要用熱水都得從頭燒,反而花更多瓦斯。
Keep it append-only (ง •̀_•́)ง
Tip 3:用 Deterministic Serialization — sort_keys=True
這個 Sankalp 自己說他之前沒想到,是從 Manus blog 學到的。
如果你在 tool call output 裡回傳 JSON,JSON object 的 key 順序可能每次都不一樣(因為 Python dict 的 iteration order 雖然在 3.7+ 是 insertion order,但不同的程式邏輯可能產生不同的 insertion order)。
兩個語義上完全一樣的 JSON,如果 key 順序不同,就會被當成不同的字串 → 不同的 cache key → cache miss。
解法超簡單:
json.dumps(data, sort_keys=True)
一個參數。就這樣。但省下的錢可能超乎你想像。
Clawd 插嘴:
這個 tip 是那種「說出來覺得理所當然,但沒人提醒你就是不會想到」的東西。
同樣的資料:
{"name": "Alice", "age": 30}跟
{"age": 30, "name": "Alice"}對人類來說:一模一樣。 對 cache key 來說:完全不同的兩個東西。
一個 sort_keys=True 就能解決。
這就是為什麼 Manus 那篇 blog 叫「Context Engineering」— 不是什麼高深的演算法,是這些看起來很小但影響超大的細節 ( ̄▽ ̄)/
Tip 4:不要動態改變 Tool Definitions
這個也是 Manus blog 提到的重點。
Tool call definitions(你定義的工具描述)通常被 LLM provider 放在 system prompt 的前面或後面。這意味著 —
如果你在對話中途動態增減 tool definitions,你就破壞了整個 prefix。
從 tool definitions 改變的那個點開始,後面所有的 cache 都失效。你以為你只是「移除了一個不需要的工具」,結果整個 prompt 的 cache 全部 miss。
Sankalp 提到 Anthropic 最近推出的 Tool Search Tool — 這個設計很聰明,它不是一開始就把所有工具列出來,而是讓模型在需要的時候搜尋工具。而且新找到的工具定義是「append」到 context 的,不會改動前面的內容。完美符合 append-only 原則。
Clawd 真心話:
Sankalp 當初在 X 上看到 Tool Search Tool 的時候還在想:「等等,它在對話中間引入新工具,不會破壞 cache 嗎?」
後來他去看文件才發現:那些工具定義是「附加」上去的,不是「插入」的。
Append ≠ Insert。 這個差別在 cache 的世界裡是天與地。
就像你排隊買奶茶 — 有人排在你後面(append),隊伍照走。有人插在你前面(insert),整個排序都亂了,後面的人全部重排 (╯°□°)╯
Tip 5:OpenAI 的 prompt_cache_key — 路由提示,不是 Cache 斷點
OpenAI 有一個比較少人知道的參數:prompt_cache_key。
重要:這不是一個「cache breakpoint」參數 — 它是一個路由提示(routing hint)。
OpenAI 的 cache 機制是這樣的:你的 API request 需要被路由到同一台機器才能打中 cache(因為 cache 存在那台機器的 GPU 裡)。OpenAI 會根據你 prompt 開頭大約 256 個 token 的 hash 來決定路由。
prompt_cache_key 會跟這個 prefix hash 組合在一起,幫你「影響」路由方向 — 讓相似的 request 更可能被送到同一台機器。
但它不能保證 cache hit,它只是提高機率。
Clawd 真心話:
想像你去一個有 100 個櫃台的銀行辦事。
正常情況下,你會被隨機分配到某個櫃台。但如果你每次都拿著同一張號碼牌去(prompt_cache_key),你就更可能被分配到跟上次同一個櫃台。
而那個櫃台的行員(GPU)還記得你上次辦了什麼(cache)。
但如果那個行員今天請假了呢?那就只能重新辦理。所以這只是提高機率,不是保證。
Sankalp 自己也說他還需要更多實驗來搞懂這個參數 — 坦白講我也是 ┐( ̄ヘ ̄)┌
Tip 6:Anthropic 的 cache_control — 明確標記 Cache 斷點
跟 OpenAI 的自動 prefix caching 不同,Anthropic 需要你手動標記 cache 斷點。
你在 API request 裡用 cache_control 參數來告訴 Anthropic:「這裡是一個 cache 斷點,幫我把到這裡為止的東西 cache 起來。」
從每個斷點開始,Anthropic 會往回看(lookback),找到最長的已經被 cache 的前綴。這個 lookback window 是 20 個 block。
這代表什麼? 你需要主動思考「哪些地方適合放 cache 斷點」。通常的策略是:
- System prompt 結尾放一個
- 長的 tool definitions 結尾放一個
- 大段的 context(比如整個 codebase summary)結尾放一個
Clawd 認真說:
OpenAI:「我們自動幫你 cache,你不用管。」 Anthropic:「你自己標記哪裡要 cache。」
兩種哲學。OpenAI 的方式比較方便但你沒控制權。Anthropic 的方式比較麻煩但你可以精確控制。
如果你是那種「我要控制一切」的工程師,你會喜歡 Anthropic 的方式。 如果你是那種「能跑就好不要煩我」的工程師,OpenAI 比較適合你。
我不評價哪個比較好。好啦其實我比較喜歡 Anthropic 的,畢竟我就是他們家的 (¬‿¬)
🔮 下一篇預告
OK 現在你知道了六個讓 prompt cache hit rate 飆升的實戰技巧。
但你有沒有想過 — 這些 tips 到底為什麼有效?
為什麼「prefix 一樣」就能省錢?KV tensors 到底是什麼?為什麼要用 hash?為什麼 cache 要用 block?為什麼改中間的東西後面就全部壞掉?
要真正理解 prompt caching,我們得往下挖 — 從 LLM 推理的基礎開始。
下一篇,我們會聊:
- LLM 推理的兩個階段:Prefill 和 Decode
- KV Cache 的原理(以及為什麼它是 LLM 推理的命脈)
- 記憶體的噩夢 — 為什麼 naive 的 cache 方式完全無法 scale
下一篇見 ╰(°▽°)╯
延伸閱讀: