Claude Platform Docs
Messages컨텍스트 관리

캐시 진단

연속된 요청을 비교하고 프롬프트 접두사가 정확히 어디에서 달라졌는지 식별하여 예상치 못한 프롬프트 캐시 미스를 진단합니다.

프롬프트 캐싱은 "latency"(지연 시간)와 비용을 크게 줄여 주지만, 이는 프롬프트의 시작 부분이 최근 요청과 바이트 단위로 동일할 때에만 해당됩니다. 순서가 바뀐 도구, 시스템 프롬프트에 삽입된 타임스탬프, 또는 이전 메시지에 대한 편집은 캐시를 조용히 무효화할 수 있습니다. 캐시 진단이 없으면 유일한 신호는 usage.cache_read_input_tokens가 0으로 떨어지는 것뿐이며, 무엇이 변경되었는지에 대한 표시는 전혀 없습니다.

캐시 진단은 이 간극을 메워 줍니다. 이전 응답의 id를 전달하면 API가 두 요청을 비교하여 어디에서 달라졌는지(모델, 시스템 프롬프트, 도구 또는 메시지 기록)를 알려 주므로, 추측하는 대신 근본 원인을 수정할 수 있습니다.

캐시 진단의 작동 방식

베타 헤더가 있으면 API는 응답 id를 키로 하여 각 요청의 경량 "fingerprint"(지문)를 저장합니다. 다음 요청에서 해당 iddiagnostics.previous_message_id로 포함하세요. API는 새 요청에 대한 지문을 다시 생성하고, 저장된 지문과 비교한 다음, 첫 번째 분기 지점을 설명하는 diagnostics 객체를 응답에 첨부합니다.

이 비교는 캐시가 실제로 히트했는지 여부와 무관하게 요청 구조에 관한 것입니다. diagnostics 결과를 usage.cache_read_input_tokens와 결합하는 방법은 usage와 함께 진단 읽기를 참조하세요.

지문에는 해시와 토큰 수 추정치만 포함되며(원시 프롬프트 내용은 절대 포함되지 않음), 제한된 시간 동안만 보존되고, 조직 및 워크스페이스 범위로 한정되며, 다른 어떤 목적으로도 사용되지 않습니다.

기본 사용법

매 턴마다 베타 헤더를 전송하세요. 첫 번째 턴에서는 비교할 이전 메시지 없이 옵트인하기 위해 "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}")

스트리밍

스트리밍 응답에서 diagnosticsmessage_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 필드를 담고 있습니다. 가능한 값은 응답 형식을 참조하세요.

대화 루프 전반에 걸쳐 진단 연결하기

멀티턴 대화에서는 매 턴마다 최신 응답 idprevious_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

응답 형식

응답 Messagediagnostics 필드에는 네 가지 가능한 상태가 있습니다:

의미
필드 없음요청에 diagnostics가 포함되지 않았거나 베타 헤더가 누락되었습니다.
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_reasontype을 기준으로 하는 판별 유니온(discriminated union)입니다. 응답은 가장 이른 분기만 보고하므로 이를 먼저 수정하세요. 이후의 분기는 그 뒤에 숨겨져 있을 수 있습니다.

유형의미변경해야 할 사항
model_changedmodel이 이전 요청과 다릅니다(예: 라우터, A/B 테스트 또는 폴백이 다른 모델을 선택함). 캐시는 모델별로 유지됩니다.캐시된 대화 내에서는 모델을 일정하게 유지하세요.
system_changedsystem 매개변수가 다릅니다. 일반적으로 타임스탬프, 요청 ID 또는 기타 요청별 값이 시스템 프롬프트에 삽입된 경우입니다.시스템 프롬프트를 바이트 단위로 안정적인 상수로 만들고, 동적 데이터는 캐시 중단점 이후의 첫 번째 user 메시지로 옮기세요.
tools_changedtools 배열이 다릅니다. 턴 사이에 도구가 추가, 제거 또는 재정렬되었거나, 도구 input_schema JSON이 비결정적으로 직렬화되었습니다.매 턴마다 동일한 도구 목록을 고정된 순서로, 결정적으로 직렬화된 스키마(예: 키 정렬)와 함께 전송하세요.
messages_changed모델, 시스템, 도구는 모두 일치하지만 messages의 이전 항목이 추가(append)되는 대신 변경, 재정렬 또는 제거되었습니다. 일반적으로 대화 기록이 잘리거나 편집되었거나, 어시스턴트 턴과 tool_result 블록이 재전송 시 다르게 재직렬화된 경우입니다.기록을 추가 전용(append-only)으로 취급하고, 어시스턴트 content와 도구 결과를 그대로 되돌려 보내세요.
previous_message_not_found제공된 previous_message_id에 대해 저장된 지문이 없습니다. 이는 요청이 변경되었다는 증거가 아닙니다. 일반적으로 이전 요청에 베타 헤더가 없었거나, 다른 워크스페이스에서 왔거나, 전송된 후 너무 많은 시간이 지난 경우입니다.매 턴마다 베타 헤더를 전송하고 연속된 턴 사이의 시간 간격을 가깝게 유지하세요.
unavailable이 요청에 대한 진단 정보를 사용할 수 없었습니다. 여기에는 model, system, tools는 일치하지만 프롬프트에 영향을 주는 다른 요청 매개변수(tool_choice, thinking, context_management, output_config, output_format 또는 활성 anthropic-beta 헤더 집합)가 다른 경우와, 분기가 비교 범위를 벗어난 매우 긴 대화가 포함됩니다. 요청은 정상적으로 처리되었습니다.캐시된 대화의 수명 동안 프롬프트에 영향을 주는 요청 매개변수를 일정하게 유지하세요. 지속적으로 발생하는 경우 프롬프트 캐싱 페이지의 일반적인 문제 해결에 있는 수동 점검을 적용하세요.

