3 月底 Claude Code 原始碼洩漏之後,社群裡開始有人回報一個詭異現象:在 Claude Code 對話裡貼上特定字串,session 的 token 消耗會突然暴增——有人說是正常值的十幾倍,也有人說帳單翻了好幾翻,但一開始沒人知道為什麼。

根據洩漏原始碼的分析,問題出在一個叫做 Native Client Attestation 的內部機制。這個機制會掃描 HTTP body 裡的 cch=00000 placeholder 並替換成 hash——但如果使用者自己打出了這個字串,替換邏輯會改動對話內容,導致 prompt cache 全滅,接下來每條訊息都付全價。

社群裡陸續有開發者在 GitHub issue 回報類似症狀:「session 的 token 異常快速耗盡。」很多人以為是自己用太兇就停手了,但根據洩漏的原始碼來看,這不是用量問題。

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

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

帳單到底怎麼算的

要理解 caching,先要知道 LLM 的計費邏輯。

每一次 API 請求進來,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 補個刀:

「完全一樣」這三個字要特別強調。不是「差不多一樣」,是一個 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 自己的工程師,也沒辦法讓所有東西都住進 cacheable zone。

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

洩漏的原始碼裡還有另一個發現:Claude Code 內部似乎追蹤了約 14 種 cache-break vectors——也就是可能讓 prompt cache 悄悄失效的情境清單。(這個數字來自社群對洩漏程式碼的分析,Anthropic 官方從未確認。)

14 種。

Clawd 偷偷說:

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

以前的 prompt engineering 是在跟 AI 溝通——怎麼說才能讓它聽懂。但 2026 年的現實是,prompt 同時也在跟 inference engine 的計費系統博弈。同一個 prompt,結構放對了便宜 10 倍,放錯了貴 10 倍,AI 的輸出品質可能完全沒差。

如果洩漏的資訊準確,一份 14 行的 cache-break checklist 說明了一件事:cache 管理在 Anthropic 內部是被認真對待的工程問題,不是可有可無的優化。

等等,這讓我想到 SD-11(Claude Code 架構拆解那篇)提到的子 agent 設計:根據洩漏原始碼的分析,forked subagents 繼承 parent context 作為 byte-identical copies,很可能就是為了讓 cache 能完整 hit。如果這個推測正確,Anthropic 整個多 agent 架構的核心設計動機之一就是 cache economics,不只是什麼 AI 哲學。 ┐( ̄ヘ ̄)┌


一個字串毀掉整個 session

現在回來把開頭提到的技術細節拆開來講。

根據洩漏原始碼的分析(注意:以下描述基於社群對洩漏程式碼的逆向工程,Anthropic 官方未公開確認這些實作細節),Claude Code 內部有一個 Native Client Attestation(原生客戶端認證)機制。邏輯是:每個 API 請求裡面有一個 placeholder cch=00000。請求被傳出去之前,一個 Zig native 模組掃描整個 HTTP body,把這個字串換成真正的 hash,藉此向 Anthropic 的伺服器證明:這個請求來自真正的 Claude Code binary,而不是第三方 client 在繞過計費。

聽起來很合理。但根據社群開發者的測試和回報,這個掃描邏輯有個副作用:它掃描的是整個 HTTP body,包含使用者的對話內容。

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

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

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

Clawd 碎碎念:

讓我整理一下這個 irony。

如果社群的分析正確,Anthropic 設計了一個機制來防止第三方繞過計費系統(保護自己的營收),結果這個機制的副作用讓使用者多付了錢。

用 Zig 寫 native 模組是聰明的。在 HTTP body 裡換字串做 attestation 也是聰明的。但讓這個換字串的邏輯影響到使用者自己打的文字——聊天框裡的內容,應該是最神聖不可侵犯的部份——就是設計上的盲點了。

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 碎碎念:

我有個頗強烈的意見: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 想補充:

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

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 的 OS:

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

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,每次都全額付費。那不是省不省的問題,是根本沒接上水管。

然後是那個每個人都會不小心做的事:在 system prompt 裡塞動態內容。使用者 ID、當天日期、session 狀態——每一個都是 cache killer。前面講的 stable/dynamic boundary 不是理論,是需要回去翻自己的 codebase 一行一行檢查的東西。

長對話也是隱形殺手。每條訊息加進 context 都會讓 cache key 的「後綴」增長,前面的 stable zone cache hit rate 不受影響,但整體計費還是會漲。定期把舊對話壓縮成摘要——這正是 Claude Code context compaction 背後的原理。

然後,如果有在用 Claude Code——根據目前社群的測試結果,避免在對話裡打出 cch=00000。在 Anthropic 確認或修復之前,這個字串出現很可能代表 cache 被清掉了。

(這篇文章裡提了好幾次這個字串。如果有人用 Claude Code 讀這篇文章的話… (╯°□°)⁠╯)

Clawd 碎碎念:

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

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

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


結語

這整件事的弔詭之處在哪?

DANGEROUS_uncachedSystemPromptSection

根據洩漏的原始碼,Anthropic 的工程師很清楚 cache 管理是危險地帶。一個名字帶 DANGEROUS 的 constant,一張 cache-break vector 清單,從架構層面圍繞 cache economics 在設計——但這些知識從來沒有出現在任何官方文件裡。開發者只能從洩漏事件和社群逆向工程中拼湊出這張地雷圖。

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