7.5 KiB
7.5 KiB
질문
그렇다면, “작업 1의 결과물 파일을 KV-caching처럼 저장해 두고, 작업 2에서 추가 토큰 없이 동일 Claude 모델이 그것을 hit하여 쓰게 설계할 수 있는가?”
답변
결론부터: "완전한 0 토큰"은 불가능하지만, 구조적으로 거의 동일하게 설계 가능합니다
왜 완전한 KV-cache hit가 안 되는가
진짜 KV-cache (Transformer 내부)
─────────────────────────────────
[Task 1 실행] → KV matrix 메모리에 상주
[Task 2 실행] → 동일 KV matrix를 attention에서 그대로 hit → 0 토큰
Anthropic API의 Prompt Caching
─────────────────────────────────
[Task 1 실행] → 서버에 캐시 저장 (5분 TTL)
[Task 2 실행] → cache_read_input_tokens 발생 (기본 가격의 10%)
→ "추가 토큰 없음"이 아니라 "90% 할인된 토큰"
API는 Stateless이기 때문에, Task 1의 출력이 Task 2의 입력이 되는 순간 반드시 토큰이 발생합니다. 단, 캐시를 제대로 설계하면 90% 절감된 비용으로 "hit에 가까운" 동작을 구현할 수 있습니다.
설계 원리: 대화 히스토리 캐시 체인
Task 1의 출력을 Task 2의 **캐시된 프리픽스(prefix)**에 포함시키는 것이 핵심입니다.
캐시 프리픽스 구조:
┌─────────────────────────────────────────────────────┐
│ System [file_content] ← cache_control (캐시 앵커 1) │
│ User: "Task 1 수행하라" │
│ Assistant: [Task 1 출력] │ ← 이 전체가
│ User: "Task 2 수행하라" ← cache_control (캐시 앵커 2)│ ← 캐시됨
└─────────────────────────────────────────────────────┘
↑
Task 2 실행 시 이 prefix 전체를 hit
구현 코드
import anthropic
client = anthropic.Anthropic()
with open("input_document.md", "r", encoding="utf-8") as f:
file_content = f.read()
# 변하지 않는 시스템 + 파일 (캐시 앵커 1)
CACHED_SYSTEM = [
{
"type": "text",
"text": "당신은 한국 민사소송 전문 AI입니다. 최종 결과물만 출력하세요.",
},
{
"type": "text",
"text": file_content,
"cache_control": {"type": "ephemeral"} # ← 앵커 1
}
]
# ──────────────────────────────────────────────
# Task 1 실행
# ──────────────────────────────────────────────
print("=== Task 1 실행 ===")
response1 = client.messages.create(
model="claude-opus-4-6",
max_tokens=4000,
system=CACHED_SYSTEM,
messages=[
{
"role": "user",
"content": "이 사건의 법적 쟁점을 추출하세요."
}
]
)
task1_output = response1.content[0].text
u1 = response1.usage
print(f" 캐시 생성: {u1.cache_creation_input_tokens} | 캐시 읽기: {u1.cache_read_input_tokens} | 일반 입력: {u1.input_tokens}")
# ──────────────────────────────────────────────
# Task 2 실행 - Task 1 출력을 캐시 프리픽스에 포함
# ──────────────────────────────────────────────
print("\n=== Task 2 실행 ===")
response2 = client.messages.create(
model="claude-opus-4-6",
max_tokens=6000,
system=CACHED_SYSTEM, # ← 앵커 1 재사용 (hit)
messages=[
# Task 1의 전체 대화를 히스토리로 포함
{
"role": "user",
"content": "이 사건의 법적 쟁점을 추출하세요."
},
{
"role": "assistant",
"content": task1_output # ← Task 1 출력이 히스토리에 포함
},
# Task 2 지시 + 캐시 앵커 2
{
"role": "user",
"content": [
{
"type": "text",
"text": "위 법적 쟁점을 바탕으로 소장을 작성하세요.",
"cache_control": {"type": "ephemeral"} # ← 앵커 2
}
]
}
]
)
task2_output = response2.content[0].text
u2 = response2.usage
print(f" 캐시 생성: {u2.cache_creation_input_tokens} | 캐시 읽기: {u2.cache_read_input_tokens} | 일반 입력: {u2.input_tokens}")
# ──────────────────────────────────────────────
# Task 3가 있다면 - Task 1+2 모두 캐시로 hit
# ──────────────────────────────────────────────
print("\n=== Task 3 실행 ===")
response3 = client.messages.create(
model="claude-opus-4-6",
max_tokens=2000,
system=CACHED_SYSTEM,
messages=[
{"role": "user", "content": "이 사건의 법적 쟁점을 추출하세요."},
{"role": "assistant","content": task1_output},
{
"role": "user",
"content": [{"type": "text", "text": "위 법적 쟁점을 바탕으로 소장을 작성하세요.",
"cache_control": {"type": "ephemeral"}}]
},
{"role": "assistant","content": task2_output},
# Task 3 신규 지시만 새 토큰
{
"role": "user",
"content": "소장의 청구취지 부분만 요약하세요."
}
]
)
u3 = response3.usage
print(f" 캐시 생성: {u3.cache_creation_input_tokens} | 캐시 읽기: {u3.cache_read_input_tokens} | 일반 입력: {u3.input_tokens}")
각 Task별 토큰 흐름
Task 1 호출:
[file_content 10,000 토큰] → cache_creation (1회 비용 발생, 이후 캐시)
[user 지시 20 토큰] → 일반 input
Task 2 호출 (5분 이내):
[file_content 10,000 토큰] → cache_read ✓ (10% 비용)
[Task1 user 20 토큰] → cache_read ✓ (prefix 일치)
[Task1 output 1,000 토큰] → cache_read ✓ (prefix에 포함)
[Task2 지시 30 토큰] → cache_creation (앵커 2 생성)
Task 3 호출 (5분 이내):
[위 전체 ~11,050 토큰] → cache_read ✓
[Task2 output 2,000 토큰] → cache_read ✓ (prefix에 포함)
[Task3 지시 20 토큰] → 일반 input (신규 토큰)
Law-aid Agent 파이프라인 적용 시 핵심 제약
| 조건 | 이유 |
|---|---|
| 5분 내 다음 Task 호출 | TTL 만료 시 캐시 miss → 전체 재과금 |
| prefix가 완전히 동일해야 함 | 앞 메시지 하나라도 다르면 cache miss |
| 동일 모델 사용 | claude-opus-4-6 ↔ sonnet 간 캐시 공유 불가 |
| 캐시 앵커는 최소 1,024 토큰 이상 | 미만이면 캐시 대상에서 제외됨 |