AI 에이전트 LLM 비용이 폭증하는 구조와 60~80% 절감 전략
에이전트형 AI 시스템을 프로덕션에 올려본 경험이 있다면, 한 번쯤은 청구서를 보고 멈칫했던 적이 있을 겁니다. "분명히 프롬프트 몇 번 날린 것뿐인데, 왜 이렇게 많이 나왔지?" 저도 처음에는 단순히 토큰 수를 줄이면 된다고 생각했는데, 실제로는 그게 아니었습니다.
문제의 핵심은 에이전트형 워크플로의 비용이 단순 토큰 소비량에 비례하지 않는다는 데 있습니다. 에이전트는 사용자 요청 하나를 처리하기 위해 내부적으로 Planning(계획 수립) → Tool Calling(도구 호출) → Self-correction(결과 검증) → Synthesis(최종 답변 생성) 순서로 여러 번의 LLM 호출을 연쇄 실행합니다. 이 실행 경로(trajectory)가 달라질 때마다 비용이 비선형적으로 증가합니다. GPT-4o 기준으로, 단순 Q&A는 약 $0.001~$0.004지만 멀티에이전트 시스템은 같은 태스크에 $0.3~$1.5 이상을 소비하기도 합니다.
이 글에서는 동적 워크플로 비용 구조가 왜 이렇게 설계되는지를 뜯어보고, 실제로 비용을 60~80% 절감한 사례들에서 어떤 원리가 작동했는지를 살펴봅니다. "달러/태스크"라는 새로운 비용 지표와 모델 캐스케이드 라우팅을 이해하고 나면, 에이전트 시스템의 예산을 설계하는 방식이 완전히 달라집니다.
핵심 개념
왜 예상보다 10배 이상 비용이 나오는가 — 비용 구조의 세 레이어
에이전트 워크플로의 비용은 크게 세 레이어로 분해됩니다.
| 변수 | 설명 | 비중 |
|---|---|---|
| 입력 토큰 | 매 호출마다 누적되는 컨텍스트 + 시스템 프롬프트 반복 | 전체의 60~70% |
| 출력 토큰 | 추론 결과 + 도구 호출 스펙 생성 | 입력 대비 단가 5배 |
| 오케스트레이션 오버헤드 | 에이전트 루프, 재시도, 멀티에이전트 조율 | 단일 호출 대비 4~15배 |
여기서 많은 분들이 놓치는 부분이 입력 토큰의 반복 비용입니다. 2,000 토큰짜리 시스템 프롬프트를 가진 에이전트가 1020턴을 돌면, 그 프롬프트만으로 20,00040,000 토큰이 순수하게 반복 소비됩니다. 의미 있는 처리는 없는데 돈은 나가는 구조죠.
오케스트레이션 오버헤드(Orchestration Overhead): 에이전트가 다음 행동을 결정하고, 도구를 선택하고, 결과를 평가하는 과정 자체가 LLM 호출을 소비합니다. 단순히 "도구를 한 번 쓴다"가 아니라, "어떤 도구를 왜 써야 하는지 판단하는 것"에도 토큰이 들어갑니다.
에이전트 수가 늘어날수록 비용이 폭발하는 이유
AutoGen GroupChat에서 4개 에이전트가 5라운드 토론을 수행하면 단순 계산으로만 최소 20회의 LLM 호출이 발생합니다. 실제로는 각 라운드마다 다음 발화자를 결정하는 speaker selection 호출이 추가되어 그보다 더 많아집니다. 에이전트 수가 선형으로 늘어날 때 조율 비용은 지수적으로 커질 수 있습니다.
import autogen
# 종료 조건 없이 설정하면 비용이 조용히 폭증합니다
groupchat = autogen.GroupChat(
agents=[user_proxy, planner, coder, reviewer],
messages=[],
# max_round 미설정 → 루프가 자동 종료되지 않습니다
)
# 권장: 아키텍처 수준에서 명시적으로 상한을 선언합니다
groupchat_safe = autogen.GroupChat(
agents=[user_proxy, planner, coder, reviewer],
messages=[],
max_round=10,
)실 운영 사례에서 라운드 상한을 설정하지 않은 GroupChat은 단일 호출 워크플로 대비 3~5배 비용이 발생하는 것이 확인됐습니다.
토큰 단가 대신 "달러/태스크"로 생각해야 하는 이유
원시 토큰 단가($/M tokens)만 보다가는 함정에 빠집니다. 솔직히 말하면, 저도 초기에 "GPT-3.5가 GPT-4보다 20배 싸니까 무조건 3.5로 돌리자"는 생각을 했었는데, 실패율이 높아지면서 재시도 비용이 폭증해 오히려 더 비싸게 나온 경험이 있습니다.
$/successful workflow step: 성공한 워크플로 스텝당 비용. 실패한 스텝의 재시도 비용을 포함해야 진짜 TCO(Total Cost of Ownership)가 보입니다. 단순 $/M tokens 지표는 실패율을 숨깁니다.
Galileo의 Agent Leaderboard v2(2025년 중반 기준)가 평균 액션 완료율, 도구 선택 품질, 세션당 평균 비용을 통합 측정하기 시작한 것도 이런 맥락입니다.
모델 캐스케이드 라우팅 — 복잡도에 따라 모델을 자동 분기하는 방식
태스크를 복잡도에 따라 모델 티어로 분기하는 캐스케이드 아키텍처는 현재 가장 효과적인 비용 최적화 패턴입니다. 저가 모델($0.10~$1/M 토큰)과 프런티어 모델($15~$30+/M 토큰) 사이에는 150배 이상의 단가 차이가 있습니다.
from litellm import completion
def cascade_route(task: str, context: dict) -> str:
complexity = estimate_complexity(task) # 0.0 ~ 1.0
if complexity < 0.3:
model = "claude-haiku-4-5" # 단순 분류, 형식 변환, 요약
elif complexity < 0.7:
model = "claude-sonnet-4-6" # 코드 생성, 멀티스텝 추론
else:
model = "claude-opus-4-8" # 복잡한 아키텍처 설계, 모호한 요구사항 분석
response = completion(
model=model,
messages=[{"role": "user", "content": task}],
metadata={"cost_tag": f"complexity_{complexity:.1f}"} # 비용 추적용
)
return response.choices[0].message.content
estimate_complexity의 구현 방식이 라우팅 품질 전체를 결정합니다. 토큰 수나 키워드 기반의 단순한 방식으로 시작할 수도 있고, 경량 분류 모델을 별도로 두는 방식도 있습니다. 어떤 기준으로 0~1 스케일을 정의하느냐가 이 구조의 핵심입니다.
단순 라우팅(복잡도 무관하게 하나의 모델 사용) 대비, 캐스케이드 라우팅은 복잡한 벤치마크에서 14% 추가 성능 향상과 60~80% 비용 절감을 동시에 달성합니다.
실전 적용
예시 1: 에이전트 궤적 압축으로 입력 토큰 40~60% 줄이기
arXiv의 AgentDiet(2509.23586) 연구에서 확인된 패턴입니다. 에이전트가 도구 호출 결과를 그대로 누적하면, 후반부 호출에서는 초반의 중간 결과물이 컨텍스트를 가득 채웁니다. 이미 끝난 서브태스크의 세부 내용은 최종 답변에 필요 없는 경우가 많습니다.
class TrajectoryCompressor:
def __init__(self, max_tokens: int = 4000):
self.max_tokens = max_tokens
def compress(self, trajectory: list[dict]) -> list[dict]:
compressed = []
for step in trajectory:
if step["status"] == "completed":
# step["summary"]는 별도 LLM 호출로 미리 생성해둔 요약입니다.
# 요약 생성 비용이 따로 발생하지만, 이후 호출에서 절감하는 토큰이
# 훨씬 크기 때문에 전체 비용은 줄어듭니다.
compressed.append({
"role": "assistant",
"content": f"[완료] {step['summary']}",
"status": "compressed"
})
else:
compressed.append(step)
return compressed| 지표 | 압축 전 | 압축 후 | 절감율 |
|---|---|---|---|
| 입력 토큰 | 100% | 40~60% | 39.9~59.7% |
| 총 연산 비용 | 100% | 64~79% | 21.1~35.9% |
| 에이전트 성능 | 기준 | 동등 | 손실 없음 |
예시 2: 플랜 캐싱으로 반복 플래닝 비용 절반으로
같은 종류의 태스크를 반복 처리한다면—예를 들어 PR 리뷰 에이전트를 하루 수백 번 돌린다면—플랜 캐싱(Agentic Plan Caching, arXiv 2506.14852)이 가장 빠른 선택입니다. 이미 성공한 플래닝 결과를 "테스트 타임 메모리"로 재사용하는 방식입니다.
실제로 이 패턴을 처음 적용했을 때, 생각보다 간단한 구현으로 바로 효과를 볼 수 있었습니다. 핵심은 태스크 유형과 컨텍스트 구조를 기준으로 캐시 키를 만드는 것입니다.
import hashlib
import json
class AgentPlanCache:
def __init__(self):
self._cache: dict[str, dict] = {}
def _task_fingerprint(self, task_type: str, context_keys: list[str]) -> str:
payload = json.dumps({"type": task_type, "keys": sorted(context_keys)})
return hashlib.sha256(payload.encode()).hexdigest()[:16]
def get_plan(self, task_type: str, context_keys: list[str]) -> dict | None:
key = self._task_fingerprint(task_type, context_keys)
return self._cache.get(key)
def store_plan(self, task_type: str, context_keys: list[str], plan: dict, score: float):
if score >= 0.95: # 성공률 95% 이상인 플랜만 캐싱
key = self._task_fingerprint(task_type, context_keys)
self._cache[key] = {"plan": plan, "score": score}
# 결과: 서빙 비용 50.31% 절감, 레이턴시 27.28% 절감, 최적 성능의 96.61% 유지예시 3: 프롬프트 캐싱 전략 — 어떤 부분을 캐싱하느냐가 핵심
arXiv 2601.06007의 비교 실험에서 세 가지 전략을 테스트했습니다. 세 전략 모두 동일한 API 호출 구조로 비교해볼 수 있습니다.
import anthropic
client = anthropic.Anthropic()
# 전략 1: 전체 컨텍스트 캐싱
# 구현이 단순하지만, 컨텍스트 일부가 바뀌면 캐시 전체가 무효화됩니다
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system=[{
"type": "text",
"text": full_context,
"cache_control": {"type": "ephemeral"}
}],
messages=[{"role": "user", "content": user_task}]
)
# 전략 2: 시스템 프롬프트만 캐싱 — 가장 범용적이고 대부분의 상황에 적합합니다
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system=[{
"type": "text",
"text": system_prompt, # 고정된 시스템 프롬프트만 캐싱
"cache_control": {"type": "ephemeral"}
}],
messages=[{"role": "user", "content": user_task}] # 동적 부분은 캐싱 제외
)
# 전략 3: 동적 도구 결과 제외 캐싱
# 시스템 프롬프트와 고정 지침은 캐싱하고, 자주 바뀌는 도구 호출 결과는 제외합니다
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system=[{
"type": "text",
"text": system_prompt,
"cache_control": {"type": "ephemeral"}
}],
messages=[{
"role": "user",
"content": [
{"type": "text", "text": static_instructions, "cache_control": {"type": "ephemeral"}},
{"type": "text", "text": dynamic_tool_results} # 캐싱 범위에서 제외
]
}]
)| 전략 | 비용 절감 | 적합한 상황 |
|---|---|---|
| 전체 컨텍스트 캐싱 | 41~55% | 정적 태스크, 배치 처리 |
| 시스템 프롬프트만 캐싱 | 55~70% | 범용 에이전트, 대부분의 상황 |
| 동적 도구 결과 제외 캐싱 | 65~80% | 멀티턴 에이전트, 도구 의존 워크플로 |
세 전략 모두 전 공급사(Anthropic, OpenAI, Google)에서 41~80% 비용 절감이 일관되게 측정됐습니다.
예시 4: LangGraph로 상태 기반 비용 제어
LangGraph의 상태 머신 구조는 반복 LLM 호출을 40~50% 줄이는 데 유리합니다. 이미 처리된 상태를 명시적으로 추적하기 때문에 불필요한 재평가를 방지합니다. 특히 토큰 예산을 상태의 일부로 다루면, 비용 제어 로직이 워크플로 그래프 안에 자연스럽게 녹아들어갑니다.
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator
class AgentState(TypedDict):
task: str
plan: list[str]
completed_steps: Annotated[list[str], operator.add]
token_budget: int # 비용 상한을 상태로 관리
tokens_used: int
def check_budget(state: AgentState) -> str:
if state["tokens_used"] >= state["token_budget"]:
return "budget_exceeded"
if not state["plan"]:
return END
return "execute_step"
builder = StateGraph(AgentState)
builder.add_node("planner", plan_task)
builder.add_node("executor", execute_step)
builder.add_node("synthesizer", synthesize_result)
# 비용 제어 분기를 그래프 구조에 내장합니다
builder.add_conditional_edges("executor", check_budget, {
"execute_step": "executor",
"budget_exceeded": "synthesizer", # 예산 초과 시 중간 결과로 종료
END: "synthesizer"
})장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 비용의 복잡도 비례화 | 정적 워크플로의 비용 ∝ 볼륨 구조 대신 비용 ∝ 복잡도로 재편 — 단순 태스크의 과금 낭비 제거 |
| 모델 계층 활용 | 150배 단가 차이 활용으로 품질 손실 없이 60~80% 절감 가능 |
| 도메인 특화 효율 | 범용 LLM 대비 도메인 특화 에이전트는 더 높은 정확도를 유지하면서 비용은 4.4~10.8배 낮은 것으로 집계됩니다 |
| 사전 비용 제어 | 게이트웨이 단 정책 적용으로 사후 청구서 충격 방지 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 시스템 프롬프트 반복 | 20턴 루프에서 2K 토큰 프롬프트가 40K 토큰 낭비 | 프롬프트 캐싱 또는 경량화 |
| 컨텍스트 폭증 | 2 |
초기 설계에 동적 절단 정책 내장 |
| 침묵적 비용 증가 | 관측 도구 없이는 탐지 지연 | AgentOps/Langfuse 도입 (월 200~1,500달러) |
| 멀티에이전트 오버헤드 | 에이전트 수 증가 시 조율 비용 4~15배 배수 증가 | 에이전트 수 결정 시 비용 모델 함께 고려 |
| 라우팅 오분류 리스크 | 잘못된 모델 티어 분류 → 품질 저하 또는 불필요한 고비용 | 분류 모델 평가 지표를 $/successful step 기준으로 설정 |
KV 캐시(Key-Value Cache): 트랜스포머 모델이 이전에 처리한 토큰의 계산 결과를 저장해 동일 컨텍스트 재계산을 생략하는 메커니즘. vLLM, SGLang 같은 추론 엔진은 KV 캐시 최적화로 반복 컨텍스트 비용을 인프라 수준에서 줄입니다.
TCO(Total Cost of Ownership): 단순 토큰 비용뿐 아니라 재시도 비용, 오케스트레이션 오버헤드, 관측가능성 도구 비용, 엔지니어링 시간을 모두 포함한 총 비용.
실무에서 가장 흔한 실수
- 단가만 비교하고 성공률을 무시: 저렴한 모델로 라우팅했지만 실패율이 높아져 재시도 비용이 프런티어 모델 직접 호출보다 더 비싸지는 경우가 생각보다 흔합니다.
- 컨텍스트 절단 정책을 나중으로 미루기: "지금은 테스트니까 괜찮다"는 생각으로 시작하면, 프로덕션에 올라간 후 2~3주 만에 컨텍스트가 100K를 넘기고 레이턴시와 비용이 동시에 폭증하는 상황을 맞이하게 됩니다.
- 관측가능성 없이 최적화 시도: 어디서 토큰이 새는지 보이지 않는 상태에서 캐싱을 추가하거나 모델을 교체하면, 효과가 있는지 없는지조차 알 수 없습니다. Langfuse나 AgentOps 같은 도구는 비용이 아니라 투자입니다.
마치며
에이전트 워크플로의 비용 문제는 "더 싼 모델 쓰기"가 아니라, 실행 경로 전체에 비용 제어 구조를 설계하는 문제입니다.
지금 바로 시작해볼 수 있는 3단계:
- 현재 워크플로의 단계별 토큰 소비를 측정해 보시길 권합니다. LangChain/LangGraph를 사용하고 있다면
pip install langfuse로 설치하고 콜백 한 줄만 추가하면 어느 스텝에서 토큰이 가장 많이 나가는지 바로 확인할 수 있습니다. 그 스택이 아니더라도 AgentOps나 OpenTelemetry export로 동일한 가시성을 확보할 수 있습니다. - 시스템 프롬프트 캐싱부터 적용해 보시길 권합니다. Anthropic API라면
cache_control: {"type": "ephemeral"}헤더 추가만으로 반복 시스템 프롬프트 비용을 55~70% 줄일 수 있습니다. 구현 난이도가 낮고 효과가 즉각적입니다. - 성능 지표를 "정확도"에서 "정확도/달러"로 바꿔보시길 권합니다. 기존에 모델 평가 시 정확도만 보고 있었다면, 같은 데이터셋에 비용을 함께 측정해 $/successful step 기준으로 비교해 보시면 직관과 다른 최적 모델 조합을 발견하는 경우가 많습니다.
이 세 가지만 적용해도 대부분의 팀에서 첫 달 안에 의미 있는 변화를 체감할 수 있습니다. 북마크해 두고 다음 스프린트에서 하나씩 적용해 보시는 걸 권해드립니다.
참고 자료
- Reducing Cost of LLM Agents with Trajectory Reduction | arXiv
- Don't Break the Cache: Evaluation of Prompt Caching for Long-Horizon Agentic Tasks | arXiv
- Agentic Plan Caching: Test-Time Memory for Fast and Cost-Efficient LLM Agents | arXiv
- LLM Cost Optimization for Agent Workflows: A Practical Guide | DEV Community
- The Hidden Costs of Agentic AI: Why 40% of Projects Fail Before Production | Galileo
- AI Cost Optimization: A Practical Guide for 2026 | TrueFoundry
- Dynamic Routing for Multi-Agent AI Workflows | TDCommons
- Efficient LLM Serving for Agentic Workflows: A Data Systems Perspective | arXiv
- Agentic AI Costs More Than You Budgeted | DataRobot