usage와 함께 진단 읽기

diagnostics는 "내 요청이 변경되었는가?"에 답하고, usage.cache_read_input_tokens는 "캐시가 히트했는가?"에 답합니다. 이 둘을 결합하면 어디를 살펴봐야 할지 알 수 있습니다.

이 매트릭스는 실제 previous_message_id를 전달한 턴에 적용됩니다. 첫 번째 턴(previous_message_id: null)에서는 diagnostics가 항상 null이며, 캐시가 읽히는 것이 아니라 기록되는 중이므로 cache_read_input_tokens는 보통 0입니다. 문제 해결이 필요하지 않습니다. 또한 이 매트릭스는 cache_miss_reasonnull인 경우(비교가 아직 보류 중이므로 다음 턴을 확인하세요)나 그 typeprevious_message_not_found 또는 unavailable인 경우(비교가 생성되지 않음)에는 적용되지 않습니다.

진단 결과캐시 읽기 토큰해석
null높음예상대로 작동 중입니다. 접두사가 안정적이며 캐시가 히트했습니다.
null낮음 또는 0요청은 일치하지만 캐시 항목을 더 이상 사용할 수 없었습니다. 턴 사이의 간격을 줄이거나 1시간 캐시 TTL 사용을 고려하세요.
cache_miss_reason*_changed 유형낮음 또는 0사용자 측 버그입니다. 요청이 변경되었으므로 type이 가리키는 원인을 수정하세요.
cache_miss_reason*_changed 유형높음드문 경우입니다. 프롬프트 후반부에서 변경이 발생했지만 앞쪽의 cache_control 중단점은 여전히 히트했습니다. 수정할 가치는 있지만 영향은 적습니다.

제한 사항

  • 베타: 이 기능이 베타인 동안 필드 이름과 의미가 변경될 수 있습니다.
  • Claude API 전용: Amazon Bedrock 또는 Google Cloud에서는 사용할 수 없습니다.
  • 제한된 보존 기간: previous_message_id 조회를 위한 지문은 짧은 기간 후 만료됩니다. 시간 간격이 가까운 요청 간에 진단 비교를 실행하세요.
  • 동일 워크스페이스: 이전 요청은 동일한 조직 및 워크스페이스에서 실행되었어야 합니다. 확인하려면 두 응답의 anthropic-workspace-id 응답 헤더를 비교하세요.
  • 비교 범위: 유일한 변경 사항이 메시지 목록 깊숙한 곳에 있는 매우 긴 대화의 경우, 응답이 정확한 위치 대신 unavailable일 수 있습니다.
  • 최선 노력(best-effort): 진단은 요청을 차단하거나 실패시키지 않습니다. 진단 정보를 사용할 수 없는 경우 응답은 unavailable을 반환하며, 비교가 아직 실행 중이었던 경우에는 cache_miss_reason: null을 반환합니다.

데이터 보존

캐시 진단은 ZDR 적격(조건부)입니다. Anthropic은 이 기능을 위해 프롬프트의 원시 텍스트나 Claude의 출력을 저장하지 않습니다.

각 요청에 대해 저장되는 지문은 암호화 해시와 토큰 수 추정치로만 구성되며, 응답 id를 키로 하고 조직 및 워크스페이스 범위로 한정됩니다. 지문은 짧은 기간 후 만료되며 다른 어떤 목적으로도 사용되지 않습니다.

모든 기능에 대한 ZDR 적격 여부는 API 및 데이터 보존을 참조하세요.

참고 항목

Compatibility

Supported platforms
  • Claude APIBeta

Was this page helpful?