비용 최적화

Claude 프롬프트 캐싱 비용: 쓰기·읽기 요금과 적중률 계산

Claude 캐시 쓰기·읽기, TTL, 적중률과 라우팅 영향을 이해하세요. 손익분기점 계산과 재현 가능한 Python 테스트로 비용을 검증합니다.

고정 접두사를 캐시하고 재사용량 측정하기

Claude 프롬프트 캐싱은 이미 처리한 입력 접두사를 재사용합니다. 답변 자체를 저장하는 기능이 아니며 새로 생성되는 출력 토큰에는 계속 요금이 붙습니다. 고정 지침, 도구 정의, 참고 자료를 변하는 사용자 입력 앞에 두고 유효기간 안에 얼마나 다시 쓰는지로 캐싱 여부를 판단하세요.

이 글은 Sonnet 4.6과 20,000토큰 접두사로 계산합니다. 실제 적중률을 예측하는 수치는 아닙니다. 공급자 선택은 요금 비교, 클라이언트 연결은 Claude Code 설정을 참고하세요.

기술 동작과 외부 요금은 2026년 10월 17일 공식 캐싱 문서와 대조했습니다. 아래 단가와 최소 길이를 모든 Claude 모델에 적용하지 마세요.

쓰기·읽기·일반 입력을 구분하세요

Anthropic 형식의 input_tokens는 캐시되지 않은 입력, cache_creation_input_tokens는 새로 쓴 입력, cache_read_input_tokens는 재사용한 입력입니다. 별개 항목으로 계산하세요. cache_creation.ephemeral_5m_input_tokens와 cache_creation.ephemeral_1h_input_tokens가 있으면 전체 쓰기의 TTL별 내역이므로 합계와 세부 항목을 중복해서 더하지 마세요.

Sonnet 4.6 공식 글로벌 API: USD / 100만 토큰
항목단가예산에 반영할 점
일반 입력$3.00캐시 읽기나 쓰기로 처리되지 않은 입력
5분 쓰기$3.75최초 쓰기는 일반 입력보다 비쌈
1시간 쓰기$6.00더 긴 보관 시간에는 더 높은 쓰기 비용 적용
캐시 읽기$0.30적중한 부분도 읽기 단가로 청구
출력$15.00입력 캐싱으로 출력 요금이 없어지지는 않음

출처는 공식 표준 글로벌 요금입니다. API Route는 현재 모델·그룹 요금에서 두 쓰기 기간도 함께 확인하세요. 업스트림 가격 배수 대신 실제 공급자 단가로 계산해야 합니다.

요청 간격에 맞춰 5분 또는 1시간 TTL 선택하기

ephemeral 캐시 기본 TTL은 5분이며 적중하면 유효기간이 갱신됩니다. 지원되는 경우 cache_control: {"type":"ephemeral","ttl":"1h"}로 1시간을 지정합니다. Sonnet 4.6의 문서상 최소 캐시 가능 접두사는 1,024토큰입니다. 길이와 내용이 맞더라도 모든 게이트웨이에서 적중하는 것은 아닙니다.

실제 요청 간격을 기록하세요. 20분씩 쉬는 작업은 5분 캐시를 계속 다시 쓸 수 있지만 활발한 대화는 짧은 TTL로 충분할 수 있습니다. 재사용으로 추가 쓰기 비용을 회수할 수 있을 때 긴 TTL을 선택하세요. 유효기간 내에도 접두사가 바뀌면 콜드 요청이 될 수 있습니다.

가능하다면 시각, 요청 ID, 사용자별 변경 내용은 고정 접두사 뒤에 두고 도구 정의와 순서를 유지하세요. 운영 환경에서 임계값을 넘기려고 불필요한 텍스트를 채우지 마세요. 아래 생성 텍스트는 동작 확인용입니다.

손익분기점과 토큰 기준 읽기 비율 계산하기

