| |

Claude 緩存命中率掉到 0?3 步用 diagnostics 查出是哪一段變了

Claude 緩存命中率掉到 0?3 步用 diagnostics 查出是哪一段變了

作者注:Claude 提示詞緩存命中率突然掉到 0,或者賬單一直降不下來,可以用 diagnostics 三步找到前綴分叉的位置,再結合 usage 判斷緩存是否真正命中。本文也會說明走 API易 中轉時怎麼自查。(如果你有這個問題,可以在看完這篇文章後把這篇文章發給你的 Agent,讓他去排查)

緩存沒命中不是玄學,API 會告訴你前綴從哪一段開始分叉。你只需要把上一輪響應的 id 傳回去,就能看到第一個不一致的地方落在 model、system、tools 還是 messages。

這不是一篇功能發佈解讀。本文寫給已經在用 Claude 提示詞緩存、但命中率突然歸零或賬單居高不下的開發者,所有排查步驟都會寫清楚每一步會看到什麼。

核心價值:讀完本文,你能在三輪請求之內定位緩存失效的原因。你也能分清 diagnostics 和 usage 各自說明什麼,並知道走中轉時用哪兩個字段互相比對。

claude-prompt-cache-diagnostics-hit-rate-troubleshooting-zh-hant-image-0


Claude 緩存診斷 diagnostics 核心要點

先用一句話說清它是什麼:把上一輪響應的 id 傳進來,API 會對比兩次請求,指出第一個分叉點落在 model、system、tools 還是 messages。以前緩存悄悄失效時,你只能看到 usage.cache_read_input_tokens 變成 0,卻不知道是哪一段變了。

要點 說明 對你的價值
開關 請求體頂層帶 diagnostics 對象纔會存指紋 不用改業務邏輯,加一個字段即可
對比方式 previous_message_id 傳上一輪響應 id 直接定位第一個分叉段
結論字段 cache_miss_reason 給出六種類型之一 每種類型都有明確的修復位置
配合指標 需要和 usage.cache_read_input_tokens 一起看 避免把「沒分歧」誤判成「沒生效」
可用範圍 只有官方 Claude API 提供 走其他通道需要換用 usage 自檢

Claude 緩存診斷的時間線

這個功能在 2026 年 5 月以 beta 形式上線,當時需要帶 anthropic-beta: cache-diagnosis-2026-04-07 這個 header。9 月期間規則收緊過一次:只有顯式帶了 diagnostics 對象的請求才會存指紋,只帶 header 的請求不會被記錄。

9 月 23 日它轉爲正式版,不再需要 cache-diagnosis-2026-04-07,帶着舊 header 的請求也照常能用。現在 POST /v1/messages 的響應裏總會出現 diagnostics 字段,沒有開啓時值爲 null。所以看到這個字段是 null,並不代表接口出了問題。

指紋裏只保存哈希和 token 數估計,不保存原始提示詞內容。它按 response id 索引,作用域限定在本組織和本 workspace,保留時間較短。


3 步用 diagnostics 排查 Claude 緩存命中率

下面三步按官方字段說明來寫。如果你通過 API易 調用,Messages 原生接口的 base_url 填 https://api.apiyi.com/v1,請求路徑是 /v1/messages。不過要注意,後文實測部分會說明中轉場景下 diagnostics 的實際回傳情況。

claude-prompt-cache-diagnostics-hit-rate-troubleshooting-zh-hant-image-1

第 1 步:在請求頂層加 diagnostics 對象

diagnostics 是唯一的開關,沒帶它的請求不會存指紋,後面也就無從比較。第一輪沒有可以對比的對象,所以 previous_message_id 傳 null,意思是「從這一輪開始記錄」。

  • 請求頭保留常規版本號:anthropic-version: 2023-06-01
  • 正式版之後不需要再帶:anthropic-beta: cache-diagnosis-2026-04-07
  • 在請求體頂層加上:"diagnostics": {"previous_message_id": null}
  • 需要緩存的 system 塊照常打斷點:"cache_control": {"type": "ephemeral"}
  • 會看到什麼:響應裏 diagnostics 爲 null,cache_read_input_tokens 通常爲 0,cache_creation_input_tokens 大於 0

第一輪看到 null 不代表失敗。這一輪是在寫緩存,本來就沒有可比的上一輪,不需要排錯。

第 2 步:之後每一輪都傳上一輪響應的 id

從第二輪開始,把上一輪響應的 id 填進 previous_message_id。最常見的錯誤是隻在第一輪帶 diagnostics,之後漏掉。下一輪指向的那次請求沒有被記錄,結果會返回 previous_message_not_found。

  • 第二輪請求體:"diagnostics": {"previous_message_id": "<第一輪響應的 id>"}
  • 第三輪請求體:"diagnostics": {"previous_message_id": "<第二輪響應的 id>"}
  • 多輪循環的規律:第一輪傳 null,第二輪傳 r1.id,第三輪傳 r2.id,以此類推
  • 流式調用時,結果出現在 message_start 事件裏,id 同樣一輪輪往後傳
  • 會看到什麼:相鄰兩輪間隔夠短、且在同一 workspace 內時,響應裏開始出現比較結果

