프로덕션 AI의 숨겨진 비용: 조용히 실패하지 않는 폴백 체인(Fallback Chains) 구축 방법
요약
프로덕션 환경에서 LLM 제공업체의 부분적 장애나 품질 저하에 대응하기 위한 폴백 체인(Fallback Chains) 구축 전략을 다룹니다. 3계층 아키텍처와 비용 인식 라우팅을 통해 시스템의 안정성과 효율성을 높이는 방법을 제시합니다.
핵심 포인트
- 단일 제공업체 의존은 속도 제한 및 부분적 장애에 취약함
- 품질과 비용을 고려한 3계층(기본-폴백-저하 모드) 아키텍처 권장
- 단순 HTTP 200 응답 확인을 넘어 유효한 응답인지 검증하는 로직 필수
- 작업의 복잡도에 따라 모델을 분리하는 비용 인식 라우팅 적용
가장 최악의 프로덕션 버그는 시스템을 중단시키지 않습니다. 그저 조용히 성능을 저하시킬 뿐입니다. 흔한 패턴 중 하나는 LLM 제공업체에 부분적인 장애가 발생하여, 빈 응답이나 터무니없는 응답을 보내면서도 200 OK를 반환하는 경우입니다. 에러도, 알림도, 5xx 에러도 없습니다. 그저 성공으로 위장한 침묵뿐입니다.
이것이 바로 프로덕션 AI의 숨겨진 비용입니다. API 비용이나 지연 시간(Latency)이 아닙니다. 사용자가 무언가 잘못되었다고 말하기 전까지는 정상 작동처럼 보이는 바로 그 실패들입니다.
저는 매일 10,000개 이상의 채용 공고를 점수화하는 프로덕션 LLM 파이프라인을 운영하고 있습니다. 스택의 다양한 지점에서 OpenAI, Anthanthic, Gemini, DeepSeek, 그리고 Groq를 사용합니다. 실제로 작동하는 폴백 체인(Fallback Chains)을 구축하는 것에 대해 제가 배운 점들을 공유합니다.
단일 제공업체 아키텍처가 위험 요소인 이유
대부분의 팀은 하나의 LLM 제공업체로 시작합니다. 개발 단계에서는 잘 작동합니다. 그러다 프로덕션 트래픽이 몰리면 테스트 스위트(Test suite)에서는 나타나지 않았던 실패 모드들을 발견하게 됩니다.
가장 최악의 순간에 속도 제한(Rate limits)에 걸립니다. 제공업체의 API는 부하가 걸리면 저하된 응답을 반환할 수 있습니다. 모델 버전이 충분한 예고 없이 지원 중단(Deprecated)되기도 합니다. 그리고 가장 최악인 것은, API는 응답하지만 콘텐츠는 쓰레기인 부분적 장애(Partial outages)입니다.
취미 프로젝트와 프로덕션 시스템을 구분 짓는 패턴은 테스트가 완료되었고, 비용을 고려하며, 관찰 가능(Observable)한 폴백 체인입니다.
목표는 실패를 없애는 것이 아닙니다. 모든 실패가 조용히 일어나는 대신, 우아하게 성능이 저하(Degrade gracefully)되도록 만드는 것입니다.
3계층 폴백 패턴
여러 프로젝트를 거치며 반복 실험한 끝에, 저는 복잡성을 크게 높이지 않으면서 대부분의 실패 모드를 처리할 수 있는 3계층 아키텍처를 정착시켰습니다.
Layer 1: 기본 모델 (최상의 품질, 가장 높은 비용)
Layer 2: 폴백 모델 (좋은 품질, 낮은 비용)
Layer 3: 저하 모드 (최소한의 품질, 거의 제로에 가까운 비용)
핵심 통찰: 각 계층은 서로 다른 실패 프로필을 가진 서로 다른 제공업체여야 합니다. 한 제공업체가 느려지거나 다운되면, 다른 제공업체는 아마 영향을 받지 않을 것입니다. 만약 둘 다 느려진다면, 더 저렴하거나 빠른 모델이 서비스를 유지할 수 있게 해줍니다.
제가 실제로 이를 구성하는 방법은 다음과 같습니다:
interface LLMFallbackConfig {
primary: ModelConfig;
fallback: ModelConfig;
...
isValidResponse 체크는 매우 중요합니다. 단순히 HTTP 응답이 200(OK)인 것뿐만 아니라, 출력이 실제로 유용한지 검증해야 합니다. 구조화된 출력(Structured outputs)의 경우, 이는 스키마 검증(Schema validation)을 의미합니다. 텍스트의 경우, 길이 체크와 콘텐츠 품질 휴리스틱(Heuristics)을 의미합니다.
비용 인식 라우팅(Cost-Aware Routing): 어떤 모델을 언제 사용할 것인가
모든 요청에 GPT-4가 필요하지는 않습니다. 핵심은 어떤 요청에 필요한지 파악하고 그에 따라 라우팅하는 것입니다.
제 직무 점수 산정(Job scoring) 파이프라인에서는 세 가지 티어(Tier)를 사용합니다:
Tier 1: 엄격한 스키마를 가진 함수 호출(Function calling)이 필요한 복잡한 추출 작업입니다. 이 작업들은 GPT-4o 또는 Claude 3.5 Sonnet으로 보냅니다. 비용은 높지만 신뢰성이 높습니다.
Tier 2: 스키마는 단순하지만 추론(Reasoning)이 중요한 분류 및 점수 산정 작업입니다. 이 작업들은 GPT-4o mini 또는 Gemini 2.0 Flash로 보냅니다. 비용의 아주 일부만으로도 좋은 품질을 제공합니다.
Tier 3: 품질보다 속도가 더 중요한 전처리 및 폴백(Fallback) 작업입니다. 이 작업들은 Groq 또는 DeepSeek V4 Flash로 보냅니다. 거의 즉각적인 응답과 최소한의 비용이 특징입니다.
라우팅 로직은 간단합니다:
function selectModel(task: Task, context: RequestContext): ModelConfig {
if (task.complexity === 'high' || task.requiresStrictSchema) {
return getPrimaryModel();
...
이러한 접근 방식은 비싼 모델을 실제로 필요할 때만 사용함으로써 API 비용을 절감하는 동시에, 대부분의 작업에서 수용 가능한 품질을 유지합니다.
임베딩 중복성(Embedding Redundancy): 간과하기 쉬운 실패 모드
대부분의 사람들은 LLM 폴백에 대해서는 생각하지만, 임베딩(Embedding) 폴백에 대해서는 거의 생각하지 않습니다. 하지만 RAG 파이프라인의 임베딩 제공업체가 다운되면, 전체 검색(Retrieval) 레이어가 작동을 멈추게 됩니다.
임베딩 API에 장애가 발생했다고 가정해 봅시다. 벡터 검색(Vector search)은 결과가 0건을 반환합니다. 사용자는 빈 응답을 보게 됩니다. 에러도 없고, 컨텍스트도 없이, 그냥 아무것도 없는 상태가 됩니다.
그래서 저는 제가 구축하는 모든 RAG 파이프라인에 대해 두 개의 임베딩 제공업체를 병렬로 유지합니다:
interface EmbeddingProvider {
name: string;
embed(text: string): Promise<number[]>;
...
벡터 스토어 (Vector Store)는 여러 개의 임베딩 차원 (Embedding Dimensions)을 지원해야 합니다. 저는 각 임베딩 제공업체(Embedding Provider)를 위한 별도의 컬럼을 사용하여 pgvector를 사용합니다. 쿼리는 데이터가 있는 컬럼을 확인합니다.
관측 가능성 (Observability): 조용한 실패 포착하기
프로덕션 AI에서 가장 위험한 실패는 실패처럼 보이지 않는 실패입니다. 빈 응답, 저하된 품질, 스키마 검증(Schema Validation)을 통과하는 환각 데이터(Hallucinated Data) 등이 이에 해당합니다.
저는 모든 LLM 호출에 대해 세 가지 지표를 추적합니다:
응답 시간 (Response Time). 복잡한 프롬프트에 비해 응답 시간이 의심스러울 정도로 빠르다면, 무언가 잘못되었을 가능성이 높습니다. 모델이 캐시된 응답이나 잘린(Truncated) 응답을 반환했을 수 있습니다.
출력 길이 (Output Length). 빈 응답이나 매우 짧은 응답은 위험 신호입니다. 응답 길이가 작업 유형에 대해 설정 가능한 임계값(Threshold) 미만으로 떨어지면 경고를 로그로 남깁니다.
스키마 준수 여부 (Schema Compliance). 구조화된 출력(Structured Outputs)의 경우, 기대되는 스키마에 따라 응답을 검증합니다. 스키마는 통과했지만 내용이 쓰레기 값(모두 null이거나, 기본값, 반복되는 텍스트 등)이라면, 그것은 조용한 실패(Silent Failure)입니다.
function monitorLLMCall(call: LLMCallResult, context: TaskContext) {
const metrics = {
duration: call.endTime - call.startTime,
...
이 방식은 실패가 연쇄적으로 발생하기 전에 조용한 실패를 포착합니다. 첫 번째 실패 직후에 알람이 울리므로, 곧바로 수정 사항을 배포할 수 있습니다.
프로덕션에서의 실제 모습
잘 설계된 폴백 체인 (Fallback Chain)은 각 요청이 여러 계층을 통과함을 의미합니다. 기본 모델이 실패하면 폴백(Fallback)이 빠르게 제어권을 넘겨받습니다. 두 모델 모두 실패하더라도, 저하 모드 (Degraded Mode)는 에러 대신 여전히 사용 가능한 응답을 반환합니다.
비용 측면의 트레이드오프 (Tradeoff)는 실재합니다. 사용하지 않는 용량에 대해 비용을 지불하게 됩니다. 하지만 그 대안은 몇 시간 또는 며칠 동안 사용자 신뢰를 갉아먹는 조용한 서비스 중단입니다.
만약 귀하의 팀이 프로덕션에 AI 기능을 배포하고 있으며 신뢰성에 대해 고민하고 있다면, 그것이 바로 제가 도움을 드리는 분야입니다. 귀하의 특정 환경에서 무엇이 작동하고 무엇이 작동하지 않았는지에 대해 기꺼이 의견을 나누고 싶습니다.
Abdul Rehman 작성, 프로덕션 SaaS, MVP 및 AI 자동화를 구축하는 풀스택 AI 엔지니어. 더 많은 정보는 PrimeStrides에서 확인하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기