LLM 게이트웨이 설계: 멀티 프로바이더 라우팅과 폴백 전략
프로덕션 LLM 서비스가 OpenAI 하나에만 의존한다면 위험합니다. 2023년 11월 OpenAI 장애 때 수많은 서비스가 함께 멈췄습니다. LLM 게이트웨이는 여러 프로바이더를 단일 인터페이스 뒤에 두고 라우팅, 폴백, 비용 최적화를 중앙에서 관리합니다.
LLM 게이트웨이의 역할
클라이언트
↓
LLM 게이트웨이 (라우팅 / 폴백 / 캐싱 / 로깅)
↓ ↓ ↓
OpenAI Anthropic Google- 라우팅: 어떤 모델로 보낼지 결정
- 폴백: 실패 시 다른 프로바이더로 자동 전환
- 레이트 리밋: 프로바이더별 쿼터 관리
- 비용 추적: 통합 비용 모니터링
LiteLLM으로 빠르게 시작
LiteLLM은 100개 이상의 LLM을 OpenAI 호환 인터페이스로 통합합니다.
from litellm import completion
# 모델명만 바꾸면 모든 프로바이더 동일하게 사용
def llm_call(model, messages):
response = completion(model=model, messages=messages)
return response.choices[0].message.content
llm_call("gpt-4o", [{"role": "user", "content": "안녕"}])
llm_call("claude-sonnet-4-6", [{"role": "user", "content": "안녕"}])
llm_call("gemini/gemini-1.5-pro",[{"role": "user", "content": "안녕"}])폴백 체인 구현
from litellm import completion
FALLBACK_CHAIN = [
"gpt-4o",
"claude-sonnet-4-6",
"gemini/gemini-1.5-pro"
]
def resilient_llm_call(messages, preferred_model=None):
chain = ([preferred_model] + FALLBACK_CHAIN) if preferred_model else FALLBACK_CHAIN
chain = list(dict.fromkeys(chain))
last_error = None
for model in chain:
try:
response = completion(model=model, messages=messages, timeout=10, num_retries=1)
return {
"content": response.choices[0].message.content,
"model_used": model,
"was_fallback": model != chain[0]
}
except Exception as e:
last_error = e
print(f"[WARN] {model} 실패, 다음 모델로 시도")
raise RuntimeError(f"모든 모델 실패: {last_error}")비용 기반 라우팅
ROUTING_TABLE = {
"low": {"primary": "gpt-4o-mini", "fallback": "claude-haiku-4-5-20251001"},
"medium": {"primary": "claude-sonnet-4-6", "fallback": "gpt-4o"},
"high": {"primary": "claude-opus-4-7", "fallback": "gpt-4o"},
}
def classify_task(messages):
user_text = " ".join(m["content"] for m in messages if m["role"] == "user")
if any(kw in user_text for kw in ["분석", "설계", "아키텍처", "전략"]):
return "high"
if any(kw in user_text for kw in ["번역", "요약", "분류", "추출"]):
return "low"
return "medium"
def smart_route(messages):
complexity = classify_task(messages)
route = ROUTING_TABLE[complexity]
return resilient_llm_call(messages, preferred_model=route["primary"])LiteLLM Proxy 서버 배포
팀 전체가 단일 엔드포인트를 쓰게 합니다.
# litellm_config.yaml
model_list:
- model_name: gpt-4o
litellm_params:
model: gpt-4o
api_key: os.environ/OPENAI_API_KEY
- model_name: claude-sonnet
litellm_params:
model: claude-sonnet-4-6
api_key: os.environ/ANTHROPIC_API_KEY
router_settings:
routing_strategy: cost-based-routing
fallbacks:
- {"gpt-4o": ["claude-sonnet", "gemini-pro"]}
general_settings:
master_key: sk-my-master-key
database_url: os.environ/DATABASE_URLlitellm --config litellm_config.yaml --port 4000이제 팀 어디서나 http://localhost:4000을 OpenAI 엔드포인트처럼 씁니다. 키 관리, 비용 추적, 폴백이 모두 중앙화됩니다.
게이트웨이 책임
| 기능 | 설명 |
|---|---|
| 라우팅 | 모델/테넌트/비용 정책 |
| 폴백 | primary timeout → secondary |
| 한도 | RPM/TPM·예산 |
| 관측 | latency·error·$ |
client → gateway → [openai | anthropic | local]
↳ cache / rate-limit폴백이 동작하지 않거나 과하게 동작할 때
게이트웨이의 폴백 설정은 장애 때에만 실제로 쓰이기 때문에, 평소에는 잘못된 설정을 알아차리기 어렵습니다.
원인
- 폴백 조건이 5xx에만 걸려 있고 타임아웃·429(한도 초과)는 재시도만 반복
- 모든 오류에 폴백해 요청 형식 오류(400) 같은 우리 쪽 버그까지 다른 공급사로 보내 비용만 늘어남
- 폴백 대상 모델이 기본 모델과 출력 형식(JSON 모드, 도구 호출 방식)을 다르게 처리
재현: 장애 주입 테스트 스테이징에서 기본 공급사 호출을 일부러 실패시켜 폴백 경로를 확인합니다. LiteLLM을 쓴다면 잘못된 API 키로 기본 모델을 설정하거나, 네트워크 정책으로 해당 공급사 도메인을 차단해 볼 수 있습니다.
# 프록시에 요청을 보내 어떤 모델이 실제로 응답했는지 확인
curl -s http://localhost:4000/v1/chat/completions \
-H "Authorization: Bearer $LITELLM_KEY" -H "Content-Type: application/json" \
-d '{"model":"default-chat","messages":[{"role":"user","content":"ping"}]}' | jq '.model'해결
- 폴백 대상 오류를 명시적으로 나눕니다: 타임아웃·429·5xx는 폴백, 400·401 같은 요청/인증 오류는 즉시 실패.
- 폴백 모델에 대해서도 출력 스키마 검증을 통과하는지 회귀 테스트를 돌립니다.
재발 방지
- 폴백 발생 횟수를 모델·원인별 지표로 남기고, 급증하면 알림을 받습니다. 분기마다 장애 주입 테스트를 반복합니다.
참고 자료
검증 환경·편집자 주
- 2026-09. 프롬프트·툴 스키마 호환성을 폴백 전에 검증하세요.
자주 묻는 질문 (FAQ)
Q. 게이트웨이 없이 SDK 재시도만으로?
소규모는 가능. 멀티 프로바이더·예산·테넌트 정책이 생기면 게이트웨이가 유리합니다.
글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보
이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.
댓글
첫 번째 댓글을 남겨보세요.