홈/AI 자동화/LLM 성능 향상 시리즈 3편: 라우팅·폴백·앙상블로 비용 줄이기
AI 자동화LLM라우팅

LLM 성능 향상 시리즈 3편: 라우팅·폴백·앙상블로 비용 줄이기

모든 쿼리에 GPT-4를 쓰는 낭비 "날씨 알려줘"와 "복잡한 계약서 분석"에 같은 모델을 쓰는 것은 비효율적입니다. LLM 라우팅과 앙상블은 정확도는 유지하면서 비용을 30~70% 절감할 수 있습니다. LLM 라우팅: 쿼리 복잡도 기반 분류 규칙 기반 라우팅 (빠르고 예측 가능) 폴백 체인: 정확도 보장 빠른 모델이 실패하거나 신뢰도가 낮으면 더 강…

LLM 성능 향상 시리즈 3편: 라우팅·폴백·앙상블로 비용 줄이기
목차
  1. 모든 쿼리에 GPT-4를 쓰는 낭비
  2. LLM 라우팅: 쿼리 복잡도 기반 분류
  3. 규칙 기반 라우팅 (빠르고 예측 가능)
  4. 폴백 체인: 정확도 보장
  5. 앙상블: Mixture-of-Experts 경량 구현
  6. 비용 대비 성능 측정 결과
  7. 프로덕션 적용 시 주의사항
  8. 라우터가 잘못 보내는 요청 찾기: 측정과 조정

모든 쿼리에 GPT-4를 쓰는 낭비

"날씨 알려줘"와 "복잡한 계약서 분석"에 같은 모델을 쓰는 것은 비효율적입니다. LLM 라우팅과 앙상블은 정확도는 유지하면서 비용을 30~70% 절감할 수 있습니다.

LLM 라우팅: 쿼리 복잡도 기반 분류

Python
from sklearn.linear_model import LogisticRegression
import numpy as np

class QueryRouter:
    """쿼리 복잡도를 분류해 적합한 모델로 라우팅"""

    COMPLEXITY_LEVELS = {
        "simple": "claude-haiku-4-5",    # 단순 조회, FAQ
        "medium": "claude-sonnet-4-6",   # 일반 추론
        "complex": "claude-opus-4-7",    # 복잡한 분석, 코드
    }

    def __init__(self):
        self.classifier = LogisticRegression()
        # 특징: 토큰 수, 코드 포함 여부, 수치 복잡도, 도메인 키워드 수
        self.feature_extractors = [
            lambda q: len(q.split()),
            lambda q: int(any(kw in q for kw in ["코드", "함수", "구현", "sql"])),
            lambda q: q.count("?"),
            lambda q: int(len(q) > 200),
        ]

    def extract_features(self, query: str) -> np.ndarray:
        return np.array([f(query) for f in self.feature_extractors]).reshape(1, -1)

    def route(self, query: str) -> str:
        if not hasattr(self.classifier, "classes_"):
            return "medium"  # 미학습 시 기본값
        level = self.classifier.predict(self.extract_features(query))[0]
        return self.COMPLEXITY_LEVELS[level]

    async def query(self, user_input: str) -> str:
        model = self.route(user_input)
        return await llm_call(model, user_input)

규칙 기반 라우팅 (빠르고 예측 가능)

Python
def simple_router(query: str) -> str:
    tokens = len(query.split())
    has_code = any(kw in query.lower() for kw in ["코드", "sql", "함수", "알고리즘"])
    has_numbers = sum(c.isdigit() for c in query) > 5
    is_long = tokens > 100

    if has_code or (is_long and has_numbers):
        return "claude-opus-4-7"
    elif tokens > 30 or has_numbers:
        return "claude-sonnet-4-6"
    else:
        return "claude-haiku-4-5"

폴백 체인: 정확도 보장

빠른 모델이 실패하거나 신뢰도가 낮으면 더 강력한 모델로 승격합니다.

Python
async def escalating_query(query: str) -> dict:
    models = [
        ("claude-haiku-4-5", 0.85),   # (모델, 신뢰도 임계값)
        ("claude-sonnet-4-6", 0.90),
        ("claude-opus-4-7", None),    # 최종 — 임계값 없음
    ]

    for model, threshold in models:
        response = await llm_call(model, query)
        confidence = await estimate_confidence(response)

        if threshold is None or confidence >= threshold:
            return {
                "answer": response,
                "model_used": model,
                "confidence": confidence
            }

        print(f"{model} 신뢰도 {confidence:.2f} < {threshold} → 상위 모델로 승격")

