본문 바로가기
Q&A해결2026년 7월 18일1분 읽기

Claude로 멀티턴 에이전트 만드는데 대화가 길어질수록 토큰 비용이 눈덩이처럼 불어납니다

c
claude_soyeon
조회 944댓글 4

Anthropic SDK로 툴 콜링 붙은 멀티턴 에이전트를 돌리고 있습니다. API가 stateless라서 매 요청마다 system + 지금까지의 messages 전체를 다시 실어 보내야 하는 건 이해했는데, 문제는 대화가 10턴, 20턴 넘어가면 입력 토큰이 계속 누적돼서 뒤쪽 턴 한 번 호출하는 비용이 앞쪽의 몇 배가 됩니다.

system 프롬프트가 좀 큰 편이에요. 툴 정의랑 few-shot 예시, 그리고 회사 룰 같은 게 들어가서 고정으로 꽤 됩니다. 이걸 매 턴 풀로 다시 계산해서 과금되는 것 같은데, 히스토리를 통째로 요약해서 줄여버리면 맥락이 깨질까 봐 겁납니다.

usage를 찍어보면 input_tokens가 턴마다 쭉쭉 올라가는 게 보이는데, 이 고정 프리픽스 부분을 매번 풀로 안 물게 하는 방법이 있을까요? 아니면 그냥 감수해야 하나요?

댓글 4

김김영삼3개월 전
요약으로 줄이기 전에 프롬프트 캐싱부터 켜세요. 지금 상황에 정확히 맞는 기능입니다. 고정 프리픽스를 매 턴 풀 가격으로 무는 게 문제인데, 캐시된 프리픽스는 읽을 때 원가의 10분의 1 수준으로 떨어집니다.

방법은 안정적인 부분의 마지막 블록에 cache_control ephemeral을 붙이는 거예요. 렌더 순서가 tools 다음 system 다음 messages라서, 마지막 system 블록에 브레이크포인트를 걸면 툴 정의랑 system이 통째로 캐시로 잡힙니다. 대화 히스토리는 가장 최근에 추가된 턴의 마지막 블록에도 하나 걸어두면 다음 요청이 그 앞부분 전체를 캐시로 재사용해요.

확인은 응답의 usage에서 cache_read_input_tokens 값이 0보다 큰지 보면 됩니다. 계속 0이면 프리픽스 어딘가가 매번 바뀌고 있다는 뜻이에요. system 프롬프트에 현재시각이나 요청 UUID 같은 걸 끼워 넣고 있으면 그 뒤가 통째로 캐시 무효화됩니다. 그거부터 빼세요.

요약(compaction)은 컨텍스트 창 한계에 실제로 근접했을 때 얹는 거지, 비용 문제의 1차 해법은 캐싱입니다.
cclaude_soyeon(guest)3개월 전
캐싱 켜니까 cache_read_input_tokens 잡히면서 뒤쪽 턴 비용이 확 내려갔습니다. 근데 궁금한 게 있는데요, 운영 중에 system 프롬프트를 상황 따라 살짝 바꿔야 할 때가 있거든요. 그때마다 캐시가 다 날아가는 건가요? 모드 전환 같은 걸 넣고 싶은데 그러면 캐싱이랑 상충될 것 같아서요.
김김영삼3개월 전
네 맞아요. system은 프리픽스 맨 앞이라 한 바이트만 바꿔도 그 뒤 전체가 무효화됩니다. 그래서 중간에 들어가는 운영 지시는 top-level system을 건드리지 말고 messages 배열 끝에 role system 메시지로 붙이는 방식을 쓰세요.

그러면 캐시된 히스토리 프리픽스는 그대로 살아있고, 새 지시는 그 뒤에 추가되는 거라 캐시가 안 깨집니다. 모드 전환, 갱신된 컨텍스트 같은 걸 이 채널로 넣으면 돼요. (이건 최신 Opus에서 지원됩니다. 안 되는 모델이면 유저 턴 안에 시스템 리마인더 형태로 넣는 걸로 폴백하고요.)

원칙은 하나예요. 안 바뀌는 건 앞에, 자주 바뀌는 건 브레이크포인트 뒤에. 그 선만 지키면 캐시 히트율이 유지됩니다.
cclaude_soyeon(guest)3개월 전
아 이거였네요. system을 계속 수정하던 게 캐시 다 날리고 있었던 거였군요. messages 끝에 system 메시지로 붙이는 방식으로 바꿨습니다. 정확한 진단 감사합니다.
Ctrl+Enter로 등록