0Pricing
AI Engineering Academy · Урок

Резервные поставщики и автоматические размыкатели

Создайте каскад поставщиков, который автоматически переключается с OpenAI на Anthropic, а затем на локальную модель, если основной поставщик работает медленно или недоступен, используя шаблон автоматического размыкателя.

«Резервные поставщики и автоматические размыкатели» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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')

Что такое автоматический размыкатель цепи

Автоматический размыкатель цепи не позволяет неисправному сервису получать шквал запросов во время сбоя. Он назван по аналогии с электрическими автоматическими выключателями и имеет три состояния: замкнутое (запросы проходят обычно), разомкнутое (запросы немедленно отклоняются) и полуразомкнутое (пропускается один проверочный запрос, чтобы выяснить, восстановился ли сервис). Это защищает и зависимый сервис, и Ваше приложение во время инцидентов.

# 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 к резервным, автоматические выключатели не позволяют постоянно отправлять запросы неисправному поставщику, быстро прерывая выполнение после достижения порога ошибок, а тайм-ауты для каждого поставщика обеспечивают быстрый переход к резервному поставщику вместо блокировки пользователей из-за медленных вызовов. Далее Вы зададите бюджеты тайм-аутов и реализуете плавное снижение качества обслуживания.

Часто задаваемые вопросы

Урок «Резервные поставщики и автоматические размыкатели» бесплатный?

Да — полный текст урока «Резервные поставщики и автоматические размыкатели» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.

Чему я научусь в уроке «Резервные поставщики и автоматические размыкатели»?

Создайте каскад поставщиков, который автоматически переключается с OpenAI на Anthropic, а затем на локальную модель, если основной поставщик работает медленно или недоступен, используя шаблон автомат… Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать AI Engineering Academy?

Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Резервные поставщики и автоматические размыкатели»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке AI Engineering Academy?

Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Измерение задержки LLM: TTFT и TPOT
  2. Балансировка нагрузки и стратегии с несколькими ключами
  3. Резервные поставщики и автоматические размыкатели
  4. Бюджеты времени ожидания и плавное ухудшение качества
← Назад к AI Engineering Academy