모델 라우팅: 모든 작업에 단일 모델을 사용하지 마세요

적절한 작업에 적합한 모델

Page content

200단어짜리 이메일을 요약하기 위해 700억 파라미터 모델 실행은 낭비입니다. 프로덕션 코드를 검토하기 위해 30억 파라미터 모델을 실행하는 것은 무모합니다. 대부분의 시스템은 이 두 극단 사이의 어딘가에 위치해 있으며, 바로 여기서 모델 라우팅(Model Routing)의 역할이 시작됩니다.

모델 라우팅은 작업의 복잡성을 모델의 능력에 맞게 일치시킵니다. 여기서 발생하는 트레이드오프는 현실적이지만, 동시에 얻는 비용 절감 효과도 실재합니다.

LLM model routing strategies diagram

라우팅 문제

대부분의 사용자는 단일 모델로 시작해 그 모델을 계속 사용합니다. 이는 비용이나 지연 시간(Latency), 혹은 둘 다에 문제를 인지하기 전까지는 잘 작동합니다. 대안은 라우터를 구축하는 것입니다. 라우터는 어떤 요청을 어떤 모델이 처리할지 결정하는 역할을 합니다.

실무에서 작동하는 네 가지 전략이 있습니다:

  1. 역량 기반(Capability-based) — 모델이 수행할 수 있는 작업에 따라 라우팅
  2. 비용 기반(Cost-aware) — 지출할 의사 있는 비용에 따라 라우팅
  3. 지연 시간 기반(Latency-aware) — 필요한 응답 속도에 따라 라우팅
  4. 하이브리드(Hybrid) — 위 요소들을 결합

각 전략은 서로 다른 것을 최적화합니다. 특정 전략을 선택하는 것은 주로 어떤 요소가 가장 큰 고통으로 작용하는지에 대한 결정입니다.

역량 기반 라우팅

가장 단순한 접근법입니다. 작업을 분류하고, 해당 작업을 처리할 수 있는 모델로 요청을 보냅니다.

작업 모델 크기 예시
분류, 태깅 1-3B Qwen3-1.7B, Gemma-2-2B
요약, 추출 3-7B Qwen3-8B, Llama-3.1-8B
코드 생성 7-14B Qwen2.5-Coder-7B, DeepSeek-Coder-V2
복잡한 추론 14-32B Qwen3-32B, Llama-3.1-70B
창의적 글쓰기, 분석 32B+ Qwen2.5-72B, Claude, GPT-4

작업이 더 큰 모델을 필요로 하지 않는다면, 큰 모델을 사용하지 마십시오. 1.5B 파라미터 모델은 감정 분류를 잘 처리합니다. 다만, 일관된 에세이를 작성하지 못할 뿐입니다.

구현은 직관적입니다:

ROUTING_RULES = {
    "classify": {"model": "qwen3-1.7B", "max_tokens": 100},
    "summarize": {"model": "qwen3-8B", "max_tokens": 500},
    "code_review": {"model": "qwen2.5-coder-7b", "max_tokens": 2000},
    "reason": {"model": "qwen3-32b", "max_tokens": 4000},
    "creative": {"model": "claude-sonnet-4", "max_tokens": 8000},
}

def route_request(task_type: str) -> dict:
    return ROUTING_RULES.get(task_type, ROUTING_RULES["reason"])

여기서 주의할 점은 분류 자체입니다. 작업 유형을 잘못 분류하면 잘못된 모델로 라우팅됩니다. 코드 검토를 ‘요약’으로 분류하여 품질이 조용히 저하되는 시스템을 본 적이 있습니다.

비용 기반 라우팅

로컬 추론이 빛을 발하는 영역입니다. 하드웨어 원가償還 이후 로컬 모델은 사실상 무료입니다. 적당한 API 사용량 기준 RTX 5080은 약 6개월이면 원가 회수가 가능합니다.

모델 입력 ($/M tokens) 출력 ($/M tokens) 로컬 비용/시간
GPT-4o $2.50 $10.00
Claude Sonnet 4 $3.00 $15.00
Qwen2.5-72B (API) $0.50 $2.00
Qwen3-32B (local) $0.00 $0.00 ~$0.10
Qwen3-8B (local) $0.00 $0.00 ~$0.05

세션당 수천 개의 요청을 처리한다면, 전기비 $0.05도 $15/M tokens보다 낫습니다.

예산 기반 라우팅은 지출이 증가함에 따라 모델이 대체됩니다:

class CostAwareRouter:
    def __init__(self, budget_per_session: float = 0.10):
        self.budget = budget_per_session
        self.spent = 0.0
        self.models = {
            "cheap": {"model": "qwen3-8B", "cost": 0.0},
            "medium": {"model": "qwen3-32b", "cost": 0.0},
            "expensive": {"model": "claude-sonnet-4", "cost": 0.000015},
        }

    def route(self, task: str) -> str:
        ratio = self.spent / self.budget
        if ratio < 0.5:
            return self.models["expensive"]["model"]
        elif ratio < 0.8:
            return self.models["medium"]["model"]
        return self.models["cheap"]["model"]