第 3 步:讀 cache_miss_reason 並對號修復

diagnostics 有三種取值,讀之前先分清楚:

  • null:請求沒帶 diagnostics;或者 previous_message_id 爲 null;或者比較已經完成且沒有發現分歧
  • {"cache_miss_reason": null}:比較還在進行中,因爲響應開始得太快,這一輪先不下結論,看下一輪
  • cache_miss_reason 是一個對象:已經有結論,裏面帶着分叉類型和細節
  • 會看到什麼:拿到類型後按下一節的對照表修改,先修最早的那個分叉,因爲它只報第一個分歧點,後面的問題可能被它擋住了

🚀 快速開始:如果想先確認自己的緩存前綴能不能寫入和命中,可以在 API易 apiyi.com 用同一個 Key 切換 Claude 各型號做兩輪對照,重點看 usage 裏緩存寫入和讀取兩個數字是否對得上。


cache_miss_reason 六種類型與修復位置

拿到 cache_miss_reason 之後,按下表對應到要改的地方:

類型 含義 改哪裏
model_changed 模型和上一輪不同,比如路由、A/B 測試或 fallback 換了模型。緩存按模型隔離 同一段緩存對話裏固定模型
system_changed system 變了,最常見的是插入了時間戳、請求 ID 等每次都不同的值 system 改成逐字節穩定的常量,動態數據挪到斷點之後的第一條 user 消息
tools_changed 工具被增刪、調換順序,或 input_schema 序列化結果不穩定 每輪發送同一份工具列表,順序固定,序列化按鍵排序
messages_changed 更早的消息被改動、重排或刪除,而不是在末尾追加 歷史只追加,assistant 內容和工具結果原樣回傳
previous_message_not_found 找不到對應指紋,不等於請求變了 每輪都帶 diagnostics,不跨 workspace,間隔不要太長
unavailable 這一輪拿不到診斷結果,比如 tool_choice、thinking、context_management、output_config、output_format 或 beta header 集合不一致,也可能是對話太長 一條緩存對話的整個生命週期裏,這些參數保持不變

四個 *_changed 類型還會附帶 cache_missed_input_tokens,表示分叉點之後大約丟掉了多少可緩存的輸入 token。這個數是按字節長度換算的,只能用來判斷數量級,不是賬單數字,偶爾還會比 usage.input_tokens 還大。


單看 diagnostics 會誤判:結合 usage 判斷 Claude 緩存命中

diagnostics 回答的是「我的請求變了嗎」,usage.cache_read_input_tokens 回答的是「緩存命中了嗎」。兩個問題不一樣,只看其中一個就容易得出錯誤結論。

diagnostics 結果 緩存讀 token 結論 怎麼做
null 高 前綴穩定,緩存命中 不用處理
null 低或爲 0 請求沒變,但緩存已經過期 縮短請求間隔,或改用 1 小時 TTL
*_changed 低 請求變了,導致沒有命中 按上一節的對照表修復
*_changed 高 變化發生在靠後的位置,前面的斷點仍然命中 值得修,但影響較小

需要再強調一次:null 不等於沒生效,它可能正是最好的結果。還有幾種情況不適用這張表:第一輪,cache_miss_reason 爲 null(比較還在進行),以及 previous_message_not_found 和 unavailable。這幾種情況下根本沒有產生有效比較。

claude-prompt-cache-diagnostics-hit-rate-troubleshooting-zh-hant-image-2

如果你還沒確定哪一輪屬於哪個格子,可以在 API易 apiyi.com 上把同一份請求連發兩輪,記錄每輪的緩存讀寫數字,再對照上面的矩陣逐項排除。


Claude 緩存命中率低的常見原因:三個硬條件

很多時候命中率低,並不是請求變了,而是緩存從一開始就沒寫進去。下面三個條件缺一個,緩存就不會生效:

  1. 必須顯式打 cache_control:可以放在具體內容塊上做顯式斷點,也可以在請求頂層放一個 cache_control,由系統自動把斷點打在最後一個可緩存塊上。如果 content 是純字符串,沒有可以掛標記的內容塊,就永遠不會緩存。
  2. 長度達到最小可緩存長度:在 Claude API 上,Sonnet 5.5 和 Opus 5.5 都是 512 tokens。前綴不夠長時,緩存會被靜默忽略,也不會報錯。
  3. 前綴逐字節相同:歷史只追加,system 和 tools 保持穩定。空格、JSON 字段順序、時間戳任何一處變了,都會被當成新的前綴。

