AI Engineering Academy · 강의

대체 제공업체와 회로 차단기

주 제공업체가 느리거나 사용할 수 없을 때 회로 차단기 패턴을 사용해 OpenAI에서 Anthropic, 로컬 모델로 자동 전환하는 제공업체 연쇄 구조를 구축합니다.

레슨 3/413개 단계

대체 제공업체와 회로 차단기은(는) CoddyKit의 무료 AI Engineering Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Engineering Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

단일 제공업체의 위험

단일 LLM 제공업체에 의존하면 단일 장애 지점이 생깁니다. OpenAI는 해결에 수분에서 수 시간이 걸리는 장애를 겪은 적이 있습니다. 애플리케이션 전체가 GPT-4o의 가용성에 의존한다면 어떤 제공업체 장애든 즉시 사용자에게 서비스 중단으로 이어집니다. 대체 제공업체 전략은 기본 제공업체가 실패했을 때 대체 제공업체로 라우팅하여 서비스 연속성을 유지합니다.

제공업체 연쇄 정의하기

제공업체 연쇄는 순서대로 시도할 제공업체와 모델의 목록입니다. 기본 제공업체가 실패하거나 시간 초과되면 시스템은 자동으로 다음 제공업체를 시도합니다. 일반적인 연쇄는 다음과 같을 수 있습니다. OpenAI GPT-4o → Anthropic Claude 3.5 Sonnet → 로컬에 배포된 Llama 모델. 각 단계가 대체 경로이며, 로컬 모델은 중단될 수 없는 최후의 수단으로 사용됩니다.

from dataclasses import dataclass
from typing import Optional

@dataclass
class Provider:
    name: str
    base_url: Optional[str]
    api_key_env: str
    model: str
    priority: int  # lower = higher priority

CASCADE = [
    Provider('openai',    None,                              'OPENAI_API_KEY',    'gpt-4o',              1),
    Provider('anthropic', 'https://api.anthropic.com/v1',   'ANTHROPIC_API_KEY', 'claude-3-5-sonnet',   2),
    Provider('local',     'http://localhost:8000/v1',        'LOCAL_KEY',         'llama-3.1-8b-inst',   3),
]

대체 처리 반복문 구현하기

연쇄를 순회하는 간단한 try/except로 대체 처리 반복문을 구현하십시오. 일시적인 오류(시간 초과, 500, 503)를 포착하고 다음 제공업체로 이동하십시오. 인증 오류(401)나 잘못된 요청 오류(400)는 포착하지 마십시오. 이러한 오류는 다른 제공업체로 전환하기보다 프로그래밍 실수로서 즉시 드러나야 합니다.

import openai
import os

TRANSIENT_ERRORS = (openai.APITimeoutError, openai.InternalServerError, openai.APIConnectionError)

async def call_with_fallback(messages: list, **kwargs) -> str:
    for provider in CASCADE:
        try:
            client = openai.AsyncOpenAI(
                api_key=os.environ[provider.api_key_env],
                base_url=provider.base_url
            )
            resp = await client.chat.completions.create(
                model=provider.model,
                messages=messages,
                timeout=10.0,
                **kwargs
            )
            return resp.choices[0].message.content
        except TRANSIENT_ERRORS as e:
            print(f'Provider {provider.name} failed: {e}, trying next...')
    raise RuntimeError('All providers failed')

회로 차단기란 무엇인가요?

회로 차단기는 장애가 발생한 서비스에 장애 중 요청이 쏟아지는 것을 방지합니다. 전기 회로 차단기에서 이름을 따온 이 장치에는 세 가지 상태가 있습니다. Closed(요청이 정상적으로 통과), Open(요청이 즉시 거부됨), Half-Open(서비스가 복구되었는지 확인하기 위해 단일 테스트 요청만 통과). 이는 장애가 발생하는 동안 하위 서비스와 자체 애플리케이션을 모두 보호합니다.

# Circuit breaker state machine:
#
# CLOSED --> (failure_count >= threshold) --> OPEN
#    ^                                          |
#    |     (test_request succeeds)              | (timeout expires)
#    +------------ HALF_OPEN <-----------------+
#
# In OPEN state: immediately return fallback/error
# In HALF_OPEN: allow one request through to test recovery
# In CLOSED: normal operation, count failures

회로 차단기 구현하기

다음은 최소한의 회로 차단기 구현입니다. 실패 횟수와 회로가 열린 시간을 추적하십시오. 실패 횟수가 임계값을 초과하면 회로를 여십시오. 설정 가능한 재설정 시간 초과가 지나면 하나의 탐색 요청을 허용하십시오. 탐색 요청이 성공하면 회로를 닫으십시오. 실패하면 회로를 열린 상태로 유지하고 시간 초과를 재설정하십시오.

