AI 에이전트를 운영할 때는 API 비용과 응답 지연시간(Latency)을 함께 관리해야 합니다.
사용자의 모든 요청과 에이전트 내부 루프(Agent Loop)를 대형 모델(예: Claude 3.7 Sonnet 또는 GPT-4o)로 처리하면 요청 증가에 따라 API 비용도 커집니다. 경량 모델(예: Claude 3.5 Haiku, GPT-4o-mini)로 바꾸면 비용을 줄일 수 있지만, 복잡한 도구 호출과 추론에서 필요한 품질을 충족하는지 확인해야 합니다.
이 글에서는 Model Cascade와 Dynamic Routing의 차이를 살펴보고, 분류기 코드와 비용 계산 방법을 정리합니다.
먼저 수치의 성격을 구분해야 합니다. 이 글의 ‘약 70% 절감’ 계산은 실제 서비스에서 측정한 성과가 아닙니다. 4절에서 임의의 요청 분포와 단가 비율, 추가 호출 비용을 넣어 계산한 예시입니다. 실제 절감률과 지연시간은 해당 서비스의 호출 로그와 요금 자료로 검증해야 합니다.
1. 단일 모델 병목과 Dynamic Routing의 필요성
모든 입력을 하나의 대형 모델에 보내는 구조에서는 작업 난이도가 낮아도 대형 모델 단가를 지불합니다. 아래 비율은 특정 서비스 로그에서 관측한 값이 아니라, 계산 과정을 설명하기 위해 만든 가정입니다.
- 단순 조회 및 분류 (가정: 50%): "내 주문 배송 상태 확인해줘", "이 문장의 감정을 긍정/부정으로 분류해줘"
- 조건부 정보 추출 및 Tool Calling (가정: 35%): "지난달 결제 내역 중 10만 원 이상 건을 조회해서 CSV로 정리해줘"
- 다단계 추론 및 코드 수정 (가정: 15%): "백엔드 에러 로그에서 메모리 누수 지점을 찾아 수정 PR을 만들어줘"
이 분포라면 요청의 85%를 더 저렴한 모델로 보낼 후보로 분류할 수 있습니다. 하지만 실제로 그 비율이 안전하게 처리되는지는 작업별 정답률과 도구 실행 결과를 평가해야 합니다. 모델마다 지연시간도 다르므로 단순 요청이 반드시 몇 초 더 빠르다고 단정할 수 없습니다.
Dynamic Routing은 경량 Router Classifier로 입력을 분류한 뒤, 작업에 맞는 모델에 배정합니다. 그림의 모델명은 계층을 구분하는 예시입니다. 실제 도입 시에는 현재 모델 카탈로그와 요금표로 후보를 비교해야 합니다.
2. Dynamic Routing & Cascade 아키텍처 패턴
Model Routing 전략은 크게 두 가지 접근법으로 나뉩니다.
A. Intent/Complexity-based Classifier Routing
입력 텍스트의 특성, 필요 도구의 복잡도와 기대 출력 형식을 분류기(규칙, 경량 모델, 임베딩 등)로 판단하고 선택한 모델로 바로 전달합니다. 분류 결과가 맞는지는 별도 정답 데이터셋으로 측정해야 합니다.
[User Request] │ ▼┌───────────────────────────────┐│ Router Classifier ││ (Rule-based / Small LLM / Bert)│└──────────────┬────────────────┘ │ ┌─────────┼─────────┐ │ Low │ Med │ High Complexity ▼ ▼ ▼┌─────────┐┌─────────┐┌─────────┐│ Small ││ Medium ││ Large ││ Model ││ Model ││ Model │└─────────┘└─────────┘└─────────┘
B. Fallback Cascade (Sequential Escalation)
먼저 경량 모델이 답을 생성하고 스키마·도구 실행 결과·업무별 검사 조건을 확인합니다. 검증에 실패하면 상위 모델로 재요청합니다. 모델이 스스로 출력한 신뢰도 숫자만으로 성공을 판정해서는 안 됩니다. 재시도는 비용과 지연시간을 늘릴 수 있으므로 실패율도 함께 기록해야 합니다.
3. Python으로 분류 결과를 반환하는 코드 예시
아래 코드는 OpenAI Python SDK와 Pydantic으로 요청의 난이도 등급을 선택하는 분류 단계만 보여줍니다. 반환값은 공급자별 모델 식별자가 아니라 서비스가 해석할 등급입니다. 실제 모델 호출, 공급자 API 연결, 실패 후 승격, 품질 평가와 비용 집계는 이 코드에 구현되어 있지 않습니다. 예시는 실제 API 호출로 검증한 결과가 아닙니다.
from enum import Enumfrom pydantic import BaseModel, Fieldfrom openai import OpenAI class ModelTier(str, Enum): FAST = "fast" # 서비스에서 해석하는 등급 BALANCED = "balanced" ADVANCED = "advanced" class RouteDecision(BaseModel): selected_tier: ModelTier = Field(description="선택된 모델 티어") complexity_score: float = Field(ge=0, le=1, description="추정 난이도 0.0~1.0") reasoning: str = Field(description="라우팅 결정 이유") class DynamicModelRouter: def __init__(self, client: OpenAI, classifier_model: str): self.client = client self.classifier_model = classifier_model def route_query(self, user_prompt: str, available_tools: list[str]) -> RouteDecision: router_system_prompt = ( "당신은 AI 에이전트 워크플로우의 Request Router입니다. " "사용자의 질문과 사용할 도구 목록을 분석하여 최소 비용으로 정확하게 처리 가능한 모델 티어를 선정하세요." ) # 실제 분류 정확도·지연시간은 서비스 데이터로 측정해야 한다. completion = self.client.beta.chat.completions.parse( model=self.classifier_model, messages=[ {"role": "system", "content": router_system_prompt}, {"role": "user", "content": f"Query: {user_prompt}\nTools: {available_tools}"} ], response_format=RouteDecision ) parsed = completion.choices[0].message.parsed if parsed is None: raise RuntimeError("분류 결과가 구조화된 스키마로 반환되지 않았습니다.") return parsed # 사용 예시: 환경변수 OPENAI_API_KEY를 설정한다.router = DynamicModelRouter(client=OpenAI(), classifier_model="gpt-4o-mini")decision = router.route_query( user_prompt="지난주 매출 데이터 요약해서 그래프 데이터 JSON으로 뽑아줘", available_tools=["fetch_sales_db", "format_json"]) print(f"Tier: {decision.selected_tier.value}")print(f"Complexity: {decision.complexity_score}")print(f"Reason: {decision.reasoning}")# 실제 모델과 공급자 선택은 별도의 검증된 설정·디스패처에서 처리한다.
4. 절감 폭 산정 방법과 주의점
절감률은 모델 가격만으로 정해지지 않습니다. 실제로는 난이도별 요청 비중, 모델별 입력·출력 토큰 수와 단가, 분류기 호출, 실패 후 재시도 비용을 함께 계산해야 합니다. 다음은 동일한 요청 묶음을 대형 모델에 전부 보냈을 때를 기준으로 한 비교입니다.
기준 비용 = Σ(요청별 입력 토큰 × 기준 모델 입력 단가 + 출력 토큰 × 기준 모델 출력 단가)라우팅 비용 = Σ(요청별 선택 모델 입력·출력 비용) + 분류기 비용 + 실패 재시도 비용절감률(%) = (기준 비용 - 라우팅 비용) / 기준 비용 × 100
동일한 입력·출력 토큰 사용량을 가정하고, 요청 비중이 단순 50%, 중간 35%, 고난도 15%라고 해봅시다. 대형 모델의 요청당 비용을 1로 놓고 경량 모델은 1/15, 중간 모델은 1/5로 두면, 모델 호출 비용의 가중 평균은 0.5/15 + 0.35/5 + 0.15 = 약 0.253입니다. 추가 비용이 전혀 없다면 이 가정의 절감률은 약 74.7%입니다.
여기에서 **분류기 호출과 실패 재시도 비용을 기준 비용의 5%**로 가정하면 전체 비용은 약 0.303이 되고 절감률은 **약 69.7%, 반올림하면 70%**입니다. 이것이 이 글에 제시한 약 70%의 가정 계산 근거입니다. 어느 비율도 실서비스의 측정 결과나 업계 평균을 뜻하지 않습니다.
실제 서비스에서는 두 경로에 동일한 요청들을 보내고 입력·출력 토큰, 단가, 라우터 추가 호출, 승격 빈도, 실패율을 수집해야 합니다. 토큰 분포가 등급별로 다르거나 재시도가 빈번하면 실제 절감률은 크게 달라질 수 있습니다.
응답 시간도 평균뿐 아니라 P95/P99를 각각 측정해야 합니다. 특히 승격이 발생하는 요청은 두 번 이상 모델을 호출하므로 꼬리 지연시간을 늘릴 수 있습니다. 라우터 자체의 처리 시간도 포함해야 합니다.
품질 회귀 여부는 감으로 판단하지 말고 AI 에이전트 평가와 지표에서 정리한 오프라인 평가셋을 라우팅 적용 전후로 동일하게 돌려 비교하세요. Pass Rate가 유의미하게 떨어지면 구간 경계를 올리는 것이 먼저입니다.
주의해야 할 운영 트랩
- Router 오버헤드 측정: 라우터 자체가 비싸거나 느리면 절감 효과가 상쇄됩니다. 고정 모델명을 정답으로 두지 말고 후보 분류기의 비용·정확도·P95 지연시간을 동일 요청 집합에서 비교하세요.
- Context Passing 유지: 승격이 발생하면 검증에 필요한 이전 단계의 결과와 실패 이유를 전달하되, 비밀 정보나 도구 결과 전체를 무조건 다시 전달하지 말고 최소 권한·정보 노출 범위를 지키세요.
- 라우팅 전에 캐시부터 확인: 같은 질문이 반복되는 서비스라면 라우터를 붙이기 전에 LLM 응답 캐시 적용 방법을 먼저 검토하는 편이 비용 대비 효과가 큽니다. 캐시 히트는 라우팅과 달리 모델 호출 자체를 없앱니다.
결론 및 다음 단계
Dynamic Routing은 요청에 맞는 모델을 처음부터 선택하고, Cascade는 검증 실패 후 더 강한 모델로 단계적으로 넘기는 패턴입니다. 두 방식을 함께 사용할 수도 있지만, 70% 절감은 보장된 결과가 아닌 계산 예시입니다. 적용 전에는 캐시 적중률·토큰 예산·분류 정확도와 재시도 비용을 비교해야 합니다.
이어서 볼 글:
- prompt caching으로 토큰 예산 통제하기 — 라우팅보다 먼저 손대야 하는 비용 항목
- Structured Outputs로 응답 스키마 고정하기 — 라우터 판정 결과를 스키마로 강제하는 방법
- AI 에이전트 관측과 트레이스 — 어떤 구간이 어느 모델로 갔는지 남기는 방법

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