토큰 드리프트(Token Drift) 설명: 에이전트가 점점 느려지고 비용이 많이 드는 이유
요약
에이전트 세션이 길어질수록 대화 기록, 도구 결과, RAG 문서 등이 누적되어 비용과 지연 시간이 급증하는 '토큰 드리프트' 현상을 설명합니다. 컨텍스트를 무제한 기록이 아닌 관리 가능한 리소스로 취급해야 함을 강조합니다.
핵심 포인트
- 토큰 드리프트는 누적된 컨텍스트로 인해 모델 호출 비용과 지연 시간이 증가하는 현상임
- 대화 기록 외에도 도구 스키마, API 응답, RAG 문서 등이 주요 토큰 축적 원인임
- 단순히 메시지 수를 제한하는 것만으로는 대규모 도구 출력에 의한 비용 문제를 해결하기 어려움
- 컨텍스트를 예산이 책정된 시스템 리소스로 관리하는 전략이 필요함
데모 중에는 에이전트가 빠르게 느껴집니다. 하지만 실제 세션이 20회 이상의 턴(turn)에 도달하고, 여러 도구(tool)가 대량의 페이로드(payload)를 반환하기 시작하면, 모든 응답이 더 오래 걸리고 더 많은 비용이 들기 시작합니다.
이러한 패턴은 흔히 **토큰 드리프트 (token drift)**라고 불립니다. 에이전트가 더 많은 대화 기록, 도구 출력, 검색된 문서 및 상태(state)를 각 모델 호출(model call)에 포함함에 따라 유효 입력 컨텍스트(effective input context)가 커지는 현상을 의미합니다. 모델 자체가 점진적으로 효율성이 떨어지는 것이 아닙니다. 애플리케이션이 매 턴마다 모델에게 더 많은 자료를 처리하도록 요청하고 있는 것입니다.
토큰 드리프트는 관리 가능하지만, 컨텍스트를 무제한의 기록이 아닌 예산이 책정된 시스템 리소스로 취급할 때만 가능합니다.
토큰 드리프트의 실제 의미
대부분의 대화형 에이전트는 시스템 프롬프트(system prompt), 도구 정의(tool definitions), 최근 메시지, 검색된 컨텍스트(retrieved context), 지속성 메모리(durable memory), 그리고 때로는 이전 작업의 요약본 등 여러 소스로부터 각 요청을 구성합니다. 애플리케이션이 매 요청마다 해당 상태를 보내든, 제공자(provider)가 그 중 일부를 관리하든, 모델은 여전히 처리해야 할 유효한 컨텍스트를 갖게 됩니다.
예시 세션을 살펴보겠습니다:
| 턴 (Turn) | 유효 입력 (Effective input) | 변경 사항 |
|---|---|---|
| 1 | 1,200 tokens | 시스템 프롬프트, 도구, 그리고 하나의 사용자 메시지 |
| ... |
정확한 가격과 지연 시간(latency)은 모델, 제공자, 캐시 동작 및 워크로드(workload)에 따라 달라집니다. 중요한 신호는 추세입니다. 즉, 후반부의 호출일수록 반복적으로 더 큰 컨텍스트를 처리하게 됩니다.
에이전트가 토큰을 그렇게 빠르게 축적하는 이유
대화 기록은 성장의 한 가지 원인일 뿐입니다. 프로덕션 에이전트는 종종 여러 곳에서 동시에 토큰을 축적합니다:
- 반복되는 전사 데이터 (Repeated transcripts): 이전의 모든 사용자 및 어시스턴트 메시지가 컨텍스트 (Context)에 남아 있습니다.
- 도구 스키마 (Tool schemas): 대규모 도구 설명 및 JSON 스키마가 모든 모델 호출에 첨부될 수 있습니다.
- 도구 결과 (Tool results): 검색 결과, 스택 트레이스 (Stack traces), 데이터베이스 행, API 응답은 사용자의 요청보다 훨씬 더 클 수 있습니다.
- 검색된 문서 (Retrieved documents): RAG 파이프라인은 때때로 너무 많은 청크 (Chunks)를 추가하거나, 여러 턴에 걸쳐 오래된 검색 결과를 유지합니다.
- 재시도 및 복구 (Retries and repairs): 실패한 도구 호출 및 유효성 검사 오류는 더 이상 유용하지 않게 된 후에도 계속 추가될 수 있습니다.
- 중복된 메모리 (Duplicated memory): 요약, 구조화된 상태, 그리고 원본 전사 데이터가 모두 동일한 사실을 포함할 수 있습니다.
이것이 채팅 메시지 수를 제한하는 것이 유용하지만 불충분한 이유입니다. 하나의 도구가 30,000 토큰의 JSON을 반환했다면, 10개의 메시지로 구성된 대화라도 여전히 비용이 많이 들 수 있습니다.
비용 곡선 (The Cost Curve)
하나의 요청이 시스템 지침 및 도구를 위해 s 토큰의 고정 비용을 가지고, 각 턴마다 대략 m 토큰의 새로운 히스토리가 추가된다고 가정해 봅시다. t 번째 턴의 입력은 대략 다음과 같습니다:
input(t) = s + (t * m)
턴당 입력은 대략 선형적으로 증가합니다. 그러나 n 번의 턴에 걸쳐 처리되는 누적 입력은 대략 다음과 같습니다:
session input = (n * s) + m * n * (n + 1) / 2
두 번째 항은 이차식 (Quadratic)입니다. 이 차이는 중요합니다. 최종 요청이 반드시 지수적으로 커지는 것은 아니지만, 점점 커지는 전사 데이터를 반복해서 다시 보내는 것은 전체 세션 비용을 턴 수보다 훨씬 더 빠르게 상승시킬 수 있습니다.
프롬프트 캐싱 (Prompt caching)은 이를 지원하는 제공업체에서 반복되는 접두사 (Prefixes)의 비용을 줄일 수 있습니다. 하지만 이는 컨텍스트 창 (Context-window) 제한, 무관한 히스토리 문제, 또는 도구 및 검색 페이로드 (Payloads)를 제어해야 할 필요성을 제거하지는 않습니다.
다듬기 전에 드리프트(Drift)를 측정하세요
가능한 한 모델 API에서 반환하는 토큰 사용량을 사용하십시오. 문자 기반 추정치는 진입 제어 (Admission control) 용도로는 허용될 수 있지만, 모델이나 언어 전반에 걸쳐 신뢰할 수 있는 과금 지표는 아닙니다.
사용자 요청(user request) 단위가 아니라, 모델 호출(model call) 단위로 사용량을 추적하세요. 하나의 에이전트 턴(agent turn)에는 계획(planning), 도구 호출(tool calls), 재시도(retries), 그리고 최종 응답이 포함될 수 있습니다.
type UsageSample = {
turn: number;
step: string;
...
유용한 트레이스(trace)는 모델, 작업(operation), 도구 이름, 재시도 횟수, 검색 청크(retrieval chunk) 수, 그리고 캐시된 입력(cached input) 사용 여부도 기록해야 합니다. 개인정보 보호 정책에서 명시적으로 허용하지 않는 한, 민감한 프롬프트(prompt) 내용은 기록하지 마세요.
메시지 제한이 아닌 컨텍스트 예산(Context Budget)을 사용하세요
슬라이딩 윈도우(sliding window)는 합리적인 대안이 될 수 있지만, 메시지 크기가 극적으로 변하기 때문에 토큰 예산(token budget)이 더 강력한 제어 수단입니다. 출력(output)과 모델이 수행할 수 있는 모든 도구 호출(tool calls)을 위한 공간을 예약한 다음, 남은 공간을 고정된 컨텍스트(fixed context)와 최근 턴(recent turns)에 할당하세요.
완전한 에이전트 턴(agent turn)을 삭제 단위로 취급하세요. 이를 참조하는 어시스턴트 메시지(assistant message)는 유지하면서 개별 도구 결과(tool result)만 삭제하면, 유효하지 않거나 혼란스러운 대화 기록(transcript)이 남을 수 있습니다.
type AgentMessage = {
role: 'user' | 'assistant' | 'tool';
content: string;
...
이 예시의 토큰 수(token counts)는 제공업체의 토크나이저(tokenizer) 또는 선택한 모델과 호환되는 토크나이저에서 가져와야 합니다. 도구, 검색 결과(retrieval results), 또는 요약(summary)이 변경될 때마다 계획을 다시 계산하세요.
대화와 상태(State)를 분리하세요
모든 사실이 대화 기록(transcript)에 포함될 필요는 없습니다. 장기 실행되는 에이전트(long-running agents)는 중요한 상태(state)를 명시적으로 저장할 때 더 신뢰할 수 있게 됩니다.
type TaskState = {
objective: string;
constraints: string[];
...
구조화된 상태(structured state)는 산문 형태의 요약(prose summary)보다 압축적이고, 검사 가능하며, 검증하기가 더 쉽습니다. 또한 오래된 메시지가 최근 윈도우(recent window) 밖으로 벗어났다는 이유만으로 중요한 결정 사항이 사라지는 것을 방지합니다.
실제적인 컨텍스트(context)는 종종 네 가지 계층을 가집니다:
- 안정적인 지침 (Stable instructions): 시스템 프롬프트 (System prompt) 및 안전 제약 조건 (Safety constraints).
- 구조화된 작업 상태 (Structured task state): 목표, 결정 사항, 식별자 (Identifiers), 그리고 미결 작업.
- 압축된 이력 (Compressed history): 이전 턴 (Turns)들의 요약본.
- 최근 턴 (Recent turns): 연속성을 위해 필요한 메시지 및 도구 교환 (Tool exchanges) 내용 그대로.
이러한 계층적 설계는 개별 메시지에 중요도 점수를 매기는 방식보다 일반적으로 더 유용합니다. 휴리스틱 점수 (Heuristic scores) 방식은 설명하기 어려울 수 있으며, 조용하지만 필수적인 제약 조건을 버릴 위험이 있습니다.
의도적으로 요약하기 (Summarize Deliberately)
요약은 세션이 충분히 길어져서 최근의 윈도우 (Window)가 필요한 컨텍스트 (Context)를 잃어버릴 때 가치가 있습니다. 모든 턴마다 맹목적으로 실행해서는 안 됩니다.
이전 턴들이 특정 임계값 (Threshold)을 넘을 때만 요약을 생성하거나 갱신하십시오. 요약기 (Summarizer)에게 사실, 결정 사항, 제약 조건, 실패 사례, 미결 질문, 그리고 외부 아티팩트 (Artifacts)에 대한 참조를 보존하도록 요청하십시오. 요약본은 원본 기록 (Raw transcript)과 별도로 저장하여 검토하거나 재생성할 수 있도록 해야 합니다.
요약은 정보 손실이 발생하며 오류를 유발할 수 있습니다. 계정 ID, 승인 상태, 금융 금액, 파일 경로와 같은 정형 데이터 (Canonical values)는 생성된 산문 (Prose)을 신뢰하기보다 구조화된 저장소 (Structured storage)에 보관하십시오.
도구 및 검색(Retrieval)도 제한하기
겉으로 보이는 많은 메모리 문제는 실제로는 페이로드 (Payload) 문제입니다. 유용한 컨텍스트 정책은 다음 사항도 포함해야 합니다:
- 현재 상태에서 사용 가능한 도구 정의 (Tool definitions)만 전송합니다.
- 큰 도구 결과물을 압축된 타입화된 투영 (Typed projection)으로 대체합니다.
- 전체 결과는 외부에 저장하고, 컨텍스트에는 ID 또는 URL만 유지합니다.
- 관련성 (Relevance)과 토큰 예산 (Token budget) 모두를 기준으로 검색 (Retrieval)을 제한합니다.
- 프롬프트에 추가하기 전에 중복되는 청크 (Chunks)를 제거합니다.
- 진단적 가치가 만료된 실패한 재시도 (Failed retries) 기록은 삭제합니다.
예를 들어, 에이전트가 데이터베이스 응답 전체를 필요로 하는 경우는 드뭅니다. 대신 선택된 5개의 필드, 결과 개수, 그리고 나중에 더 많은 데이터를 가져오는 데 사용할 수 있는 식별자 (Identifier)만 필요할 수 있습니다.
작업량에 따른 전략 선택
| 작업량 (Workload) | 권장 시작 전략 (Good starting strategy) |
|---|---|
| 짧은 고객 지원 채팅 (Short support chat) | 엄격한 입력 예산(Input budget)을 적용한 최근 턴 윈도우 (Recent-turn window) |
| ... |
대부분의 시스템은 첫날부터 정교한 메모리 순위 지정 알고리즘 (Memory-ranking algorithm)을 필요로 하지 않습니다. 대신 가시성(Visibility), 예산(Budget), 그리고 무엇이 컨텍스트(Context)에 들어올 수 있는지에 대한 명확한 규칙이 필요합니다.
프로덕션 품질 게이트 (Production Quality Gates)
컨텍스트 축소 (Context reduction)는 작업 품질을 손상시키지 않으면서 리소스 사용량을 줄일 때만 성공적입니다. 대표적인 세션들을 대상으로 정책을 비교하고 다음 항목들을 추적하십시오:
- 턴당 및 완료된 작업당 입력 토큰 (Input tokens)
- 캐시된 입력 토큰 (Cached input tokens) 대 캐시되지 않은 입력 토큰 (Uncached input tokens)
- 모델 호출 횟수 (Model-call count), 재시도 (Retries), 도구 실패 (Tool failures)
- 중앙값 및 꼬리 지연 시간 (Median and tail latency)
- 작업 완료율 (Task completion) 및 인간 수정률 (Human correction rates)
- 압축 후 누락된 사실 또는 제약 조건 (Missing facts or constraints)
- 요약 갱신 빈도 (Summary refresh frequency) 및 비용
중요한 사실과 도구 시퀀스(Tool sequences)에 대해서는 결정론적 회귀 테스트 (Deterministic regression cases)를 실행하십시오. 예를 들어, 원래의 턴이 요약된 이력(Summarized history)으로 이동한 후에도 에이전트가 승인 제약 조건 (Approval constraint)을 여전히 기억하는지 확인하십시오.
실질적인 기준점 (A Practical Baseline)
단순한 정책으로 시작하십시오: 실제 사용량을 측정하고, 출력 여유 공간 (Output headroom)을 확보하며, 입력 컨텍스트 (Input context)를 제한하고, 구조화된 작업 상태 (Structured task state)를 유지하며, 최근의 완전한 턴들을 작은 윈도우로 유지하고, 오래된 이력은 필요할 때만 요약하십시오. 도구 및 검색 페이로드 (Tool and retrieval payloads)는 독립적으로 제한하십시오.
토큰 드리프트 (Token drift)는 신비로운 모델 성능 저하가 아닙니다. 그것은 누적된 애플리케이션 상태 (Application state)입니다. 일단 그 상태를 측정하고 예산을 할당하면, 비용과 지연 시간은 세션 후반부에 발생하는 갑작스러운 놀라움이 아니라 예측 가능한 엔지니어링 트레이드오프 (Engineering trade-offs)가 됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기