LLM API Prompt Caching은 언제 이득일까: Claude 비용·TTL·캐시 무효화
긴 시스템 지시와 tool definition(도구 정의)을 TTL 안에서 정확히 재사용할 수 있다면 Claude Prompt Caching은 두세 번째 요청부터 반복되는 prefix(앞부분 구간)의 입력 비용을 낮춥니다. 반대로 짧은 프롬프트나 한 번뿐인 요청, 혹은 자주 바뀌는 tools·system·대화 이력을 다루는 상황에서는 최초 cache write(캐시 기록) 할증 비용만 지불하거나 cache hit(캐시 적중)의 이점을 얻지 못할 수 있습니다. prefix가 매번 달라져 전부 다시 써야 하는 최악의 경우라면, 그 prefix의 입력 비용은 cache를 켜지 않았을 때의 1.25배(5분 TTL)에서 2배(1시간 TTL)로 증가합니다. 출력과 동적 입력 비용은 여기에 그대로 더해집니다.
그래서 LLM API 비용은 토큰 단가만으로 결정되지 않습니다. 재사용할 prefix가 얼마나 안정적인지, 그리고 응답의 cache_read_input_tokens가 실제로 얼마나 나오는지를 함께 살펴보아야 합니다.
이 글의 가격과 API 동작은 2026년 9월 5일 Claude API 공식 문서와 Anthropic Python SDK 0.87.0을 기준으로 확인했습니다. 중심 사례는 2026년 9월 1일 출시된 Claude Fable 5.1(claude-fable-5-1)입니다. 가격은 바뀔 수 있으므로 실제 도입 전에는 Claude API 가격표를 다시 확인해야 합니다.
Prompt Caching은 무엇을 아끼는가
Claude API의 Prompt Caching은 이전 요청에서 처리한 동일한 prompt prefix를 다시 이용하는 기능입니다. 첫 요청에서는 지정된 prefix를 처리해 cache에 쓰고, 이후의 요청에서 같은 prefix가 발견되면 cache에서 읽습니다. 긴 시스템 지시, 많은 tool definition, 예시 묶음, 참조 문서, 누적 대화 이력을 반복해서 보내는 작업에 잘 맞습니다.
여기서 prefix는 단순히 문자열 하나가 아닙니다. Claude API는 요청을 다음 순서로 이어 붙여 처리합니다.
flowchart LR
A[고정 tools] --> B[고정 system]
B --> C[고정 예시와 참조 문서]
C --> D[cache breakpoint]
D --> E[append-only messages]
E --> F[동적 질문과 메타데이터]
공식 순서는 tools → system → messages입니다. cache_control이 표시된 block(블록)까지의 누적 prefix가 cache 대상이 됩니다. Prompt Caching 공식 문서는 cache가 비용뿐 아니라 긴 입력의 처리 시간을 줄일 수 있다고 설명하지만, 출력 생성 자체를 생략하는 기능은 아닙니다.
Prompt Caching을 통해 절감할 수 있는 것은 cache에서 읽어온 입력 토큰의 비용입니다. 다음과 같은 항목의 비용은 그대로 발생합니다.
- cache에 포함되지 않은 동적 입력 토큰
- 모델이 생성한 출력 토큰
- 웹 검색처럼 별도 요금이 있는 server tool(서버 도구) 사용료
- cache miss(캐시 미스)가 발생했을 때 다시 기록하는 비용
에이전트 환경에서 이 부분이 중요한 이유는, 하나의 과업이 여러 번의 모델 호출로 이어지기 때문입니다. 도구 호출과 관찰을 반복하는 기본 구조는 AI Agent 완벽 가이드에서 볼 수 있습니다. 각 호출마다 동일한 tool schema와 시스템 규칙을 다시 보내면 안정적인 prefix의 비중이 커지고, cache hit의 가치 또한 높아집니다.
Fable 5.1 가격표에서 먼저 볼 숫자
Claude Fable 5.1 모델 페이지와 공식 가격표가 제시하는 백만 토큰(MTok)당 가격은 다음과 같습니다.
| 과금 항목 | Fable 5.1 단가 | 일반 입력 대비 |
|---|---|---|
| 일반 입력 | $10/MTok | 1배 |
| 5분 cache write | $12.50/MTok | 1.25배 |
| 1시간 cache write | $20/MTok | 2배 |
| cache read | $0.25/MTok | 0.025배 |
| 출력 | $50/MTok | 별도 과금 |
표에서 놓치기 쉬운 부분은 최초 cache write가 일반 입력보다 비싸다는 점입니다. 5분 cache를 처음 기록할 때는 일반 입력의 1.25배, 1시간 cache의 경우에는 2배의 비용을 지불해야 합니다. 이후 발생하는 매우 저렴한 cache read 비용을 통해 앞선 할증 비용을 회수하는 구조입니다.
Fable 5.1의 cache read 단가인 $0.25/MTok는 일반적인 Claude 모델의 0.1배 규칙이 아니라 기본 입력 단가의 0.025배로 특별하게 책정된 예외입니다. 공식 가격표 기준으로 이러한 예외는 Fable 5.1과 Mythos 5.1에만 적용되며, Opus 5를 포함한 나머지 모델은 기존의 0.1배 규칙을 그대로 따릅니다. Anthropic은 이전 버전인 Fable 5의 cache read 단가($1/MTok)와 비교해 비용이 75% 낮아졌다고 설명합니다. 하지만 이는 전체 API 비용이 75% 줄었다는 의미가 아닙니다. 출력, 동적 입력, 그리고 cache miss에 따른 비용은 여전히 별도로 계산되어야 합니다.
Anthropic은 Fable 제품 페이지에서 새로운 cache read 단가가 일반적인 workload(워크로드)의 비용을 약 25%, 에이전트 workload의 비용을 최대 약 45%까지 낮출 수 있다고 추정합니다. 하지만 공개된 자료에는 prefix 비율, hit ratio, 출력 비중, 호출 횟수 등 이를 재현하기 위한 구체적인 조건이 명시되어 있지 않습니다. 따라서 이 수치는 어디까지나 Anthropic의 추정치로만 참고해야 하며, 실제 서비스에서 무조건 보장되는 절감률로 생각해서는 안 됩니다.
flowchart LR
A["일반 입력
$10 / MTok
1×"]
B["5분 cache write
$12.50 / MTok
1.25×"]
C["1시간 cache write
$20 / MTok
2×"]
D["cache read
$0.25 / MTok
0.025×"]
E["출력
$50 / MTok"]
A -->|"cache 생성"| B
A -->|"긴 TTL"| C
B -->|"이후 hit"| D
C -->|"이후 hit"| D
D --> F["반복 요청이 많을수록
초기 write 할증 회수"]
E -. "별도 과금" .-> F
style B fill:#fff3e0,stroke:#ef6c00
style C fill:#ffebee,stroke:#c62828
style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px
style F fill:#e3f2fd,stroke:#1565c0
5분 cache는 두 번째 요청부터 이득이다
손익분기점을 직접 계산해 보겠습니다. 조건을 단순하게 만들기 위해 다음과 같이 가정합니다.
- 동일한 cacheable prefix: 100,000 tokens, 즉 0.1 MTok
- 첫 요청에서 cache write 1회
- 뒤의 요청은 모두 TTL 안에 들어오며 prefix가 정확히 같아 cache hit
- 동적 suffix, 출력 토큰, server tool 비용, Batch 할인과 지역 할증은 제외
n은 첫 cache write를 포함한 전체 요청 횟수
이때 비용식은 다음과 같습니다.
일반 입력 비용(n) = n × 0.1 × 10
5분 cache 비용(n) = 0.1 × [12.50 + (n - 1) × 0.25]
1시간 cache 비용(n) = 0.1 × [20.00 + (n - 1) × 0.25]
절감률 = 1 - (cache 비용 / 일반 입력 비용)
계산 결과는 이렇습니다.
| 전체 요청 횟수 | cache 없음 | 5분 cache | 5분 절감률 | 1시간 cache | 1시간 절감률 |
|---|---|---|---|---|---|
| 1 | $1.0000 | $1.2500 | -25.00% | $2.0000 | -100.00% |
| 2 | $2.0000 | $1.2750 | 36.25% | $2.0250 | -1.25% |
| 3 | $3.0000 | $1.3000 | 56.67% | $2.0500 | 31.67% |
| 5 | $5.0000 | $1.3500 | 73.00% | $2.1000 | 58.00% |
| 10 | $10.0000 | $1.4750 | 85.25% | $2.2250 | 77.75% |
| 20 | $20.0000 | $1.7250 | 91.38% | $2.4750 | 87.62% |
xychart-beta
title "0.1 MTok prefix 기준 누적 비용"
x-axis "전체 요청 횟수" [1, 2, 3, 5, 10, 20]
y-axis "비용 USD" 0 --> 20
line "cache 없음" [1, 2, 3, 5, 10, 20]
line "5분 cache" [1.25, 1.275, 1.30, 1.35, 1.475, 1.725]
line "1시간 cache" [2.0, 2.025, 2.05, 2.10, 2.225, 2.475]
5분 cache의 손익분기점을 계산해 보면, 전체 요청 횟수 n이 2 이상일 때부터 일반 입력보다 저렴해집니다. 즉, 첫 cache write 뒤 단 한 번만 hit해도 cache write 할증 비용을 회수할 수 있습니다. 반면 1시간 cache는 n이 3 이상이어야 하므로, 첫 cache write 이후 최소 두 번의 hit이 필요합니다.
최악의 상황도 고려해 볼 필요가 있습니다. 매번 prefix가 달라져 모든 요청에서 cache miss가 발생하고 매번 새로 기록해야 한다면, 전체 비용은 요청 횟수와 무관하게 일반 입력의 1.25배(5분) 또는 2배(1시간)로 고정됩니다. 위 표의 n = 1 행이 바로 이러한 상황을 나타내며, 요청 횟수를 아무리 늘려도 이 할증 비율은 낮아지지 않습니다. 따라서 cache 기능을 사용할지 결정할 때는 “hit했을 때 얼마나 절감되는가”뿐만 아니라 “hit하지 않았을 때 얼마나 비용이 증가하는가”도 함께 고려해야 합니다.
위 표에 나타난 절감률은 100,000 토큰 분량의 prefix 부분만 비교한 수치입니다. 실제 요청에서는 출력 토큰이 많거나 cache 이후에 이어지는 동적 프롬프트가 길어질수록, 전체 청구 비용에서 차지하는 실질적인 절감률은 낮아집니다. 반대로 방대한 문서나 tool schema를 수십 번 반복해서 재사용한다면, 공통 prefix가 비용 절감에 기여하는 차이는 더욱 커지게 됩니다.
이 수치들은 다음 Python 코드를 통해 직접 계산해 볼 수 있습니다. 외부 패키지 설치나 API 키 없이 로컬 환경에서 바로 실행 가능한 코드입니다.
base_input = 10.00
cache_read = 0.25
prefix_mtok = 0.1 # 100,000 tokens
for requests in [1, 2, 3, 5, 10, 20]:
uncached = requests * prefix_mtok * base_input
cached_5m = prefix_mtok * (12.50 + (requests - 1) * cache_read)
cached_1h = prefix_mtok * (20.00 + (requests - 1) * cache_read)
save_5m = 1 - cached_5m / uncached
save_1h = 1 - cached_1h / uncached
print(
requests,
f"uncached=${uncached:.4f}",
f"5m=${cached_5m:.4f} ({save_5m:.2%})",
f"1h=${cached_1h:.4f} ({save_1h:.2%})",
)
5분과 1시간 TTL 중 무엇을 고를까
Prompt Caching의 기본 TTL(Time-To-Live)은 5분입니다. cache hit가 발생하면 해당 cache의 TTL은 추가 cache write 요금 없이 다시 초기화되어 연장됩니다. 따라서 5분 이내에 요청이 계속 이어지는 agent loop(에이전트 루프) 환경이라면 5분 cache만으로도 상태를 지속적으로 유지할 수 있습니다.
1시간 TTL은 cache write 비용이 일반 입력의 2배에 달합니다. 하지만 사용자의 응답을 기다리는 데 5분 이상이 걸릴 수 있는 긴 채팅 세션, 한 단계의 처리에 오랜 시간이 소요되는 작업, 혹은 호출 주기가 5분보다는 길지만 1시간 이내에는 반드시 반복되는 공통 프롬프트를 다룰 때 적합합니다.
오히려 주의해야 할 것은 그 반대의 상황입니다. 호출 간격이 1시간을 초과하게 되면 매번 새로운 cache write가 발생하므로 일반 입력의 2배에 해당하는 비용을 계속 지불하게 됩니다. 만약 같은 조건에서 5분 TTL을 선택했다면 손실이 1.25배에 그쳤을 것이며, 아예 재사용 가능성이 없다면 cache 기능을 끄는 것이 비용 면에서 가장 유리합니다. 결론적으로 TTL을 선택할 때는 “cache가 얼마나 오래 유지되길 바라는가”가 아니라 “실제 API 호출 간격의 분포가 어떠한가”를 기준으로 삼아야 합니다.
| 조건 | 5분 cache | 1시간 cache |
|---|---|---|
| cache write 단가 | 일반 입력의 1.25배 | 일반 입력의 2배 |
| 손익분기 | 전체 2회 요청 | 전체 3회 요청 |
| 맞는 호출 간격 | 대체로 5분 이내 | 5분을 넘지만 1시간 이내 |
| 대표 사례 | 연속 agent loop, 짧은 간격의 공통 system prompt | 느린 사용자 응답, 5분 넘는 장기 작업 |
하나의 요청에서 두 가지 TTL을 혼용하는 것도 가능하지만, 여기에는 순서상의 제약이 따릅니다. Prompt Caching 공식 문서에 따르면 TTL이 긴 cache entry가 더 앞쪽에 위치해야 합니다. 따라서 1시간 breakpoint(중단점)를 5분 breakpoint보다 앞에 배치해야만 정상적으로 작동합니다.
flowchart TD
A["공통 prefix를 다시 호출하는 간격은?"]
A -->|"대체로 5분 이내"| B["5분 TTL"]
A -->|"5분 초과 ~ 1시간 이내"| C["1시간 TTL"]
A -->|"대체로 1시간 초과"| D["Caching 재검토"]
B --> B1["write 비용: 1.25×"]
B --> B2["연속 agent loop에 적합"]
B --> B3["hit 때 TTL 갱신"]
C --> C1["write 비용: 2×"]
C --> C2["긴 사용자 대기"]
C --> C3["장시간 작업"]
D --> D1["매번 새 write가 발생할 수 있음"]
D --> D2["cache 없이 처리하는 편이 저렴할 수 있음"]
style B fill:#e8f5e9,stroke:#2e7d32
style C fill:#ede7f6,stroke:#5e35b1
style D fill:#ffebee,stroke:#c62828
cache breakpoint는 마지막 고정 block에 둔다
Prompt Caching에는 두 가지 설정 방식이 있습니다.
- Automatic caching: 요청의 최상위에
cache_control을 한 번만 설정하면, 마지막 cacheable block까지 API가 자동으로 관리합니다. - Explicit breakpoint: 특정 content block이나 tool definition 내에
cache_control을 삽입하여 cache의 끝점을 직접 지정합니다.
대화 기록이 뒤로 계속 이어지기만 하는 일반적인 구조에서는 automatic caching 방식이 간편합니다. 하지만 마지막 block에 timestamp, 요청 ID, 혹은 사용자별 동적 데이터처럼 매번 달라지는 값이 포함되어 있다면, automatic caching의 끝점 또한 계속해서 바뀌게 됩니다. 이런 경우에는 내용이 동일하게 유지되는 마지막 block 끝에 explicit breakpoint를 설정하는 편이 훨씬 안전합니다.
flowchart TB
subgraph BAD["나쁜 배치"]
direction LR
A1["고정 tools"] --> A2["고정 system"]
A2 --> A3["고정 문서"]
A3 --> A4["timestamp"]
A4 --> A5["사용자 질문"]
A5 --> A6["breakpoint"]
A6 --> AX["매 요청마다 prefix 변경
cache miss 위험"]
end
subgraph GOOD["좋은 배치"]
direction LR
B1["고정 tools"] --> B2["고정 system"]
B2 --> B3["고정 문서"]
B3 --> B4["breakpoint"]
B4 --> B5["timestamp"]
B5 --> B6["사용자 질문"]
B4 --> BX["고정 prefix 유지
cache hit 가능"]
end
style AX fill:#ffebee,stroke:#c62828
style BX fill:#e8f5e9,stroke:#2e7d32
style A6 fill:#ffccbc,stroke:#d84315
style B4 fill:#c8e6c9,stroke:#2e7d32
하나의 API 요청에는 breakpoint를 최대 4개까지 설정할 수 있습니다. 만약 automatic caching과 명시적 방식을 함께 사용한다면, 자동 breakpoint 역시 이 4개의 제한 중 하나를 차지하게 됩니다. 따라서 명시적 breakpoint를 이미 4개 지정한 상태에서 automatic caching까지 활성화하면 여유 자리가 부족해져 요청이 400 오류와 함께 거부됩니다. 공식 문서에서 여러 개의 breakpoint를 사용하는 목적은 크게 두 가지로 설명됩니다. 첫째는 갱신 주기가 서로 다른 영역들을 분리하는 것이고, 둘째는 대화가 길어지면서 마지막 cache write 지점이 뒤로 밀리더라도 이전 내용들에 대한 cache hit를 최대한 확보하는 것입니다.
또 다른 중요한 제약 사항으로는 20-block lookback (20개 block 역탐색) 규칙이 있습니다. API는 특정 breakpoint를 기준으로 자기 자신을 포함해 최대 20개의 위치를 거슬러 올라가며 이전 cache entry를 탐색하고, 그 범위를 벗어나면 탐색을 중단합니다. 다행히 Claude API에서는 연속으로 이어진 tool_use block들이나 연속된 tool_result block 묶음은 각각 하나의 위치로 취급됩니다. 즉, 한 턴에 병렬 도구 호출이 많이 발생하더라도 직전 요청의 cache entry가 곧바로 탐색 범위 밖으로 밀려나지는 않습니다. 그럼에도 불구하고 단일 턴 안에 서로 다른 종류의 block이 20개 이상 쌓이는 복잡한 구조라면, 중간중간 명시적인 breakpoint를 미리 설정해 두는 것이 안전합니다.
Fable 5.1 모델에서 cache를 적용할 수 있는 프롬프트의 최소 길이는 512 토큰입니다. 이보다 짧은 프롬프트에 cache_control을 설정하더라도 실제로는 cache가 생성되거나 읽히지 않을 수 있습니다. 따라서 단순히 cache 설정을 추가했는지 여부만 믿지 말고, 반드시 API 응답에 포함된 usage 값을 통해 cache 동작 여부를 직접 확인해야 합니다.
tools를 바꾸면 왜 전체 cache가 깨질까
cache hit가 발생하려면 breakpoint까지의 prompt segment가 단 하나의 차이도 없이 100% 동일해야 합니다. 프롬프트는 tools, system, messages 순으로 계층적으로 이어지기 때문에 앞부분에서 변경이 발생할수록 그 영향이 미치는 범위는 더 커집니다.
| 변경 | 무효화 범위 | 대응 방법 |
|---|---|---|
| tool definition·도구 배열 변경 | tools + system + messages | 고정 toolset 유지, tool search와 defer_loading 또는 아래 mid-conversation tool change 검토 |
| top-level system 변경 | system + messages | 과거 system을 고치지 말고 mid-conversation system message로 뒤에 추가 |
| 과거 message 수정·삭제 | 변경 지점 이후 | append-only history 유지 |
tool_choice 변경 | messages | 설정을 고정하거나 변화 지점 앞에 breakpoint 배치 |
disable_parallel_tool_use 변경 | messages | 같은 cache를 쓰는 세션에서는 고정 |
| 이미지 유무 변경 | messages | 입력 형식을 일관되게 유지 |
| thinking·effort 변경 | 모델에 따라 messages 또는 앞 영역까지 | 세션 안에서 최상위 설정 고정, Fable 5.1은 아래 per-message effort 검토 |
| timestamp·request ID를 앞부분에 삽입 | 해당 지점 이후 | 동적 값은 breakpoint 뒤로 이동 |
예를 들어 tool description에 마침표 하나를 추가하거나, tool schema의 속성 순서를 다르게 직렬화하기만 해도 tool prefix 전체가 달라진 것으로 인식됩니다. Tool use with Prompt Caching 공식 문서에서도 도구의 정의를 수정할 경우 tools뿐만 아니라 그 뒤에 이어지는 system과 messages 영역의 cache까지 모두 무효화된다고 경고하고 있습니다. 또한 직렬화 라이브러리의 특성이나 딕셔너리(dict) 순회 순서의 차이로 인해, 논리적으로는 동일한 schema임에도 매 요청마다 다른 JSON 문자열로 직렬화되어 cache miss를 유발할 수 있습니다. 따라서 도구를 정의할 때는 속성 순서를 일관되게 정렬하여 고정된 형태로 전달하는 것이 훨씬 안전합니다.
선언된 도구는 많지만 실제로는 그중 일부만 사용하는 경우라면, 매번 모든 도구의 schema를 요청에 포함시키기보다는 tool search 방식이나 defer_loading 기법을 활용해 볼 수 있습니다. deferred tool은 초기 tools prefix에서 제외되며, 실제로 필요해진 시점에 대화 이력 내에 tool_reference 형태로 추가되기 때문에 기존에 고정된 prefix를 그대로 보존할 수 있습니다.
server tool에는 고유한 동작 방식도 있습니다. 사용자의 요청 내에 cache_control이 최소 하나 이상 포함된 상태에서 웹 검색, 웹 가져오기, 코드 실행 등 server tool이 호출되면, API는 다음 iteration으로 넘어가기 전 tool result 부분에 자동으로 breakpoint를 삽입합니다. 흥미롭게도 이 자동 breakpoint는 사용자가 명시적으로 1시간 TTL을 지정해 두었더라도 자체적인 5분 TTL을 적용받습니다. 만약 응답의 Usage 항목에서 예상치 못한 ephemeral_5m_input_tokens 비용이 발견된다면 가장 먼저 이 동작을 의심해 보아야 합니다. 반면, 요청에 cache_control 설정이 아예 없다면 이러한 자동 breakpoint도 생성되지 않습니다.
cache를 깨지 않고 설정을 바꾸는 경로
앞선 표의 내용만 보면 세션 중간에 설정을 바꾸는 것이 무조건 손해인 것처럼 보이지만, 다행히 Fable 5.1 모델에는 기존 cache prefix를 보존하면서 설정을 유연하게 변경할 수 있는 공식적인 방법이 존재합니다. 이 방법들은 모두 기존의 앞쪽 prefix 영역을 건드리지 않고, messages 배열 내에 {"role": "system"} 형태의 메시지를 추가함으로써 변경이 필요한 시점 이후에만 새로운 설정을 덧붙이는 원리로 동작합니다.
| 바꾸려는 것 | 방법 | 필요한 베타 헤더 |
|---|---|---|
| system 지시 | messages에 role: "system" 메시지 추가 | 없음(정식 기능) |
| 사용 가능한 도구 | 그 메시지 안에 tool_addition·tool_removal block | mid-conversation-tool-changes-2026-07-01 |
| effort 수준 | output_config.effort만 담은 role: "system" 메시지 | mid-conversation-output-config-2026-07-01 |
Mid-conversation system messages 공식 문서에 따르면, 대화 도중 시스템 메시지를 삽입하는 기능은 Fable 5.1, Mythos 5.1, Fable 5, Mythos 5, Opus 4.8, Opus 5 모델에서만 사용할 수 있으며 Sonnet 5 모델에서는 아직 지원되지 않습니다. 여기서 사용되는 tool_addition이나 tool_removal 속성은 도구를 완전히 새로 정의하는 것이 아니라, 이미 최상위 요청의 tools 배열에 선언되어 있는 도구의 이름을 참조하는 역할만 수행합니다. 만약 선언되지 않은 도구 이름을 전달하면 400 오류가 반환됩니다. 또한 Effort 옵션 관련 문서에서 알 수 있듯 최상위 수준에 설정된 effort 값을 도중에 변경하면 전체 cache가 무효화되고 처음부터 다시 시작되지만, 메시지 단위(per-message)로 effort 값을 부여하는 방식을 사용하면 앞서 저장된 cache prefix를 안전하게 보존할 수 있습니다.
다만, 프로그래밍 환경의 SDK 타입 정의가 이러한 최신 기능을 완벽히 지원하지는 못하고 있습니다. Anthropic Python SDK 0.87.0 버전을 기준으로 MessageParam.role 필드는 user와 assistant 역할만을 허용하도록 정의되어 있어서, {"role": "system"} 메시지를 직접 넣으려 하면 type checker에서 오류가 발생할 수 있습니다. 추가로 베타 헤더 설정이 필요한 두 가지 기능은 client.beta.messages.create(..., betas=[...]) 형식으로 호출해야 하므로, 실제 프로덕션에 도입하기 전에 현재 사용 중인 SDK 버전이 이 기능들을 명시적으로 지원하는지 꼼꼼히 확인해야 합니다.
flowchart LR
A["tools"] --> B["system"]
B --> C["messages"]
A1["tool definition 변경"] --> A
A -->|"무효화"| X1["tools cache"]
A -->|"뒤 영역까지 영향"| X2["system cache"]
A -->|"뒤 영역까지 영향"| X3["messages cache"]
B1["system 변경"] --> B
B -->|"무효화"| Y1["system cache"]
B -->|"뒤 영역까지 영향"| Y2["messages cache"]
C1["과거 message 수정"] --> C
C -->|"변경 지점 이후"| Z1["messages cache"]
style A1 fill:#ffebee,stroke:#c62828
style B1 fill:#fff3e0,stroke:#ef6c00
style C1 fill:#ede7f6,stroke:#5e35b1
style X1 fill:#ffebee,stroke:#c62828
style X2 fill:#ffebee,stroke:#c62828
style X3 fill:#ffebee,stroke:#c62828
Python SDK에서 설정하는 방법
아래의 예시 코드는 Anthropic Python SDK 0.87.0 버전에 정의된 messages.create() 함수의 시그니처와 공식 문서를 기준으로 작성되었습니다. 코드 문법 및 인자 지원 여부는 확인하였으나, 실제 API 호출을 수행한 것은 아닙니다. Anthropic() 클라이언트 객체는 실행 환경의 ANTHROPIC_API_KEY 환경 변수를 자동으로 읽어오므로 코드 내에나 로그 파일에 API 키를 직접 하드코딩하지 않아도 됩니다.
flowchart TD
A["Prompt Caching 설정"]
A --> B["Automatic caching"]
A --> C["Explicit breakpoint"]
B --> B1["top-level cache_control"]
B1 --> B2["마지막 cacheable block까지
API가 자동 관리"]
B2 --> B3["append-only 대화에 간편"]
C --> C1["content block / tool definition에
cache_control 지정"]
C1 --> C2["마지막 고정 block을 직접 선택"]
C2 --> C3["동적 값이 뒤에 붙는 구조에 유리"]
B3 --> D["응답 usage 확인"]
C3 --> D
D --> E["cache_creation_input_tokens"]
D --> F["cache_read_input_tokens"]
style B fill:#e3f2fd,stroke:#1565c0
style C fill:#ede7f6,stroke:#5e35b1
style D fill:#e8f5e9,stroke:#2e7d32
Automatic caching으로 시작하기
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-fable-5-1",
max_tokens=512,
cache_control={"type": "ephemeral"},
system=(
"You are a code review assistant. "
"Follow the team's stable review policy and output format."
),
messages=[
{
"role": "user",
"content": "Review this change and list correctness risks first.",
}
],
)
print(response.usage.model_dump())
이 예시에 사용된 짧은 system prompt는 단지 구조적인 이해를 돕기 위한 것입니다. 실제로 cache가 올바르게 작동하려면, Fable 5.1 모델을 기준으로 breakpoint까지의 프롬프트 길이가 최소 512 토큰 이상이어야 합니다.
고정된 tool definitions에 breakpoint 두기
Tool schema 전체를 공유하려면 마지막 tool definition에 cache_control을 놓을 수 있습니다.
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "search_documents",
"description": "Search approved internal documents.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
},
"required": ["query"],
"additionalProperties": False,
},
},
{
"name": "read_document",
"description": "Read one document returned by search_documents.",
"input_schema": {
"type": "object",
"properties": {
"document_id": {"type": "string"},
},
"required": ["document_id"],
"additionalProperties": False,
},
"cache_control": {"type": "ephemeral", "ttl": "1h"},
},
]
response = client.messages.create(
model="claude-fable-5-1",
max_tokens=512,
tools=tools,
system="Use only the approved document tools and cite document IDs.",
messages=[
{"role": "user", "content": "Find the current incident policy."}
],
)
print(response.usage.model_dump())
마지막 도구에 설정된 breakpoint는 첫 번째 도구부터 두 번째 도구까지 누적된 전체 tools prefix를 포괄하게 됩니다. 만약 이 cache를 계속해서 재사용하고자 한다면, 도구의 배치 순서, 각 도구의 설명(description) 및 schema, 그리고 TTL 설정 등을 변경 없이 안정적으로 유지해야 합니다.
RAG(Retrieval-Augmented Generation) 시스템처럼 긴 문서를 기반으로 반복적인 질의를 수행하는 구조 역시 동일한 원리로 접근할 수 있습니다. 검색과 문서를 LLM에 전달하는 기본적인 아키텍처에 대해서는 RAG 완벽 가이드를 참고하시기 바랍니다. 여기서 주의할 점은 어떤 콘텐츠를 cache 대상으로 삼을지 신중히 결정해야 한다는 점입니다. 제공되는 문서가 모든 요청에 공통으로 사용되는 것인지, 아니면 사용자의 질문에 따라 검색 결과가 매번 달라지는 것인지를 명확히 구분해야 합니다. 만약 질문마다 내용이 바뀌는 검색 결과를 고정된 prefix 영역에 억지로 밀어 넣는다면, 결과적으로 cache hit에 실패하여 이점을 잃게 될 것입니다.
usage로 실제 청구 구조를 기록하자
단지 코드상에서 Prompt Caching 옵션을 활성화했다고 해서 자동으로 비용 절감 효과를 장담할 수 있는 것은 아닙니다. 실제로 효과가 있는지 검증하려면 API 응답의 usage 객체에서 다음과 같은 항목들의 값을 꼼꼼히 수집하고 분석해야 합니다.
input_tokens: cache write·read에 포함되지 않은 일반 입력 토큰cache_creation_input_tokens: 새 cache entry에 쓴 토큰cache_read_input_tokens: cache에서 읽은 토큰cache_creation.ephemeral_5m_input_tokens: 5분 TTL로 쓴 토큰cache_creation.ephemeral_1h_input_tokens: 1시간 TTL로 쓴 토큰output_tokens: 생성된 출력 토큰
다음 함수는 2026년 9월 5일에 공개된 Fable 5.1 모델의 단가를 기준으로 단일 응답에서 발생한 토큰 비용을 상세하게 계산해 줍니다. API가 반환하는 usage 객체를 입력받아 동적 입력, TTL 종류별 cache write, cache read, 출력 토큰을 각각 분리해 비용을 합산합니다.
def estimate_fable_5_1_cost(usage) -> float:
creation = usage.cache_creation
write_5m = 0 if creation is None else creation.ephemeral_5m_input_tokens
write_1h = 0 if creation is None else creation.ephemeral_1h_input_tokens
regular_input = usage.input_tokens
cache_read = usage.cache_read_input_tokens or 0
output = usage.output_tokens
return (
regular_input / 1_000_000 * 10.00
+ write_5m / 1_000_000 * 12.50
+ write_1h / 1_000_000 * 20.00
+ cache_read / 1_000_000 * 0.25
+ output / 1_000_000 * 50.00
)
실제 상용 서비스 환경이라면 위에서 계산한 금액 외에도 server tool 요금, Batch API 사용 시의 할인율, 데이터가 처리되는 리전(지역) 할증이나 서드파티 파트너 플랫폼에 따른 가격 차이까지 모두 반영해야 합니다. 또한 개별 요청 단위로 비용을 산정하기보다는 다음과 같은 지표들을 동일한 workload 단위로 묶어 종합적으로 평가하는 방식이 권장됩니다.
cache read ratio
= cache_read_input_tokens
/ (input_tokens + cache_creation_input_tokens + cache_read_input_tokens)
성공 작업당 비용
= 같은 workload에서 발생한 모든 요청·도구 비용의 합계 (실패와 재시도 포함)
/ 최종적으로 성공한 작업 수
재시도 과정 역시 하나의 유효한 작업 단위라기보다는 비용을 발생시키는 요인이므로 분자 항목에 포함되어야 합니다. 실패한 시도와 재시도 과정에서 소모된 모든 비용을 분자에 합산하고, 분모에는 최종적으로 성공한 작업의 수만 남깁니다. 만약 실패로 발생한 비용을 제외하고 계산하게 되면, 비용은 저렴해 보일지라도 실제로는 잦은 실패로 인해 전체 효율이 떨어지는 구성을 과대평가하는 오류를 범할 수 있습니다. 아울러 비용과 더불어 p50·p95 latency, time-to-first-token, 전반적인 성공률, 그리고 평균 재시도 횟수 등을 꾸준히 기록해 두면, 향후 모델이나 cache 정책을 변경했을 때 그 실질적인 효과를 훨씬 명확히 비교할 수 있습니다. 모델의 단순 정확도를 넘어 이러한 운영 지표를 종합적으로 평가하는 방법에 대해서는 AI 모델 성능 평가 지표 가이드 문서를 함께 참고해 보시길 권합니다.
flowchart LR
A["API Response Usage"]
A --> B["input_tokens"]
A --> C["cache_creation_input_tokens"]
A --> D["cache_read_input_tokens"]
A --> E["output_tokens"]
C --> C1["5분 write tokens"]
C --> C2["1시간 write tokens"]
B --> F["실제 비용 계산"]
C1 --> F
C2 --> F
D --> F
E --> F
F --> H["$/task"]
A --> G["cache read ratio
토큰 수로 계산"]
L["별도 관측"] --> I["p50 / p95 latency"]
L --> J["성공률 · 재시도 횟수"]
H --> K["실패·재시도 비용도 포함"]
style D fill:#e8f5e9,stroke:#2e7d32
style G fill:#e3f2fd,stroke:#1565c0
style H fill:#ede7f6,stroke:#5e35b1
style K fill:#fff3e0,stroke:#ef6c00
Prompt Caching이 맞는 경우와 아닌 경우
Prompt Caching 기능이 빛을 발하는 이상적인 조건은 다음과 같이 명확합니다.
- 512 tokens 이상의 긴 system prompt나 tool schema를 여러 요청이 공유한다.
- 같은 문서에 여러 질문을 던진다.
- 에이전트가 TTL 안에서 여러 번 모델을 호출한다.
- 과거 대화를 수정하지 않고 뒤에 새 message를 추가한다.
- tools, thinking, effort 같은 요청 설정을 안정적으로 유지할 수 있다.
반대로 다음과 같은 상황에 속한다면, 무작정 cache 기능을 켜기보다는 현재 workload의 특성을 먼저 면밀히 측정하고 분석하는 과정이 선행되어야 합니다.
- 한 번만 호출하고 끝난다.
- 매 요청마다 tools나 system이 크게 달라진다.
- 호출 간격이 선택한 TTL을 계속 넘는다.
- 전체 비용의 대부분이 출력 토큰이나 별도 tool 요금이다.
- 재사용 가능한 prefix가 모델의 최소 길이에 못 미친다.
cache miss가 반복적으로 발생할 때는 단순히 프롬프트에 포함된 문구만 비교해서는 진짜 원인을 파악하기 어렵습니다. 도구가 선언된 순서, tool_choice 옵션, 첨부된 이미지의 유무, thinking 또는 effort 레벨 설정, 과거 메시지 배열의 사소한 변경, 심지어는 데이터 직렬화 순서의 미세한 차이까지 폭넓게 점검해 보아야 합니다. 문제를 예방하기 위해서는 가변적인 동적 정보를 항상 고정된 prefix 뒤편으로 배치하고, 고정된 영역의 hash 값이나 버전 ID를 애플리케이션 로그에 명시적으로 남기는 습관을 들이는 것이 좋습니다. 이렇게 하면 서로 다른 요청들이 실제로 동일한 cache entry를 재사용할 수 있는 유효한 후보였는지를 쉽게 추적하고 디버깅할 수 있습니다.
flowchart TD
A["이 workload에 Prompt Caching을 쓸까?"]
A --> B{"긴 고정 prefix가 있는가?"}
B -->|"아니오"| X["효과 제한적"]
B -->|"예"| C{"TTL 안에서 반복 호출되는가?"}
C -->|"아니오"| Y["write 할증만 반복될 수 있음"]
C -->|"예"| D{"tools / system / history가 안정적인가?"}
D -->|"아니오"| Z["cache miss 원인 먼저 해결"]
D -->|"예"| E{"비용에서 입력 토큰 비중이 큰가?"}
E -->|"아니오"| W["출력·tool 비용 최적화가 우선"]
E -->|"예"| F["Prompt Caching 유력"]
F --> G["실제 usage로 hit ratio와 $/task 검증"]
style F fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px
style X fill:#ffebee,stroke:#c62828
style Y fill:#ffebee,stroke:#c62828
style Z fill:#fff3e0,stroke:#ef6c00
비용을 결정하는 것은 단가와 재사용 구조다
Claude Fable 5.1 모델이 제공하는 $0.25/MTok 수준의 cache read 단가는 길고 반복되는 입력이 많은 작업에서 눈에 띄는 비용 절감 효과를 가져다줍니다. 하지만 최초 cache write에는 오히려 더 비싼 할증 요금을 치러야 하며, prefix가 달라지면 그 지점부터 뒤는 cache read 요금을 쓰지 못하고 다시 써야 합니다. 앞쪽의 같은 구간은 유효한 cache entry가 20-block 탐색 범위 안에 남아 있을 때만 계속 재사용됩니다. 앞서 살펴보았듯이 비용의 손익분기점은 5분 cache를 사용할 경우 전체 2번째 요청부터, 1시간 cache를 사용할 경우 전체 3번째 요청부터 달성됩니다. 단, 이 계산은 최초 cache write 이후 이어지는 모든 후속 요청이 한 번도 빠짐없이 hit한다는 이상적인 가정을 전제로 합니다.
결국 실전 환경에서 우리가 취해야 할 전략은 생각보다 단순명료합니다. 변경되지 않는 고정된 tools와 system prompt를 가장 앞부분에 배치하고, 가변적인 동적 정보는 반드시 breakpoint 뒤쪽에 추가하며, 대화 내역은 가능한 한 과거 내용을 수정하지 않는 append-only 방식으로 관리해야 합니다. 세션 진행 도중 불가피하게 설정을 변경해야 할 상황이 온다면, 최상위 구조의 값을 직접 수정하기보다는 mid-conversation system message 방식을 활용하는 것이 유리합니다. 요청을 처리한 뒤에는 응답에 포함된 cache_creation_input_tokens와 cache_read_input_tokens 지표를 기록하여 실제로 cache hit가 의도대로 발생하고 있는지 꼼꼼히 모니터링합니다. 마지막 단계에서는 단순 입력 비용뿐만 아니라 모델의 출력 토큰 비용, 부가적인 도구 사용료, 심지어 실패 시 발생하는 낭비 비용까지 모두 합산한 $/task를 기준으로 전체 caching 정책의 타당성을 객관적으로 평가합니다. 이러한 체계적인 과정을 거치고 나면, Prompt Caching은 더 이상 단순하고 막연한 할인 기능이 아니라, 정밀하게 측정하고 최적화할 수 있는 핵심적인 API 설계 요소로 자리 잡게 될 것입니다.
flowchart LR
A["고정 tools · system
앞쪽 배치"]
A --> B["마지막 고정 block에
cache breakpoint"]
B --> C["동적 값은
breakpoint 뒤"]
C --> D["append-only history"]
D --> E["API 호출"]
E --> F["cache_creation / read
usage 기록"]
F --> G["hit ratio · $/task · latency
성공률 분석"]
G --> H{"Caching이 실제로
이득인가?"}
H -->|"예"| A
H -->|"아니오"| I["TTL / breakpoint / 구조 수정
또는 cache 비활성화"]
I --> A
style H fill:#fff3e0,stroke:#ef6c00
style F fill:#e3f2fd,stroke:#1565c0
style G fill:#e8f5e9,stroke:#2e7d32
참고한 공식 자료
- Anthropic, Claude Fable 5.1 모델 개요
- Anthropic, Claude API 가격
- Anthropic, Prompt Caching
- Anthropic, Tool use with Prompt Caching
- Anthropic, Mid-conversation system messages and tool changes
- Anthropic, Effort
- Anthropic, Claude Fable 제품 페이지
- Anthropic Python SDK, Usage 타입 정의
