Claude Platform Docs
Messages上下文管理

快取診斷

透過比較連續的請求並精確找出提示前綴發生分歧的位置,診斷非預期的提示快取未命中。

Prompt caching(提示快取)能大幅降低延遲與成本,但前提是您的提示開頭必須與最近的某個請求逐位元組完全相同。工具順序被重新排列、系統提示中插入了時間戳記,或是對較早訊息的編輯,都可能在無聲無息中使快取失效。若沒有快取診斷,唯一的訊號就是 usage.cache_read_input_tokens 降為零,卻沒有任何跡象顯示是什麼發生了變化。

「Cache diagnostics」(快取診斷)填補了這個缺口。傳入您上一個回應的 id,API 便會比較這兩個請求,並告訴您它們在何處發生分歧(模型、系統提示、工具或訊息歷史),讓您能修正根本原因,而不是憑空猜測。

快取診斷的運作方式

當 beta 標頭存在時,API 會為每個請求儲存一份輕量的「fingerprint」(指紋),以回應的 id 作為鍵值。在您的下一個請求中,將該 id 作為 diagnostics.previous_message_id 傳入。API 會為新請求重建指紋,將其與已儲存的指紋進行比較,並在回應中附加一個 diagnostics 物件,描述第一個分歧點。

此比較針對的是請求結構,與快取是否實際命中無關。請參閱搭配 usage 解讀診斷結果,了解如何將 diagnostics 結果與 usage.cache_read_input_tokens 結合使用。

指紋僅包含雜湊值與 token 數量估計值(絕不包含原始提示內容),保留時間有限,範圍限定於您的組織與工作區,且不會用於任何其他用途。

基本用法

在每一輪都傳送 beta 標頭。在第一輪,傳入 "previous_message_id": null 以選擇加入,此時沒有先前的訊息可供比較。在後續各輪,傳入上一個回應的 id

client = anthropic.Anthropic()

SYSTEM = "You are an AI assistant analyzing a large document. <document>...</document>"

# 第 1 輪:以 previous_message_id=None 選擇加入
r1 = client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=SYSTEM,
    messages=[{"role": "user", "content": "Summarize section 1."}],
    diagnostics={"previous_message_id": None},
    betas=["cache-diagnosis-2026-04-07"],
)

# 第 2 輪:參照前一個回應的 id
r2 = client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=SYSTEM,
    messages=[
        {"role": "user", "content": "Summarize section 1."},
        {"role": "assistant", "content": r1.content},
        {"role": "user", "content": "Now summarize section 2."},
    ],
    diagnostics={"previous_message_id": r1.id},
    betas=["cache-diagnosis-2026-04-07"],
)

diagnostics = r2.diagnostics
if diagnostics is None:
    print("No divergence detected.")
elif diagnostics.cache_miss_reason is None:
    print("Comparison still pending.")
else:
    print(f"cache_miss_reason: {diagnostics.cache_miss_reason.type}")

串流

在「streaming」(串流)回應中,diagnostics 會出現在 message_start 事件上。

