🔥 고급2026-06-255~7분
Prompt Caching 캐시 무효화 패턴: 히트율 90% 이상 유지하는 설계 전략
Anthropic Prompt Caching은 잘못 설계하면 히트율이 20% 이하로 떨어진다. 캐시 레이아웃 원칙과 무효화 트리거를 제어해 비용과 지연을 동시에 줄이는 운영 전략을 다룬다.
prompt-cachingcost-optimizationobservability
왜 캐시 히트율이 낮아지는가
Prompt Caching은 cache_control: {type: 'ephemeral'} 블록을 기준으로 prefix를 캐싱한다. 캐시 수명은 5분이며, prefix가 1토큰이라도 바뀌면 캐시 미스가 발생한다. 흔한 실패 모드는 세 가지다.
- 동적 콘텐츠를 캐시 앞에 배치 — 타임스탬프, 요청 ID, 사용자 이름을 system prompt 상단에 넣으면 매 요청마다 prefix가 달라진다.
- 캐시 경계를 여러 곳에 분산 —
cache_control을 3개 이상 선언하면 Anthropic은 가장 긴 prefix 하나만 캐싱하므로 의도와 다르게 동작한다. - 5분 TTL을 무시한 배치 설계 — 배치 간격이 6분이면 캐시가 매번 콜드 스타트된다.
캐시 레이아웃 설계 원칙
불변 → 준불변 → 가변 순서로 배치하는 것이 핵심이다.
- 불변: 시스템 역할 정의, 도메인 지식 문서, 툴 스키마 전체 → 여기에만
cache_control1개 부착 - 준불변: 세션 내 대화 히스토리 (세션 단위 캐시가 필요하면 두 번째
cache_control추가) - 가변: 현재 사용자 메시지, 실시간 컨텍스트
아래는 올바른 레이아웃 예시다.
import anthropic
client = anthropic.Anthropic()
SYSTEM_DOCS = """[500줄 분량의 도메인 지식 문서]"""
def call_with_cache(user_message: str, history: list[dict]) -> str:
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": SYSTEM_DOCS,
"cache_control": {"type": "ephemeral"} # 불변 블록에만 1개
}
],
messages=[
*history, # 준불변: 히스토리
{"role": "user", "content": user_message} # 가변
]
)
usage = response.usage
hit = usage.cache_read_input_tokens
miss = usage.cache_creation_input_tokens
print(f"캐시 히트: {hit}tok / 미스(생성): {miss}tok")
return response.content[0].text
cache_read_input_tokens이 0이면 미스다. 첫 요청은 항상 미스(캐시 워밍)이고 두 번째부터 히트가 발생한다.
수치 트레이드오프
| 시나리오 | 입력 비용 비율 | 지연 변화 | |---|---|---| | 캐시 미스 (캐시 생성) | 기본 가격 × 1.25 | +10~20% | | 캐시 히트 | 기본 가격 × 0.10 | -30~50% | | 히트율 90% 달성 시 전체 절감 | ~85% | 지연 개선 동반 |
캐시 생성 비용이 기본보다 25% 높으므로, 히트율 60% 미만이면 캐싱이 오히려 손해다. 최소 목표는 70% 이상이다.
운영 체크리스트
- [ ]
cache_control블록이 전체 messages에서 2개 이하인지 확인 - [ ] 불변 system 블록이 요청마다 바이트 단위로 동일한지 해시 비교
- [ ]
cache_read_input_tokens/ (cache_read+cache_creation) 비율을 메트릭으로 기록 - [ ] 5분 TTL 안에 트래픽이 재도달하는지 p95 요청 간격 모니터링
- [ ] 타임스탬프·요청 ID는 반드시 user 메시지 또는 별도 변수로 분리