본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 18. 04:41

컨텍스트 세금 (The Context Tax): 왜 12번째 단계가 1번째 단계보다 42배 더 비싼가 (40줄의 코드로 측정하기)

요약

에이전트가 매 단계마다 전체 세션 기록을 다시 입력값으로 전송하며 발생하는 '컨텍스트 세금(Context Tax)' 문제를 분석합니다. 세션이 진행될수록 입력 토큰량이 기하급수적으로 증가하여 비용이 폭증하는 구조적 원인을 설명합니다.

핵심 포인트

  • LLM은 상태가 없는(stateless) 특성 때문에 매 단계 이전 기록을 모두 재전송해야 함
  • 총 비용은 단계 수(n)에 따라 n(n+1)/2의 비율로 증가하는 구조를 가짐
  • 토큰 단가가 낮아져도 세션이 길어지면 전체 비용은 오히려 상승할 수 있음
  • 측정 결과, 특정 디버깅 세션에서 마지막 단계 비용이 첫 단계의 42.8배에 달함

요약: **컨텍스트 세금 (context tax)**이란 에이전트의 매 단계마다 전체 세션 기록(transcript)을 입력값으로 다시 전송할 때 지불하게 되는 비용을 의미합니다. 따라서 N번째 단계는 1부터 N까지의 턴을 다시 청구하게 되며, 총비용은 n(n+1)/2에 따라 증가합니다. 토큰 가격이 저렴해지는 것은 단위 비용을 낮출 뿐, 비용 구조(shape) 자체를 바꾸지는 못합니다. context_tax.py는 오프라인에서 재청구 배수(re-bill multiplier)를 측정하며, 한 디버깅 세션에서는 42.8배로 측정되었습니다.

AI 공개: 저는 AI 글쓰기 어시스턴트를 사용하여 이 글을 초안했습니다. 도구, 피스처(fixtures), 그리고 아래의 모든 수치는 이 포스트에 포함된 스크립트를 tiktoken o200k_base에서 실제로 로컬 실행한 결과입니다. 저는 게시하기 전에 내용을 검토하고 편집했습니다.

토큰 가격은 일 년 내내 하락해 왔습니다. 하지만 여러분의 에이전트 청구서는 아마 그렇지 않을 것입니다.

저의 FinOps 노트에서도 똑같은 혼란을 계속 겪었습니다. 토큰당 요금은 떨어지는데, 월간 청구 금액은 반대로 올라가는 현상 말입니다. 일반적인 답변들("더 큰 모델을 사용하고 있다", "사용자가 더 많아졌다")은 단일 세션이 실행됨에 따라 왜 점점 더 비싸지는지는 설명하지 못했습니다. 그래서 저는 아무도 차트로 만들지 않는 단 한 가지, 즉 세션 기록(session transcript) 자체를 살펴보기 위해 40줄짜리 측정기를 작성했습니다. 합성되었지만 현실적인 디버깅 세션에서, 마지막 단계는 첫 번째 단계 입력값의 42.8배를 청구했습니다. 동일한 모델, 동일한 작업, 새로운 사용자 없음에도 말입니다.

이 격차에는 지루한 원인과 짜증 나는 결과가 있습니다. 스크립트와 함께 두 가지 모두를 소개합니다.

요약 (TL;DR). 에이전트 루프의 매 단계는 지금까지의 전체 대화(히스토리 및 도구 출력값)를 _입력(input)_으로 다시 전송합니다. 따라서 N번째 단계는 1부터 N까지의 턴을 다시 지불하며, 총 입력값은 대략 n(n+1)/2에 따라 증가합니다. 저렴한 토큰은 이 구조를 해결하지 못하며, 단지 계속 상승하는 수치의 단위 비용을 낮출 뿐입니다. 아래의 context_tax.py(키가 필요 없는 오프라인 도구)는 세션 JSON에서 세 가지를 측정합니다: 재청구 곡선(re-bill curve), 재청구 배수(re-bill multiplier), 그리고 사장 비용(dead-weight) 추정치입니다. 저의 비대해진 피스처에서 이 도구는 42.8배의 배수와 19.3%의 사장 비용을 보고했으며, CI 게이트로서 1을 반환하며 종료되었습니다.

왜 매 단계마다 기록(transcript)이 다시 청구되는가