import time
from enum import Enum

class State(Enum):
    CLOSED = 'closed'
    OPEN = 'open'
    HALF_OPEN = 'half_open'

class CircuitBreaker:
    def __init__(self, failure_threshold=5, reset_timeout=60):
        self.state = State.CLOSED
        self.failure_count = 0
        self.failure_threshold = failure_threshold
        self.reset_timeout = reset_timeout
        self.opened_at = None

    def record_success(self):
        self.failure_count = 0
        self.state = State.CLOSED

    def record_failure(self):
        self.failure_count += 1
        if self.failure_count >= self.failure_threshold:
            self.state = State.OPEN
            self.opened_at = time.time()

    def can_attempt(self) -> bool:
        if self.state == State.CLOSED:
            return True
        if self.state == State.OPEN:
            if time.time() - self.opened_at > self.reset_timeout:
                self.state = State.HALF_OPEN
                return True  # allow one probe
            return False
        return True  # HALF_OPEN: allow probe

제공업체에 회로 차단기 통합하기

제공업체마다 하나의 회로 차단기를 유지하십시오. 제공업체를 호출하기 전에 해당 회로 차단기가 시도를 허용하는지 확인하십시오. 각 호출 후 성공 또는 실패를 기록하십시오. 제공업체의 회로가 열리면 대체 처리 반복문이 자연스럽게 해당 제공업체를 건너뛰고 연쇄의 다음 제공업체를 시도하므로, 매번 시간 초과를 기다릴 필요가 없습니다.

breakers = {p.name: CircuitBreaker(failure_threshold=5, reset_timeout=60) for p in CASCADE}

async def call_with_circuit_breaker(messages: list) -> str:
    for provider in CASCADE:
        breaker = breakers[provider.name]
        if not breaker.can_attempt():
            continue  # skip this provider, circuit is open
        try:
            result = await call_provider(provider, messages)
            breaker.record_success()
            return result
        except TRANSIENT_ERRORS as e:
            breaker.record_failure()
            print(f'{provider.name} failed ({breaker.failure_count}/{breaker.failure_threshold})')
    raise RuntimeError('All providers exhausted')

느린 호출을 실패로 감지하기

사용자 경험 측면에서 30초 후 응답하는 제공업체는 완전히 중단된 제공업체와 거의 다르지 않습니다. 제공업체 호출마다 공격적인 시간 초과를 설정하고 시간 초과 예외를 회로 차단기의 실패로 처리하십시오. 10초의 시간 초과를 설정하면 대체 처리가 충분히 빠르게 시작되어 사용자는 멈춘 화면이 아니라 짧은 지연만 경험하게 됩니다.

async def call_provider(provider: Provider, messages: list) -> str:
    client = openai.AsyncOpenAI(
        api_key=os.environ[provider.api_key_env],
        base_url=provider.base_url
    )
    try:
        resp = await asyncio.wait_for(
            client.chat.completions.create(model=provider.model, messages=messages),
            timeout=10.0  # fail fast, let circuit breaker count it
        )
        return resp.choices[0].message.content
    except asyncio.TimeoutError:
        raise openai.APITimeoutError('Provider timed out')

제공업체 상태 대시보드

각 제공업체의 현재 회로 차단기 상태를 보여 주는 /health/providers 엔드포인트를 노출하십시오. 실패 횟수, 상태(닫힘/열림/반열림), 재설정까지 남은 시간을 포함하십시오. 이를 통해 장애가 발생했을 때 어떤 제공업체가 정상인지 한눈에 확인할 수 있으며, 수동으로 재설정을 강제할지 자동 복구를 기다릴지 결정하는 데 도움이 됩니다.

from fastapi import FastAPI

app = FastAPI()

@app.get('/health/providers')
def provider_health():
    return {
        name: {
            'state': cb.state.value,
            'failure_count': cb.failure_count,
            'seconds_until_reset': (
                max(0, cb.reset_timeout - (time.time() - cb.opened_at))
                if cb.state == State.OPEN else None
            )
        }
        for name, cb in breakers.items()
    }

제공업체 출력 맞추기

제공업체마다 응답 형식, 안전 필터, 기능이 다릅니다. GPT-4o에서 Claude로 전환하면 GPT-4o가 답변했을 요청을 모델이 거부할 수도 있습니다. 각 제공업체의 관례에 맞게 프롬프트를 조정하는 제공업체별 프롬프트 래퍼를 유지하십시오. 각 대체 제공업체를 독립적으로 테스트하여 사용 사례에 적합한 출력을 생성하는지 확인하십시오.