API易 的緩存文檔還寫了兩個硬限制:單次請求最多 4 個 cache_control 斷點,每個斷點只向前回溯最近 20 個 content block。長的多輪對話只在最後一條打標記,是覆蓋不到全部歷史的。

TTL 也經常被忽略。默認是 5 分鐘,每次命中都會續期,但計時從寫入或讀取那次請求開始算,而不是從響應結束時算。如果一次流式響應輸出了 4 分鐘,下一次請求大約要在 1 分鐘內發出。需要更長的窗口時,可以在 cache_control 裏寫 "ttl": "1h"。


Claude 緩存命中率掉到 0 的成本賬

下面先列出價格,單位是美元/百萬 tokens,數據時間爲 2026-10-25,以控制檯實時價格爲準:

型號 輸入 輸出 緩存讀 5 分鐘寫 1 小時寫
Claude Sonnet 5.5 $2 $10 $0.10 $2.50 $4
Claude Opus 5.5 $4 $20 $0.20 $5 $8
Claude Haiku 5.5(10 萬 tokens 以內) $0.1 $0.5 $0.01 $0.125 $0.20
Claude Haiku 5.5(超過 10 萬 tokens) $0.50 $2.50 $0.05 $0.625 $1

倍率需要分開看。API易 緩存指南給的通用口徑是:命中 0.1×,5 分鐘寫入 1.25×,1 小時寫入 2×。Sonnet 5.5 在 10 月 7 日隨官方調整,緩存讀從 $0.20 降到 $0.10,變成基礎輸入價的 0.05×,寫入價沒有變。

這次調整不是全線降價,Sonnet 5 的緩存讀仍然是 $0.20。所以不要拿 0.1× 去算 Sonnet 5.5,具體型號與價格以平臺型號列表爲準。

回本點按平臺的通用 0.1× 口徑計算:5 分鐘 TTL 複用兩次就能回本(1.25 + 0.1 = 1.35,低於不緩存的 2.0);1 小時 TTL 需要複用三次(2 + 0.2 = 2.2,低於 3.0)。

再看一個算例。Sonnet 5.5,固定前綴 3 萬 tokens,一天發 200 次,全部在 5 分鐘 TTL 內:

  • 全部命中:200 × 3 萬 × $0.10/百萬 = $0.60,加上一次寫入 $0.075,合計約 $0.675/天
  • 全部不命中:200 × 3 萬 × $2/百萬 = $12/天

兩者的整日賬單差約 17.8 倍,這不是單價差。單價是 $2 對 $0.10,相差 20 倍。如果改用 1 小時 TTL,一次寫入是 $0.12,命中價不變。請求間隔經常超過 5 分鐘時,可以對比一下:多付的這部分寫入費用,和反覆重建前綴的費用相比哪個更低。

claude-prompt-cache-diagnostics-hit-rate-troubleshooting-zh-hant-image-3


走 API易 時如何排查 Claude 緩存命中率

先說實測結果。2026-10-26 我們在 API易 上實測,用 Claude Sonnet 5.5 跑了兩輪:第一輪緩存寫 2523、讀 0;第二輪原樣回傳後,緩存讀 2523、寫 0,token 數完全對得上。Claude Opus 5.5 用長前綴同樣能寫入,也能命中。

但響應裏沒有 diagnostics 字段。非流式調用沒有,流式調用的 message_start 裏也沒有。我們又帶上 beta header 重發了一次,還故意修改 system 製造了一次真實的不命中(讀 0、寫 2526),響應裏仍然沒有這個字段。所有請求都返回 200,沒有報錯。中轉層的上游通道不一定是官方那條 Claude API,因此在中轉場景下,主要靠 usage 裏的兩個字段互相比對。

自檢現象 說明
本輪 cache_read_input_tokens 精確等於上一輪 cache_creation_input_tokens 前綴完整命中
本輪讀明顯小於上一輪寫 只命中了更靠前的一段,中間有內容變了
寫入一直爲 0 沒打標記、長度不夠,或者用了 OpenAI 兼容格式
第二輪又在寫入,而讀爲 0 前綴變了,檢查時間戳、UUID、序列化順序

另外要特別注意:緩存只在原生 /v1/messages 下生效。用 OpenAI 兼容格式 /v1/chat/completions 調用 Claude,無論怎麼傳參都拿不到緩存計費。

💡 排查建議:API易 apiyi.com 會把 cache_control 原樣轉發給上游,響應裏的兩個緩存字段也原樣返回,代碼不需要爲中轉層額外適配。把「本輪讀 = 上一輪寫」做成日誌告警,就能取代大部分人工排查。


Claude 緩存診斷 diagnostics 的限制