# 第 2 輪:串流,並參照前一個回應的 id
with client.beta.messages.stream(
    model="claude-opus-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=SYSTEM,
    messages=[
        {"role": "user", "content": "Summarize section 1."},
        {"role": "assistant", "content": r1.content},
        {"role": "user", "content": "Now summarize section 2."},
    ],
    diagnostics={"previous_message_id": r1.id},
    betas=["cache-diagnosis-2026-04-07"],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
    print()
    r2 = stream.get_final_message()

diagnostics = r2.diagnostics
if diagnostics is None:
    print("No divergence detected.")
elif diagnostics.cache_miss_reason is None:
    print("Comparison still pending.")
else:
    print(f"cache_miss_reason: {diagnostics.cache_miss_reason.type}")

message_start 事件帶有完整的 diagnostics 欄位;可能的值請參閱回應格式

在對話迴圈中串接診斷

在多輪對話中,每一輪都將最新回應的 id 作為 previous_message_id 往下傳遞。第一次迭代傳入 null 以選擇加入;之後每次迭代都傳入上一個回應的 id

client = anthropic.Anthropic()

SYSTEM = "You are an AI assistant analyzing a large document. <document>...</document>"

messages = []
prev_id = None

for i, user_message in enumerate(
    ["Summarize section 1.", "Now section 2.", "Now section 3."]
):
    messages.append({"role": "user", "content": user_message})

    r = client.beta.messages.create(
        model="claude-opus-5",
        max_tokens=1024,
        cache_control={"type": "ephemeral"},
        system=SYSTEM,
        messages=messages,
        diagnostics={"previous_message_id": prev_id},
        betas=["cache-diagnosis-2026-04-07"],
    )

    if r.diagnostics is not None and r.diagnostics.cache_miss_reason is not None:
        print(f"Turn {i + 1} cache_miss_reason: {r.diagnostics.cache_miss_reason.type}")

    messages.append({"role": "assistant", "content": r.content})
    prev_id = r.id

回應格式

回應 Message 上的 diagnostics 欄位有四種可能的狀態:

意義
欄位不存在請求未包含 diagnostics,或缺少 beta 標頭。
nullprevious_message_idnull(第一輪,無可比較對象),或是比較已執行且未發現分歧。
{"cache_miss_reason": null}回應序列化時比較仍在執行中。當回應開始得非常快時可能發生這種情況。請將其視為無定論,並檢查下一輪。
{"cache_miss_reason": {...}}附加了 cache_miss_reason。對於 *_changed 類型,這會指出第一個分歧點;previous_message_not_foundunavailable 則是未產生比較結果的情況。

cache_miss_reason 不為 null 時,其內容如下:

{
  "id": "msg_01Xyz...",
  "type": "message",
  "role": "assistant",
  "content": [{ "type": "text", "text": "..." }],
  "usage": {
    "input_tokens": 42,
    "cache_read_input_tokens": 0,
    "cache_creation_input_tokens": 41850,
    "output_tokens": 210
  },
  "diagnostics": {
    "cache_miss_reason": {
      "type": "system_changed",
      "cache_missed_input_tokens": 41850
    }
  }
}

快取未命中原因類型

cache_miss_reason 是以 type 區分的「discriminated union」(可辨識聯集)。回應只會回報最早的分歧,因此請先修正它;後面的分歧可能被它遮蔽。

類型意義應變更之處
model_changedmodel 與上一個請求不同(例如路由器、A/B 測試或備援機制選擇了不同的模型)。快取是依模型區分的。在快取的對話中保持模型不變。
system_changedsystem 參數不同。通常是時間戳記、請求 ID 或其他每次請求不同的值被插入到系統提示中。讓系統提示成為逐位元組穩定的常數,並將動態資料移至快取斷點之後的第一則 user 訊息中。
tools_changedtools 陣列不同:各輪之間新增、移除或重新排序了工具,或工具的 input_schema JSON 以非確定性方式序列化。每一輪都以固定順序傳送相同的工具清單,並以確定性方式序列化 schema(例如排序鍵值)。
messages_changed模型、系統提示與工具皆相符,但 messages 中較早的項目被修改、重新排序或移除,而非僅附加新內容。通常是對話歷史被截斷或編輯,或是 assistant 輪次與 tool_result 區塊在重新傳送時以不同方式重新序列化。將歷史視為僅可附加;將 assistant 的 content 與工具結果原封不動地回傳。
previous_message_not_found所提供的 previous_message_id 沒有對應的已儲存指紋。這並不代表您的請求發生了變化。通常是上一個請求未帶有 beta 標頭、來自不同的工作區,或距離傳送時間已過太久。每一輪都傳送 beta 標頭,並讓連續各輪在時間上保持接近。
unavailable此請求無法取得診斷資訊。這包括 modelsystemtools 皆相符,但另一個會影響提示的請求參數(tool_choicethinkingcontext_managementoutput_configoutput_format,或啟用中的 anthropic-beta 標頭集合)不同的情況,以及分歧點超出比較範圍的極長對話。您的請求已正常處理。在快取對話的整個生命週期內,保持會影響提示的請求參數不變。若問題持續,請套用提示快取頁面中常見問題疑難排解下的手動檢查。

搭配 usage 解讀診斷結果

diagnostics 回答的是「我的請求是否改變了?」,而 usage.cache_read_input_tokens 回答的是「快取是否命中?」。將兩者結合即可知道該從何處著手。

此矩陣適用於您傳入了實際 previous_message_id 的輪次。在第一輪(previous_message_id: null),diagnostics 一律為 null,且 cache_read_input_tokens 通常為零,因為此時是在寫入快取而非讀取;無需進行疑難排解。當 cache_miss_reasonnull(比較仍在進行中;請檢查下一輪),或其 typeprevious_message_not_foundunavailable(未產生比較結果)時,此矩陣亦不適用。

診斷結果快取讀取 token 數解讀
null運作正常。您的前綴穩定且快取命中。
null低或零您的請求相符,但快取項目已不再可用。請考慮縮短各輪之間的間隔,或使用 1 小時快取 TTL
cache_miss_reason*_changed 類型低或零您的程式錯誤。請求發生了變化;請依 type 所指出的原因進行修正。
cache_miss_reason*_changed 類型罕見。變化發生在提示的後段,但較早的 cache_control 斷點仍然命中。值得修正,但影響不大。

限制

  • Beta: 在此功能處於 beta 期間,欄位名稱與語意可能會變更。
  • 僅限 Claude API: 不適用於 Amazon Bedrock 或 Google Cloud。
  • 有限的保留期: 用於 previous_message_id 查詢的指紋會在短時間後過期。請在時間間隔接近的請求之間執行診斷比較。
  • 相同工作區: 上一個請求必須在相同的組織與工作區中執行。若要確認,請比較兩個回應上的 anthropic-workspace-id 回應標頭
  • 比較範圍: 對於唯一變化位於訊息清單深處的極長對話,回應可能是 unavailable 而非精確位置。
  • 盡力而為: 診斷絕不會阻擋您的請求或使其失敗。若無法取得診斷資訊,回應會傳回 unavailable;若比較仍在執行中,則傳回 cache_miss_reason: null

資料保留

快取診斷符合 ZDR 資格(有條件)。Anthropic 不會為此功能儲存您提示的原始文字或 Claude 的輸出。

為每個請求儲存的指紋僅由加密雜湊值與 token 數量估計值組成,以回應的 id 作為鍵值,且範圍限定於您的組織與工作區。指紋會在短時間後過期,且不會用於任何其他用途。

關於所有功能的 ZDR 資格,請參閱 API 與資料保留

另請參閱

Compatibility

Supported platforms
  • Claude APIBeta

Was this page helpful?