LLM 라우터를 만들 때 모델 순위표보다 먼저 정해야 할 정책
여러 LLM을 함께 쓰는 제품이 늘고 있다. 빠른 모델은 분류와 초안에 쓰고, 큰 모델은 복잡한 추론에 쓰고, 로컬 모델은 민감한 데이터 처리에 쓰고, 외부 API는 장문 생성에 쓴다. 이때 필요한 것이 LLM 라우터다. 하지만 라우터를 “가장 좋은 모델을 고르는 함수”로 만들면 오래 버티지 못한다.
운영 환경의 라우팅은 benchmark ranking보다 정책 문제에 가깝다. 데이터 민감도, latency budget, 비용 한도, context 길이, tool 사용 가능성, 장애 시 fallback, 고객별 계약 조건이 모델 선택을 결정한다. 같은 질문이라도 무료 플랜 사용자와 엔터프라이즈 관리자에게 다른 경로가 필요할 수 있다.
이 글은 LLM 라우터를 설계할 때 먼저 정해야 할 정책과 로그를 다룬다. 핵심은 모델을 추상화하되, 모델별 차이를 숨기지 않는 것이다.
라우터의 입력은 prompt 문자열 하나가 아니다
초기 구현은 route(prompt)처럼 시작하기 쉽다. 하지만 실제 라우터는 prompt만으로 충분하지 않다. 같은 문장이라도 사용자 권한, tenant 정책, 데이터 위치, 응답 시간 목표에 따라 선택이 달라진다.
라우터 입력에는 최소한 task_type, tenant_id, data_sensitivity, latency_budget_ms, max_cost_usd, context_tokens, requires_tools, requires_json_schema, locale, user_plan 같은 metadata가 필요하다. 이 값들이 없으면 라우터는 모델 품질만 보고 선택하게 된다.
예를 들어 “이 계약서 위험 조항을 찾아줘”라는 요청이 있다. 개인 사용자의 공개 샘플 계약서라면 외부 고성능 모델을 쓸 수 있다. 기업 고객의 미공개 계약서라면 특정 region, 특정 provider, 또는 self-hosted 모델만 허용될 수 있다. prompt 내용이 비슷해도 정책은 다르다.
모델 capability는 이름이 아니라 계약으로 관리한다
모델명을 코드 곳곳에 박아 넣으면 라우팅이 금방 꼬인다. gpt-, claude-, gemini-, qwen- 같은 이름은 바뀌고, 같은 provider 안에서도 기능 차이가 크다. 라우터는 모델을 capability contract로 관리해야 한다.
예시는 이렇다. 각 모델 프로필에 max_context_tokens, supports_tool_calling, supports_structured_output, supports_vision, supports_audio, avg_ttft_ms, cost_per_1k_input, cost_per_1k_output, data_residency, rate_limit_tier, allowed_sensitivity를 둔다. 라우터는 이 프로필을 기준으로 후보를 거른다.
중요한 것은 capability를 수동 문서로만 두지 않는 것이다. provider API가 바뀌거나 deployment 설정이 달라지면 실제 capability가 달라진다. 주기적인 smoke test로 tool call, schema output, 긴 context, vision 입력이 여전히 동작하는지 확인해야 한다.
비용 정책은 평균이 아니라 요청별 상한으로 잡아야 한다
LLM 비용은 평균만 보면 안전해 보인다. 하지만 일부 요청이 긴 context와 여러 retry로 크게 튈 수 있다. 라우터에는 요청별 비용 상한이 필요하다. max_cost_usd 또는 토큰 예산을 넘으면 다른 전략을 선택해야 한다.
전략은 여러 가지다. 긴 문서를 먼저 요약해 context를 줄인다. 더 싼 모델로 초벌 분류를 한다. top-k retrieval 수를 줄인다. 사용자가 명시적으로 고품질 모드를 선택한 경우에만 큰 모델로 보낸다. 또는 “이 요청은 예상 비용이 높아 확인이 필요합니다”라고 UX에서 멈춘다.
비용 상한은 고객 플랜과도 연결된다. 무료 플랜에는 긴 추론형 모델을 바로 쓰지 않고, 작은 모델로 의도 분류 후 필요한 경우만 escalation한다. 엔터프라이즈 플랜에는 SLA와 품질 기준 때문에 더 비싼 경로를 허용할 수 있다. 이 정책이 코드에 흩어지면 나중에 가격 변경 때 고치기 어렵다.
fallback은 품질 저하가 아니라 동작 모드 변경이다
provider 장애가 나면 다른 모델로 보내면 된다고 생각하기 쉽다. 하지만 모델이 바뀌면 출력 형식, tool call 방식, 안전 정책, context window, 말투가 달라진다. fallback은 단순 대체가 아니라 동작 모드 변경으로 봐야 한다.
예를 들어 primary 모델은 strict JSON schema를 지원하지만 fallback 모델은 약한 JSON만 지원한다고 하자. 이때 같은 downstream parser로 보내면 장애가 늘 수 있다. fallback 경로에서는 schema를 단순화하거나, 모델 출력을 validation 후 human review로 보내야 한다.
또 다른 예는 장문 RAG다. primary는 128K context를 처리하고 fallback은 32K만 처리한다면 같은 문서 묶음을 넣을 수 없다. fallback 시 retrieval top-k를 줄이거나, 문서를 map-reduce 요약으로 바꾸어야 한다. 라우터는 “대체 모델명”뿐 아니라 “대체 prompt strategy”를 가져야 한다.
latency 라우팅은 TTFT와 전체 시간을 나눠야 한다
사용자 경험에서 중요한 latency는 기능마다 다르다. 채팅 UI에서는 첫 토큰까지의 시간(TTFT)이 중요하다. batch 보고서 생성에서는 전체 완료 시간이 중요하다. API automation에서는 timeout budget이 중요하다. 라우터가 이 차이를 모르면 빠른 모델을 잘못 고른다.
작은 모델은 TTFT가 빠르지만 긴 출력에서는 품질이나 총 시간이 아쉬울 수 있다. 큰 모델은 prefill이 느려도 한 번에 좋은 구조를 만들 수 있다. streaming이 가능한 모델과 불가능한 모델도 다르다. 그래서 라우터 입력에 interaction_mode가 필요하다. chat_streaming, background_batch, tool_agent_step, classification_sync는 다른 경로를 타야 한다.
대시보드도 latency_ms 하나로 끝내면 안 된다. router_decision_ms, provider_queue_ms, ttft_ms, generation_ms, tool_wait_ms, retry_delay_ms를 분리한다. 그래야 라우터가 느린지, provider가 느린지, 모델이 긴 출력을 생성하는지 알 수 있다.
평가셋은 라우팅 결정까지 포함해야 한다
모델 eval은 보통 “이 모델이 정답을 맞혔는가”를 본다. 라우터 eval은 한 단계 더 봐야 한다. “이 요청을 이 모델에 보낸 결정이 맞았는가”를 평가해야 한다. 품질, 비용, latency, 정책 준수의 합성 문제다.
평가 케이스에는 prompt뿐 아니라 metadata가 있어야 한다. 같은 질문을 data_sensitivity=public과 data_sensitivity=restricted로 나눠 넣는다. context length가 4K인 경우와 80K인 경우를 나눈다. JSON schema가 필요한 경우와 자연어 답변이면 되는 경우를 나눈다.
결과 지표는 quality_pass, policy_violation, cost_over_budget, latency_over_budget, fallback_used, parser_fail을 함께 본다. 한 모델이 품질은 높지만 정책 위반이 잦으면 라우터 후보에서 제외해야 한다. 반대로 작은 모델이 품질은 조금 낮아도 분류 작업에서 비용과 latency가 압도적으로 좋으면 우선 경로가 될 수 있다.
로그 없이는 라우터가 정치 문제가 된다
여러 팀이 모델 선택에 관여하면 논쟁이 생긴다. 제품팀은 품질을 원하고, 재무팀은 비용을 보고, 보안팀은 데이터 경계를 본다. 로그가 없으면 “요즘 모델이 느리다”, “비싼 모델을 너무 많이 쓴다” 같은 말만 오간다.
라우터는 모든 결정에 이유를 남겨야 한다. candidate_models, filtered_by_policy, selected_model, selection_reason, estimated_cost, actual_cost, latency_budget_ms, fallback_reason을 기록한다. 후보에서 제외된 이유가 특히 중요하다. “큰 모델이 왜 안 쓰였나”라는 질문에 data_sensitivity_restricted 또는 cost_budget_exceeded라고 답할 수 있어야 한다.
또한 고객별 override를 기록해야 한다. 특정 엔터프라이즈 고객이 외부 provider 사용을 금지했다면 그 정책 때문에 self-hosted 모델로 간 사실이 로그에 남아야 한다. 그렇지 않으면 품질 차이가 모델 문제인지 계약 정책 때문인지 구분할 수 없다.
실무 판단 기준
LLM 라우터는 처음부터 ML 기반 classifier일 필요가 없다. 오히려 초기에는 규칙 기반이 낫다. 민감도와 기능 요구사항으로 후보를 거르고, latency와 비용으로 정렬하고, 품질 eval에서 검증된 우선순위를 적용한다. 규칙이 쌓이고 예외가 많아질 때 routing model이나 bandit을 검토해도 늦지 않다.
도입 순서는 모델 프로필 작성, task taxonomy 정의, 정책 필터 구현, 비용/latency 예산 적용, fallback strategy 분리, 결정 로그 저장, offline eval 순서가 안전하다. 이 순서를 건너뛰고 “가장 똑똑한 모델로 보내다가 비싸면 싼 모델”로 만들면 나중에 보안과 비용 정책을 끼워 넣기 어렵다.
라우터의 목적은 항상 최고 모델을 고르는 것이 아니다. 주어진 요청을 정책 안에서 충분히 좋은 모델과 실행 전략에 보내는 것이다. 이 차이를 분명히 해야 LLM 제품이 모델 출시 뉴스에 흔들리지 않고 안정적으로 운영된다.
라우터에는 수동 override 경로도 있어야 한다. 신규 모델을 테스트할 때 전체 트래픽을 바로 넘기지 말고 특정 task, 특정 tenant, 특정 내부 사용자에게만 적용한다. override는 코드 배포 없이 설정으로 바뀌어야 하지만, 아무나 바꿀 수 있으면 안 된다. 누가 어떤 이유로 contract_review 작업을 새 모델로 보냈는지 감사 로그가 남아야 한다.
마지막으로 라우터는 사용자 경험과 연결된다. fallback이 발생했을 때 품질이 낮아질 수 있다면 조용히 숨기기보다 기능 모드를 조정하는 편이 낫다. 예를 들어 장문 보고서 생성에서 긴 context 모델이 unavailable이면 “빠른 초안 모드”로 전환하고 근거 문서 수를 줄였다는 사실을 내부 trace에 남긴다. 고객에게 provider 이름을 말할 필요는 없지만, 제품은 스스로 어떤 약속을 낮췄는지 알아야 한다. 그래야 장애 후 보상, 재시도, SLA 보고가 가능하다.