diagnostics 不能解決所有問題,使用前先了解它的邊界:

  • 只有官方 Claude API 提供,Amazon Bedrock 和 Google Cloud 託管的 Claude 都沒有
  • 指紋只短期保留,需要連續幾輪、間隔較短地發送請求
  • 兩次請求必須屬於同一組織和同一 workspace,可以用響應頭 anthropic-workspace-id 覈對
  • 長對話裏,如果變化藏在消息列表很深的位置,可能只會返回 unavailable
  • 它是 best-effort 機制,拿不到結果也不會阻塞請求,也不會讓請求失敗

常見問題 FAQ

Q1:爲什麼 diagnostics 一直是 null?

分三種情況:沒帶 `diagnostics` 對象;當前是第一輪,`previous_message_id` 爲 `null`;比較完成且沒有分歧。要結合緩存讀數來判斷:讀數高就說明一切正常,讀數爲 0 多半是緩存過期了。如果是走中轉,字段可能根本不會回傳,這時改用 usage 自檢。

Q2:previous_message_not_found 是不是說明請求變了?

不是。它只表示找不到對應的指紋。常見原因有三個:上一輪沒帶 `diagnostics`,上一輪來自別的 workspace,或者間隔太久。每輪都帶上 `diagnostics` 並縮短間隔即可。

Q3:診斷會不會額外收費?

官方文檔沒有把「診斷費用」單獨列爲一項,它隨普通 Messages 請求一起計費。文檔也沒有明確寫不加價,所以計費請以賬單和控制檯爲準。

Q4:走中轉還能用這套診斷嗎?

我們在 API易 的實測中沒有拿到 `diagnostics` 字段,但緩存的寫入和命中都正常。可以用「本輪讀是否精確等於上一輪寫」來判斷,接入方式見 API易 apiyi.com 的緩存計費文檔。

Q5:寫了 cache_control 爲什麼一次都沒寫進緩存?

先檢查三點:content 是不是純字符串;前綴是否達到 512 tokens(Sonnet 5.5 和 Opus 5.5);是否走的是 OpenAI 兼容格式。三者中有任何一個不滿足,緩存都會被靜默跳過。

Q6:同時出現多個問題,應該先修哪個?

先修 `cache_miss_reason` 報出來的那一個。它只報最早的分叉點,修好之後再跑一輪,後面被擋住的問題纔會顯現出來。在 API易 apiyi.com 上用同一份請求反覆回放,可以比較快地逐個排除。


Claude 緩存診斷核心要點 Key Takeaways

  • diagnostics 是什麼:傳入上一輪響應 id,定位前綴第一個分叉段
  • 怎麼開啓:頂層帶 diagnostics,第一輪傳 null,之後每輪都要傳
  • null 的含義:第一輪或沒有分歧時也是 null,不能單獨當作故障信號
  • 怎麼判斷:diagnostics 配合 cache_read_input_tokens 一起看才能下結論
  • 硬條件:顯式打標記、達到 512 tokens、前綴逐字節相同
  • 成本差距:3 萬前綴每天 200 次,整日賬單相差約 17.8 倍
  • 中轉自檢:在 API易 apiyi.com 上看「本輪讀是否等於上一輪寫」

總結

Claude 緩存命中率排查可以歸納爲三件事:

  1. 定位分叉:每輪帶上 diagnostics,讀 cache_miss_reason,先修最早報出的那一處
  2. 交叉判斷:diagnostics 和 usage 兩邊一起看,null 不代表沒生效
  3. 守住硬條件:打好標記,長度達標,歷史只追加,system 和 tools 保持穩定

如果你的調用鏈路拿不到 diagnostics,就用 usage 裏的兩個字段建立自檢。推薦先在 API易 apiyi.com 用兩輪請求驗證緩存讀寫數字,再上線到生產環境。


📚 參考資料

  1. Claude 官方文檔:Cache diagnostics:診斷字段、六種類型與判斷矩陣
  2. 鏈接: platform.claude.com
  3. 說明: 本文 diagnostics 的字段、時間線和限制均以該文檔爲準

  4. Claude 官方文檔:Prompt caching:緩存規則、TTL、最小長度與價格

  5. 鏈接: platform.claude.com
  6. 說明: 最小可緩存長度和官方價格表的來源

  7. API易 型號列表:Claude 各型號的輸入、輸出與緩存讀價格

  8. 鏈接: docs.apiyi.com/models
  9. 說明: 價格數據更新於 2026-10-25

  10. API易 Claude Prompt Caching 緩存計費指南:倍率、回本點與自檢判據

  11. 鏈接: docs.apiyi.com/api-capabilities/claude-prompt-caching
  12. 說明: 中轉場景下的排查依據

作者:APIYI 技術團隊
技術交流:歡迎在 API易 網站首頁聯繫我們,更多資料可訪問 docs.apiyi.com 文檔中心

延伸閱讀

Similar Posts