라우터를 붙이면 애플리케이션은 모델 이름을 몰라도 된다. 그런데 모델 이름을 모른다는 것이 그냥 편해지는 것으로 끝나지 않는다. 어느 모델이 응답할지 미리 알 수 없다는 상태가 네 곳에서 제약으로 돌아온다. 요청을 보내기 전에 지켜야 하는 한도가 가장 좁은 쪽에 맞춰진다 대상 모델 목록이 나도 모르게 바뀔 수 있다 응답이 이상했을 때 어느 모델이 응답했는지...
Model Router 시리즈 3번이다. 앞 글 어느 모델로 보낼지 무엇으로 정하는가에서 판단 방식 넷과 그 판단에 드는 시간을 다뤘다. 이 글은 그 판단을 라우터에 맡긴 대가로 새로 생기는 제약을 다룬다.
라우터를 붙이면 애플리케이션은 모델 이름을 몰라도 된다. 그런데 모델 이름을 모른다는 것이 그냥 편해지는 것으로 끝나지 않는다. 어느 모델이 응답할지 미리 알 수 없다는 상태가 네 곳에서 제약으로 돌아온다.
가장 먼저 걸리는 제약이다.
Azure AI Foundry 의 model router 는 유효 컨텍스트 한도가 라우팅 대상 중 가장 작은 모델의 한도로 내려앉는다.
이유는 구조상 어쩔 수 없다.
요청을 받는 시점에 어느 모델로 갈지 아직 정해지지 않았으므로, 가장 좁은 모델로 갈 가능성을 배제할 수 없다.
그 모델에 들어가지 않는 입력을 받아 두면 라우팅 결과에 따라 실패한다.
이 제약이 실무에서 걸리는 순서는 이렇다.
| 용도 | 걸리는가 |
|---|---|
| 짧은 질의응답, 분류, 요약 | 대체로 걸리지 않는다 |
| 긴 문서를 그대로 넣는 용도 | 먼저 걸린다 |
| 대화 이력이 계속 쌓이는 용도 | 이력이 한도에 다다르는 시점에 걸린다 |
대상 목록에 작은 모델을 하나 넣으면 전체 한도가 그 모델에 묶인다는 것이 핵심이다. 싼 모델을 후보에 넣는 이유가 비용을 줄이는 것인데, 그 모델의 컨텍스트 한도가 좁으면 긴 요청 전체가 막힌다. 비용 후보를 늘리는 판단과 긴 입력을 받는 요건이 정면으로 충돌한다.
자체 구축으로 넘어가면 이 제약을 다르게 풀 수 있다. 입력 토큰 수를 먼저 세고 그 길이에 들어가는 모델만 후보로 남기는 것이다. LiteLLM 은 이것을 두 장치로 제공한다.
| 장치 | 동작 |
|---|---|
enable_pre_call_checks: true | 호출 전에 컨텍스트 초과 여부를 확인한다 |
context_window_fallbacks | litellm.ContextWindowExceededErrors 에만 발동하는 별도 fallback 목록 |
앞의 것은 실패를 막고, 뒤의 것은 이미 실패한 요청을 더 큰 모델로 넘긴다. 이 둘의 설정 상세는 LiteLLM 로 라우팅 정책을 설정하는 방법에서 다룬다.
Azure model router 는 이미지 입력을 받는다.
그런데 라우팅 판단은 텍스트만 본다.
이미지가 붙은 요청도 텍스트 부분만 읽고 모델을 고른다는 뜻이다.
오디오는 받지 않는다.
여기서 읽을 것은 이 상품 하나의 사양이 아니다. 라우터가 요청의 일부만 읽고 판단한다는 구조가 일반적이라는 점이다. 직접 만들 때도 같은 선택을 하게 된다. 이미지를 판단에 넣으려면 그 이미지를 읽는 모델을 라우팅 경로에 하나 더 두어야 하고, 그 호출 시간이 모든 요청에 얹힌다.
요청의 어느 부분을 판단에 쓰지 않는지를 문서에 적어 두어야 한다. 적어 두지 않으면 이미지 위주의 요청이 계속 잘못된 모델로 가는 것을 나중에 지표로 발견하게 된다.
관리형 라우터는 대상 목록을 고정하는 장치를 제공한다.
Azure model router 의 경우다.
마지막 항목이 중요하다. fallback 목록을 따로 두지 않고 라우팅 대상 목록을 그대로 쓰는 이유는, 승인하지 않은 모델로 요청이 넘어가는 것을 막기 위해서다. 장애가 났을 때 자동으로 다른 모델로 넘기는 기능과, 검토하지 않은 모델에 사내 데이터를 보내지 않는 요건이 충돌한다. 대상 목록 하나로 둘을 함께 잠근 것이 그 충돌에 대한 답이다.
직접 만들 때 이것을 그대로 옮길 수 있다. 후보 목록과 fallback 목록을 따로 관리하면 fallback 쪽을 갱신하지 않고 지나쳐 승인 범위를 넘게 된다.
새 모델이 자동으로 편입되지 않는 쪽이 기본값이어야 하는 이유도 같다. 모델이 새로 나오면 품질과 단가와 응답 형식이 다르고, 그중 응답 형식이 다르면 애플리케이션이 파싱에 실패한다. 편입 여부를 사람이 정하게 두면 그 검토가 강제된다.
사용자가 "답이 이상하다" 고 할 때 어느 모델이 응답했는지 모르면 원인을 좁힐 수 없다. 모델을 고정해 두던 때는 이 정보가 필요하지 않았다. 라우터를 붙인 순간부터 요청마다 답이 달라지므로 기록해야 한다.
Azure model router 는 요청별로 시도한 모델 순서와 fallback 발생 여부를 조회하는 별도 API 를 제공한다.
LiteLLM 은 응답 헤더로 준다.
| 헤더 | 담는 것 |
|---|---|
x-litellm-model-id | 실제로 응답한 배포의 식별자 |
x-litellm-model-api-base | 그 배포의 endpoint |
x-litellm-response-cost | 그 호출의 비용 |
x-litellm-call-id | 호출 식별자 |
직접 만드는 라우터도 최소한 이 넷에 해당하는 값을 남겨야 한다. 호출 식별자가 있어야 애플리케이션 로그와 라우터 로그를 이어 볼 수 있고, 비용이 요청 단위로 남아야 팀별 집계가 가능하다.
집계 지표도 별도로 필요하다. LiteLLM 이 노출하는 것을 기준으로 보면 갈래가 셋이다.
| 갈래 | 지표 |
|---|---|
| 배포별 결과 | litellm_deployment_success_responses, litellm_deployment_failure_responses, litellm_deployment_total_requests |
| fallback 발생 | litellm_deployment_successful_fallbacks, litellm_deployment_failed_fallbacks, litellm_deployment_cooled_down |
| 남은 예산 | litellm_remaining_team_budget_metric, litellm_remaining_api_key_budget_metric 계열 |
요청 단위 기록과 집계 지표는 역할이 다르다. 전자는 특정 요청 하나를 되짚을 때 쓰고, 후자는 "fallback 이 평소보다 자주 나고 있다" 를 알아채는 데 쓴다. 전자만 있으면 문제가 났다는 사실 자체를 늦게 안다.
Uber 의 GenAI Gateway 는 이 기록을 감사 로그 하나로 통합해 비용 귀속과 보안 감사와 품질 평가에 함께 쓴다. 같은 로그를 세 목적에 쓰려면 처음부터 무엇을 남길지 정해야 한다는 뜻이기도 하다.
이 절이 라우터 설계에서 가장 반직관적인 부분이다.
외부 제공자의 prompt caching 은 값이 셋이다. 아래는 2026년 공개 자료 기준이고, 제공자마다 다르며 단가는 바뀐다.
| 값 | 크기 |
|---|---|
| cache write | 기본 입력 단가에 25% 프리미엄 |
| cache read | 기본 입력 단가의 10% 수준 |
| 유효 시간 | 제공자마다 다르다 |
Claude Haiku 4.5 를 예로 들면 100만 토큰당 cache read 0.10대기본입력1.00 이다.
같은 앞부분에 적중이 2회 이상 나면 손익이 넘어간다. 한 번 쓰고 버리는 앞부분에 캐시를 붙이면 25% 프리미엄만 내고 끝난다.
여기서 라우팅과 충돌이 생긴다.
캐시는 같은 모델의 같은 앞부분에만 붙는다. 그런데 라우터는 요청마다 가장 싼 모델을 고르려 한다. 요청이 모델 사이를 오가면 각 모델에서 캐시가 쌓이지 못하고, cache write 프리미엄만 반복해서 낸다.
| 라우팅 방식 | 캐시 상태 |
|---|---|
| 요청마다 가장 싼 모델 | 앞부분이 같아도 모델이 갈려 적중이 나지 않는다 |
| 같은 앞부분은 같은 모델로 고정 | 적중이 쌓이지만 그 모델이 그 요청에 최선이 아닐 수 있다 |
그래서 토큰 단가만 보고 고른 "가장 싼 모델" 이 실제로 가장 싼 선택이 아닐 수 있다. 캐시가 이미 붙어 있는 비싼 모델이 더 싸게 끝나는 구간이 있다. DigitalOcean 은 자사 inference router 를 소개하며 가장 싼 모델이 가장 싸지 않은 이유를 이 캐시 문제로 설명한다.
정책에 넣으려면 값을 셋 알아야 한다.
앞의 것은 예측이므로 정확할 수 없다. 그래서 실무에서는 요청 성격으로 대신 판단한다. system prompt 가 긴 서비스, 같은 문서를 여러 질문이 참조하는 서비스는 앞부분 재사용이 많은 쪽으로 본다.
자체 호스팅 모델을 쓰면 같은 문제가 한 층 아래에서 다시 나온다. 인스턴스가 여럿일 때 어느 인스턴스로 보내는지가 prefix cache 적중을 바꾼다. 그 계층은 캐시를 보고 인스턴스를 고른다에서 다룬다.
다음 글에서는 자체 호스팅 모델이 여러 대일 때 어느 인스턴스로 보낼지를 다룬다. 위 계층의 캐시와 아래 계층의 캐시가 종류가 다르고, prefix aware 와 KV cache aware 가 판단하는 것이 다르다. 인스턴스를 늘리거나 줄일 때 적중이 무너지는 문제도 그 글에서 다룬다.
캐시를 보고 인스턴스를 고른다로 이어진다.