이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • LLM은 왜 token 단위로 비용과 제한을 가지는가?
  • context window가 커지면 어떤 문제가 해결되고 어떤 문제는 남는가?
  • 백엔드 API에서 token budget을 어떻게 설계해야 하는가?

개요

LLM은 입력을 token 단위로 처리하고 출력을 token 단위로 생성한다. token은 모델 내부 처리 단위이면서 백엔드 운영에서는 비용, latency, quota, context limit을 계산하는 단위가 된다.

OpenAI, Anthropic, Google 같은 provider 문서를 볼 때 input token, output token, context window, max output token 같은 용어가 반복해서 나온다. 이 용어를 알아야 비용과 장애를 설명할 수 있다.

왜 백엔드 개발자에게 중요한가

백엔드에서는 요청 크기를 제한하고, DB query timeout을 두고, file upload size를 제한한다. LLM에서도 같은 감각이 필요하다. token budget은 AI 요청의 size limit이다.

token을 관리하지 않으면 다음 문제가 생긴다.

  • 긴 context로 비용이 증가한다.
  • 응답 latency가 증가한다.
  • output이 중간에 잘린다.
  • prompt cache 효율이 떨어진다.
  • tenant별 비용 예측이 어렵다.

핵심 원리

LLM 요청의 token은 대략 다음으로 구성된다.

  • system instruction
  • developer instruction
  • user input
  • conversation history
  • retrieved context
  • tool result
  • output schema

출력 token은 모델이 생성한 답변이다. 비용은 보통 input과 output 모두에서 발생한다. output이 길수록 사용자 대기 시간도 길어진다.

Context Window

context window는 모델이 한 요청에서 볼 수 있는 최대 token 범위다. 큰 context window는 긴 문서나 대화 이력을 넣는 데 유리하다.

그러나 큰 context는 만능이 아니다.

  • 관련 없는 정보가 섞일 수 있다.
  • 오래된 정보가 최신 정보와 충돌할 수 있다.
  • 비용과 latency가 증가한다.
  • 모델이 긴 context의 모든 부분을 균등하게 잘 쓰지 못할 수 있다.

따라서 context window가 커져도 context engineering은 필요하다.

코드로 이해하기

요청 전 대략적인 budget check를 둔다.

def estimate_tokens(text: str) -> int:
    return max(1, len(text) // 4)
 
 
def validate_token_budget(*, prompt: str, max_input_tokens: int) -> None:
    estimated = estimate_tokens(prompt)
    if estimated > max_input_tokens:
        raise ValueError(
            f"prompt too large: estimated={estimated}, limit={max_input_tokens}"
        )

실제 비용 정산은 provider가 반환한 usage 값을 사용한다. 추정치는 사전 차단과 UX 안내용이다.

백엔드 설계 판단

  • 기능별 max input token과 max output token을 둔다.
  • 대화 이력은 모두 넣지 말고 요약하거나 최근 n개로 제한한다.
  • RAG context는 top-k와 token budget을 함께 적용한다.
  • output이 길어야 하는 작업은 비동기 job으로 넘긴다.
  • token usage를 tenant와 feature별로 기록한다.

장애 상황과 대응

token 관련 장애:

  • 요청이 갑자기 느려졌다.
  • 비용이 증가했다.
  • 응답이 중간에 잘렸다.
  • JSON이 끝나지 않아 파싱 실패했다.

대응:

  • prompt version별 input token을 본다.
  • output token limit을 확인한다.
  • RAG top-k와 chunk 크기를 줄인다.
  • 긴 요청은 queue로 분리한다.

인프라 협업 포인트

인프라팀은 token을 직접 보지 않을 수 있지만, token은 latency와 비용의 원인이다. P95 input token, P95 output token, token per tenant를 metric으로 공유하면 좋다.

실전 팁

  • token budget은 기능 요구사항으로 문서화한다.
  • context를 늘리기 전에 retrieval 품질을 먼저 본다.
  • output token은 사용자 UX와 비용을 동시에 좌우한다.
  • prompt caching을 고려하면 고정 prefix를 안정적으로 유지한다.

위험 신호!

  • max output token이 없다.
  • 대화 이력을 무제한으로 넣는다.
  • RAG 검색 결과를 token budget 없이 넣는다.
  • usage 값을 저장하지 않는다.

확인 질문

확인 질문

  • context window가 커져도 context engineering이 필요한 이유는 무엇인가?
    • 비용, latency, 관련성, 최신성, 권한 문제가 여전히 남기 때문이다.
  • token usage를 기록할 때 어떤 dimension이 중요한가?
    • feature, tenant, user, prompt version, model, input/output token이다.

참고 문서