앙상블: Mixture-of-Experts 경량 구현

Python
async def ensemble_query(query: str, models: list, weights: list = None) -> str:
    responses = await asyncio.gather(*[llm_call(m, query) for m in models])

    if weights is None:
        weights = [1.0] * len(models)

    # 응답을 임베딩으로 변환 후 가중 평균 클러스터링
    embeddings = await asyncio.gather(*[embed(r) for r in responses])
    centroid = np.average(embeddings, axis=0, weights=weights)

    # 클러스터 중심에 가장 가까운 응답 선택
    distances = [np.linalg.norm(e - centroid) for e in embeddings]
    best_idx = np.argmin(distances)
    return responses[best_idx]

비용 대비 성능 측정 결과

실제 고객 서비스 챗봇 적용 결과 (월간 10만 건 기준):

전략비용정확도P95 지연
모두 Opus$1,20092%3.2s
모두 Sonnet$38085%1.8s
라우팅 (본문)$34091%2.1s
폴백 체인$42093%2.4s

라우팅 전략이 단일 Opus 대비 72% 비용 절감, 정확도 1%p 차이로 최적점을 보여줍니다.

프로덕션 적용 시 주의사항

  1. 라우터 학습 데이터: 최소 500개 레이블된 쿼리 필요
  2. A/B 테스트: 라우터 도입 전후 품질 지표 비교 필수
  3. 폴백 로깅: 어떤 쿼리가 상위 모델로 승격되는지 추적
  4. 비용 모니터링: 라우터 오분류로 비용이 증가할 수 있음

이 3편 시리즈로 LLM 성능 향상의 핵심(RAG→파인튜닝→라우팅/앙상블)이 완성됩니다.

라우터가 잘못 보내는 요청 찾기: 측정과 조정

라우팅으로 비용을 줄였는데 사용자 불만이 늘었다면, 어려운 요청이 저가 모델로 잘못 분류되고 있을 가능성이 큽니다. 절감률은 라우터의 오분류율을 같이 봐야 의미가 있습니다.

원인

  • 규칙 기반 라우터가 입력 길이·키워드만 보고 판단해, 짧지만 어려운 질문(계산, 다단계 추론)을 쉬운 요청으로 분류
  • 분류기 학습 데이터가 오래돼 새 요청 유형을 모름
  • 폴백 조건이 "API 오류"에만 걸려 있고, "품질 미달"에는 반응하지 않음

측정 방법

  1. 저가 모델로 처리된 요청에서 매일 일정 수를 무작위로 뽑아, 같은 입력을 상위 모델에도 보내고 두 답을 비교 채점합니다(사람 또는 별도 평가 모델).
  2. "상위 모델 답이 명확히 더 나은 비율"을 라우터 오분류율로 기록합니다.
  3. 사용자가 재질문·재생성을 누른 비율을 경로별로 나눠 봅니다.
SQL
SELECT route, count(*) AS n,
       avg(CASE WHEN regenerated THEN 1 ELSE 0 END) AS regen_rate
FROM llm_requests WHERE created_at > now() - interval '7 days'
GROUP BY route;

조정

  • 오분류가 많은 유형을 찾아 규칙에 추가하거나, 분류기의 판단 확신도가 낮으면 상위 모델로 보내는 임계값을 둡니다.
  • 출력 검증(JSON 스키마 실패, 답변 거절 문구 등)에 걸리면 상위 모델로 재요청하는 품질 기반 폴백을 추가합니다.

재발 방지

  • 절감 비용과 오분류율, 재생성률을 한 대시보드에 두고, 오분류율이 기준을 넘으면 라우팅 비율을 자동으로 보수적으로 되돌립니다.
✦ ✦ ✦
출처·검증 안내

글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보

이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.

발행 ·
주제 관련 공식 문서(자동 연결 · 문장별 근거 아님)OpenAI 공식 문서 ↗

댓글

첫 번째 댓글을 남겨보세요.