사람들을 혼란스럽게 만드는 지점이 바로 여기입니다. LLM 호출은 상태가 없습니다 (stateless). 모델은 당신이 12번째 단계를 수행할 때 3번째 단계를 "기억"하지 못합니다. 당신의 프레임워크는 모델이 이를 볼 수 있도록 1단계부터 11단계까지의 내용을 입력값으로 다시 전송합니다. 매. 단계.마다. 말이죠.

따라서 한 단계의 비용은 해당 단계에서 생성된 새로운 텍스트의 비용이 아닙니다. 그 시점까지의 전체 기록 (history) 비용입니다. 1단계는 짧은 사용자 메시지에 대해 비용을 청구합니다. 12단계는 사용자 메시지 플러스 파일 덤프 (file dump) 플러스 광범위한 grep 플러스 스택 트레이스 (stack trace) 플러스 그 사이의 모든 어시스턴트(assistant) 답변에 대해 비용을 청구합니다. 12단계에서의 새로운 토큰은 아주 작을 수 있습니다. 하지만 청구되는 입력값은 그렇지 않습니다.

Logan (Waxell)은 _The Compounding Math Your Architecture Is Hiding_에서 이 구조를 명확하게 설명했습니다: "총 비용은 대략 n(n+1)/2에 비례하여 증가하며," 10번째 단계의 컨텍스트 (context)는 80,000~200,000 토큰에 달할 수 있습니다. 해당 포스트는 문제를 정확히 짚어낸 뒤 특정 독점 런타임 (proprietary runtime)을 추천합니다. 저는 그 반대를 원했습니다. 제 자신의 기록 (transcript)에 실행하여 CI에 체크인할 수 있는 아주 작은 스크립트 말입니다. 그래서 이것을 만들었습니다.

그리고 이것이 바로 "토큰 가격이 저렴해졌다"는 말이 잘못된 위로인 이유입니다. Edwin Lisowski의 _Token Prices Are Falling. So Why Is Your AI Bill Going Up?_는 그 원인들을 나열합니다: 매 단계마다 전체 컨텍스트 재전송, 사용자 콘텐츠가 들어오기도 전에 컨텍스트 창 (window)의 30~60%를 차지하는 도구 스키마 (tool schemas), 그리고 24시간 내내 돌아가는 재시도 (retries) 및 서브 에이전트 (sub-agents) 등입니다. 이러한 스키마 오버헤드 (schema overhead)는 그 자체로 측정할 가치가 있는 형제 격인 세금입니다. 저는 measure your MCP server's token tax에서 MCP 서버에 대해 정확히 그 작업을 수행했는데, 여기서 도구 정의 (tool definitions)는 단 하나의 사용자 토큰이 사용되기 전 모든 호출에서 비용이 청구됩니다. 그의 설명은 직설적입니다. 그는 AT&T가 18개월 동안 하루 토큰 사용량이 10억 개에서 270억 개로 증가한 사례를 인용합니다. (이는 Lisowski의 예시이며 제 측정값이 아닙니다. 저자는 그에게 출처를 돌립니다.) 단위 가격은 낮아졌지만, n이 커졌습니다. 단위를 잃은 것입니다.

이것은 추측할 수 없습니다. 직접 측정해야 합니다.

추측 대신 측정(meter)을 해야 하는 두 번째 이유는 에이전트(Agents)가 자신의 지출을 예측하는 데 서투르기 때문입니다.

arXiv 논문 How Do AI Agents Spend Your Money? (Bai, Huang, Wang, Sun, Mihalcea, Brynjolfsson, Pentland, Pei)는 에이전트 기반 코딩 작업(agentic coding tasks)을 측정하였으며, 반드시 주목해야 할 세 가지 사실을 발견했습니다. 첫째, 에이전트 기반 실행(agentic runs)은 일반적인 코드 채팅(code-chat)보다 약 1000배 더 많은 토큰을 소모합니다. 둘째, 동일한 작업이라도 실행할 때마다 비용이 최대 30배까지 차이 날 수 있습니다. 셋째, 모델은

종료 코드(exit code)가 핵심입니다. 배수가 임계값 미만이면(절제된 세션) 0, 임계값을 초과하면(아키텍처가 복리적으로 비용을 증가시키므로 빌드 실패) 1, 사용량 관련이면 2를 반환합니다. 이를 CI(지속적 통합)에 적용하면, 비용이 급증하는 세션은 예상치 못한 지출 항목이 아니라 빨간색 체크 표시(실패)로 나타나게 됩니다.

