프롬프트 캐싱으로 LLM API 비용 줄이기: Anthropic·OpenAI 캐시 조건과 프롬프트 배치 순서
캐시가 맞으려면 무엇이 같아야 하는지, 최소 길이·수명·가격 배수, 적중률을 사용량 필드로 확인하는 방법

핵심 요약
- 캐시는 요청 앞부분이 바이트 단위로 같을 때만 재사용되므로 도구 → 고정 지시 → 참고 자료 → 이력 → 가변 입력 순으로 배치한다.
- Claude는 cache_control로 지정하며 기본 5분(쓰기 1.25배), 1시간(쓰기 2배), 읽기는 대부분 0.1배다.
- OpenAI는 지원 모델에서 자동 적용되고 cached_tokens 필드로 적중량을 확인한다.
- 요청 간격이 5분보다 길면 Claude의 1시간 TTL을 검토한다.
- 적중률과 가중 비용 배수를 로그에서 계산해 캐시가 실제로 비용을 줄였는지 확인한다.
긴 시스템 프롬프트, 도구 정의, 참고 문서를 매 요청마다 보내는 LLM 기능을 운영하고 있다면 입력 토큰 비용의 상당 부분은 "지난번과 똑같은 앞부분"에서 나온다. 프롬프트 캐싱은 이 앞부분의 처리 결과를 잠시 보관했다가 다음 요청에서 재사용하는 기능이다. 이 글을 읽고 나면 Anthropic과 OpenAI에서 캐시가 걸리는 조건을 구분하고, 프롬프트를 캐시가 잘 맞는 순서로 재배치하고, 응답의 사용량 필드로 실제 적중 여부를 측정할 수 있다.
두 제공자의 캐시 동작 비교
공통 원리는 같다. 요청의 앞에서부터 바이트 단위로 동일한 접두부(prefix)만 재사용된다. 중간 한 글자만 달라도 그 지점 뒤는 새로 처리된다. 차이는 켜는 방식과 수명, 가격 구조에 있다.
| 항목 | Anthropic (Claude) | OpenAI |
|---|---|---|
| 켜는 방법 | 요청 최상위 또는 콘텐츠 블록에 cache_control 지정 | 지원 모델은 기본 활성화 |
| 최소 길이 | 모델별 512~4,096토큰 (예: Opus 5.5·Sonnet 5.5·Haiku 5.5는 512, Haiku 4.5는 4,096) | GPT-5.6 이후 1,024 토큰 |
| 수명 | 기본 5분, "ttl": "1h"로 1시간 | 메모리 보관은 보통 비활성 5~10분(최대 1시간), 24시간 보관 옵션 |
| 쓰기 비용 | 5분: 기본 입력 단가의 1.25배, 1시간: 2배 | 별도 쓰기 할증 없음 |
| 읽기 비용 | 대부분 기본 입력의 0.1배(일부 모델은 더 낮음) | 대부분 GPT-5.6+ 모델에서 0.1배 |
| 적중 확인 필드 | usage.cache_read_input_tokens, cache_creation_input_tokens | usage.input_tokens_details.cached_tokens |
수치는 Claude 프롬프트 캐싱 문서와 OpenAI 프롬프트 캐싱 가이드를 2026-10-13에 대조한 값이다. 모델이 추가될 때마다 최소 길이와 배수가 바뀌므로 적용 전 해당 모델 행을 다시 확인한다.
캐시가 맞는 프롬프트 배치 순서
Claude 문서는 캐시 접두부의 순서를 tools → system → messages로 정의하고, 앞 단계가 바뀌면 그 뒤 단계 캐시가 모두 무효가 된다고 설명한다. OpenAI 가이드도 고정된 지시를 앞에, 변하는 내용을 뒤에 두라고 권한다. 실무에서는 다음 순서로 재배치한다.
- 도구 정의 — 요청마다 도구 목록을 바꾸지 않는다. 사용자 권한에 따라 도구를 빼야 한다면 정의는 그대로 두고 호출 단계에서 막는 방식을 검토한다.
- 고정 시스템 지시 — 역할, 출력 형식, 금지 규칙.
- 긴 참고 자료 — 제품 매뉴얼, 코드베이스 요약, few-shot 예시. 같은 문서를 여러 질문에 쓰는 경우 효과가 가장 크다.
- 대화 이력 — 이전 턴은 그대로 두고 뒤에 추가만 한다.
- 이번 요청의 가변 부분 — 사용자 질문, 현재 시각, 요청 ID, 검색 결과.
가장 흔한 실수는 시스템 프롬프트 맨 앞에 "오늘 날짜: 2026-10-13 14:03:22" 같은 값을 넣는 것이다. 이 한 줄 때문에 뒤에 오는 수천 토큰이 매번 새로 처리된다. 날짜가 필요하면 날짜 단위로만 넣거나 사용자 메시지 쪽으로 옮긴다.
Claude에서 캐시 지정하기
가장 간단한 방법은 요청 최상위에 cache_control을 하나 두는 자동 캐싱이다. 문서에 따르면 시스템이 마지막 캐시 가능 블록에 중단점을 두고, 대화가 길어지면 앞으로 옮긴다. 세밀하게 제어하려면 블록마다 중단점을 지정하며, 요청당 최대 4개까지 둘 수 있다.
import anthropic
client = anthropic.Anthropic()
MANUAL = open('manual.md', encoding='utf-8').read() # 매 요청 동일한 긴 문서
def ask(question: str):
return client.messages.create(
model='claude-sonnet-5-5',
max_tokens=1024,
system=[
{'type': 'text', 'text': '너는 사내 제품 매뉴얼에 근거해 답하는 지원 담당자다.'},
{'type': 'text', 'text': MANUAL,
'cache_control': {'type': 'ephemeral', 'ttl': '1h'},
},
],
messages=[{'role': 'user', 'content': question}],
)
for q in ['환불 기한은?', '계정 이전 방법은?']:
u = ask(q).usage
print(q, 'write', u.cache_creation_input_tokens,
'read', u.cache_read_input_tokens, 'uncached', u.input_tokens)
요청 간격이 5분보다 길면 기본 수명으로는 매번 다시 쓰기 비용(1.25배)만 내고 읽기 이득을 못 볼 수 있다. 문서가 1시간 수명을 권하는 경우가 바로 이 상황이다. 반대로 같은 접두부가 몇 초 간격으로 계속 들어오는 챗봇이라면 기본 5분이 쓰기 비용이 더 싸다.
문서의 무효화 표에서 놓치기 쉬운 항목은 도구 정의 변경(전체 무효), 이미지 추가·삭제와 tool_choice 변경(messages 단계 무효), thinking 설정 변경(messages 단계 무효)이다. 또 중단점마다 이전 캐시를 찾을 때 뒤로 최대 20개 블록까지만 확인하므로, 대화가 매우 길어지면 중간에 중단점을 하나 더 두는 것이 적중률 유지에 유리하다.
OpenAI에서 적중률 높이기
OpenAI는 별도 지정 없이 접두부를 캐시한다. 가이드에 따르면 prompt_cache_key는 GPT-5.6 이전 모델에서 관련 요청을 같은 캐시로 보내는 데 쓰이고, GPT-5.6 이후에는 고객·사용자별 캐시 집계와 다른 사용자 간 캐시 탐지 방지에 쓰인다. 장시간 보관은 모델 세대에 따라 prompt_cache_retention 또는 prompt_cache_options.ttl로 지정한다. 도구 목록을 바꿔야 할 때는 스키마를 다시 정의하지 말고 allowed_tools로 사용 범위만 바꾸라고 권한다.
from openai import OpenAI
client = OpenAI()
res = client.responses.create(
model='gpt-5.6',
instructions=FIXED_INSTRUCTIONS + MANUAL, # 고정 접두부
input=question, # 가변 부분은 뒤에
prompt_cache_key=f'tenant-{tenant_id}',
)
print(res.usage.input_tokens, res.usage.input_tokens_details.cached_tokens)
적중률을 수치로 남기는 방법
캐시는 "켰다"가 아니라 "맞았다"를 확인해야 한다. 응답마다 사용량 필드를 로그에 남기고 일 단위로 적중률을 계산한다. Claude 문서는 전체 입력 토큰을 cache_read_input_tokens + cache_creation_input_tokens + input_tokens로 정의한다.
// 로그 레코드 배열에서 Claude 캐시 적중률과 예상 비용 배수 계산
function cacheStats(rows) {
let read = 0, write = 0, plain = 0;
for (const u of rows) {
read += u.cache_read_input_tokens || 0;
write += u.cache_creation_input_tokens || 0;
plain += u.input_tokens || 0;
}
const total = read + write + plain;
// 5분 TTL 기준 배수: 읽기 0.1, 쓰기 1.25, 일반 1.0 (모델별로 확인)
const weighted = read * 0.1 + write * 1.25 + plain;
return { hitRate: read / total, costRatio: weighted / total };
}
costRatio가 1보다 크면 캐시 쓰기만 반복되고 읽기가 거의 없는 상태다. 이때는 가변 값이 접두부에 섞였는지, 요청 간격이 수명보다 긴지 먼저 확인한다. 호출 로그 스키마 설계는 LLMOps 모니터링 항목과 구현에, 사용자별 토큰 예산과 429 처리는 AI API 비용과 Rate Limit 설계에 정리했다.
적용 전에 점검할 세 가지
첫째, 캐시 대상 접두부가 해당 모델의 최소 길이를 넘는지 토큰 수를 센다. 최소 길이 미만이면 지정해도 아무 일도 일어나지 않는다. 둘째, 접두부 안에 사용자별 값(이름, 권한, 세션 ID)이 섞여 있지 않은지 본다. 사용자마다 달라지면 사실상 개인 캐시가 되어 적중률이 낮아진다. 셋째, 개발 도구에서 같은 원리를 쓰는 사례도 함께 확인한다. Claude Code의 설정 파일과 세션 관리가 토큰 사용량에 미치는 영향은 Claude Code 토큰 사용량 줄이는 설정에서 다뤘다.
초보자가 자주 실수하는 포인트
- 시스템 프롬프트 맨 앞에 초 단위 현재 시각이나 요청 ID를 넣어 매번 캐시가 깨지는 경우
- 모델 최소 길이보다 짧은 프롬프트에 cache_control을 붙이고 효과가 없다고 판단하는 경우
- 사용자 권한에 따라 도구 정의를 요청마다 바꿔 전체 캐시를 무효화하는 경우
체크리스트
- 캐시 대상 접두부의 토큰 수가 모델 최소 길이 이상인가
- 접두부에 시각·요청 ID·사용자별 값이 없는가
- 도구 정의가 요청 간에 동일한가
- 사용량 필드를 로그에 남기고 적중률을 집계하는가
- 요청 간격에 맞는 TTL을 골랐는가
자주 묻는 질문
캐시된 내용이 다른 고객에게 노출될 수 있나요?
캐시는 응답이 아니라 입력 처리 결과를 재사용하는 것이며, 두 제공자 모두 조직 단위로 분리한다고 문서화하고 있습니다. OpenAI는 GPT-5.6 이후 prompt_cache_key로 사용자 간 캐시 탐지를 막는 용도를 안내하므로 다중 테넌트 서비스는 테넌트별 키를 지정하는 편이 좋습니다.
배치 API와 함께 쓸 수 있나요?
제공자와 모델별로 지원 범위와 할인 중복 적용 여부가 다릅니다. 배치 처리 문서의 가격 항목에서 캐시 관련 설명을 확인한 뒤 적용합니다.
참고 자료 · 검증 기준
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.