P는 접두사 토큰 수, N은 사용 횟수, W는 쓰기 단가, R은 읽기 단가, I는 일반 입력 단가로 놓고 단위는 모두 100만 토큰당 달러로 맞춥니다. 한 번 쓰고 N−1번 적중할 때 비용은 P × (W + (N−1)R) / 1,000,000, 캐시가 없으면 N × P × I / 1,000,000입니다. 실제 총비용에는 매번 고유한 입력과 출력도 더합니다.

20,000-token prefix, ten uses, Sonnet 4.6 direct rates:
Uncached: 10 * 0.02 * 3 = $0.6000
5m: 0.02 * (3.75 + 9 * 0.30) = $0.1290
1h: 0.02 * (6.00 + 9 * 0.30) = $0.1740

5m, two uses: $0.0810 vs $0.1200 uncached
1h, two uses: $0.1260 vs $0.1200 uncached
1h, three uses: $0.1320 vs $0.1800 uncached

접두사 길이가 달라지면 요청 횟수보다 토큰으로 측정하세요. 읽기 비율 = read_tokens / (uncached_input_tokens + write_tokens + read_tokens)은 전체 입력 중 캐시에서 처리한 비율입니다. 캐시 가능한 접두사만을 분모로 한 적중률도 쓸 수 있지만 두 지표의 분모를 명확히 구분해야 합니다.

캐시되지 않은 추가 입력이 없고 접두사 길이가 같으며 미스가 매번 쓰기가 되는 조건에서는 평균 단가가 hR + (1−h)W입니다. 이 글의 공식 단가에서 5분 캐시는 h가 21.74%를, 1시간 캐시는 52.63%를 넘어야 일반 입력보다 저렴합니다. 실측 적중률이 아닌 수학적 조건이며 가격이나 청구 방식이 바뀌면 다시 계산해야 합니다.

두 번의 요청으로 캐시 테스트 재현하기

Python 3 표준 라이브러리만 사용하는 아래 코드를 cache_probe.py로 저장하세요. 고정 system 접두사에 명시적 캐시 경계를 넣고 동일한 Messages 요청을 순서대로 두 번 보냅니다. 두 요청 모두 비용이 생길 수 있으므로 첫 테스트에는 기밀 데이터를 쓰지 마세요.

ANTHROPIC_BASE_URL과 ANTHROPIC_MODEL을 설정하세요. API Route에는 https://global.api-route.com, 활성 Claude 모델, 비공개 ANTHROPIC_AUTH_TOKEN을 사용합니다. 공식 직접 연결에는 https://api.anthropic.com과 ANTHROPIC_API_KEY를 사용하며 인증 변수는 정확히 하나만 설정합니다. 키 입력 숨김은 셸 예제를 참고하세요.

import json
import os
import urllib.request
import urllib.error

base = os.environ.get("ANTHROPIC_BASE_URL", "https://global.api-route.com").rstrip("/")
model = os.environ.get("ANTHROPIC_MODEL", "claude-sonnet-4-6")
token = os.environ.get("ANTHROPIC_AUTH_TOKEN")
api_key = os.environ.get("ANTHROPIC_API_KEY")
if bool(token) == bool(api_key):
    raise SystemExit("Set exactly one of ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY")

# Fixed, non-sensitive prefix, comfortably above Sonnet 4.6's minimum.
# Use the same bytes on both requests; no timestamps or random IDs.
prefix = "Follow the project rules below.\n" + "\n".join(
    f"Rule {i}: Validate inputs, document assumptions, and keep errors actionable."
    for i in range(400)
)
payload = {
    "model": model,
    "max_tokens": 32,
    "system": [{"type": "text", "text": prefix,
                "cache_control": {"type": "ephemeral"}}],
    "messages": [{"role": "user", "content": "Reply with OK."}],
}
headers = {"Content-Type": "application/json", "anthropic-version": "2023-06-01"}
headers.update({"Authorization": f"Bearer {token}"} if token else {"x-api-key": api_key})
for attempt in (1, 2):
    req = urllib.request.Request(base + "/v1/messages",
        data=json.dumps(payload).encode(), headers=headers, method="POST")
    try:
        with urllib.request.urlopen(req, timeout=120) as res:
            result = json.load(res)
    except urllib.error.HTTPError as exc:
        # Error bodies can contain provider details; inspect them privately.
        raise SystemExit(f"HTTP {exc.code}; inspect provider logs privately") from None
    usage = result.get("usage", {})
    print(json.dumps({"attempt": attempt, "model": result.get("model"), "usage": usage}))
    if "cache_read_input_tokens" not in usage or "cache_creation_input_tokens" not in usage:
        print("Cache fields absent: result is inconclusive; check provider billing logs.")
