지난 글까지 작은 모델을 어디서 돌릴지를 봤다. 기기, 엣지, 서버 어느 쪽이든 결국 같은 질문이 남는다. 어느 요청을 작은 모델이 처리하고 어느 요청을 큰 모델로 올릴 것인가.
모든 요청을 큰 모델로 보내면 단순하지만 비싸고, 전부 작은 모델로 보내면 싸지만 어려운 요청에서 무너진다. 실무에서 쓰는 답은 둘을 섞는 것이고, 섞는 방식은 크게 둘로 갈린다.
사전 라우팅과 캐스케이드
| 사전 라우팅 | 캐스케이드 | |
|---|---|---|
| 판단 시점 | 요청을 보고 미리 | 작은 모델의 답을 보고 |
| 근거 | 요청의 겉모습 | 실제 결과의 품질 |
| 추가 비용 | 라우터 실행분(작다) | 승격된 요청은 두 번 낸다 |
| 지연 | 예측 가능 | 승격되면 두 배 이상 |
| 만들기 | 라우터 학습 데이터가 필요 | 검사기만 있으면 시작 가능 |
사전 라우팅은 요청만 보고 "이건 어려워 보인다"를 판단해 바로 큰 모델로 보낸다. 캐스케이드는 일단 작은 모델에게 시켜 보고 결과가 미덥지 않을 때만 올린다.
먼저 시작할 것은 대개 캐스케이드다. 라우터를 학습시키려면 "이 요청은 큰 모델이 필요했다"는 라벨이 있어야 하는데, 그 라벨이 바로 캐스케이드를 돌린 로그에서 나오기 때문이다. 캐스케이드로 시작해 로그를 모으고, 승격 패턴이 뚜렷해지면 그 앞에 라우터를 붙이는 순서가 자연스럽다.
캐스케이드의 산수
승격률을 라고 하면 요청당 기대 비용은 이렇다.
전부 큰 모델로 보내는 것보다 싸려면 , 정리하면 이다. 작은 모델이 큰 모델의 20분의 1이라면 승격률이 95% 아래이기만 하면 이득이라는 뜻이다. 이 조건은 사실상 항상 만족한다. 그래서 캐스케이드를 도입할지 말지를 비용으로 판단하면 답은 언제나 "도입"이 되고, 이건 판단이 아니다.
실제로 봐야 할 것은 지연이다.
작은 모델이 0.4초, 검사가 0.1초, 큰 모델이 2초라고 하자. 승격률 15%면 평균 응답은 0.4 + 0.15 × 2.1 ≈ 0.72초로 전부 큰 모델로 보낼 때의 2초보다 훨씬 낫다. 그런데 승격된 15%의 요청은 2.5초를 기다린다 — 원래보다 느리다. 평균은 좋아지고 꼬리는 나빠지는 구조다.
이 꼬리가 어디에 떨어지는지가 중요하다. 승격되는 요청은 어려운 요청이고, 어려운 요청을 보내는 사용자는 대개 중요한 일을 하는 중이다. 평균 지연 그래프가 예뻐지는 동안 가장 중요한 사용자의 경험이 나빠질 수 있다.
무엇을 보고 올릴 것인가
캐스케이드의 성패는 검사기에 달려 있다. 쓸 수 있는 신호는 대략 이렇게 나뉜다.
확실한 신호 — 이건 이견의 여지가 없고 가장 먼저 붙인다. 스키마를 못 맞춘 JSON, 존재하지 않는 도구를 부른 호출, 필수 필드 누락, 정해 둔 라벨 집합 밖의 값, 빈 응답. 형식이 있는 작업이라면 이것만으로도 승격 대상의 상당 부분이 잡힌다.
괜찮은 신호 — 도메인 규칙으로 만든 검사다. 인용한 문서 번호가 실제 검색 결과에 없다, 계산 결과가 검산과 안 맞는다, 답이 입력에 없는 고유명사를 만들어 냈다. 만들기는 번거롭지만 정확도가 높다.
작은 판정 모델 — 답을 보고 "이 답이 요청을 충족하는가"를 재는 별도의 모델이다. 큰 모델을 판정에 쓰면 비용의 이유가 사라지므로 작은 모델이나 전용 분류기를 쓴다. 판정 자체가 틀릴 수 있으니 라벨을 모아 정확도를 재 두어야 한다.
믿기 어려운 신호 — 모델에게 "확신도를 0에서 1로 매겨라"라고 시키는 방식이다. 편하지만 잘 안 맞는다. 모델은 틀린 답에도 자신 있게 높은 점수를 준다. 로그 확률도 마찬가지로 유창함에 가깝지 사실성과는 거리가 있다. 다른 신호가 없을 때의 마지막 수단이지 기본값이 아니다.
실무에서 잘 도는 구성은 대개 확실한 신호 몇 개 + 판정 모델 하나의 조합이다. 확실한 신호는 즉시 승격시키고, 나머지는 판정 점수로 임계값을 넘는 것만 올린다.
임계값은 곡선을 그려서 고른다
임계값을 감으로 정하면 대개 너무 낮게 잡아 절반을 올려 보내게 된다. 곡선을 한 번 그려 두면 이 논쟁이 끝난다.
방법은 이렇다. 평가 묶음 몇 백 건을 작은 모델과 큰 모델 양쪽으로 돌려 두고, 판정 점수도 함께 기록한다. 그다음 임계값을 0부터 1까지 훑으면서 각 지점의 승격률·비용·품질을 계산해 점을 찍는다. 두 모델의 답을 이미 갖고 있으므로 이 작업은 추가 호출 없이 표 계산만으로 끝난다.
그리고 무릎을 찾는다. 대개 승격률 10~25% 구간에서 품질 이득의 대부분이 나오고, 그 뒤로는 비용만 오른다. 무릎이 아주 오른쪽에 있다면 그건 임계값 문제가 아니라 작은 모델이 이 작업에 안 맞는다는 뜻이므로, 임계값을 만지는 대신 모델을 바꾸거나 파인튜닝을 고려할 자리다.
스트리밍과는 상성이 나쁘다
캐스케이드에는 잘 안 알려진 함정이 하나 있다. 답을 다 만들어야 검사할 수 있으므로, 작은 모델의 출력을 사용자에게 흘려보낼 수 없다. 흘려보낸 뒤에 승격하면 이미 나간 글자를 지워야 한다.
선택지는 셋이다. 스트리밍을 포기하고 검사 후에 한 번에 보여 주거나, 흘려보내되 승격이 일어나면 화면에서 답을 교체하거나(사용자에게는 "다시 확인하는 중"으로 보이게 한다), 스트리밍이 필요한 기능은 아예 캐스케이드 대상에서 빼고 사전 라우팅만 쓰는 것이다.
짧은 답을 내는 기능이라면 첫 번째가 무난하다. 긴 글을 쓰는 기능이라면 세 번째가 대개 맞다 — 긴 생성은 작은 모델 비용도 이미 크고, 다 만든 뒤 버리면 그 비용이 통째로 낭비된다.
운영에서 실제로 보는 것
캐스케이드를 켠 뒤에 반드시 대시보드에 올려 둘 값은 승격률 하나다. 이 값은 비용·품질·검사기 상태를 한꺼번에 반영하는 지표라서, 여기가 흔들리면 어딘가 바뀐 것이다.
- 승격률이 서서히 오른다 — 입력 분포가 바뀌었거나 작은 모델의 프롬프트가 누군가에 의해 길어졌다.
- 승격률이 갑자기 0에 가까워진다 — 검사기가 고장 났을 가능성이 가장 높다. 품질이 좋아진 것이 아니다.
- 승격률은 그대로인데 비용이 오른다 — 승격되는 요청의 길이가 길어졌다.
함께 볼 것이 하나 더 있다. 승격된 요청에서 큰 모델의 답이 실제로 더 나았는가. 표본을 조금씩 뽑아 두 답을 비교해 두면, 검사기가 엉뚱한 것을 올리고 있는 상황을 잡을 수 있다. 이 확인이 없으면 캐스케이드는 "비용은 줄었는데 품질이 좋아졌는지는 아무도 모르는" 장치가 된다.
프롬프트 캐시를 쓰고 있다면 상호작용도 봐야 한다. 두 모델이 같은 앞부분을 공유해도 캐시는 모델마다 따로 잡히므로, 승격이 잦으면 양쪽 캐시 적중률이 함께 떨어진다.
안 쓰는 편이 나은 경우
마지막으로 도입하지 않는 것이 맞는 자리도 분명히 있다.
트래픽이 적으면 절약액보다 두 모델을 관리하는 비용이 크다. 하루 몇 천 건 수준에서 아끼는 돈은 대개 사람 반나절 값도 안 된다. 지연 상한이 엄격한 실시간 기능도 맞지 않는다 — 꼬리가 두 배가 되는 구조를 감당할 수 없기 때문이다. 그리고 작은 모델의 품질이 애초에 크게 모자라면 승격률이 높아져 아무것도 아끼지 못한 채 복잡도만 는다.
반대로 트래픽이 많고, 요청의 난이도가 고르지 않고, 형식이 있어 검사하기 쉬운 작업이라면 캐스케이드는 손에 꼽을 만큼 효율이 좋은 구조다. 요청의 대부분은 원래 쉬웠고, 그 사실을 시스템이 이용하게 만드는 일이기 때문이다.
읽어주셔서 감사합니다. 😊

