다운타임 없는 AI 구축: 다중 모델 장애 조치 라우터 만들기 (n8n + Python)
요약
AI 서비스의 신뢰성을 높이기 위한 '장애 조치 라우터(failover router)' 아키텍처를 소개합니다. 이 패턴은 단일 모델 제공업체 의존성 문제를 해결하며, n8n과 Python을 활용하여 구현할 수 있습니다.
핵심 포인트
- 단순 다운 방지 이상의 4가지 실패 유형 대응 필요
- 우선순위 목록(ordered list)과 요청당 타임아웃 설정이 핵심
- Rate limit에 대한 냉각 시간(cooldown) 추적 메커니즘 도입 권장
- 출력 검증기(Validator)를 통해 정확성까지 보장해야 함
제가 출시한 모든 AI 제품에는 공통된 숨겨진 의존성이 있습니다. 바로 하나의 모델 제공업체입니다. 그리고 모든 제공업체는 다운되거나, Rate-limited 되거나, 혹은 침묵하며 지연 시간 프로필을 변경합니다. 이런 일이 새벽 2시에 발생하면, 당신의 '자율적인' 워크플로우는 그저 멈춰버립니다.
장애 조치 라우터(failover router)는 추가할 수 있는 가장 저렴한 신뢰성 업그레이드입니다. 여기 제가 n8n과 순수 Python으로 사용하는 아키텍처를 소개합니다.
실제로 우회해야 하는 장애 모드들
장애 조치란 단순히 '다운되면 다른 것을 사용'하는 것 이상의 의미입니다. 네 가지의 명확한 실패 유형이 있으며, 각각은 다른 대응 방식이 필요합니다:
| Failure | Symptom | Right response |
|---|---|---|
| Hard outage | 5xx / connection refused | Immediate failover |
| ... | ||
| 마지막 것이 가장 치명적입니다. 제공업체는 파서가 사용할 수 없는 응답을 담아 완벽하게 유효한 HTTP 200을 반환할 수 있습니다. 검증(validate)하지 않으면, 당신은 '성공적으로' 실패하게 됩니다. |
핵심 아이디어: 우선순위 목록 + 상태 게이트
단일 모델에 하드코딩하지 마십시오. 순서가 지정된 목록과 요청당 예산(per-request budget)을 정의하세요:
PROVIDERS = [
{"name": "claude", "call": call_claude, "timeout": 25},
{"name": "gpt4o", "call": call_gpt, "timeout": 25},
...
그리고 이 목록을 순회하면서, 타임아웃(timeout), 429, 5xx 또는 유효하지 않은 출력 등 어떤 실패 클래스에서도 다음 항목으로 넘어갑니다. Rate-limited 된 제공업체가 계속해서 부하를 받는 대신 다음 N초 동안 건너뛰도록 냉각 시간(cooldown)을 추적합니다:
import time
_cooldown: dict[str, float] = {}
...
이것이 전체 패턴입니다. 세 가지 아이디어가 있습니다: 순서 지정 목록(ordered list), 요청당 타임아웃(per-request timeout), Rate limit에 대한 냉각 시간(cooldown).
검증기(Validator)가 중요한 부분입니다
대부분의 '장애 조치' 구현은 예외(exception)만 포착합니다. 그래서 여전히 쓰레기를 배포합니다. 라우터에 검증기(validator)를 제공하면, 그것은 단순히 가동 시간 게이트(uptime gate)가 아니라 정확성 게이트(correctness gate)가 됩니다:
import json
def valid_json_with_keys(*keys):
...
이제 JSON 대신 산문(prose)을 반환하는 제공업체는 자동으로 순위가 하락합니다.
n8n에서 구현하기
만약 n8n을 사용하고 있다면, 형태는 같지만 시각적으로 구축할 수 있습니다:
- 제공자 A를 위한 타이트한 시간 제한(timeout)이 설정된 HTTP Request 노드를 사용합니다 (기본값을 사용하지 말고 명시적으로 설정해야 합니다).
- 상태 코드를 확인하는 IF 노드와 본문(body)을 파싱하고 검증하는 Code 노드를 사용합니다.
- 실패 분기(failure branch)에서는 제공자 B의 HTTP Request 노드로 연결합니다. 이 과정을 반복합니다.
- 각 제공자별로 작은 static data / Redis 카운터를 유지하여, 속도 제한(rate-limit) 분기가 쿨다운 시간을 설정할 수 있도록 합니다.
- 체인이 완전히 소진되었을 때 알림을 받기 위해 Telegram/Slack으로 전송되는 최종 "모두 실패(all failed)" 웹훅을 설정합니다.
n8n의 함정: 사람들은 폴백(fallback) 로직을 순차적 분기(sequential one)가 아닌 병렬 분기(parallel branch)에 넣는 경향이 있어, 모든 제공자에게 매번 호출하게 되어 비용이 두 배로 발생합니다. 장애 조치(Failover)는 반드시 순차적이어야 합니다.
주의할 점 (Gotchas)
- 모든 오류에 대해 장애 조치를 수행하지 마세요. 형식화되지 않은 프롬프트(prompt) 하나가 세 제공자 모두에서 실패를 일으킬 수 있습니다. 먼저 입력을 검증해야 하며, 그렇지 않으면 잘못된 요청으로 전체 목록을 소진하게 됩니다.
- 출력값을 정규화하세요 (Normalize the output). 각 제공자는 다른 형태(shape)의 응답을 반환합니다. 검증기(validator)는 각 제공자의 원시 응답이 아닌, 정규화된 객체에 대해 실행되어야 합니다.
- 어떤 제공자가 요청을 처리했는지 기록하세요. 이것 없이는 건강한 90%의 주력 사용률(primary-hit-rate)과 조용히 성능이 저하된 경우를 구분할 수 없습니다.
- 시간 제한을 호출자(caller)의 시간 제한보다 낮게 설정하세요. 워크플로우가 60초를 허용하고 각 제공자가 25초가 걸린다면, 세 번의 실패는 75초가 되어 이미 예산을 초과합니다. 개별 호출당 시간이 아닌 총 시간을 제한해야 합니다.
- 무료 등급도 속도 제한이 있습니다. 무료 프론티어 모델은 제공자 C로서는 훌륭하지만, 제공자 A로는 그렇지 않습니다.
왜 수고를 할까 (Why bother)
단일 제공자 워크플로우는 생산성이라는 옷을 입은 단일 실패 지점(single point of failure)에 불과합니다. 라우팅 로직 30줄을 추가하는 것은 "AI가 다운됨"이라는 새벽 2시의 페이지를 로그 한 줄로 바꿉니다. 이것이 데모와 제품의 차이입니다.
저는 이 프로덕션 버전, 즉 쿨다운 상태(cooldown state), 출력 검증, BANT 리드 분류, Telegram 운영 알림 기능을 갖춘 3단계 캐스케이딩 라우터(Claude → GPT-4o → DeepSeek)를 n8n으로 바로 가져와 쓸 수 있는 워크플로우 형태로 패키징했습니다:
https://ancuboy.gumroad.com/l/agentflow-os
그리고 동일한 라우터의 Python 버전(stdlib 전용, 로컬 장애 조치 체인 포함)은 AI Agent Production Kit에 있습니다:
https://ancuboy.gumroad.com/l/production-ai-agent-kit
둘 다 원하는 만큼 지불하는 방식(pay-what-you-want)입니다. '조용한 쓰레기(silent garbage)' 케이스를 어떻게 처리하는지 궁금하신가요? 그 부분이 제가 가장 많이 조정하고 있는 부분입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기