python cache_probe.py

실제 usage 두 개를 확인하세요. 첫 응답의 캐시 쓰기와 두 번째 응답의 캐시 읽기가 양수라면 그 라우트에서 그 시점에 재사용이 발생한 근거입니다. 앞선 실행으로 캐시가 이미 준비됐다면 첫 요청부터 읽기가 나올 수도 있습니다. 고정 토큰 수나 적중 결과를 미리 단정하지 마세요.

응답의 사용량을 확정 청구액과 대조하기

  1. 모델, 라우트·그룹, 시각, 토큰 항목, 제공되는 요청 ID를 기록하고 인증 정보는 남기지 않습니다.
  2. 사용 로그에서 요청을 찾아 분류별 단가를 곱한 값과 확정 청구액을 비교하세요. 명시된 반올림 방식도 고려합니다.
  3. 나중에 TTL보다 긴 유휴 간격을 둔 뒤 다시 실행해 새 쓰기가 발생하는지 확인하세요. 스크립트 자체는 기다리거나 무한 호출하지 않습니다.
  4. system 접두사를 바꾸어 콜드 사례도 확인하되 모델과 라우트는 고정하세요.
  5. 캐시 없는 기준이 필요하면 cache_control을 제거하세요. 다만 게이트웨이가 자체 캐싱을 적용할 수 있으므로 실제 usage가 조건을 충족할 때만 기준으로 채택합니다.

캐시 필드가 없으면 결론을 내릴 수 없습니다. 읽기 0은 짧은 접두사, 만료, 내용 변경, 라우트 전환, 캐시 제어 미지원, 프로토콜 변환 시 필드 누락 때문일 수 있습니다. 필드가 없다고 무료 입력이나 캐시 고장으로 단정하지 말고 특정 요청을 공급자와 대조하세요.

라우트 전환이 재사용을 줄일 수 있는 이유

폴백은 다음 요청을 다른 업스트림 캐시로 보낼 수 있습니다. 공급자·계정·엔드포인트가 다르면 캐시도 공유되지 않을 수 있습니다. 가용성을 위한 전환이 콜드 접두사 비용을 늘릴 수 있으므로 재시도와 폴백을 포함한 총비용을 평가하세요.

OpenRouter는 캐시를 고려한 고정 라우팅과 폴백·공급자 순서 관련 예외를 설명합니다. 다른 서비스도 똑같이 동작한다고 가정하지 마세요. API Route에서는 같은 키·모델·선택 라우트로 반복 호출한 뒤 실제 캐시 청구를 확인하세요. 이 글은 캐시 라우트 유지나 적중률을 보장하지 않습니다.

운영 판단에는 일반적인 세션 길이와 유휴 간격을 포함한 샘플을 사용하세요. 일반 입력·읽기·쓰기·출력 비용과 비용이 청구된 실패까지 합쳐 조건이 명확한 기준과 비교합니다. 읽기 비율이 높아도 출력 중심 작업은 비쌀 수 있습니다.

API Route 계정으로 대표 프롬프트를 시험하고 현재 요금을 확인하세요. 빠른 시작에 따라 첫 요청을 검증하고 로그가 예상 비용과 맞는 것을 확인한 뒤 지속 사용을 위한 충전을 진행하세요.