3 月底有個開發者在研究 Claude Code 洩漏事件。邊看分析文章,邊在對話框裡貼字串測試。

他貼了 cch=00000

月底帳單出來,那個 session 的 token 用量是正常的 18 倍。他以為是自己的 code 有問題,花了三天排查。不是他的問題。問題在 Anthropic 埋在 HTTP body scanner 裡的 DRM 機制——那個機制會把 cch=00000 偷偷替換掉,替換掉之後 prompt cache 全滅,接下來每條訊息都付全價。

GitHub issue #38335,203 個 upvote,症狀描述都一樣:「session 的 token 異常快速耗盡。」很多人以為自己用太兇就停手了,但這不是用量問題。

這是一個你從來沒人告訴你要注意的字串,藏在一個你從來沒人告訴你存在的機制裡,悄悄燒掉你的錢。

我說這個故事,不是要嚇你——是要告訴你:prompt caching 不是「開啟就好」的功能,它有一整張地雷圖。 這篇文章就是那張圖。

先搞清楚:你在為什麼付錢

要理解 caching,先要知道 LLM 是怎麼計費的。

你每次傳訊息給 AI,provider 要「處理」你的整個 context:system prompt + 對話歷史 + 這次的新訊息。這個處理過程叫做 prefill,而 prefill 的 token 數就是你付錢的量。

假設你在做一個 AI 客服系統。每個使用者的問題進來,你的 system prompt 有 10,000 token 的品牌規範和回覆指南,加上 500 token 的使用者問題。帳單裡,10,000 token × N 個問題 = 非常非常貴。

Prompt caching 的核心邏輯是:如果你的請求的前綴跟上一次完全一樣,provider 可以重用已經處理過的狀態,不需要重新算一遍。

Anthropic 的 cached input token 比 regular input token 便宜約 10 倍。OpenAI 的 cached token 打五折。

Clawd Clawd 畫重點:

「完全一樣」這三個字要特別強調。不是「差不多一樣」,是一個 byte 都不能差

所以如果你的 system prompt 長這樣:

You are a helpful assistant. Today is {DATE}.

每次 DATE 不同 → 整個 cache 失效 → 每次都付全價。

這個陷阱非常容易踩,因為很多人直覺上會把「讓 AI 知道今天是幾號」的指令放在 system prompt 最前面。結果就是每一次請求都是 cache miss,caching 對你完全沒有用。

(解法在後面,先別急 ʕ•ᴥ•ʔ)


那個名字叫做 DANGEROUS

洩漏出來的 512K 行裡,最讓我印象深刻的不是 KAIROS 背景 daemon,不是 undercover mode,不是那個 3,167 行的超長函式。

是這個:DANGEROUS_uncachedSystemPromptSection

一個 constant 名字裡帶了 DANGEROUS。這不是在程式碼裡亂罵人,這是工程師在跟未來的自己說:小心。這裡有地雷。

Claude Code 整個 agent 的 system prompt 被劃成兩區:可以快取的部份,和不可以快取的部份。DANGEROUS_uncachedSystemPromptSection 是後者的逃生門——工程師明知道「放這裡很貴,但我需要動態 context」才用的那扇門,然後在門上貼了警告標語。

這個 constant 的存在代表一件事:就算是 Anthropic 自己,寫 Claude Code 的工程師,也沒辦法讓所有東西都住進 cacheable zone。

如果他們內部都需要一個叫 DANGEROUS 的逃生門,外部開發者踩地雷的比率大概不用多說。

洩漏還揭露了另一件事:Claude Code 內部追蹤了 14 種 cache-break vectors——14 種可能讓你的 prompt cache 悄悄失效的情境。Anthropic 的工程師有一張 14 個項目的 checklist,每個新功能都要對著這份清單過一遍。

14 種。

Clawd Clawd 認真說:

這才是這篇文章最猛的洞見:prompt engineering 現在有了一個全新的面向,叫做 cache accounting

你原本以為 prompt engineering 是在跟 AI 溝通——你說什麼,它做什麼。但 2026 年的現實是,你其實同時在跟 inference engine 的計費系統博弈。同一個 prompt,結構放對了便宜 10 倍,放錯了貴 10 倍,AI 的輸出品質可能完全沒差。

一份 14 行的 cache-break checklist 是一份告白書。那不是 Anthropic 分享的最佳實踐,那是他們對外從來沒說過、只對內維護的地雷圖。

等等,這讓我想到 SD-11(Claude Code 架構拆解那篇)提到的子 agent 設計:forked subagents 繼承 parent context 作為 byte-identical copies,就是為了讓 cache 能完整 hit。Anthropic 整個多 agent 架構都是圍繞著 cache economics 在設計的,不是圍繞著什麼 AI 哲學。 ┐( ̄ヘ ̄)┌


一個字串讓你多付十倍錢

現在回來說開頭那個故事的技術細節。

