프롬프트 캐시는 할인 기능이 아니라 요청 형태를 바꾸는 설계 문제다
프롬프트 캐싱을 켰는데 비용이 거의 줄지 않는 팀은 대개 공급자 옵션을 놓친 것이 아니라 요청이 매번 달라 캐시 가능한 prefix가 짧다. 시스템 지침, 도구 스키마, 제품 정책처럼 반복되는 큰 블록 앞에 현재 시각이나 사용자별 값이 들어가면 그 뒤의 동일한 토큰까지 재사용되지 못한다. 캐시는 텍스트 조각 저장이 아니라 앞에서부터 일치하는 계산 상태의 재사용으로 이해해야 한다.
또한 캐시 히트는 공짜가 아니다. 읽기 가격, 쓰기 가격, TTL, 최소 캐시 길이, 라우팅되는 모델 버전은 공급자마다 다르며 변경될 수 있다. 특정 수치는 공식 가격표로 확인해야 한다. 여기서는 어떤 공급자에도 적용되는 요청 정규화, 관측 필드, 손익 계산, 개인정보 경계를 다룬다.
왜 prefix가 같아야 하는가
Transformer는 앞 토큰의 key/value 상태를 이용해 다음 토큰을 계산한다. 앞부분이 동일하면 해당 구간의 계산 결과를 재사용할 수 있지만 중간 토큰 하나가 바뀌면 그 이후 상태도 달라진다. 따라서 “같은 문서가 요청 어딘가에 포함됐다”는 사실만으로 히트하지 않는다. 반복 블록이 가능한 한 앞쪽에 같은 순서와 같은 바이트 표현으로 있어야 한다.
도구 배열 순서가 매번 바뀌거나 JSON의 공백·필드 순서가 달라지는 것도 문제다. 의미는 같아도 토큰열이 달라질 수 있다. 빌드 단계에서 tool schema를 canonical JSON으로 직렬화하고 버전 해시를 만든다. 요청 로그에는 원문 대신 prefix_template_version, toolset_hash, policy_version을 남겨 어떤 변경이 히트율을 깨뜨렸는지 추적한다.
정적 블록을 앞에, 동적 블록을 뒤에 둔다
좋은 요청 순서는 대개 장기간 고정되는 시스템 정책, 고정 도구 정의, 공통 지식 블록, 세션 대화, 현재 사용자 입력 순이다. 현재 시각, request ID, 사용자 이름을 시스템 프롬프트 첫 줄에 넣으면 뒤의 수천 토큰이 같아도 prefix가 일찍 갈라진다. 현재 시각이 필요하면 동적 컨텍스트 블록을 정적 prefix 뒤로 이동한다.
그렇다고 모든 사용자에게 같은 정책을 강제로 공유하면 안 된다. 테넌트별 데이터 경계나 권한 지침은 정확성이 우선이다. 공통 안전 정책까지 공유하고 테넌트 정책부터 분기하거나, 권한 조합별 template version을 만든다. 캐시 히트율 때문에 권한 정보를 생략하는 것은 비용 최적화가 아니라 보안 결함이다.
캐시 손익은 히트율 하나로 계산되지 않는다
월간 절감액을 보려면 cacheable input tokens, write tokens, read tokens, miss rate, TTL 내 재사용 횟수를 알아야 한다. 긴 prefix가 하루 한 번만 쓰이면 쓰기 비용과 복잡성만 늘 수 있다. 반대로 동일한 20K 정책·도구 블록을 분당 수백 번 읽는다면 효과가 크다. 공급자의 입력, cache write, cache read 단가를 변수로 두고 모델별로 계산한다.
관측 식은 단순하다. 캐시 미사용 비용과 miss_write_cost + hit_read_cost + uncached_tail_cost를 같은 트래픽 샘플에서 비교한다. 여기에 TTFT 개선과 캐시 준비 때문에 생기는 지연도 포함한다. 청구서 금액만 보면 요청량 변화가 섞이므로 1천 요청당 비용과 성공 task당 비용을 함께 본다.
시나리오 1: 도구가 80개인 B2B 에이전트
CRM, 결제, 티켓 도구 80개의 스키마가 15K 토큰이고 모든 요청에 붙는다고 하자. 첫 번째 최적화는 캐싱보다 도구 선택이다. 로그인한 사용자의 역할과 현재 화면에서 가능한 도구 8개만 라우팅하면 입력 자체가 줄고 모델의 잘못된 선택 공간도 작아진다. 그 8개 묶음이 역할별로 반복될 때 캐시가 추가 이득을 준다.
도구 설명을 런타임마다 DB에서 읽어 정렬 없이 배열로 만들면 hash가 흔들린다. tool_registry_version과 stable sort를 적용하고 스키마 변경을 배포 이벤트로 취급한다. 캐시 히트율이 갑자기 하락하면 모델 장애보다 먼저 toolset hash cardinality가 늘었는지 본다. 사용자별로 불필요하게 다른 description을 생성하는 것도 피한다.
시나리오 2: 100페이지 계약서를 반복 질의하는 제품
계약서 전체를 매 질문마다 앞에 넣으면 같은 세션에서 prefix 재사용 가능성이 있다. 하지만 질문 전에 대화가 계속 추가되면 계약서 이후의 prefix는 턴마다 길어지고 구조가 달라진다. 계약서와 고정 분석 지침을 먼저 배치하고, 그 뒤에 요약된 대화 상태와 현재 질문을 붙이면 재사용 구간이 안정된다.
문서가 수정되면 document_content_hash가 달라져야 한다. 파일명만 키로 쓰면 오래된 계약서 상태를 재사용할 위험이 있다. 페이지별 OCR 결과가 비결정적으로 달라지는 파이프라인도 prefix를 흔든다. 추출 결과를 한 번 정규화해 버전화하고, 문서 권한이 철회됐을 때 캐시 TTL만 믿지 말고 요청 생성 단계에서 접근을 차단한다.
캐시 때문에 정확도가 바뀌었는지 검증하는 법
이론상 동일 토큰 prefix의 재사용은 결과 의미를 바꾸지 않아야 하지만, 요청 재배열 과정에서 동적 정보 위치를 잘못 옮기거나 잘라내면 품질이 바뀐다. 캐시 적용 전후의 실제 serialized request를 토큰 단위로 diff하고, 정적 블록 이후 동적 tail이 보존됐는지 확인한다. 단순 문자열 diff는 Unicode 정규화나 tokenizer 차이를 놓칠 수 있다.
A/B에서는 동일 모델과 decoding 설정을 유지하고 cache layout만 바꾼다. task success, tool argument correctness, citation accuracy와 함께 TTFT를 측정한다. hit 요청만 골라 품질을 보면 쉬운 반복 요청 편향이 생기므로 원래 트래픽 기준 intent별 결과를 비교한다. 캐시 miss도 정상 경로로 충분히 테스트해야 새 template 배포 직후 장애를 피할 수 있다.
운영 중 캐시 오염과 개인정보 경계
애플리케이션 수준에서 직접 prefix cache를 운영한다면 키에 모델 ID, tokenizer, adapter, system policy version, toolset hash, 권한 경계를 포함한다. 모델만 같다고 KV 상태를 공유할 수 있는 것이 아니다. LoRA adapter나 rope 설정이 다르면 상태 의미가 달라진다. 공급자 관리형 캐시는 격리 정책과 보존 기간을 공식 문서에서 확인한다.
로그에는 프롬프트 원문 대신 cache_eligible_tokens, cache_read_tokens, cache_write_tokens, cache_hit, prefix_version, tenant_scope를 남긴다. 테넌트 간 공유를 허용하지 않았다면 scope cardinality와 교차 접근 테스트를 둔다. 삭제 요청을 받았을 때 원본 저장소뿐 아니라 애플리케이션 캐시와 파생 문서 버전이 어떻게 만료되는지도 데이터 보존 정책에 포함한다.
대시보드에서 봐야 할 순서
먼저 전체 입력 중 반복 가능한 prefix 비중을 본다. 다음으로 prefix_template_version별 요청 수를 세어 충분한 재사용 빈도가 있는지 확인한다. 그 뒤 cache read/write token 비율, 모델별 비용, hit와 miss의 TTFT p50/p95를 비교한다. 히트율이 높아도 cacheable prefix가 200토큰이면 절감액은 작다.
히트율 하락 시에는 template 배포, toolset hash 증가, JSON 직렬화 변경, 모델 버전 변경 순서로 조사한다. 비용 절감을 위해 긴 고정 문구를 억지로 추가하지 않는다. 최선의 입력 토큰은 싸게 처리한 토큰이 아니라 보내지 않아도 되는 토큰이다. 도구 선택과 문서 압축으로 입력을 줄인 뒤, 남은 크고 반복적인 prefix에 캐시를 적용하는 순서가 안전하다.
배포 과정에서 prefix 안정성을 지키는 방법
프롬프트 파일을 수정하지 않았는데도 프레임워크 업데이트로 role 직렬화, 도구 JSON, 특수 토큰이 달라질 수 있다. CI에서 대표 요청의 tokenizer 결과와 prefix hash를 golden artifact로 저장하고 예상치 않은 변경을 검출한다. hash가 바뀌는 변경이 항상 나쁜 것은 아니지만 캐시가 한꺼번에 차가워져 TTFT와 비용이 튈 수 있으므로 rollout 계획이 필요하다.
새 prefix version은 처음부터 100%로 보내지 않는다. 이전 버전과 새 버전을 일정 기간 병행해 cache warm-up을 만들고, version별 read token과 miss latency를 본다. 다만 품질이나 보안상 즉시 폐기해야 하는 정책은 캐시 효율보다 빠른 전환이 우선이다. 긴 TTL 때문에 과거 정책이 사용되지 않도록 요청이 명시적으로 현재 policy_version을 선택하게 한다. 캐시 존재 여부가 버전 선택을 결정해서는 안 된다.
개발 환경의 낮은 트래픽에서는 캐시 이득이 작아 production 동작을 재현하기 어렵다. 실제 원문을 복사하지 않고 template version과 길이 분포를 보존한 합성 부하로 재사용 패턴을 만든다. 배포 전 예상 cacheable tokens, version cardinality, 10분 내 반복 횟수를 산출하고, 배포 후 실제 값과 비교한다. 이 차이가 크면 가격 계산을 다시 하기 전에 요청 생성기의 비결정적 필드부터 찾는 것이 빠르다.
팀 회의에서 숫자로 답해야 할 질문
이 주제를 운영 회의에 올릴 때는 ‘좋아졌다’는 표현을 금지하고 cache read tokens, prefix version cardinality, toolset hash의 이전 값, 변경 후 값, 표본 수를 한 화면에 놓는다. 특히 동적 필드가 정적 prefix 앞에 배치된 요청를 재현한 부하 또는 회귀 샘플을 고정해야 다음 배포와 비교할 수 있다. 정상 요청만 모은 평균은 사고 직전의 위험을 설명하지 못한다. 실패·강등·재시도가 포함된 분모와 제외 규칙도 함께 기록한다.
담당자는 지표 변화에 대응하는 단 하나의 조치를 미리 연결한다. cache read tokens가 경계를 넘으면 어떤 배포를 되돌릴지, prefix version cardinality가 늘면 어떤 큐나 샘플을 열어볼지, toolset hash의 이상을 누가 확인할지를 runbook에 적는다. 알람이 다섯 개 동시에 울리는데 모두 ‘조사 필요’로 끝나면 제어면이 없는 것과 같다. 변경자는 release ID와 예상 효과를 남기고, 관찰 창이 끝난 뒤 실제 효과와 부작용을 기록한다.
마지막 검토에서는 비용과 품질을 같은 요청 ID로 연결한다. 비용이 줄었지만 실패 재시도가 늘었거나, 정확도가 올랐지만 p95가 제품 예산을 넘으면 성공이 아니다. 이 글에서 제시한 메트릭은 보고용 숫자가 아니라 다음 행동을 고르는 분기 조건이다. 임계치, 관찰 시간, rollback 조건까지 적혀 있을 때 비로소 구현 지식이 운영 정책이 된다.