#!/usr/bin/env python3
"""context_tax.py - 단일 에이전트 세션의 트랜스크립트에 대한 재청구 세금(re-bill tax)을 측정합니다."""
import json, re, sys
...

API 키도, 네트워크도 필요 없는 읽기 전용 도구입니다. pip install tiktoken을 실행하고 트랜스크립트 JSON 파일을 지정하기만 하면 끝입니다. 만약 tiktoken이 설치되어 있지 않으면 len/4 휴리스틱(heuristic) 방식으로 대체되며 이를 명시적으로 알립니다(실제 BPE와 약 ±15% 오차 발생). 저는 숫자가 정확한 척하기보다는 주의 사항을 출력하는 쪽을 택했습니다.

실제 실행

스크립트에는 두 가지 피스처(fixtures)가 포함되어 있습니다. 둘 다 합성된 코딩 세션(개인 데이터 없음)이지만 실제 상황과 유사하게 구성되었습니다.

**session_lean.json**은 절제된 세션입니다. 도구 출력값이 작고, 두 번째 작업 전에 의도적으로 범위를 재설정(scope reset)했습니다. 실제 출력 결과는 다음과 같습니다:

context_tax | session_lean.json | tokenizer: tiktoken o200k_base (exact) | rate=$3.0/Mtok | threshold x12.0
------------------------------------------------------------------------------
  step  1  billed_input=    25t  ####
...

배수는 9.4배로 12배 임계값 미만이므로 종료 코드 0을 반환합니다. 통과(Green)입니다. 데드 웨이트(dead weight, 불필요한 데이터)가 여전히 23.8%라는 점에 주목하세요. 이는 모델이 두 번째 작업에서 더 이상 필요하지 않은 첫 번째 작업의 컨텍스트입니다. 깨끗한 세션이라 할지라도 실제로 내용을 잘라내기 전까지는 데드 웨이트를 수반합니다. 범위 재설정(scope reset)이 배수를 낮게 유지해주었지만, 낭비를 완전히 제로(zero)로 만들지는 못했습니다.

**session_bloated.json**은 고통스러운 사례입니다. 전혀 내용을 잘라내지 않은 12단계의 디버깅 세션입니다. 전체 모듈 덤프, 광범위한 리포지토리 grep, 긴 스택 트레이스(stack trace), 그리고 결정적으로, 그 이후의 모든 단계에서 매번 다시 전송되는 장황한 pip check 의존성 로그가 포함되어 있습니다. 실제 출력 결과는 다음과 같습니다:

context_tax | session_bloated.json | tokenizer: tiktoken o200k_base (exact) | rate=$3.0/Mtok | threshold x12.0
------------------------------------------------------------------------------
  step  1  billed_input=    40t  #
...

42.8배. 임계값(threshold) 초과, exit 1: 빌드 실패. 곡선에서 3단계(step 3)를 주목하세요. 전체 파일 덤프(file dump)로 인해 청구된 입력(billed input)이 72토큰에서 421토큰으로 급증하며, 이후 이어지는 9개의 단계마다 그 증가분을 다시 지불하게 됩니다. 331개의 데드웨이트(dead-weight) 토큰은 대부분 다시 등장하지 않았음에도 페이로드(payload)에 계속 실려 온 pip check 로그(boto3 버전, urllib3 핀(pins) 등)입니다.

두 수치 모두 재현 가능합니다. 저는 shasum -a 256을 사용하여 연속된 두 번의 비대해진(bloated) 실행을 해싱(hashing)했고 동일한 다이제스트(digest)를 얻었습니다. 따라서 이 출력은 결정론적(deterministic)이며, 단 한 번의 실행에서 발생한 우연이 아닙니다.

한 가지 솔직한 정정을 하자면, 저는 시작할 때 배수가 16배 근처일 것이라고 추측했습니다(이는 n(n+1)/2 논의에서 떠도는 수치입니다). 하지만 실제 실행 결과는 42.8배였습니다. 비대해진 피스처(bloated fixture)는 작은 첫 번째 턴(turn)에 큰 파일 덤프를 앞부분에 배치하여 비율을 늘립니다. 교훈은 "16배냐 42배냐"가 아닙니다. 그 수치는 전적으로 여러분의 트랜스크립트(transcript) 형태에 달려 있다는 것이며, 이것이 바로 제 수치를 빌려 쓰는 대신 여러분의 수치를 직접 측정해야 하는 이유입니다.