대체 모델로 넘어갈수록 품질이 저하됩니다. Claude로 시작해 Qwen3-32B로, 그리고 Qwen3-8B로 이동합니다. 긴 세션이 끝날 즈음에는 출력 품질이 현저히 나빠집니다. 이것이 문제될지는 구축 중인 시스템에 달려 있습니다.

지연 시간 기반 라우팅

인터랙티브 도구는 빠른 첫 토큰 응답이 필요합니다. 배치 작업은 기다릴 수 있습니다. 그 차이는 보통 모델 크기의 5배 차이입니다.

사용 사례 첫 토큰 완료 최대 모델 크기
실시간 채팅 < 200ms < 2s < 7B
인터랙티브 도구 < 500ms < 5s < 14B
배치 처리 < 1s < 30s Any
연구/분석 < 2s < 60s Any

사용자에게 토큰을 스트리밍할 때, 사용자가 체감하는 것은 첫 토큰 지연 시간입니다. 32B 모델이 시작하는 데 0.5초가 걸리는 것은 즉시 응답하는 1.5B 모델에 비해 느리게 느껴집니다.

class LatencyAwareRouter:
    def __init__(self):
        self.model_latencies = {
            "qwen3-1.7b": {"first_token": 0.05, "complete": 0.5},
            "qwen3-8B": {"first_token": 0.15, "complete": 2.0},
            "qwen3-32b": {"first_token": 0.5, "complete": 10.0},
            "claude-sonnet-4": {"first_token": 0.3, "complete": 5.0},
        }

    def route(self, target_latency: float) -> str:
        for model, latencies in sorted(
            self.model_latencies.items(),
            key=lambda x: x[1]["complete"]
        ):
            if latencies["complete"] <= target_latency:
                return model
        return "qwen3-1.7b"

지연 시간 수치는 대략적입니다. 하드웨어, 양자화(Quantization), 배치 크기에 따라 달라집니다. 반드시 자체 환경에서 측정해야 합니다.

대체(Fallback) 전략

모델은 실패합니다. API는 속도 제한을 걸고, 타임아웃은 발생합니다. 작동하는 패턴은 가장 이상적인 모델부터 가장 신뢰할 수 있는 모델까지 순서대로 구성된 대체 연쇄(Fallback Chain)입니다:

class FallbackRouter:
    def __init__(self):
        self.fallback_chain = [
            {"model": "claude-sonnet-4", "timeout": 30},
            {"model": "qwen2.5-72b", "timeout": 60},
            {"model": "qwen3-32b", "timeout": 120},
            {"model": "qwen3-8b", "timeout": 300},
        ]

    def route_with_fallback(self, prompt: str) -> str:
        for config in self.fallback_chain:
            try:
                return self.call_model(
                    config["model"], prompt,
                    timeout=config["timeout"]
                )
            except (TimeoutError, APIError) as e:
                log.warning(f"Model {config['model']} failed: {e}")
                continue
        raise RuntimeError("All fallback models failed")

연쇄의 마지막 모델은 로컬 모델이어야 합니다. 속도는 느리지만, 네트워크 문제나 API 키 문제로 실패하지 않습니다.

라우팅이 도움이 될 때

워크로드가 혼합되어 있을 때 라우팅이 의미가 있습니다. 같은 시스템에서 분류, 요약, 추론을 수행한다면 라우터는 비용과 지연 시간을 절약해 줍니다.

모든 작업이 동일한 복잡도를 가진다면 라우팅은 의미가 없습니다. 해당 작업에 적합한 모델 하나만 사용하십시오. 라우터는 불필요한 복잡성을 추가합니다.

초기 프로토타이핑 역시 라우팅을 생략해야 하는 또 다른 이유입니다. 단일 모델로 작업을 작동시키는 데 성공한 후, 비용이나 지연 시간이 실제로 문제가 될 때 라우팅을 추가하십시오.

트레이드오프

모든 라우팅 전략은 무언가를 최적화하고 다른 것을 희생합니다:

  • 단일 모델 — 가장 단순, 가장 비쌈, 일관된 품질
  • 역량 기반 — 더 나은 비용, 작업별 더 높은 품질, 중간 복잡도
  • 비용 기반 — 가장 저렴, 품질 변동, 중간 복잡도
  • 지연 시간 기반 — 가장 빠름, 품질 희생 가능, 중간 복잡도
  • 하이브리드 — 모든 장점兼具, 구현 가장 복잡

프로덕션 시스템은 보통 하이브리드로 수렴합니다. 역량 기반 라우팅으로 시작하고, 청구서가 오면 비용 기반 기능을, 사용자가 느리다고 불평하면 지연 시간 기반 기능을 추가합니다.

관련 자료

구독하기

시스템, 인프라, AI 엔지니어링에 관한 새 글을 받아보세요.