---
schemaVersion: 1
slug: gp-31-20260205-dejavucoder-prompt-caching-part1-tips
ticketId: GP-31
lang: zh-tw
title: Prompt Caching 省錢指南：你的 API 帳單可以少一個零（系列 1/3）
summary: 一個 AI 工程師把 user-specific data 塞進 system prompt，帳單直接翻倍的故事。以及六個讓你穩穩打中 prompt cache 的實戰技巧。（三篇系列第一篇）
originalDate: 2025-11-30
translatedDate: 2026-02-05
source: "@dejavucoder on bearblog"
sourceUrl: https://sankalp.bearblog.dev/how-prompt-caching-works/
author: null
authorshipNote: null
canonicalUrl: https://gu-log.vercel.app/posts/gp-31-20260205-dejavucoder-prompt-caching-part1-tips
status: published
replacementTicketId: null
replacementUrl: null
---

# Prompt Caching 省錢指南：你的 API 帳單可以少一個零（系列 1/3）

> **來源:** [@dejavucoder on bearblog](https://sankalp.bearblog.dev/how-prompt-caching-works/)

> 📘 這是「Prompt Caching 深入淺出」系列的 **第一篇（共三篇）**。
>
> - **Part 1（本篇）：** 省錢指南 — 為什麼 prompt caching 重要 + 六個實戰 tips
> - **Part 2：** [LLM 推理基礎](https://gu-log.vercel.app/posts/gp-32-20260205-dejavucoder-prompt-caching-part2-kv-cache) — KV Cache 是什麼？記憶體的噩夢
> - **Part 3（即將推出）：** Paged Attention + Prefix Caching — 深入引擎底層
>
> 原文作者：Sankalp Shubham（[@dejavucoder](https://x.com/dejavucoder)），Nevara 的 Founding AI Engineer，專注 AI engineering、context engineering 和 coding agents。

---

## 💸 一個帳單翻倍的故事

Sankalp 在 Nevara（一家 AI 銷售助理新創）趕 deadline，要做一個 chat + tool calling 的功能。時間緊迫，先 ship 再說，prompt caching 什麼的之後再想。

一週後他開始優化，才發現自己犯了一個「看起來很合理但其實很蠢」的錯誤 —

**他把每個用戶的專屬資料塞進 system prompt 的尾巴。**

他的邏輯是這樣的：

> 0. \[system prompt + tool definitions + **用戶專屬資料**\]
> 1. user: 幫我建這個 feature
> 2. assistant: 程式碼在哪？
> 3. user: 去看 kv\_caching 那個資料夾
> 4. assistant: 好的我去看
> 5. tool output: _讀檔案中…_
> 6. …

他心想：「沒問題啊，從第 4 輪開始，前面 0-3 都一樣，cache 會打中的。」

**對，但只對了一半。**

> **Mogu 補個刀：**
>
> 這就像你在全聯買東西，結帳的時候覺得「我有帶購物袋耶，很環保！」
>
> 但你忘記你開車來的，而且冷氣開著沒關。
>
> 節省了一個袋子的塑膠，浪費了一公升的油 ┐(￣ヘ￣)┌

他只想到「同一個用戶的對話裡面」可以打中 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。一千個用戶就算一千次。**

帳單：📈📈📈

> **Mogu 插嘴：**
>
> Sankalp 原文的自白很真實：
>
> 他說自己當初的心智模型是把 LLM 推理想像成「一台本地跑的同步引擎」— 一個用戶一個 process，跑完再跑下一個。
>
> 但實際上 OpenAI 和 Anthropic 的推理引擎是異步分散式系統 — 多 GPU、多節點、有 scheduler、有 message queue，像一個超大型的非同步餐廳廚房。
>
> **你的 system prompt 不是只給你一個人用的，它是給整個組織共用的。**
>
> 想通這件事之後，優化方向就完全不一樣了 (๑•̀ㅂ•́)و✧

---

## 🧮 [Prompt](https://gu-log.vercel.app/glossary#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 的那次呼叫，你要付更多錢。

> **Mogu 想補充：**
>
> Sankalp 在 X 上直接噴 Anthropic「so greedy（太貪了吧）」 — 因為 OpenAI 的 cache write 不額外收費。
>
> 不過他後來也自己圓回來了：從工程師的角度看，把 KV tensors 存在 GPU VRAM 裡面是有成本的，這個額外收費其實反映了真實的硬體成本。
>
> 我個人覺得兩邊都有道理。但 Anthropic 已經夠貴了你還收 cache write 溢價… 嗯… 反正我是 [Mogu](https://gu-log.vercel.app/glossary#mogu)，我不評論老闆的定價策略 (⌐■\_■)

**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](https://gu-log.vercel.app/glossary#codex)、[Claude Code](https://gu-log.vercel.app/glossary#claude-code)、Cursor 的時候，去看 API usage — 大部分 token 都是 input（把整個 codebase 的 context 餵進去），output 只佔一小部分（生成的程式碼）。

這意味著：**你的錢幾乎都花在 input token 上。** 而 prompt caching 就是在這裡幫你省錢的。

如果沒有 prompt caching，你每次對話都要重新 prefill 整個 context。帳單會爆到你懷疑人生。

> **Mogu 想補充：**
>
> 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](https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus)（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 就能跨用戶共享。

> **Mogu 碎碎念：**
>
> 想像 system prompt 是便利商店的固定菜單看板 — 每個客人進來都看到一樣的菜單。
>
> 如果你把每個客人的名字印在菜單上（「歡迎光臨！王小明你好！」），那你就得為每個客人印一份新菜單。
>
> **把名字印在收據上就好了嘛。** 菜單不要動。
>
> 這就是 prefix stability 的精髓 ╰(°▽°)╯

---

### Tip 2：保持 Context Append-Only — 不要截斷

Sankalp 的 feature 有很多 tool call，tool 的輸出會存在 messages array 裡。對話變長之後，他擔心 [context rot](https://gu-log.vercel.app/glossary#context-rot)（太長的 context 讓模型表現下降），所以開始**截斷 tool call outputs**。

結果 — **prefix 被破壞了。** 因為你改動了中間的內容，從那個點之後所有的 cache 都失效。

他最後的決定：**停止截斷，讓 context 保持 append-only。** 寧願 context 長一點，也要保住 cache hit 帶來的成本和延遲優勢。

> 他猜測 Claude Code 的 compaction（壓縮）機制應該也是 append-only 的。

**核心原則：** 只在尾巴加東西，不要去動前面已經有的東西。就像寫日記 — 你可以一直往後寫，但不要回去塗改前面的內容。

> **Mogu OS：**
>
> 這個 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)

一個參數。就這樣。但省下的錢可能超乎你想像。

> **Mogu 碎碎念：**
>
> 這個 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 原則。**

> **Mogu 插嘴：**
>
> 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，它只是提高機率。

> **Mogu 真心話：**
>
> 想像你去一個有 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）結尾放一個

> **Mogu OS：**
>
> 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

下一篇見 ╰(°▽°)╯

---

**延伸閱讀：**

- [Manus — Context Engineering for AI Agents](https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus)
- [OpenAI — Prompt Caching Guide](https://platform.openai.com/docs/guides/prompt-caching)
- [Anthropic — Prompt Caching Docs](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) (๑˃ᴗ˂)ﻭ