def adapt_messages_for_provider(provider: Provider, messages: list) -> list:
    if provider.name == 'anthropic':
        # Claude prefers explicit task descriptions
        system = next((m['content'] for m in messages if m['role'] == 'system'), '')
        if 'JSON' not in system:
            messages = [{'role': 'system', 'content': system + ' Respond in JSON.'}] + [
                m for m in messages if m['role'] != 'system'
            ]
    return messages

대체 처리 동작 테스트하기

잘못된 API 키를 제공하거나 오류를 발생시키는 모의 객체를 사용하여 기본 제공업체가 실패하도록 강제하고, 대체 처리가 시작되어 유효한 응답을 반환하는지 확인하는 테스트를 작성하십시오. 또한 설정된 실패 횟수에 도달한 후 회로 차단기가 올바르게 열리는지, 재설정 시간 초과 후 복구되는지도 테스트하십시오. 한 번도 테스트하지 않은 대체 처리 로직은 실제 장애에서 신뢰할 수 없습니다.

import pytest
from unittest.mock import AsyncMock, patch

@pytest.mark.asyncio
async def test_fallback_on_primary_timeout():
    # Primary provider times out
    with patch('your_module.call_provider', side_effect=[
        openai.APITimeoutError('Timeout'),  # primary fails
        'Claude response'                   # fallback succeeds
    ]):
        result = await call_with_circuit_breaker([{'role': 'user', 'content': 'Hello'}])
    assert result == 'Claude response'

제공자 연쇄의 비용 고려 사항

대체 제공자는 주 제공자와 가격이 다른 경우가 많습니다. Anthropic Claude는 모델 등급에 따라 OpenAI GPT-4o보다 비쌀 수도 있고 저렴할 수도 있습니다. 각 요청을 처리한 제공자를 추적하고 비용 귀속을 별도로 계산하십시오. 대체 제공자가 지속적으로 더 비싸다면, 주 제공자의 용량이 부족한 것은 아닌지, 그리고 대체 제공자를 자주 사용하는 것보다 더 높은 요청 한도 등급으로 업그레이드하는 편이 비용 효율적인지 조사하십시오.

# Approximate costs per 1M tokens (2026):
PROVIDER_COSTS = {
    'openai/gpt-4o':          {'input': 2.50, 'output': 10.00},
    'anthropic/claude-3.5-sonnet': {'input': 3.00, 'output': 15.00},
    'openai/gpt-4o-mini':     {'input': 0.15, 'output': 0.60},
    'local/llama-3.1-8b':     {'input': 0.00, 'output': 0.00},  # infra cost only
}

# If fallback adds $0.50/day and a Tier 2 upgrade costs $100/month:
# Tier 2 pays off if you use fallback > 200 requests/day

빠른 확인

회로 차단기와 대체 제공자에 대한 이해도를 확인해 보십시오.

단원 요약

이 단원에서는 다음을 배웠습니다. 제공자 연쇄는 주 LLM 제공자에서 백업 제공자로 이어지는 순서가 지정된 대체 처리 순서를 정의하고, 회로 차단기는 장애가 발생한 제공자에 실패 요청을 계속 보내지 않도록 일정 횟수의 실패 후 빠르게 요청을 차단하며, 제공자별 시간 제한은 느린 호출이 사용자를 차단하지 않고 빠르게 대체 처리로 전환되도록 합니다. 다음 단원에서는 시간 제한 예산을 설정하고 정상적인 성능 저하 처리를 구현합니다.

무료로 시작

AI 튜터와 함께 Python을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
30
레슨
120

자주 묻는 질문

“대체 제공업체와 회로 차단기” 강의는 무료인가요?

네 — “대체 제공업체와 회로 차단기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Engineering Academy 강의 전체를 잠금 해제할 수 있습니다. AI Engineering Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“대체 제공업체와 회로 차단기”에서 뭘 배우나요?

주 제공업체가 느리거나 사용할 수 없을 때 회로 차단기 패턴을 사용해 OpenAI에서 Anthropic, 로컬 모델로 자동 전환하는 제공업체 연쇄 구조를 구축합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Engineering Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Engineering Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Engineering Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“대체 제공업체와 회로 차단기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Engineering Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Engineering Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. LLM 지연 시간 측정: TTFT와 TPOT
  2. 부하 분산과 다중 키 전략
  3. 대체 제공업체와 회로 차단기
  4. 시간 제한 예산과 우아한 성능 저하
← AI Engineering Academy(으)로 돌아가기