블로그기초 & 개념라우터를 만들었다가 손으로 지운 팀 이야기
기초 & 개념

라우터를 만들었다가 손으로 지운 팀 이야기

Manifest는 자체 LLM 라우터를 6개월 만에 폐지했다. 무엇을 구현하지 못해서였을까.

경쟁 서비스들의 엔지니어링 블로그를 훑어보다가 라우터를 만들고 나서 스스로 지운 회사의 글을 발견했다. Manifest라는 팀이다. 이 팀은 2026년 3월에 LLM 라우터를 출시했다. 6월에는 단종을 결정했고, 9월 1일 자로 서비스를 완전히 종료한다고 밝혔다. 4개월 동안 7,000명이 쓴 기능을 스스로 걷어낸 셈이다.

이유를 읽어보면 흥미롭다. 프롬프트만 봐서는 요청이 얼마나 복잡한지 미리 가늠할 수 없었다. 캐시로 아낀 비용이 라우팅으로 아낀 비용보다 컸다. 그러니 모델을 계속 바꾸는 쪽이 오히려 손해였다. 세션 도중 모델이 바뀌면 작업 품질도 떨어졌다. 평가와 관측에 드는 비용마저 라우팅으로 절약한 돈보다 많았다. 이유는 네 가지였지만 결국 하나로 모인다. 라우팅을 손으로 짜서 운영하는 일 자체가 생각보다 훨씬 무거웠다는 얘기다.

우리가 이 글을 눈여겨본 까닭은 따로 있다. Manifest가 포기한 지점이, 우리가 4-Layer 폴백으로 짜서 풀어낸 지점과 정확히 겹친다.

6개월짜리 실험 하나

Manifest의 라우터는 구조 자체는 단순했다. 요청을 단순, 표준, 복잡, 추론 네 단계로 나눈 뒤 각 단계에 맞는 모델로 자동 넘기는 방식이다. 쉬운 질문은 저렴한 모델로 보내고 어려운 질문은 비용이 높은 모델로 보낸다는 계산이었다. 라우터를 만드는 팀이라면 한 번쯤 그려봤을 법한 그림이었다.

문제는 이 분류를 프롬프트 시점에 끝내야 한다는 전제에서 시작됐다. 실제 작업의 난이도는 도구를 호출하거나 웹을 검색한 뒤에야 드러나는 경우가 많다. 그런데 라우터는 그 전에 이미 모델을 정해버리는 구조였다. 예측이 어긋나면 모델을 다시 바꿔야 했다. 그때마다 시스템 프롬프트와 대화 기록에 걸려 있던 캐시가 깨졌다. 세션이 어긋났다. 운영 비용도 늘었다.

라우터 자체가 실패했다기보다는 두 요구가 부딪힌 결과로 보는 편이 맞다. 난이도를 미리 맞혀야 한다는 전제가 있었다. 동시에 캐시를 붙들고 있어야 한다는 요구도 있었다. 이 둘은 애초에 함께 갈 수 없는 조건이었다. 그 충돌을 하나씩 뜯어보면 4-Layer 폴백을 설계할 때 마주쳤던 문제들과 겹친다.

라우팅과 폴백은 다른 질문이다

Manifest의 라우터가 풀려던 문제는 요청이 얼마나 어려운지 미리 가늠하는 일이었다. 프롬프트만 보고 난이도를 추정한 뒤 거기 맞는 모델로 미리 보내는 방식을 썼다. 그런데 이 추정이 자주 틀렸다. 틀릴 때마다 캐시가 깨졌다. 세션 중간에 모델도 바뀌었다. 모델이 언제 바뀔지 몰라 관측하고 평가하는 데도 비용이 들었다.

4-Layer 폴백은 이 질문 자체를 다르게 세운다. 여기서 보는 건 "지금 부른 모델이 응답했는가, 하지 않았는가" 하나뿐이다. 요청은 늘 지정한 기본 모델로 먼저 간다. 그 모델이 정상이면 세션 내내 같은 모델을 쓴다. 시스템 프롬프트와 대화 기록의 캐시가 끊기지 않는 이유가 여기 있다. 두 얼개를 나란히 놓아보면 차이가 뚜렷이 드러난다.

