루프의 경제학: 이터레이션당 비용
루프 한 번의 비용이 고정이 아닌 이유
섹션 제목: “루프 한 번의 비용이 고정이 아닌 이유”단일 LLM 호출의 비용은 간단히 계산된다. 입력 토큰 수 × 입력 단가 + 출력 토큰 수 × 출력 단가. 그런데 에이전트 루프에서는 이 계산이 이터레이션마다 달라진다. 루프가 돌수록 컨텍스트 창에 쌓이는 메시지가 늘어나기 때문이다.
이터레이션 1에서 모델은 시스템 프롬프트 + 사용자 메시지를 처리한다. 이터레이션 2에서는 거기에 이터레이션 1의 모델 응답과 도구 결과가 추가된다. 이터레이션 3에서는 이터레이션 2의 결과까지 포함된다. 이렇게 메시지 히스토리가 누적되면, N번째 이터레이션의 입력 토큰 수는 이전 이터레이션의 토큰을 모두 합산한 것에 현재 새로운 내용을 더한 값이 된다.
┌──────────────────────────────────────────────────────────────────┐│ 이터레이션별 입력 토큰 누적 (단순 예시) │├──────────────────────────────────────────────────────────────────┤│ iter 1: 시스템(500) + 사용자(200) = 700 토큰 ││ iter 2: 이전 700 + 모델응답(150) + 도구결과(300) = 1,150 토큰 ││ iter 3: 이전 1,150 + 모델응답(120) + 도구결과(400) = 1,670 토큰 ││ iter N: 선형이 아닌 가속 증가 ││ ││ → 10번 루프 총 비용 ≠ (이터레이션 1 비용) × 10 ││ 실제로는 훨씬 크다 │└──────────────────────────────────────────────────────────────────┘JSON 누적이 만드는 가속 증가
섹션 제목: “JSON 누적이 만드는 가속 증가”도구 결과가 JSON 형태로 반환될 때 비용 증가는 더욱 두드러진다. 파일 탐색 도구가 디렉터리 구조를 JSON으로 반환하거나, 웹 검색이 긴 본문을 포함한 JSON 응답을 돌려주면, 그 내용 전체가 다음 이터레이션의 컨텍스트로 들어간다. 에이전트가 이미 그 정보를 처리했더라도 컨텍스트에서 지워지지 않는다.
OpenAI의 Codex 루프 분석에서도 이 패턴이 확인된다. 코드 편집 루프에서 도구 결과(파일 내용, 테스트 출력, 에러 메시지)가 이터레이션마다 쌓이면서, 후반 이터레이션의 입력 토큰 수는 초기 이터레이션의 수배에 달하는 경우가 일반적이다. 루프의 경제학은 선형이 아니다.
비용 구조를 명시적으로 모델링하기
섹션 제목: “비용 구조를 명시적으로 모델링하기”개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다.
from dataclasses import dataclass, field
@dataclassclass LoopCostTracker: """이터레이션별 비용과 누적 비용을 추적한다.""" input_price_per_1k: float # 예: 0.003 USD per 1K input tokens output_price_per_1k: float # 예: 0.015 USD per 1K output tokens iterations: list[dict] = field(default_factory=list)
def record(self, input_tokens: int, output_tokens: int) -> float: cost = ( (input_tokens / 1000) * self.input_price_per_1k + (output_tokens / 1000) * self.output_price_per_1k ) self.iterations.append({ "iteration": len(self.iterations) + 1, "input_tokens": input_tokens, "output_tokens": output_tokens, "cost_usd": cost, }) return cost
@property def total_cost(self) -> float: return sum(i["cost_usd"] for i in self.iterations)
@property def total_input_tokens(self) -> int: return sum(i["input_tokens"] for i in self.iterations)이런 트래커를 루프에 포함시키면 “이 작업이 예산 안에 있는가”를 실시간으로 확인할 수 있다. 비용 한도(USD cap)를 종료 조건 중 하나로 쓰는 이유가 여기 있다.
비용 통제 전략
섹션 제목: “비용 통제 전략”루프의 비용을 통제하는 전략은 몇 가지 레벨로 나뉜다.
도구 결과 압축: 도구가 반환하는 내용을 있는 그대로 컨텍스트에 넣지 않고 요약이나 필터링을 거친다. 파일 탐색 결과에서 에이전트가 필요한 정보만 추출해 간결한 형태로 저장한다.
컨텍스트 창 정리: 이미 처리된 도구 결과는 핵심 정보만 남기고 잘라낸다. 5-3 챕터에서 다룬 컨팩션(compaction) 패턴이 이 역할을 한다. 컨텍스트가 임계치에 도달하면 전체를 요약해 새 컨텍스트로 교체한다.
이터레이션 하드 캡: 최대 이터레이션 수를 명시적으로 설정한다. 20번의 이터레이션이면 충분히 복잡한 작업을 처리할 수 있고, 무한 루프로 인한 비용 폭발을 막는다.
비용 기반 종료: 이터레이션마다 누적 비용을 확인해 예산 초과 시 루프를 종료한다.
MAX_COST_USD = 1.0MAX_ITERATIONS = 20tracker = LoopCostTracker(input_price_per_1k=0.003, output_price_per_1k=0.015)
iteration = 0while iteration < MAX_ITERATIONS: response = call_model(messages) cost = tracker.record( response.usage.input_tokens, response.usage.output_tokens )
if tracker.total_cost > MAX_COST_USD: raise BudgetExceededError(f"총 비용 {tracker.total_cost:.4f} USD 초과")
if response.stop_reason == "end_turn": break
# ... 도구 실행 및 메시지 추가 iteration += 1루프 비용 vs 단일 호출 비용의 선택
섹션 제목: “루프 비용 vs 단일 호출 비용의 선택”루프가 항상 단일 호출보다 비싸다는 것을 이해하면, 작업에 따라 전략을 선택할 수 있다. 답이 예측 가능하고 도구 호출이 필요 없는 작업은 단일 호출로 처리하는 것이 훨씬 경제적이다. 외부 세계와 상호작용해야 하고 중간 검증이 필요한 작업에서만 루프의 비용을 정당화할 수 있다.
다음 챕터에서는 루프 비용을 줄이는 가장 효과적인 기술인 **프롬프트 캐싱(prompt caching)**을 다룬다. 특히 매 이터레이션마다 반복되는 시스템 프롬프트와 도구 정의를 캐싱해 비용을 대폭 줄이는 방법을 살펴본다.
참고 자료
- OpenAI — Unrolling the Codex agent loop — 접속 2026-06-30
- Anthropic — Building Effective AI Agents — 접속 2026-06-30