프롬프트 캐싱으로 LLM 비용 10배 줄이기: 2026년 7월 기준 OpenAI·Anthropic “캐시 히트율” 실전 최적화
들어가며
LLM 비용이 폭발하는 가장 흔한 패턴은 “매 요청마다 같은 긴 prefix(시스템 프롬프트, 툴 정의, 정책, 코드베이스 요약, Few-shot 예시)를 다시 읽게 만드는 것”입니다. 이때 prompt caching은 모델이 이미 본 prompt prefix의 KV cache(또는 동등한 내부 표현) 를 재사용하게 해서, 입력 토큰 비용과 TTFT(Time To First Token)를 동시에 낮춥니다. OpenAI는 공통 prefix 재사용 시 캐시 입력 50% 할인을 제공한다고 공개했고, Anthropic은 cache hit(리드)가 기본 입력 가격의 10% 수준으로 책정되어 “히트율”만 확보하면 비용 구조가 아예 달라집니다. (openai.com)
언제 쓰면 좋은가
- 에이전트/코드어시스턴트/긴 대화처럼, 매 턴마다 같은 시스템 지시문 + 같은 툴 스키마 + 같은 프로젝트 컨텍스트가 반복되는 워크로드
- RAG에서 “문서 묶음”이 자주 재사용되는 케이스(예: 동일 고객의 계약서/매뉴얼을 계속 참조)
언제 쓰면 안 되는가(혹은 신중해야 하는가)
- 원샷(one-shot) 요청이 대부분이라 prefix 재사용이 거의 없는 경우: 캐시가 “할인”이 아니라 “추가 write 비용”처럼 작동할 수 있습니다(특히 명시적 캐시/긴 TTL을 쓰는 설계에서). (openai.com)
- 프롬프트 상단에 시간/랜덤/세션ID 같은 휘발성 문자열을 넣는 구조(캐시 히트율을 0으로 만들기 쉬움)
🔧 핵심 개념
1) 용어 정리: cache write / cache read / breakpoint(캐시 경계)
- cache write: “이 prefix를 캐시로 저장”하는 비용. Anthropic은 5분 TTL 기준 1.25× write, 1시간 TTL은 더 비싸다고 문서로 명시합니다. (platform.claude.com)
- cache read(hit): 다음 요청에서 동일 prefix를 재사용하는 비용. Anthropic은 hit가 표준 입력의 10%라고 명시합니다. (platform.claude.com)
- breakpoint: 어디까지를 “prefix로 간주해 캐시할지”를 결정하는 경계. Anthropic은
cache_control로 “자동/명시” 두 방식을 제공하며,tools → system → messages순서로 prefix를 잡는다고 설명합니다. (platform.claude.com)
2) 내부 작동 흐름(구조/흐름 관점)
프롬프트 캐싱은 대개 다음 파이프라인을 따릅니다.
- 요청이 들어오면 provider는 “캐시 가능한 prefix”를 바이트 단위로 정규화/해시(개념적으로)
- 동일 prefix가 TTL 내 존재하면
- prefix 구간은 KV cache를 재사용(또는 동등한 계산 재사용)
- 나머지 suffix(이번 턴의 유저 입력, 최신 tool result 등)만 새로 처리
- 동일 prefix가 없으면
- 전체를 새로 처리하면서 prefix 부분을 캐시로 “write”
- 이후 동일 prefix 요청은 “read(hit)”
Anthropic은 “prefix matching”과 “캐시 경계까지 100% 동일해야 히트”를 명시적으로 강조합니다. 즉, 프롬프트 상단의 공백 하나, 툴 정의 순서 하나가 바뀌면 통째로 miss가 납니다. (claude.com)
3) OpenAI vs Anthropic: 접근 차이(2026년 7월 기준 관찰 포인트)
- OpenAI(자동 할인 중심): 공통 prefix 재사용 시 자동으로 prompt caching 할인 적용(2024 발표 기준 50% 할인). (openai.com)
또한 2026년 공개된 GPT‑5.6 프리뷰 글에서는 “명시적 cache breakpoint/최소 캐시 수명(30분)” 및 cache write가 1.25×, cache read는 90% 할인(=10% 과금) 을 언급합니다. (openai.com) - Anthropic(캐시를 설계 요소로 노출):
cache_control을 요청 구조에 직접 넣어 프롬프트를 ‘캐시-friendly’하게 재구성할 수 있고, pricing 문서에서 hit=10%, 5m write=1.25×, 1h write=2× 등 “손익분기”를 명확히 제시합니다. (platform.claude.com)
핵심은 둘 다 “기술적으로는 prefix 재사용”인데, 실무에선 (1) 어디를 prefix로 고정할지, (2) 변동 콘텐츠를 어디에 배치할지, (3) TTL과 write 비용까지 포함한 ROI 계산이 성패를 가릅니다.
💻 실전 코드
아래는 “코드리뷰 에이전트” 같은 현실적인 워크로드를 가정합니다.
- 고정 prefix:
- 프로젝트 규칙(코딩 컨벤션)
- 툴 정의(예:
get_diff,read_file) - 리뷰 체크리스트(보안/성능/테스트)
- 변동 suffix:
- 이번 PR diff
- CI 로그 일부
- 질문(이번 변경의 의도)
목표: prefix는 캐시 히트, suffix만 매번 새로 태워서 비용을 줄입니다. 또한 매 응답에서 cache hit 토큰을 로깅해 히트율을 수치로 관리합니다(“최적화”는 결국 계측이 전부).
0) 의존성/환경
1
2
3
4
python -m venv .venv
source .venv/bin/activate
pip install anthropic==0.34.0 python-dotenv==1.0.1
export ANTHROPIC_API_KEY="..."
1) 기본 동작: Anthropic Messages API + explicit breakpoint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import os
import json
import time
from anthropic import Anthropic
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
PROJECT_RULES = """
You are a senior reviewer.
Repo conventions:
- Python: ruff + mypy, prefer explicit types
- Security: never log secrets
- Performance: watch N+1 queries
Return JSON with: summary, risks[], suggestions[]
""".strip()
REVIEW_TOOLING = """
Available tools (conceptual):
- get_diff(pr_number) -> unified diff
- read_file(path) -> file contents
- search_repo(query) -> matched snippets
You MUST cite file paths when you mention code.
""".strip()
CHECKLIST = """
Checklist:
1) Correctness regressions
2) Security / injection / secrets
3) Performance hotspots
4) Test gaps
5) API compatibility
""".strip()
def review_pr(pr_number: int, diff_text: str, ci_snippet: str):
# 핵심: prefix 블록(규칙/툴/체크리스트)에 cache_control을 붙여 "여기까지"를 캐시 대상으로 고정
# Anthropic 문서상 tools/system/messages 순으로 prefix가 잡히므로, 변동 요소(diff)는 messages 뒤쪽에 둔다.
resp = client.messages.create(
model="claude-opus-4-8",
max_tokens=1200,
temperature=0.2,
system=[
{
"type": "text",
"text": PROJECT_RULES + "\n\n" + REVIEW_TOOLING + "\n\n" + CHECKLIST,
"cache_control": {"type": "ephemeral"} # 5-minute cache
}
],
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": f"PR #{pr_number} diff:\n{diff_text}\n\nCI snippet:\n{ci_snippet}\n\nDo the review."}
]
}
],
)
usage = resp.usage.model_dump()
# 언어 SDK마다 필드명이 다를 수 있으니 usage 전체를 우선 로깅하는 습관 추천
print("USAGE:", json.dumps(usage, ensure_ascii=False, indent=2))
return resp.content[0].text
if __name__ == "__main__":
# 현실적인 시나리오: 같은 프로젝트 규칙/툴/체크리스트는 고정, diff/CI만 바뀜
pr = 1842
diff1 = "diff --git a/app/db.py b/app/db.py\n... (large diff) ..."
ci1 = "FAILED: test_user_auth.py::test_token_refresh ..."
out1 = review_pr(pr, diff1, ci1)
time.sleep(2)
# 2번째 호출: prefix가 동일하면 cache hit가 잡혀 input 비용이 크게 내려가야 함
diff2 = "diff --git a/app/api.py b/app/api.py\n... (another diff) ..."
ci2 = "PASSED: unit tests; FAILED: integration/payment ..."
out2 = review_pr(pr, diff2, ci2)
예상 출력(개념)
- 첫 호출:
cache_*관련 토큰이 “write 위주”로 발생 - 두 번째 호출(5분 내):
cache_read_input_tokens같은 지표가 증가하고, “일반 input tokens”는 상대적으로 줄어듦
(정확한 필드명/구조는 모델/SDK 버전에 따라 다를 수 있어, 운영에선 usage JSON 원문을 저장해 대시보드로 파싱하는 걸 권장합니다.) (github.com)
2) 확장: 캐시 히트율(=비용 절감률) 계측 및 “캐시 안정성” 테스트
실무에서 가장 가치 있는 루프는 “프롬프트 변경이 캐시를 깨는지”를 CI처럼 자동 감지하는 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import hashlib
def stable_prefix_fingerprint(system_blocks) -> str:
# 캐시 히트는 '완전 동일'이 핵심이므로,
# 실제 요청에 들어가는 문자열을 그대로 해시해서 변경 여부를 감지한다.
raw = json.dumps(system_blocks, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
return hashlib.sha256(raw.encode("utf-8")).hexdigest()
SYSTEM_BLOCKS = [
{
"type": "text",
"text": PROJECT_RULES + "\n\n" + REVIEW_TOOLING + "\n\n" + CHECKLIST,
"cache_control": {"type": "ephemeral"}
}
]
print("prefix_fingerprint:", stable_prefix_fingerprint(SYSTEM_BLOCKS))
이 fingerprint를 배포/런타임 로그에 남기면,
- “우리가 의도치 않게 prefix를 바꾸고 있지 않은지”
- “툴 정의 순서가 바뀌어 캐시가 깨지지 않았는지” 를 정량적으로 추적할 수 있습니다. (실제로 Claude Code 관련 글에서도 “툴/프롬프트의 작은 변화가 캐시를 깨서 비싸지고 느려진다”를 반복해서 경고합니다.) (code.claude.com)
⚡ 실전 팁 & 함정
Best Practice 1) “변동 데이터는 무조건 suffix로 밀어라”
- 프롬프트 상단(prefix)에 시간, 토큰 카운터, 실시간 상태, 요청ID 같은 변동 문자열이 들어가면 히트율이 0%가 됩니다.
- 가장 안전한 규칙: “자주 바뀌는 건 마지막 user 메시지로”.
Anthropic은 캐시가tools/system/messages의 prefix를 기준으로 동작하며, breakpoint까지 100% 동일해야 한다고 명확히 말합니다. (platform.claude.com)
Best Practice 2) TTL은 “대화 패턴”으로 결정하라 (5m vs 1h)
Anthropic은 5분 TTL과 1시간 TTL을 제공하고, 1시간이 write 비용이 더 비싸다고 밝힙니다. (platform.claude.com)
즉 “오래 간직하면 무조건 이득”이 아니라,
- 재사용 간격이 대부분 수십 초~수분이면 5m TTL이 최적
- 세션이 길고 사용자가 자주 자리 비우는 형태면 1h가 유리할 수 있으나, write 비용 때문에 손익분기(필요 read 횟수) 를 먼저 계산해야 합니다. (platform.claude.com)
Best Practice 3) 캐싱은 “비용 최적화” 이전에 “구조 최적화”다
최근 연구/사례는 단순히 “전체 컨텍스트를 다 캐시”하면,
- tool result 같은 동적 요소 때문에 miss가 잦아지거나
- 오히려 지연이 증가하는 경우가 있음을 지적합니다.
따라서 동적 tool 결과를 캐시 구간 밖으로 분리하고, “고정된 규칙/툴/정책/프로젝트 컨텍스트”만 캐시하는 쪽이 일관되게 유리합니다. (arxiv.org)
흔한 함정 1) “캐시 write 비용”을 무시한 채 히트율만 본다
Anthropic은 cache hit가 10% 수준이라 매우 매력적이지만, 그 앞단에 write가 존재합니다(5m=1.25×, 1h는 더 큼). (platform.claude.com)
따라서 KPI는 보통 다음이 현실적입니다.
cache_read_input_tokens / total_input_tokens(히트율)- “캐시로 절약된 달러” = (uncached_rate - cached_rate) × cache_read_tokens - write_multiplier_penalty × cache_write_tokens
흔한 함정 2) 모델/설정 변경이 캐시를 “전부 초기화”한다
캐시는 모델/프롬프트 구조에 강하게 결합됩니다. 예컨대 Anthropic은 “긴 대화에서 Opus로 쌓아둔 캐시가 있는데, 중간에 더 싼 모델로 바꾸면 캐시를 다시 빌드해야 해서 오히려 더 비싸질 수 있다”는 취지의 사례를 공유합니다. (claude.com)
실무에선 “비용 절감” 때문에 모델을 자주 스위칭하는 라우팅이 캐시 관점에서 역효과가 날 수 있습니다.
트레이드오프: 비용 vs 안정성 vs 개인정보/정책
- 일부 연구는 provider의 캐시 정책 투명성이 부족하면 프라이버시 리스크가 생길 수 있음을 지적합니다(캐시 공유/누수 가능성). 이런 리스크 모델이 있는 조직이라면 ZDR/데이터 통제 옵션, 저장 기간, 캐시 스코프를 반드시 확인해야 합니다. (arxiv.org)
- OpenAI 쪽은 endpoint/설정에 따라 “application state retention” 같은 저장 개념이 얽힐 수 있어(특히 Responses API 계열), 컴플라이언스 요구사항이 있으면 사전에 정책 확인이 필요합니다. (platform.openai.com)
🚀 마무리
프롬프트 캐싱은 “옵션 하나 켜면 끝”이 아니라, 프롬프트를 제품처럼 설계하는 영역입니다. 2026년 7월 기준으로 정리하면:
- OpenAI/Anthropic 모두 prefix 재사용을 기반으로 비용을 크게 낮출 수 있지만, 효과는 캐시 히트율과 write 비용에 의해 결정됩니다. (openai.com)
- 실무 도입 판단 기준: 1) 동일한 system/tool/context가 반복 호출되는가? (예: 에이전트, 코드리뷰, 긴 챗)
2) 변동 데이터(tool 결과/시간/세션 메타)를 suffix로 분리할 수 있는가?
3) usage에서cache_read_*,cache_write_*를 계측/대시보드화할 수 있는가? - 다음 학습 추천:
- Anthropic의 prompt caching 구조(자동 vs 명시 breakpoint, prefix 범위) 문서 정독 (platform.claude.com)
- “Claude Code에서 캐시를 깨는 패턴” 사례(툴 정의/중간 편집/컴팩션 전략 등) (claude.com)
- 장기 에이전트 워크로드에서의 캐싱 전략 비교 연구(동적 tool 결과 분리) (arxiv.org)
원하면, 당신의 실제 워크로드(대화 길이, 평균 prefix 토큰, 턴 간 간격, 모델, 툴 호출 비율)를 기준으로 “5m vs 1h TTL 손익분기” 계산식 + 관측 지표 + 프롬프트 재배치 체크리스트까지 붙여서 최적화 플랜을 같이 만들어드릴 수 있습니다.