항목Manifest 라우터4-Layer 폴백
질문요청이 얼마나 어려운가모델이 응답했는가
전환 트리거프롬프트 시점 난이도 추정실패 신호(오류·장애)
캐시 영향전환마다 캐시 무효화정상 작동 시 캐시 유지
세션 일관성세션 중 모델 교체 가능정상 작동 시 모델 고정
관측 방식별도 평가·모니터링 필요응답 필드에 전환 이력 포함
한계난이도 사전 판단 불가난이도 기반 절감은 없음

전환은 실패라는 신호가 뚜렷할 때만 일어난다. 이 신호를 네 겹으로 나눠 처리하는 방식이 핵심이다.

계층역할전환 조건
서킷브레이커장애 모델 감지연속 실패 임계치 초과
재시도일시적 오류 흡수동일 모델 재호출
프로바이더공급사 전환같은 모델의 다른 경로
모델대체 모델 전환models 배열 순차

서킷브레이커가 장애를 감지하면 재시도가 일시적 오류를 흡수한다. 그래도 풀리지 않을 때는 같은 모델을 다른 프로바이더로 돌려본다. 마지막 단계에 가서야 비로소 다른 모델로 넘어간다. 각 단계가 순서대로 작동해 전환 시점과 이유가 로그에 고스란히 남는다. Manifest에서는 왜 모델이 바뀌었는지 알기 어려운 경우가 많았다. 이 구조에서는 그런 일이 좀처럼 일어나지 않는다. 전환 기준이 추측이 아니라 실패 신호이기 때문이다.

체인 하나를 실제로 적어보면

기본 모델을 정하고, 그 모델이 실패했을 때 넘어갈 순서를 배열로 적는다. 여기에 프로바이더 우선순위와 가격 상한을 함께 지정하면 전환 기준이 코드가 아니라 설정값으로 드러난다.

{
  "model": "deepseek-ai/DeepSeek-V3.1",
  "models": ["deepseek-ai/DeepSeek-V3.1:floor", "qwen/qwen-2.5-72b-instruct"],
  "provider": {
    "sort": "price",
    "max_price": 2.0,
    "allow_fallbacks": true
  }
}

응답에는 extra_fieldsprovider, resolved_model_used, latency가 함께 실려 온다. 어떤 요청이 기본 모델로 끝났는지, 몇 단계를 거쳐 대체 모델까지 갔는지 호출 단위로 확인할 수 있다. Manifest는 관측 비용이 라우팅 절감분을 넘어섰다고 말했지만, 여기서는 전환 기록이 응답에 이미 포함돼 있어 별도 계측을 붙일 필요가 없다. 과금 방식도 비슷한 이야기를 한다. 재시도나 프로바이더 전환 과정에서 실패한 호출에는 요금이 매겨지지 않고, 최종 성공한 모델 기준으로만 청구된다. 전환이 잦아도 비용이 이중으로 붙지 않는다.

물론 이 구조로 Manifest의 첫 번째 문제까지 풀리지는 않는다. 프롬프트만 보고 복잡도를 미리 판단할 수 없다는 그 지점 말이다. 4-Layer 폴백은 실패에 반응할 뿐, 요청의 난이도를 미리 추정해 저렴한 모델로 돌리는 기능은 아니다. 기본 모델을 무엇으로 정할지는 여전히 엔지니어의 판단으로 남는다. 모델 선택은 사람이 의도를 갖고 고정해야 한다는 Manifest의 결론과, 이 지점에서는 같은 방향을 보고 있다.

정리

Manifest가 넘어진 지점은 두 가지로 좁혀진다. 하나는 난이도를 프롬프트 단계에서 미리 판단하려던 시도다. 다른 하나는 그 판단이 어긋날 때마다 캐시 스티키니스와 충돌한 부분이다. 평가 비용은 늘었고 세션 품질은 떨어졌다. 두 문제가 겹친 결과였다.

4-Layer 폴백은 출발점부터 다르다. 난이도를 맞히려 들지 않는다. 실패 신호가 있을 때만 전환하니 캐시와 부딪힐 일도 그만큼 줄어든다. 모델을 자주 바꾸지 않는 쪽을 기본값으로 삼는다. 바꿔야 할 때만 움직인다. 전환 이유는 응답에 남는다.

이 구조는 코드를 짜지 않고도 설정값만으로 확인할 수 있다. https://llm-router.cafe24.com 에서 체인을 직접 걸어보면 된다.