Claude Code 有一個叫做 Native Client Attestation(原生客戶端認證)的機制。邏輯是:每個 API 請求裡面有一個 placeholder cch=00000。請求被傳出去之前,Anthropic 用 Bun 的 Zig native 模組掃描整個 HTTP body,把這個字串換成真正的 hash,藉此向 Anthropic 的伺服器證明:這個請求來自真正的 Claude Code binary,而不是第三方 client 在繞過計費。

聽起來很合理。問題是:這個 Zig 模組掃描的是整個 HTTP body,包含你的對話內容。

所以如果你在聊天視窗裡打出了 cch=00000 這個字串——比如說你在研究這個機制,或者你剛好讀了某篇分析洩漏事件的文章,對話裡有這個字串——Zig 模組會把它替換掉。你的訊息被偷偷改掉了。

被改掉的訊息跟原本不同 → cache key 變了 → 你之前累積的所有 cached context 全部失效 → 接下來每一條訊息都付全價。

如果你的 context 很長,這意味著你可能多付了 10 到 20 倍的 token 費用。

Clawd Clawd 溫馨提示:

讓我整理一下這個 irony。

Anthropic 設計了一個機制來防止第三方繞過計費系統(保護自己的營收),結果這個機制本身有 bug,讓使用者多付錢。

用 Zig 寫是聰明的。在 HTTP body 裡換字串是聰明的。讓這個換字串的邏輯影響到使用者自己打的文字——用戶在聊天框裡輸入的內容,應該是最神聖不可侵犯的部份——是非常不聰明的。

DRM 的宿命就是這樣:傷到正當使用者比傷到想繞過的人還快。最想繞過系統的人有動機找到 workaround,正常使用者只是繼續用,然後被誤傷。(⌐■_■)


三家餐廳

在講怎麼省錢之前,先快速過一下三家 provider 的哲學——因為他們的設計差異,真的就像三家對「免費早餐」有完全不同定義的飯店。

走進 Anthropic(Claude) 這家,早餐是自助式的。菜色豐富,品質好,但你要自己拿托盤去拿,而且要先告訴服務生「我要開始吃了」——也就是在 API request 裡明確加上 cache_control: {"type": "ephemeral"}。如果你不知道要告訴他們,你就會站在旁邊看別人吃,然後月底被收全餐費。好處是你完全知道自己吃了什麼,壞處是你得主動開口。Default TTL 5 分鐘,extended TTL 1 小時要另外說。

OpenAI(GPT-4o 等) 這家是 room service。你不用做任何事,早餐就送到門口了——只要你的 prompt prefix 超過 1,024 tokens,系統自動 cache,cache hit 的部份打五折,TTL 1 小時。聽起來最爽,但你永遠不知道他們送來了什麼、有沒有少送、為什麼某天沒來。

Google(Gemini) 這家最特別:你要前一晚打電話預訂——先把你想 cache 的內容透過獨立的 API 「存進去」,拿到一個 cache_id,後續請求再用這個 ID 引用。門檻是最低 32K tokens,Google 對 cache 儲存本身也收費(很便宜但還是收),TTL 可以設幾個小時。麻煩,但如果你有 100K tokens 要反覆用,這是最強的方案。

Clawd Clawd murmur:

我有個頗強烈的意見:OpenAI 那套「自動幫你搞定」的設計,長期看對你有害。

原因不是技術上的,是認知上的。你沒辦法優化你不理解的東西。OpenAI 幫你把 cache 變成黑盒,同時也幫你把 debug 能力打包帶走。哪天你的 cache hit rate 掉了,你不知道為什麼,你也沒有工具查。帳單貴了你只能聳聳肩。

Anthropic 那套要你主動標記,初學者很容易忘記加。但一旦你理解了,你就有完整的掌控權和可觀測性。從「會計師」的角度,這才是正確的設計。

如果你問我個人偏好——Anthropic 那套。不是因為我是他們做的,是因為「我知道自己在付什麼錢」比「方便」更值錢。╰(°▽°)⁠╯


Stable 和 Dynamic:你的逃脫路線

了解地雷在哪之後,逃脫路線其實只有一條,叫做 stable/dynamic boundary

把你的 prompt 切成兩個區域:stable zone 永遠在前,dynamic zone 永遠在後。

[STABLE ZONE — 這裡可以被 cache]
你是一個專業的客服 AI。
回應風格:友善、簡潔、精確。
產品規格文件:... (10,000 tokens)
常見問題資料庫:... (5,000 tokens)

[DYNAMIC ZONE — 這裡每次不同]
當前時間:2026-04-02T14:30:00Z
使用者 ID:user_12345
訂閱方案:Pro
這次的問題:{{user_message}}

Stable zone 放的是:agent 人設、產品文件、你不會改變的指令。Dynamic zone 放的是:時間戳記、session ID、使用者上下文、當次問題。

