OpenAI Prompt Caching 비용 절감법: Batch API와 함께 쓰는 운영 기준
OpenAI API 비용이 예상보다 빨리 늘어나는 팀을 보면 비슷한 패턴이 있습니다. 긴 시스템 프롬프트, 같은 도구 정의, 같은 예시, 같은 정책 문구를 매 요청마다 그대로 보내지만 캐시가 잘 맞도록 설계하지 않습니다. 여기에 대량 분류, 임베딩, 평가 작업까지 동기 API로 처리하면 비용과 rate limit이 같이 터집니다.
OpenAI의 Prompt Caching과 Batch API는 이 문제를 줄이는 대표적인 방법입니다. Prompt Caching은 반복되는 긴 프롬프트 prefix를 재사용해 지연 시간과 입력 토큰 비용을 낮추는 기능이고, Batch API는 즉시 응답이 필요 없는 작업을 비동기로 묶어 24시간 내 처리하면서 동기 API 대비 50% 낮은 비용을 제공하는 방식입니다. 둘 다 단순히 켜는 기능처럼 보이지만, 실무에서는 프롬프트 구조와 작업 분류를 바꾸지 않으면 효과가 작습니다.
이 글은 개발팀이 바로 적용할 수 있는 운영 기준을 정리합니다. 핵심은 실시간 요청과 비실시간 요청을 분리하고, 캐시 가능한 prefix를 앞에 고정하고, 캐시 hit 여부를 비용 지표로 추적하는 것입니다.
Prompt Caching이 실제로 줄여주는 비용은 무엇인가
Prompt Caching은 같은 프롬프트를 기억했다가 답변을 재사용하는 기능이 아닙니다. 모델 출력이 아니라 입력 prefix 처리 비용을 줄이는 기능입니다. OpenAI 문서에 따르면 캐시 hit는 정확히 일치하는 prompt prefix에서만 가능합니다. 그래서 시스템 지시문, 도구 정의, 긴 예시, 정책 설명처럼 바뀌지 않는 내용을 프롬프트 앞쪽에 두고, 사용자별 변수나 질문은 뒤쪽에 둬야 합니다.
기본적으로 1,024토큰 이상 프롬프트에서 자동 캐싱이 적용될 수 있습니다. 최근 모델에서는 요청이 같은 prefix를 공유하면 cached_tokens로 보고되고, GPT-5.6 계열 이후에는 cache_write_tokens와 cached_tokens를 함께 보면서 캐시 쓰기 비용과 읽기 절감을 비교할 수 있습니다. 중요한 점은 ‘긴 프롬프트면 자동으로 절약된다’가 아니라 ‘긴 고정 prefix가 여러 요청에서 정확히 반복되어야 절약된다’는 것입니다.
예를 들어 고객지원 분류 API를 만든다고 가정해봅시다. 시스템 프롬프트에 분류 기준 30개, 예시 20개, JSON 출력 스키마, 금칙어 정책이 들어갑니다. 이 부분이 요청마다 조금씩 바뀌면 캐시가 깨집니다. 반대로 이 부분을 완전히 고정하고, 마지막에 고객 문의 내용만 넣으면 캐시 hit 가능성이 올라갑니다.
캐시 hit를 높이는 프롬프트 배치 원칙
첫 번째 원칙은 고정 내용을 앞에 몰아넣는 것입니다. 모델 역할, 출력 포맷, 도구 정의, 예시, 정책 문서는 앞쪽에 둡니다. 사용자 ID, 현재 시간, 실험 플래그, 검색 결과, 대화 마지막 입력처럼 매번 바뀌는 값은 뒤쪽으로 보냅니다. 많은 팀이 시스템 프롬프트 중간에 현재 날짜나 사용자 등급을 끼워 넣는데, 이렇게 하면 뒤에 있는 긴 예시까지 캐시 이점을 잃을 수 있습니다.
두 번째 원칙은 prefix를 문자열 단위로 안정화하는 것입니다. 같은 의미라도 공백, 순서, JSON key 순서, 예시 정렬이 바뀌면 exact match가 깨질 수 있습니다. 도구 스키마는 빌드 시점에 정렬하고, 정책 문구는 버전 파일로 관리하는 편이 좋습니다. 프롬프트 생성 함수가 매 요청마다 배열 순서를 다르게 만들지 않는지도 확인해야 합니다.
세 번째 원칙은 prompt_cache_key를 안정적으로 나누는 것입니다. OpenAI 문서는 긴 공통 prefix를 공유하는 요청에 같은 prompt_cache_key를 사용하면 라우팅과 hit rate 개선에 도움이 된다고 설명합니다. 단, 하나의 key에 너무 많은 서로 다른 prefix를 섞으면 효과가 떨어집니다. 실무에서는 기능 단위로 key를 나눕니다. 예를 들어 support-classifier-v3, product-search-reranker-v2, eval-rubric-v1처럼 씁니다.
네 번째 원칙은 프롬프트 버전을 비용 지표와 함께 관리하는 것입니다. 프롬프트를 조금 고쳤는데 비용이 20% 늘어나는 일이 흔합니다. 배포 전후로 input_tokens, cached_tokens, cache_write_tokens, latency p50/p95를 같이 봐야 합니다. 품질 평가만 통과하고 비용 지표를 보지 않으면 운영 비용이 조용히 증가합니다.
Batch API로 보내야 하는 작업과 보내면 안 되는 작업
Batch API는 모든 요청을 싸게 만드는 만능 버튼이 아닙니다. OpenAI 문서 기준으로 Batch API는 비동기 작업 묶음을 처리하며, 동기 API 대비 50% 비용 할인을 제공하고, 별도 rate limit 풀과 24시간 내 처리 시간을 제공합니다. 즉시 응답이 필요 없는 작업에 적합합니다.
보내기 좋은 작업은 대량 데이터 분류, 콘텐츠 태깅, 오프라인 평가, 임베딩 생성, 기존 문서 요약, 배치 moderation, 대량 이미지·비디오 생성처럼 사용자가 기다리고 있지 않은 작업입니다. 예를 들어 밤마다 5만 개 고객 문의를 카테고리화하거나, 릴리즈 전 1,000개 프롬프트 평가셋을 돌리는 작업은 Batch API에 잘 맞습니다.
반대로 채팅 답변, 검색 자동완성, 결제 직후 검증, 사용자가 버튼을 누르고 결과를 기다리는 기능은 Batch API에 맞지 않습니다. 24시간 내 완료가 보장된다고 해도 사용자 경험에서는 너무 깁니다. 이런 요청은 동기 API를 쓰되 Prompt Caching, 짧은 컨텍스트, 모델 라우팅으로 비용을 줄여야 합니다.
구분 기준은 간단합니다. 사용자가 화면에서 기다리면 동기 API입니다. 결과가 나중에 반영되어도 되면 Batch API입니다. 실패 시 재시도해도 사용자 흐름이 깨지지 않으면 Batch API 후보입니다.
Prompt Caching과 Batch API를 함께 쓰는 구조
두 기능은 서로 대체 관계가 아닙니다. 실시간 API에는 Prompt Caching을 우선 적용하고, 비실시간 대량 작업에는 Batch API를 적용합니다. 그리고 Batch 작업 안에서도 같은 긴 prefix를 반복한다면 캐시 친화적인 프롬프트 구조를 유지하는 것이 좋습니다.
실무 구조는 다음처럼 나눌 수 있습니다.
- realtime: 사용자 채팅, 검색, 추천, 즉시 요약
- nearline: 몇 분 지연되어도 되는 리포트, 알림 후보 생성
- offline: 대량 분류, 평가, 임베딩, 품질 검사
realtime은 latency p95가 중요합니다. 캐시 가능한 시스템 프롬프트를 앞에 두고, 모델을 작업 난이도별로 나누며, 출력 토큰 상한을 빡빡하게 잡습니다. nearline은 큐를 두고 요청량을 평탄화합니다. offline은 Batch API로 묶고, custom_id로 결과를 원본 데이터와 정확히 매칭합니다.
예를 들어 AI 에이전트 디렉터리 서비스를 운영한다면, 사용자가 검색창에서 ‘코딩 에이전트 추천’을 입력하는 순간에는 동기 API가 필요합니다. 반면 매일 새로 들어온 에이전트 설명을 카테고리화하고, 중복 설명을 정리하고, SEO 요약을 만드는 작업은 Batch API로 보내는 것이 낫습니다. 같은 작업을 동기 API로 돌리면 비용뿐 아니라 rate limit 때문에 배포 시간이 불안정해집니다.
비용 관측: 캐시가 되는지 숫자로 확인하기
최적화는 느낌으로 하면 안 됩니다. 최소한 요청 로그에 다음 값을 남겨야 합니다.
- feature_name
- prompt_version
- model
- input_tokens
- output_tokens
- cached_tokens
- cache_write_tokens
- latency_ms
- request_mode: realtime, nearline, batch
- estimated_cost
이 지표가 있어야 ‘프롬프트를 앞쪽 고정 구조로 바꾼 뒤 cached_tokens 비율이 올랐는지’, ‘Batch API로 옮긴 작업의 단가가 실제로 내려갔는지’, ‘출력 토큰이 비용 증가의 원인인지’를 확인할 수 있습니다.
캐시 hit율은 기능별로 봐야 합니다. 전체 평균은 의미가 약합니다. support-classifier는 80% hit가 나오는데, product-reranker는 10%라면 프롬프트 구조나 key 분리가 다르다는 뜻입니다. 또한 배포 직후에는 cache_write가 늘고 cached read가 뒤따라올 수 있으므로, 1분 단위보다 30분 또는 1시간 단위로 보는 편이 낫습니다.
비용 알림도 단순 총액보다 단가 기준으로 잡는 것이 좋습니다. 예를 들어 ‘요청 1,000건당 비용’, ‘문서 1,000개 분류 비용’, ‘검색 1회당 평균 비용’을 지표로 두면 트래픽 증가와 비효율 증가를 구분할 수 있습니다.
실무 적용 체크리스트
- 시스템 프롬프트, 도구 정의, 예시는 프롬프트 앞쪽에 고정한다.
- 사용자별 변수, 현재 시간, 검색 결과는 뒤쪽으로 보낸다.
- JSON key 순서와 예시 순서가 매 요청마다 바뀌지 않게 한다.
- 기능 단위로 prompt_cache_key를 안정적으로 나눈다.
- 프롬프트 버전을 바꿀 때 cached_tokens와 latency를 함께 비교한다.
- 사용자가 기다리는 요청은 동기 API로 남긴다.
- 대량 분류, 임베딩, 평가, moderation은 Batch API 후보로 분리한다.
- Batch 결과는 custom_id로 원본 데이터와 매칭한다.
- 비용은 총액보다 요청 1,000건당 단가로 본다.
- 캐시 hit율은 전체 평균이 아니라 feature_name별로 본다.
OpenAI API 비용 절감의 핵심은 모델을 무조건 낮추는 것이 아닙니다. 같은 일을 반복 처리하는 구조를 찾아 캐시 가능한 prefix로 만들고, 즉시성이 필요 없는 작업을 Batch API로 빼는 것입니다. 이 두 가지만 제대로 분리해도 비용과 rate limit 압박이 눈에 띄게 줄어듭니다.