프롬프트 캐싱 미스터리? 접두사 때문에 API 비용 10배 폭증하는 이유!
프롬프트 캐싱, 왜 순서가 중요할까?
너 혹시 AI한테 뭐 물어볼 때 똑같은 내용인데도 결과가 달라지거나, 왜 이렇게 돈이 많이 나오지? 하고 생각해 본 적 있어? 오늘은 그 이유를 중학생 눈높이에 맞춰서 쉽게 설명해 줄게.
1. 똑같은 말인데 왜 돈이 10배 차이 나?
두 개의 프롬프트가 있다고 상상해 봐. 내용은 완전히 똑같은데, 딱 하나! 맨 앞에 붙이는 타임스탬프라는 게 있어. 이게 어떤 건 맨 앞에 붙고, 어떤 건 맨 뒤에 붙는 거야. 그런데 이렇게 순서만 바꿨는데도 API 비용이 10배나 차이가 나는 거지. 이게 왜 그럴까?
2. 캐시가 제대로 작동 안 하는 이유
분명히 프롬프트 캐싱이라는 걸 켰는데도, 아무리 해도 캐시가 작동하지 않는 거야. 마치 켜져 있는데 아무 일도 안 일어나는 것처럼 말이지.
그 이유는 바로 이거야. 캐시는 프롬프트 전체를 통째로 기억하는 게 아니야. 오직 앞부분만 재사용하는 거지. 만약 두 개의 요청이 앞부분을 공유하면, 그 공유하는 부분까지만 계산을 건너뛰고, 처음으로 달라지는 지점에서 재사용이 끝나는 거야.
그런데 타임스탬프를 맨 앞에 두면 어떻게 될까? 매번 첫 번째 토큰부터 달라지니까, 공유할 앞부분이 사라져 버리는 거지. 그래서 캐시를 켜도 잡을 게 없는 거야.
3. 왜 하필 '앞부분'만 재사용할까?
"아니, 왜 중간이나 끝은 재사용 안 하고 꼭 앞부분만 재사용하는 거야?" 라고 생각할 수 있어. 이걸 알려면 캐시가 뭘 저장하는지부터 알아야 해.
AI가 글을 쓸 때, 다음 단어를 만들려면 앞에 나왔던 모든 단어를 다시 봐야 해. 이걸 매번 처음부터 다시 계산하면 엄청난 낭비가 생기거든. 그래서 AI는 각 단어를 처음 처리할 때 뽑아둔 값을 '키(Key)'와 '밸류(Value)'라는 이름으로 저장해 둬. 이게 바로 KV 캐시라는 거야. 다음 단어를 만들 때는 저장된 걸 꺼내 쓰고, 새로 나오는 부분만 계산하면 되니까 훨씬 빠르지.
그런데 우리가 AI한테 질문할 때, 시스템 프롬프트나 도구 설명 같은 건 매번 똑같잖아? 바뀌는 건 딱 사용자 질문 한 줄뿐이지. 그런데도 이 똑같은 앞부분을 매번 다시 계산하는 건 낭비잖아. 그래서 프롬프트 캐싱은 이 KV 캐시를 요청과 요청 사이에서도 재사용하려는 시도인 거야.
4. '인과적 어텐션' 때문에 앞부분만 가능해!
문제는 이 저장된 KV 값이 아무 데서나 가져올 수 있는 게 아니라는 거야. 핵심은 '어텐션'이 '인과적'이라는 건데, 이게 무슨 말이냐면...
AI가 어떤 단어를 계산할 때, 그 앞에 있는 단어들만 볼 수 있고 뒤에 있는 단어는 절대 볼 수 없다는 뜻이야. 왜냐하면 AI는 왼쪽에서 오른쪽으로 단어를 하나씩 써 내려가니까, 아직 나오지도 않은 뒤의 단어는 볼 수 없거든.
그래서 위치 'i'의 키와 밸류는 오직 0부터 'i'까지의 단어들만 보고 결정되는 거야. 뒤에 어떤 단어가 오든 상관없이 말이지. 여기서 재사용의 근거가 나오는 거야. 앞부분이 똑같은 두 개의 요청은 같은 단어들이 같은 계산을 거치니까, 그 앞부분의 KV 값도 사실상 똑같다고 보는 거지. 그래서 캐시는 KV 값을 직접 비교하는 게 아니라, 앞부분 단어들이 똑같은지만 보고 저장된 KV를 그대로 가져오는 거야.
이것 때문에 재사용이 앞부분에만 묶이는 거고, 누가 그렇게 설계한 게 아니라 인과적 어텐션이라는 구조가 그렇게 강제하는 거야.
5. 중간만 떼어서 캐시하는 건 왜 안 될까?
그럼 "자주 쓰는 중간 문단만 따로 캐시하면 안 될까?" 라고 생각할 수 있어. 하지만 이건 원리적으로 불가능해.
그 중간 문단의 KV 값은 앞에 무엇이 왔는지에 통째로 의존하거든. 앞에 오는 내용이 바뀌면, 똑같은 문단이라도 KV 값이 달라져 버려. 그래서 중간만 떼어서 캐시하는 건 의미가 없어.
결국 앞에 단어 하나만 바뀌어도, 그 지점부터 끝까지 KV 값이 전부 달라져 버리는 거야. 마치 한 자리가 뒤 전체를 망가뜨리는 것처럼 말이지.
6. 그래서 규칙은 이거야!
이제 처음에 말했던 미스터리가 풀리지? 타임스탬프를 맨 앞에 두면 매번 첫 단어가 바뀌고, 그게 프롬프트 전체를 무효화하는 거야. 그래서 캐시 적중률이 0이었던 거지.
그래서 기억해야 할 규칙은 딱 하나야.
프롬프트를 가장 변하지 않는 것부터 가장 자주 변하는 순서대로 놓아라!
- 앞쪽 (변하지 않는 것): 시스템 프롬프트, 도구 정의, 예시처럼 안 변하는 것들
- 뒤쪽 (자주 변하는 것): 사용자 질문, 타임스탬프, 요청별로 바뀌는 내용들
도구 목록의 순서 하나, JSON 키 순서 하나만 달라져도 그 뒤에 오는 내용들이 전부 깨질 수 있어.
7. 캐싱, 조심해야 할 점도 있어!
하지만 조심해야 할 점도 있어. 캐시에 처음 저장하는 데는 오히려 더 많은 비용이 들어. 한 번 읽는 데 10배 이상이 들 수도 있지. 그래서 딱 한 번만 쓸 프롬프트에 캐시를 켜면 오히려 손해야. 최소 한 번은 재사용되어야 본전을 뽑는 거야.
그리고 캐시는 텍스트가 아니라 토큰 단위로 완전히 똑같아야 해. 공백 하나, 토크나이즈(단어를 토큰으로 나누는 과정) 차이 하나라도 다르면 실패야.
또 캐시에는 유효 시간이 있어서, 기본적으로 몇 분이야. 요청이 그보다 더 뜸하면 매번 새로 계산해야 하는 거지.
8. 다른 곳에서도 비슷한 경험을 했어!
사실 나는 이걸 완전히 다른 곳에서 먼저 경험했어. 예전에 오디오를 만들 때 문단 번호를 캐시 키로 썼는데, 앞에 문단 하나를 새로 끼우니까 뒤 번호가 전부 밀리면서 캐시가 통째로 날아갔던 경험이 있어. 앞이 바뀌면 뒤가 무너지는 구조는 KV 캐시만의 이야기가 아니라는 거지.
9. 그럼 멀티턴 대화는 어떻게 캐시할까?
마지막으로 궁금한 점이 하나 남았어. 매번 앞에 새 메시지가 쌓여가는 멀티턴 대화는 도대체 어떻게 캐시하고 있는 걸까? 이건 다음 기회에 더 자세히 이야기해 줄게!