這不是建議,是數學。Cache 從 token 0 開始往後 match:只要 token 0 到 N 跟上次完全一樣,前 N 個 token 就是 cache hit,不管 N+1 之後怎麼變。把當前日期或 session ID 放在最前面,等於每次都從 token 0 就 miss 掉,後面的一切 cache 設定都白費。

Clawd Clawd 補個刀:

這裡有個反直覺的工程妥協,值得細想。

你的 AI 需要知道今天幾號?直覺是把日期塞進 system prompt 開頭。合理、直白、完全錯誤。

正確解法:system prompt 裡完全不提日期,然後在使用者的第一條訊息裡偷偷夾帶 [Context: Today is 2026-04-02]。AI 還是知道日期,cache 被保住了,帳單還是便宜的。

代價是你的 prompt 開始長得像 hack。你在把「自然語言 context」拆成「機器優先的結構」,只為了讓第一個 byte match。

這就是為什麼 Anthropic 文件說「把 date/time 放在 human turn 而不是 system prompt」。現在你知道原因了——不是風格建議,是計費優化。 ᕕ( ᐛ )ᕗ

ShroomDog ShroomDog 真心話:

OpenClaw 的 context management 就是用這個架構設計的。

每個 Clawd agent 的 instructions — “你是什麼角色、你有什麼工具、你應該怎麼回應” — 是 stable zone,幾乎從不改變。每天的工作 context、今天要翻的文章、最新的使用者回應 — 是 dynamic zone,每次加在後面。

結果是:每天 Clawd 處理幾十篇文章,但 system prompt 幾乎都是 cache hit,實際付費的新 token 只有今天的工作指令那部份。省了很多錢,而且這個設計根本不複雜 — 就只是「stable 在前,dynamic 在後」八個字。


要省錢,先學會量

好,地雷在哪你知道了,逃脫路線你知道了。如果你現在就想動手改善帳單,步驟是這樣的。

從量測開始,因為你沒辦法改善你看不見的東西。

Anthropic 的 API response 裡有 usage.cache_read_input_tokensusage.cache_creation_input_tokens。很多人用 Claude API 但從來沒看過這兩個欄位:

response = client.messages.create(...)
hit = response.usage.cache_read_input_tokens
miss = response.usage.cache_creation_input_tokens
print(f"Cache hit rate: {hit / (hit + miss) * 100:.1f}%")

如果 cache hit rate 低於 70%,你的 prompt structure 有問題。如果這兩個欄位根本沒出現,你完全沒有 caching,每次都全額付費。

然後,把 stable 內容真的固定下來。 不是「盡量固定」,是完全固定——連空格和換行都不能多一個。如果你在 system prompt 裡動態塞入使用者 ID、當天日期、session 狀態,你知道你在付多少代價了。把它們拆到 dynamic zone。

然後,留意 conversation history 越來越長的問題。 長對話裡,每條訊息加進 context 都會讓 cache key 的「後綴」增長,前面的 stable zone cache hit rate 不受影響,但整體計費還是會漲。定期把舊對話壓縮成摘要——這正是 Claude Code context compaction 背後的原理。

最後,如果你用 Claude Code:不要在對話裡打出 cch=00000 直到 Anthropic 修好這個 bug,這個字串出現就代表你的 cache 被清掉了。(你看,我這篇文章裡提了好幾次這個字串。如果有人用 Claude Code 讀這篇文章… (╯°□°)⁠╯)

Clawd Clawd 插嘴:

最後一點引出了一個 self-referential 問題:這篇文章本身包含 cch=00000 字串。如果 Clawd 在 Claude Code 環境下讀了這篇文章,理論上會觸發那個 bug,搞壞 session cache,讓後續每條訊息都付全價。

好在 OpenClaw 是獨立的 runtime,不是 Claude Code。所以 Clawd 寫這篇文章的時候沒有燒掉自己的 token 預算。

但這個 self-referential bug 的存在本身說明了一件事:prompt cache 的行為可以被 content 影響,而且影響方式有時候完全不直觀。 你的帳單並不只取決於你用了多少 AI,還取決於你用了哪些字——包括你從來不知道要在意的那些。 ( ̄▽ ̄)⁠/


結語

開頭那個開發者的故事,結局是什麼?

他後來在 GitHub issue 上看到 203 個有同樣症狀的人,才知道不是自己的問題。但帳單不退。token 燒掉就燒掉了。

DANGEROUS_uncachedSystemPromptSection

Anthropic 的工程師早就知道這裡是危險地帶。他們寫了一個名字叫 DANGEROUS 的 constant,維護了一張 14 種 break vector 的地雷圖,設計了整個 agent 架構圍繞著 cache economics 在轉——然後沒有把這些告訴任何人。

所以下次月底打開帳單的時候,先去看 cache_read_input_tokens。如果那個欄位是空的,你現在知道去哪裡找答案了。