### 질문 > 그렇다면, “작업 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 ``` ------ ### 구현 코드 ```python 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 토큰 이상** | 미만이면 캐시 대상에서 제외됨 |