English translation is not available yet. Showing Russian content.
Что такое circuit breaker и как он применяется к LLM API вызовам?
Краткий тезис
Circuit breaker (автоматический выключатель) — это паттерн отказоустойчивости, который предотвращает каскадные отказы в распределённых системах. Применительно к LLM API он защищает приложение от перегрузок, превышения лимитов и дорогостоящих повторных вызовов при сбоях. Когда количество ошибок превышает порог (например, 50% за 10 секунд), цепь «размыкается», и все последующие вызовы мгновенно перенаправляются в fallback (кэш, другой провайдер, сообщение об ошибке). Через заданный таймаут цепь переходит в half-open состояние и пропускает пробный запрос для проверки восстановления сервиса.
1. Термин: Circuit breaker (автоматический выключатель)
Circuit breaker — это паттерн, заимствованный из электротехники. В программных системах он реализует три состояния:
| Состояние | Описание | Поведение при вызове |
|---|---|---|
| Closed (замкнут) | Нормальная работа | Вызовы проходят к LLM API, мониторится частота ошибок |
| Open (разомкнут) | Сервис считается недоступным | Все вызовы мгновенно отклоняются или идут в fallback, без реального запроса к API |
| Half-open (полуоткрыт) | Проверка восстановления | Пропускается ограниченное число пробных запросов; если успешны — цепь закрывается, иначе снова открывается |
Ключевые параметры:
- Порог ошибок (error threshold) — доля или абсолютное количество ошибок за окно времени (например, 5 ошибок за 10 секунд или 50% ошибок).
- Таймаут восстановления (timeout|reset timeout) — время, через которое цепь переходит из open в half-open (например, 30 секунд).
- Размер окна (sliding window) — период, за который считаются ошибки (фиксированное или скользящее окно).
2. Зачем circuit breaker для LLM API вызовов?
LLM API (OpenAI, Anthropic, локальные модели) имеют специфические проблемы:
- Rate limits — превышение лимита запросов в минуту/день приводит к HTTP 429.
- Перегрузка провайдера — временные сбои (5xx), высокая задержка.
- Дороговизна — повторные вызовы при ошибках тратят квоту и деньги.
- Каскадные отказы — если один компонент (например, эмбеддер) падает, все зависимые сервисы тоже начинают фейлить, усугубляя ситуацию.
- Нестабильность сети — таймауты, DNS-ошибки.
Circuit breaker позволяет:
- Быстро отключать проблемный API, не дожидаясь таймаута каждого запроса.
- Давать время сервису восстановиться (backpressure).
- Прозрачно переключаться на резервные варианты (fallback).
3. Как работает circuit breaker: пошаговый сценарий
- Closed: Все вызовы идут к LLM API. Счётчик ошибок (например, HTTP 429, 5xx, таймауты) обновляется в скользящем окне.
- Порог превышен: Доля ошибок > 50% за последние 10 секунд → цепь размыкается (open).
- Open: Любой вызов к API немедленно возвращает fallback-ответ (кэш, заглушка, другой провайдер) без реального HTTP-запроса. Таймер восстановления запущен.
- Half-open: Через 30 секунд цепь переходит в half-open. Пропускается один пробный запрос (или несколько, в зависимости от конфигурации).
- Если запрос успешен → цепь закрывается (closed), счётчики сбрасываются.
- Если запрос снова ошибка → цепь снова открывается (open), таймер сбрасывается.
- Цикл повторяется.
Формула порога (пример):
error_rate = (число ошибок за окно) / (общее число запросов за окно)
if error_rate > threshold → open
4. Реализация на Python с tenacity
Библиотека tenacity предоставляет декораторы для повторных попыток, но circuit breaker в ней нет напрямую. Однако можно реализовать свой breaker поверх tenacity или использовать готовые библиотеки (например, pybreaker, circuitbreaker). Ниже пример с circuitbreaker:
from circuitbreaker import circuit
import time
import openai # гипотетический клиент
@circuit(
failure_threshold=5, # 5 ошибок подряд
recovery_timeout=30, # 30 секунд до half-open
expected_exception=Exception # любые исключения считаются ошибкой
)
def call_llm_api(prompt: str) -> str:
response = openai.Completion.create(
engine="gpt-4",
prompt=prompt,
max_tokens=100
)
return response.choices[0].text
# Использование с fallback
def get_llm_response(prompt: str) -> str:
try:
return call_llm_api(prompt)
except Exception:
# fallback: кэш или другой провайдер
return get_from_cache(prompt) or "Сервис временно недоступен"
Важно: circuitbreaker по умолчанию считает ошибки последовательно (failure_threshold подряд). Для скользящего окна по времени нужно использовать кастомный брейкер или библиотеку resilience4j (Java) через адаптер.
5. Интеграция с RAG/Agentic системами
В архитектуре Agentic RAG circuit breaker применяется на нескольких уровнях:
- Retrieval API (векторная БД, эмбеддер) — если эмбеддер падает, retrieval не работает, breaker переключает на fallback (например, BM25).
- LLM API (генерация ответа) — breaker переключает на кэшированный ответ, другую модель (локальную) или сообщение об ошибке.
- Внешние инструменты (калькулятор, поиск в интернете) — breaker изолирует сбойный инструмент, не блокируя весь агент.
Пример конфигурации для агента:
# Словарь брейкеров для разных сервисов
breakers = {
"openai": CircuitBreaker(failure_threshold=3, recovery_timeout=60),
"pinecone": CircuitBreaker(failure_threshold=5, recovery_timeout=30),
"serpapi": CircuitBreaker(failure_threshold=2, recovery_timeout=120)
}
def agent_workflow(query):
# Попытка retrieval
if not breakers["pinecone"].call(pinecone_query, fallback=bm25_search):
return "Поиск временно недоступен"
# Генерация
response = breakers["openai"].call(llm_generate, fallback=cached_answer)
return response
6. Альтернативы и дополнения
| Паттерн | Описание | Отличие от circuit breaker |
|---|---|---|
| Retry (повтор) | Повторяет запрос при ошибке (обычно с exponential backoff) | Не защищает от каскадных отказов; может усугубить нагрузку |
| Rate limiting (ограничение частоты) | Ограничивает количество запросов в единицу времени | Предотвращает превышение лимитов, но не реагирует на ошибки сервера |
| Bulkhead (перегородка) | Изолирует ресурсы (пулы потоков) для разных сервисов | Предотвращает «заражение» одного сервиса другим, но не отключает проблемный |
| Fallback (запасной вариант) | Возвращает альтернативный ответ при сбое | Часто используется вместе с breaker как действие при open |
| Timeout (таймаут) | Ограничивает время ожидания ответа | Необходим, но не решает проблему повторных таймаутов |
Circuit breaker часто комбинируют с retry (retry внутри closed состояния) и fallback (действие при open).
7. Мониторинг и метрики
Для production-системы нужно отслеживать:
- Текущее состояние каждого breaker (closed/open/half-open) — метка в Prometheus.
- Количество переходов в open — alert при частых размыканиях.
- Доля fallback-ответов — показатель качества обслуживания.
- Время восстановления — среднее время между open и half-open.
Пример метрик (Python + prometheus_client):
from prometheus_client import Counter, Gauge
breaker_state = Gauge('llm_circuit_breaker_state', '0=closed,1=half-open,2=open', ['service'])
breaker_trips = Counter('llm_circuit_breaker_trips_total', 'Number of times breaker opened', ['service'])
8. Продвинутые техники
- Адаптивный порог — порог ошибок динамически подстраивается под историческую частоту ошибок (например, на основе скользящей медианы).
- Машинное обучение — предсказание отказов по временным рядам (задержка, ошибки) и упреждающее размыкание.
- Каскадные breaker'ы — если один breaker открыт, автоматически открываются зависимые (например, при отказе эмбеддера открывается breaker генерации).
- Полуавтоматическое восстановление — half-open может требовать ручного подтверждения для критичных сервисов.
9. Практические рекомендации для production
- Начинайте с простого — используйте готовую библиотеку (pybreaker, circuitbreaker, resilience4j).
- Настройте пороги под конкретный API — для OpenAI (частые 429) порог может быть выше, для локальной модели — ниже.
- Всегда добавляйте fallback — кэш, другой провайдер, сообщение пользователю.
- Мониторьте и алертите — breaker не должен быть «чёрным ящиком».
- Тестируйте с хаос-инжинирингом — искусственно вызывайте ошибки, чтобы проверить поведение.
- Учитывайте идемпотентность — если запрос неидемпотентен, breaker может привести к дублированию (например, оплата). Для LLM это редко актуально.
Пет-проект для закрепления
Задача: Реализовать простого Telegram-бота, который отвечает на вопросы через OpenAI API, но защищён circuit breaker'ом.
Инструменты: Python, python-telegram-bot, openai, circuitbreaker, redis (для кэша).
Шаги:
- Создайте бота, который принимает сообщения и вызывает
openai.ChatCompletion.create. - Оберните вызов в
@circuit(failure_threshold=3, recovery_timeout=60). - Реализуйте fallback: при открытом breaker возвращайте последний кэшированный ответ из Redis или заглушку «Сервис временно недоступен».
- Добавьте логирование состояний breaker в файл.
- Напишите скрипт, который имитирует ошибки (например, подменяет API-ключ на невалидный) и проверяет, что бот переключается на fallback.
Ожидаемый результат: Бот корректно обрабатывает сбои API, не зависает, пользователь видит осмысленный ответ даже при отказе.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 401 | Retry strategies для LLM API |
| 402 | Fallback-механизмы в RAG |
| 403 | Rate limiting и его применение |
| 405 | Graceful degradation в агентных системах |
| 406 | Мониторинг и алертинг LLM-вызовов |
| 407 | Кэширование ответов LLM |
Навигация
- Предыдущий: 403
- Следующий: 405
- Индекс: 00. Индекс разборов