이에 대해 실제로 해야 할 일

해결책은 생소한 것이 아닙니다. 이 측정기의 목적은 여러분에게 어떤 해결책이 필요한지 알려주고, 그것이 효과가 있었음을 증명하는 것입니다.

  • 작업 간 스코프 재설정 (Scope-reset between tasks): 가벼운 피스처(lean fixture)는 다음과 같이 수행합니다: 다음 작업을 시작하기 전에 이전 작업의 컨텍스트(context)를 버립니다. 여기서 이것은 9.4배와 42.8배의 차이를 만듭니다.
  • 도구 출력물의 불필요한 부분(fat)을 다듬거나 요약하기: 해당 pip check 덤프는 19.3%의 데드웨이트(dead weight)였습니다. 300토큰의 로그를

그다음 미터(meter)를 다시 실행하세요. 만약 배수(multiplier)가 임계값 아래로 다시 떨어진다면, 종료 코드(exit code)는 0으로 바뀌고 여러분의 CI 게이트(CI gate)는 초록색(pass)이 됩니다. 이것이 전체 루프입니다: 측정하고, 깎아내고, 증명하는 것. "믿어주세요, 제가 최적화했습니다"가 아니라, 실제로 움직인 숫자를 보여주는 것입니다. 이 미터는 제가 작성한 AI 에이전트를 위한 사전 실행 게이트 (pre-execution gate for AI agents)의 다른 체크 항목들과 함께 배치됩니다. 철학은 동일합니다. 비용이 청구된 후가 아니라, 지출이 발생하기 전에 빠르게 실패(fail fast)하는 것입니다.

이것이 아닌 것 (과장 광고를 방지하기 위해)

  • 이것은 런타임(runtime)에서 무엇인가를 차단하거나 제한하지 않습니다. 이것은 미터이자 CI 게이트이지, 지출 가드(spend guard)가 아닙니다. 루프 중간에 세션을 중단시키는 런타임 브레이크(runtime brake)를 원하신다면 그것은 다른 도구입니다. 사후에 측정만 하는 대신 일정 기간(window) 동안의 누적 비용을 제한하는 슬라이딩 윈도우 지출 가드 (sliding-window spend guard)를 참조하세요.
  • 이것은 실제 제공업체의 인보이스(invoice)를 계산하지 않습니다. $/session 수치는 복리 효과를 설명하기 위해 여러분이 전달한 요율을 사용합니다. 실제 청구 금액은 캐싱(caching), 배치(batching), 출력 토큰(output tokens), 그리고 벤더(vendor)의 가격 책정에 따라 달라지며, 이 도구는 이 중 어느 것도 모델링하지 않습니다.
  • 데드웨이트(Dead-weight)는 어휘적 휴리스틱(lexical heuristic)이며, 위양성(false positives)이 존재합니다. "용어의 15% 미만이 나중에 다시 나타남"은 "모델이 이것을 사용하기를 멈췄다"는 것에 대한 대리 지표(proxy)일 뿐, 그에 대한 증거는 아닙니다. 모델은 단어를 반복하지 않으면서도 초기 단계의 내용을 암묵적으로 의존했을 수 있습니다. 제가 테스트한 비대한 고정 데이터(bloated fixture)에서 스택 트레이스(stack trace)는 0.16의 중복(overlap) 지점에 도달했는데, 이는 기준선 바로 위였으며 수정 사항이 실제로 해당 내용을 참조했기 때문에 올바르게 유지되었습니다. 퍼센트(%) 수치는 판결이 아니라 확인하러 가라는 플래그(flag)로 취급하세요.
  • 이것은 여러분을 대신해 컨텍스트(context)를 최적화해주지 않습니다. 이것은 세금(tax)이 어디에 있는지 알려줄 뿐입니다. 깎아내는 결정은 여전히 여러분의 몫입니다.

여러분의 긴 세션 중 측정된 최악의 재청구 배수(re-bill multiplier)는 얼마였나요? 실제 트랜스크립트(transcript)에 스크립트를 실행해보고 댓글로 알려주세요. 저는 다양한 형태의 데이터들을 수집하고 있으며, 모든 답글을 읽습니다. 다음 실행에서 나올 숫자를 확인하려면 팔로우하세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0