본문으로 건너뛰기
AIPida
채택됨중급AI 모델·API

프롬프트 캐싱 적용했는데 cache_read_input_tokens가 계속 0이에요

RAG 서비스에서 시스템 프롬프트에 큰 문서(약 4만 토큰)를 박아두고 질문만 바꿔가며 호출합니다. 비용 줄이려고 cache_control을 붙였는데 두 번째, 세 번째 호출에서도 usage.cache_read_input_tokens가 0으로만 나옵니다.

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=4000,
    system=[
        {"type": "text", "text": f"오늘은 {datetime.now()}. 아래 문서를 참고...\n{doc}",
         "cache_control": {"type": "ephemeral"}}
    ],
    messages=[{"role": "user", "content": question}],
)

캐시 쓰기(cache_creation_input_tokens)는 매번 잡히는데 읽기가 0입니다. 뭐가 문제일까요?

답변 2

  • 채택된 답변에디터 검증

    범인은 system 프롬프트 맨 앞의 {datetime.now()}입니다. 저도 똑같은 거 한번 겪었어요ㅋㅋ

    프롬프트 캐싱은 prefix 매칭이에요. 캐시 키는 breakpoint까지 렌더된 바이트가 정확히 일치해야 잡힙니다. datetime.now()는 매 호출 다른 값이라 prefix가 매번 달라지고, 그 뒤 4만 토큰 전체가 통째로 무효화됩니다. 그래서 매번 새로 쓰기만(cache_creation) 하고 읽기는 0인 거예요.

    고치는 법: 변하는 값을 prefix에서 빼서 뒤로 옮기세요. system은 동결시키고, 날짜 같은 휘발성 컨텍스트는 user 메시지로:

    system=[
        {"type": "text", "text": f"아래 문서를 참고...\n{doc}",
         "cache_control": {"type": "ephemeral"}}
    ],
    messages=[{"role": "user", "content": f"오늘은 {today}.\n질문: {question}"}],
    

    같은 식으로 캐시 조용히 깨먹는 것들 — 한 번씩 다 밟아봄:

    • uuid4()나 request id를 prefix에 끼워넣기
    • json.dumps(d)sort_keys=True 없이 직렬화 (dict 키 순서가 매번 바뀜)
    • user/session id를 system 프롬프트에 f-string으로 보간
    • 도구 목록이 호출마다 바뀜 (tools는 position 0이라 여기 바뀌면 전체 무효)

    고치고 나서 cache_read_input_tokens 채워지는지 확인하세요. 읽기는 기본 입력의 약 0.1배라 두 번째 호출부터 바로 이득 봅니다.

  • 위 게 99% 정답이고, 혹시 그래도 안 되면 캐시 최소 길이도 한번 보세요.

    4만 토큰이면 문제없는데, 모델마다 캐시 걸리는 최소 prefix 길이가 있어서 그보다 짧으면 cache_control 붙여도 조용히 캐싱 안 되고 cache_creation: 0만 나옵니다. Opus 계열은 1024 토큰이 최소였던 걸로 기억하는데 정확힌 현재 docs 확인하시고요.

    그리고 디버깅 팁 — 두 요청의 렌더된 프롬프트를 파일로 떠서 diff 떠보면 invalidator가 바로 보입니다. 눈으로 안 보이는 공백 하나, 키 순서 차이까지 다 잡혀요. 저는 이걸로 한참 헤매던 거 5분 만에 찾았던 적 있음.