프로덕션 환경에서 AI 에이전트를 운영할 때 가장 심각한 경영·기술적 장벽 중 하나는 **"비용(API Cost)"과 "응답 지연시간(Latency)"**입니다.
사용자의 모든 요율 요청과 에이전트 내부 루프(Agent Loop)의 단계별 추론을 무조건 가장 강력하고 비싼 대형 모델(예: Claude 3.7 Sonnet 또는 GPT-4o)에 몰아주면, 사용자 수가 조금만 증가해도 월 API 청구액이 기하급수적으로 폭등합니다. 반대로 모든 추론을 경량 모델(예: Claude 3.5 Haiku, GPT-4o-mini)로 처리하면 복잡한 도구 연동(Tool Calling)이나 추론 단계에서 환각(Hallucination) 및 스키마 위반 오류가 빈발하게 됩니다.
이 글에서는 이 딜레마를 해결하기 위한 Model Cascade & Dynamic Routing 아키텍처의 개념부터 실전 Classifier 구현, 그리고 폴백(Fallback) 안전장치까지 단계별로 다룹니다.
1. 단일 모델 병목과 Dynamic Routing의 필요성
전통적인 AI 에이전트 시스템은 모든 입력에 동일한 LLM 백엔드를 연결합니다. 하지만 실제 사용자의 입력을 분석해 보면 다음과 같이 난이도 스펙트럼이 매우 다양합니다.
- 단순 조회 및 분류 (전체의 약 50%): "내 주문 배송 상태 확인해줘", "이 문장의 감정을 긍정/부정으로 분류해줘"
- 조건부 정보 추출 및 단순 Tool Calling (전체의 약 35%): "지난달 결제 내역 중 10만 원 이상 건을 조회해서 CSV 포맷으로 정리해줘"
- 고난도 다단계 추론 및 코드 작성 (전체의 약 15%): "현재 백엔드 에러 로그를 분석해서 메모리 누수 지점을 파악하고 수정 PR 디프를 작성해줘"
모든 요청에 대형 모델을 적용하면 85%의 요청에 오버스펙 API 비용을 지불하게 되며, 단순 응답에까지 2–3초 이상의 Latency가 소요됩니다.
Dynamic Routing 시스템은 입력을 수신한 즉시 경량 Router Classifier를 거쳐 **"최소한의 비용과 가장 빠른 시간 내에 문제를 해결할 수 있는 최적 모델"**로 작업을 할당합니다.
2. Dynamic Routing & Cascade 아키텍처 패턴
Model Routing 전략은 크게 두 가지 접근법으로 나뉩니다.
A. Intent/Complexity-based Classifier Routing
입력 텍스트의 특성, 필요 도구(Tools)의 복잡도, 기대 출력 형식을 경량 분류기(Small Model / Rule-based / Embeddings)가 사전에 분류하여 최적 모델로 직행(Direct Directing)시킵니다.
[User Request] │ ▼┌───────────────────────────────┐│ Router Classifier ││ (Rule-based / Small LLM / Bert)│└──────────────┬────────────────┘ │ ┌─────────┼─────────┐ │ Low │ Med │ High Complexity ▼ ▼ ▼┌─────────┐┌─────────┐┌─────────┐│ Small ││ Medium ││ Large ││ Model ││ Model ││ Model │└─────────┘└─────────┘└─────────┘
B. Fallback Cascade (Sequential Escalation)
먼저 빠른 경량 모델(Small Model)에게 답 생성을 시도하게 하고, 결과의 **신뢰도 스코어(Confidence Score)**나 스키마 검증/Tool Execution 성공 여부를 체크합니다. 만약 검증에 실패하면 상위 모델(Large Model)로 연쇄 승격(Escalation)시킵니다.
3. 실전 Python/TypeScript 구현 예시
다음은 Python 기반으로 쿼리 복잡도 평가와 Structured Output 기반의 Dynamic Model Router를 구현한 실전 코드 예시입니다.
import osfrom enum import Enumfrom pydantic import BaseModel, Fieldfrom openai import OpenAI class ModelTier(str, Enum): FAST = "gpt-4o-mini" # 단순 조회/클래스 BALANCED = "claude-3-5-haiku" # 일반 도구 사용 및 추출 ADVANCED = "claude-3-7-sonnet" # 복잡 추론 및 코딩 class RouteDecision(BaseModel): selected_tier: ModelTier = Field(description="선택된 모델 티어") complexity_score: float = Field(description="0.0–1.0 사이의 복잡도 점수") reasoning: str = Field(description="라우팅 결정 이유") class DynamicModelRouter: def __init__(self, api_key: str): self.client = OpenAI(api_key=api_key) def route_query(self, user_prompt: str, available_tools: list[str]) -> RouteDecision: router_system_prompt = ( "당신은 AI 에이전트 워크플로우의 Request Router입니다. " "사용자의 질문과 사용할 도구 목록을 분석하여 최소 비용으로 정확하게 처리 가능한 모델 티어를 선정하세요." ) # 경량 모델을 통한 Fast Classification (Latency < 200ms) completion = self.client.beta.chat.completions.parse( model="gpt-4o-mini", messages=[ {"role": "system", "content": router_system_prompt}, {"role": "user", "content": f"Query: {user_prompt}\nTools: {available_tools}"} ], response_format=RouteDecision ) return completion.choices[0].message.parsed # 사용 예시router = DynamicModelRouter(api_key=os.getenv("OPENAI_API_KEY"))decision = router.route_query( user_prompt="지난주 매출 데이터 요약해서 그래프 데이터 JSON으로 뽑아줘", available_tools=["fetch_sales_db", "format_json"]) print(f"Selected Tier: {decision.selected_tier.value}")print(f"Complexity: {decision.complexity_score}")print(f"Reason: {decision.reasoning}")
4. 절감 폭 산정 방법과 주의점
라우팅의 절감 폭은 서비스마다 다르므로, 측정치를 인용하기보다 직접 계산하는 방법을 정리합니다. 필요한 입력은 세 가지뿐입니다. 난이도 구간별 요청 비율, 구간별로 배정할 모델의 100만 토큰당 단가, 그리고 턴당 평균 입출력 토큰 수입니다.
절감률 = 1 - Σ(구간 비율 × 구간 모델 단가) / (대형 모델 단가)
1절의 분포(단순 50% / 중간 35% / 고난도 15%)를 가정하고, 경량 모델 단가가 대형 모델의 1/15, 중간 모델이 1/5라면 산술적으로 약 70% 절감이 나옵니다. 실제 값은 세 입력을 자기 서비스 로그에서 뽑아 대입해야 의미가 있습니다. 이때 라우터 자체의 호출 비용과 Escalation 재시도 비용을 반드시 분모가 아니라 분자에 더해야 실제와 맞습니다. 이 계산을 생략하고 도입하면 라우팅 후에도 비용이 그대로인 경우가 나옵니다.
Latency도 같은 방식으로 구간별 가중 평균을 내되, P99는 Escalation이 발생하는 요청이 지배하므로 경량 모델 비중이 높아져도 크게 개선되지 않는다는 점을 감안해야 합니다.
품질 회귀 여부는 감으로 판단하지 말고 AI 에이전트 평가와 지표에서 정리한 오프라인 평가셋을 라우팅 적용 전후로 동일하게 돌려 비교하세요. Pass Rate가 유의미하게 떨어지면 구간 경계를 올리는 것이 먼저입니다.
주의해야 할 운영 트랩
- Router 오버헤드 최소화: 라우터 자체가 비싸거나 느리면 라우팅의 이점이 상쇄됩니다. Router Classifier는 반드시
gpt-4o-mini,claude-3-5-haiku, 또는 로컬 Fine-tuned Small Model(e.g., Llama 3.2 3B)을 사용해야 합니다. - Context Passing 유지: Fallback Escalation이 발생할 때 이전 단계 실패 기록과 가드레일 에러 메시지를 상위 모델에 전달하여 동일 실패가 반복되지 않도록 해야 합니다.
- 라우팅 전에 캐시부터 확인: 같은 질문이 반복되는 서비스라면 라우터를 붙이기 전에 LLM 응답 캐시 적용 방법을 먼저 검토하는 편이 비용 대비 효과가 큽니다. 캐시 히트는 라우팅과 달리 모델 호출 자체를 없앱니다.
결론 및 다음 단계
Model Cascade & Dynamic Routing은 AI 에이전트를 PoC 단계에서 경제적으로 지속 가능한 프로덕션 서비스로 전환하는 아키텍처입니다. 다만 순서가 중요합니다. 캐시 → 프롬프트/토큰 예산 → 라우팅 순으로 검토해야 라우터를 붙이고도 비용이 그대로인 상황을 피할 수 있습니다.
이어서 볼 글:
- prompt caching으로 토큰 예산 통제하기 — 라우팅보다 먼저 손대야 하는 비용 항목
- Structured Outputs로 응답 스키마 고정하기 — 라우터 판정 결과를 스키마로 강제하는 방법
- AI 에이전트 관측과 트레이스 — 어떤 구간이 어느 모델로 갔는지 남기는 방법

댓글
GitHub 계정으로 로그인하면 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions를 통해 운영됩니다.