프롬프트 캐싱은 prefix 매치입니다. 요청 앞부분이 직전 호출과 바이트 단위로 같아야 적중합니다. GEODE의 캐싱 설계는 이 한 문장에서 다 나옵니다. 변하지 않는 것을 앞에, 변하는 것을 뒤에 두고, 턴마다 바뀌는 조각이 prefix를 다시 키잉하지 못하게 막습니다.
static/dynamic 경계
시스템 프롬프트는 core/agent/system_prompt.py의PROMPT_CACHE_BOUNDARY마커(<dynamic_context> 여는 태그)로 두 쪽이 납니다. 마커 앞은 턴 사이 불변(베이스 스캐폴드, 스타일 가이드, 기본 ON identity), 마커 뒤는 턴마다 변합니다(model card, 날짜, 메모리 레이어, 사용자 컨텍스트).
Anthropic 요청 생성기(core/llm/adapters/_anthropic_common.py)가 정적 블록에 기본 cache_control: {"type": "ephemeral", "ttl": "1h"}을 붙입니다. prompt_cache_extended_ttl=false는 5분 TTL을 선택합니다. 동적 블록에는 별도 마커를 붙이지 않지만, 뒤쪽 메시지 마커가 만드는 누적 프리픽스에는 포함됩니다. 정적 영역이 비면 빈 텍스트 오류를 피해 동적 블록에 5분 마커 하나를 사용합니다.
OpenAI·Z.AI·OpenRouter의 경계
OpenAI Platform의 등록된 GPT-5.6·GPT-6 모델은 같은 경계 앞의 정적 문자열을 developer 메시지의 input_text 블록으로 보내고prompt_cache_breakpoint를 붙입니다. 동적 문자열은 그 뒤에 두며, 대화 이력의 자동 캐싱도 유지합니다. Codex 구독 경로와 이전 모델은 기존 instructions 및 캐시 키 계약을 유지합니다. 키가 같다는 사실만으로 적중을 보장하지는 않습니다.
Z.AI는 반복 접두부를 자동으로 캐싱하며 별도 캐시 마커를 보내지 않습니다. OpenRouter의 명시적 Claude·지원 OpenAI 경로에는 해당 마커를 보존합니다. 논리 세션 ID의 해시로 제공자 선택의 연속성을 요청하고, 원래 세션 ID는 보내지 않습니다. 사용자가 설정한 제공자 우선순위는 유지하며, 자동 모델 선택에 특정 제공자 마커를 추측해 넣지 않습니다.
SDK 연결을 닫거나 다시 만들어도 서버의 캐시를 삭제하지 않습니다. 모델·계정·도구 순서·출력 스키마·추론 설정·정리된 이력이 달라지면 공유 접두부도 달라질 수 있습니다. 실제 적중은 반환된 사용량으로 확인하며, 누락된 값과 0을 구분합니다. 정책 근거는 2026-09-24에 확인한 OpenAI, Z.AI, OpenRouter공식 문서입니다.
롤링 메시지 breakpoint
요청 전체의 도구·시스템·메시지 마커는 최대 4개입니다. 정적 시스템 블록이 1개를 사용하고, apply_messages_cache_control이 기존 마커를 보존한 뒤 남은 슬롯에서 기본 최대 3개를 추가합니다.cache-policy의 messages_breakpoints는 추가할 수를 0–3으로 제한합니다. thinking·redacted_thinking과 빈 텍스트에는 직접 마커를 붙이지 않습니다. 명시적 1시간 마커는 5분 마커보다 앞에 있어야 하며, 잘못된 입력은 호출 전에 거절합니다.
마커 자체에는 별도 요금이 없습니다. Anthropic 표준 API 요율은 5분 쓰기 1.25배, 1시간 쓰기 2배이며 읽기 할인은 모델별 단가표를 따릅니다. 잦은 적중은 5분 TTL도 무료로 갱신하므로 1시간의 이득은 재사용 간격에 달려 있습니다. GEODE는 응답의 1시간 쓰기량을 비용과 영구 이벤트에 보존합니다. 과거 기록의 TTL 미상은 0으로 바꾸지 않으며, 그 기록의 추정 비용은 기존 5분 요율을 유지합니다.
동적 시스템 내용이 바뀌면 정적 마커까지는 재사용할 수 있지만 뒤쪽 메시지 프리픽스는 달라집니다. 도구 정의, 모델, thinking·effort 변경, 컨텍스트 정리도 영향을 줄 수 있습니다. 이는 새 요청의 정확성을 위한 무효화이며 캐시 적중을 보장하지 않습니다. 공급자가 보유한 캐시는 로컬 SDK 클라이언트를 닫는 동작과 별개의 수명을 가집니다.
대화 이력은 실제 turn만 append
AgenticLoop는 라운드 번호나 날짜를 합성 user message로 만들지 않습니다. 날짜와 runtime rule은 이미 core/agent/system_prompt.py의 동적 시스템 영역에 있고, 일반적인 대화 라운드는 user, assistant, tool 메시지를 뒤에 붙여 기존 프리픽스를 유지합니다. 컨텍스트 복구나 명시적 제어 전환은 이력을 바꿀 수 있으므로 별도로 확인합니다.
- 턴마다 바뀌는 시스템 정보는 메시지 이력에 중복 주입하지 않습니다.
- 컨텍스트 정리는 공유 이력을 직접 갱신하고, 그 뒤 adapter request를 조립합니다.
이 계약은 연속 AgenticLoop._call_llm 요청의 실제 AdapterCallRequest.messages prefix를 비교하는 회귀 테스트로 고정합니다.
실패 모드
| 증상 | 원인 | 해법 |
|---|---|---|
| cache_read가 늘 0 | prefix가 매 호출 변함. static 영역에 턴별 값이 새어 들어간 경우 | 턴마다 변하는 값은 경계 마커 뒤로 옮깁니다. |
| 짧은 작업의 비용 증가 | 재사용 전에 TTL 만료 또는 프리픽스 변경 | 안정적인 구간과 재사용 간격을 확인하고 필요한 TTL을 선택합니다. |
| 400: empty text block에 cache_control | 빈 static에 breakpoint를 붙이려는 시도 | 어댑터가 dynamic 승격으로 처리합니다. 직접 어댑터를 다룰 때만 해당합니다. |
다음
- 시스템 프롬프트 모드. static 영역에 무엇이 실리는지.
- 프롬프트 해싱. static 템플릿의 drift 가드.
- 비용 모니터링. 캐시 적중이 보이는 곳.