안녕하세요! 오늘은 Anthropic에서 최근 공개한 기술 블로그 글 하나를 소개해 드리려고 합니다. 바로 **“Lessons from building Claude Code: Prompt caching is everything”**이라는 주제의 글인데요, Claude Code 팀이 실제 제품을 만들면서 터득한 프롬프트 캐싱 최적화 노하우를 아낌없이 공유한 내용입니다.
tl;dr: 에이전트 제품에서 프롬프트 캐싱은 선택이 아니라 생존 전략입니다.
왜 프롬프트 캐싱가 중요할까?
“CACHE RULES EVERYTHING AROUND ME” — 엔지니어링 세계의 오래된 격언인데, 이게 AI 에이전트에서도 똑같이 적용된다고 하네요.
Claude Code 같은 장시간 실행되는 에이전트 제품은 프롬프트 캐싱 덕분에 가능한 일입니다. 이전 요청의 계산 결과를 재사용해서 지연 시간과 비용을 크게 줄일 수 있거든요.
Claude Code팀은 프롬프트 캐싱 히트율에 실시간 알림을 설정해두고, 히트율이 떨어지면 SEV(심각 인시던트)로 선언한다고 합니다. 그만큼 중요하다는 거죠.
교훈 1: 프롬프트 구조를 캐싱에 맞게 설계하라
프롬프트 캐싱은 접두사(prefix) 매칭으로 작동합니다. 요청의 시작 부분부터 cache_control 중단점까지 모든 것을 캐시하는 방식이에요.
그래서 순서가 핵심입니다:
- 정적 시스템 프롬프트 & 도구 (전역 캐시)
- CLAUDE.md (프로젝트 단위 캐시)
- 세션 컨텍스트 (세션 단위 캐시)
- 대화 메시지
정적 콘텐츠를 먼저, 동적 콘텐츠를 나중에 배치하는 게 핵심이에요.
하지만 이게 생각보다 깨지기 쉽다고 합니다. 실제로 캐시를 망가뜨린 사례들:
- 정적 시스템 프롬프트에 상세한 타임스탬프를 넣은 경우
- 도구 정의 순서를 비결정적으로 섞은 경우
- 도구의 매개변수를 업데이트한 경우 (예: Agent 도구가 호출할 수 있는 에이전트 목록 변경)
교훈 2: 업데이트는 메시지로 전달하라
프롬프트에 넣은 정보가 만료되면 어떻게 할까요? 예를 들어 시간이 지났거나 사용자가 파일을 변경한 경우.
프롬프트를 직접 수정하면 캐시 미스가 발생합니다. 대신, 다음 턴의 메시지를 통해 업데이트 정보를 전달하세요.
Claude Code에서는 다음 사용자 메시지나 도구 결과에 <system-reminder> 태그를 추가해서 업데이트된 정보를 모델에 전달한다고 하네요.
교훈 3: 세션 중간에 모델을 바꾸지 마라
이것도 직관과 반대되는 내용인데요:
“Opus로 100K 토큰까지 대화하다가 쉬운 질문이 들어와서 Haiku로 바꾸는 게 더 비싸다”
왜냐? 프롬프트 캐시는 모델별로 고유하거든요. 모델을 바꾸면 Haiku용 캐시를 처음부터 다시 만들어야 합니다.
해결책: 서브에이전트를 활용하세요. 예를 들어 Opus가 다른 모델에게 전달할 “인계 메시지”를 준비하게 하고, 서브에이전트가 그 작업을 처리하게 하는 방식입니다.
교훈 4: 세션 중간에 도구를 추가/제거하지 마라
이게 가장 흔한 캐시 파괴 범인이라고 합니다.
“지금 필요한 도구만 주는 게 낫지 않나?”라는 건 직관적이지만, 도구는 캐시된 접두사의 일부라서 추가/제거하면 전체 대화의 캐시가 무효화됩니다.
Plan Mode의 영리한 설계
Plan Mode는 이 제약을 활용한 훌륭한 예시입니다.
- 직관적 방식: Plan Mode 진입 시 → 읽기 전용 도구만 남기고 다 제거 (❌ 캐시 깨짐)
- 실제 구현: 모든 도구를 항상 유지 +
EnterPlanMode/ExitPlanMode를 도구 자체로 모델링 (✅ 캐시 보존)
사용자가 Plan Mode를 켜면, 에이전트는 “코드베이스를 탐색하되 파일을 편집하지 말고, 완료되면 ExitPlanMode를 호출하라”는 시스템 메시지를 받습니다. 도구 정의는 변하지 않죠.
보너스: 모델이 스스로 어려운 문제를 감지하고 자율적으로 Plan Mode에 진입할 수도 있습니다!
지연 로딩으로 제거 대신 연기
Claude Code에는 수십 개의 MCP 도구가 로드될 수 있는데, 전부 매 요청에 포함하면 비싸고, 중간에 제거하면 캐시가 깨집니다.
해결책: defer_loading — 가벼운 스텁(도구 이름만, defer_loading: true)을 항상 같은 순서로 보내고, 모델이 필요할 때만 전체 스키마를 로드하는 방식입니다.
교훈 5: 캐시를 깨지 않는 Compaction
Compaction은 컨텍스트 윈도우가 꽉 찼을 때 대화를 요약하고 새 세션으로 이어가는 과정입니다.
여기서 비용 함정이 있는데요:
- ❌ 잘못된 방식: “이걸 요약해줘”라는 별도 시스템 프롬프트로 새 API 호출 → 접두사가 완전히 달라져서 캐시가 하나도 안 먹힘 → 전체 대화를 캐시 없는 가격으로 지불
- ✅ 올바른 방식 (cache-safe forking): 부모 대화와 완전히 동일한 시스템 프롬프트, 컨텍스트, 도구 정의를 사용. 부모의 대화 메시지를 앞에 두고, 마지막에 compaction 프롬프트를 새 사용자 메시지로 추가.
API 관점에서는 부모의 마지막 요청과 거의 동일하게 보이기 때문에, 캐시된 접두사가 재사용됩니다. 새 토큰은 compaction 프롬프트 자체뿐이죠.
핵심 요약
Claude Code팀이 정리한 프롬프트 캐싱 최적화 5원칙:
- 프롬프트 캐싱은 접두사 매칭이다. 접두사의 어디든 변경이 있으면 그 이후의 모든 것이 무효화된다. 시스템 전체를 이 제약 중심으로 설계하라.
- 시스템 프롬프트 변경 대신 메시지를 사용하라. Plan Mode 진입, 날짜 변경 등은 프롬프트가 아니라 대화 메시지에 삽입하라.
- 대화 중간에 도구나 모델을 바꾸지 마라. 상태 전환(Plan Mode 등)은 도구를 교체하는 대신 도구 자체로 모델링하라. 도구 제거 대신 지연 로딩을 사용하라.
- 캐시 히트율을 업타임처럼 모니터링하라. 몇 % 포인트의 캐시 미스가 비용과 지연에 극적인 영향을 미친다.
- 포크 작업은 부모의 접두사를 공유해야 한다. Compaction, 요약, 스킬 실행 등 부가 연산은 동일한 캐시 안전 매개변수를 사용하라.
개인적인 생각
이 글을 읽으면서 가장 인상 깊었던 건 **“캐시를 염두에 두고 기능을 설계한다”**는 점이에요. Plan Mode를 예로 들면, 기능을 만들 때 “이게 캐시에 어떤 영향을 주나?”를 먼저 고민하고, 그 제약 안에서 창의적으로 해결책을 찾는 방식이 참 멋있다고 생각했습니다.
그리고 “쉬운 질문이라서 작은 모델로 바꾸는 게 오히려 더 비싸다”는 것도 흥미로운 통찰이었어요. 직관과 반대되는 결과니까요.
AI 에이전트 제품을 만들고 계신 분들이라면, 이 글의 교훈들은 실무에 바로 적용할 수 있는 귀중한 인사이트일 겁니다.
원문: Lessons from building Claude Code: Prompt caching is everything 작성자: Thariq Shihipar (Claude Code팀, Technical Staff)