Отметим: когда AI-агент внезапно перестаёт отвечать из-за сбоя OpenAI API или падения локального инференс-сервера, бизнес-процесс встаёт. Мы сталкивались с этим не раз: в production-системах с SLA 99.9% недоступность LLM на 10 минут означает потерю тысяч заказов. Недавно у клиента из финтеха GPT-4o упал на 15 минут из-за сбоя облака — наши fallback-агенты переключились на Claude 3.5 за 400 мс, и процесс не прервался. Поэтому мы разрабатываем архитектуру резервирования LLM и всего стека, которая держит удар — fallback провайдеры, circuit breaker'ы, checkpoint'и и human escalation. Всё под ключ, с гарантией и документацией.
Как обеспечить отказоустойчивость AI-агентов?
Ключевая задача — чтобы при отказе любого компонента (LLM API, инструмент, оркестратор) агент либо мгновенно переключался на резерв, либо корректно деградировал, не теряя прогресса. Мы реализуем четыре уровня защиты: резервирование LLM (fallback провайдеры), circuit breaker для инструментов, checkpoint восстановление для длительных задач, human escalation при неразрешимых ошибках. На практике priority fallback с circuit breaker в 2-3 раза надёжнее round-robin при пиковых нагрузках. SLA AI-агентов при таком подходе достигает 99.99%.
Почему priority fallback с circuit breaker надёжнее?
Первый и самый частый сценарий — основная модель (GPT-4o) недоступна из-за rate limit или аварии. Используем fallback цепочку: OpenAI → Anthropic → локальная модель (LLaMA 3 8B через vLLM). Переключение происходит за <500 мс с использованием circuit breaker для каждого провайдера. Health checks позволяют своевременно выявлять сбои.
Сравним популярные стратегии fallback:
| Стратегия | Время переключения | Надёжность (p99) | Сложность реализации |
|---|---|---|---|
| Round-robin | <50 мс | 99.9% | низкая |
| Priority с circuit breaker | <500 мс | 99.99% | средняя |
| Multi-head (все одновременно) | <100 мс | 99.95% | высокая |
Мы выбираем priority с circuit breaker: он даёт лучший баланс производительности и устойчивости. MLOps отказоустойчивость таких систем требует мониторинга и автоматического восстановления.
Практическая реализация: код и конфиги
Рассмотрим ключевые компоненты на Python.
Fallback LLM Provider
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import anthropic
import openai
class ResilientLLMClient:
def __init__(self):
self.providers = [
{"name": "openai", "client": openai.AsyncOpenAI(), "model": "gpt-4o"},
{"name": "anthropic", "client": anthropic.AsyncAnthropic(), "model": "claude-3-5-sonnet-20241022"},
{"name": "local_vllm", "client": openai.AsyncOpenAI(base_url="http://gpu1:8000/v1"), "model": "llama-3-8b"},
]
self.circuit_breakers = {p["name"]: CircuitBreaker() for p in self.providers}
async def generate(self, messages: list, **kwargs) -> str:
last_error = None
for provider in self.providers:
cb = self.circuit_breakers[provider["name"]]
if cb.is_open():
continue # провайдер отключён по circuit breaker
try:
result = await self._call_provider(provider, messages, **kwargs)
cb.record_success()
return result
except Exception as e:
cb.record_failure()
last_error = e
logger.warning(f"Provider {provider['name']} failed: {e}")
raise RuntimeError(f"All LLM providers failed. Last error: {last_error}")
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10),
retry=retry_if_exception_type((openai.RateLimitError, openai.APIConnectionError))
)
async def _call_provider(self, provider: dict, messages: list, **kwargs) -> str:
response = await provider["client"].chat.completions.create(
model=provider["model"],
messages=messages,
**kwargs
)
return response.choices[0].message.content
Circuit Breaker Pattern
Наш circuit breaker следует классическому Circuit Breaker pattern.
from enum import Enum
import time
class CircuitState(Enum):
CLOSED = "closed" # нормальная работа
OPEN = "open" # провайдер отключён
HALF_OPEN = "half_open" # тестируем восстановление
class CircuitBreaker:
def __init__(self, failure_threshold=5, reset_timeout=60):
self.failure_threshold = failure_threshold
self.reset_timeout = reset_timeout
self.failure_count = 0
self.last_failure_time = 0
self.state = CircuitState.CLOSED
def is_open(self) -> bool:
if self.state == CircuitState.OPEN:
if time.time() - self.last_failure_time > self.reset_timeout:
self.state = CircuitState.HALF_OPEN
return False
return True
return False
def record_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
logger.error(f"Circuit breaker OPEN after {self.failure_count} failures")
def record_success(self):
if self.state == CircuitState.HALF_OPEN:
self.state = CircuitState.CLOSED
self.failure_count = 0
logger.info("Circuit breaker CLOSED - provider recovered")
Checkpoint и восстановление задачи
Для длительных задач (>1 мин) сохраняем прогресс в Redis:
@dataclass
class AgentCheckpoint:
task_id: str
step_index: int
completed_steps: list[StepResult]
state: dict # произвольное состояние агента
saved_at: datetime
class CheckpointManager:
def __init__(self, storage: Redis):
self.storage = storage
async def save(self, checkpoint: AgentCheckpoint):
key = f"checkpoint:{checkpoint.task_id}"
await self.storage.setex(
key,
3600, # 1 час TTL
pickle.dumps(checkpoint)
)
async def load(self, task_id: str) -> AgentCheckpoint | None:
key = f"checkpoint:{task_id}"
data = await self.storage.get(key)
return pickle.loads(data) if data else None
async def resume_or_start(self, task: AgentTask) -> AgentCheckpoint:
existing = await self.load(task.id)
if existing:
logger.info(f"Resuming task {task.id} from step {existing.step_index}")
return existing
return AgentCheckpoint(task_id=task.id, step_index=0, completed_steps=[], state={}, saved_at=datetime.utcnow())
Human escalation при неразрешимых ошибках
class EscalationPolicy:
MAX_RETRIES = 3
MAX_FALLBACK_ATTEMPTS = 2
async def handle_failure(
self,
task: AgentTask,
error: Exception,
retry_count: int
) -> EscalationDecision:
if retry_count < self.MAX_RETRIES and self._is_retriable(error):
return EscalationDecision(action="retry", delay_seconds=2 ** retry_count)
if isinstance(error, ToolUnavailableError):
return EscalationDecision(action="use_fallback_tool", tool=self._get_fallback_tool(error.tool))
# Эскалация к человеку
await self.notify_human(task, error, retry_count)
return EscalationDecision(
action="escalate",
message=f"Task {task.id} requires human intervention after {retry_count} retries: {error}"
)
Этапы внедрения отказоустойчивости
Мы работаем по проверенному процессу:
- Аналитика (1-2 дня): аудит текущей архитектуры, выявление единых точек отказа, сбор требований по RTO/RPO. Для многоагентной резервации важно определить критичные сценарии.
- Проектирование (3-5 дней): выбор паттернов (retry, circuit breaker, checkpoint), прототипирование интеграции с провайдерами.
- Реализация (2-4 недели): написание кода, настройка circuit breaker'ов, развёртывание checkpoint storage, настройка эскалации.
- Тестирование (1 неделя): chaos testing — эмуляция отказов LLM API, падения инструментов, перегрузок. Измеряем p99 latency и процент успешных восстановлений.
- Деплой и документация (2-3 дня): канареечный rollout, мониторинг всех слоёв, написание runbook'ов.
Дополнительно проводим нагрузочное тестирование с эмуляцией сбоев в каждом слое. Это позволяет выявить скрытые проблемы, например, несимметричные таймауты между разными провайдерами.
Сравнение слоёв отказоустойчивости
| Слой | Компонент | Паттерн | Время восстановления |
|---|---|---|---|
| LLM | Провайдеры | Fallback + circuit breaker | <500 мс |
| Инструменты | API вызовы | Retry + exponential backoff | <10 сек |
| Агент | Логика задачи | Checkpoint + resume | <100 мс |
| Оркестратор | Распределение | Health check + reassign | <1 сек |
Сроки и что входит
Ориентировочные сроки: от 2 до 6 недель в зависимости от сложности системы и количества провайдеров. Стоимость рассчитывается индивидуально после аудита.
В результаты работы входит:
- Внедрение fallback LLM клиента с circuit breaker
- Checkpoint система на Redis (или другом хранилище)
- Политика human escalation с интеграцией в мессенджер
- Тестовый сценарий chaos testing
- Документация (runbook, архитектурная схема)
- Обучение команды (1 час)
- Гарантия на реализованные компоненты 3 месяца
Пример конфигурации circuit breaker для production
circuit_breaker:
failure_threshold: 5
reset_timeout: 60
half_open_max_requests: 1
monitor_interval: 10
Настройка подбирается под p99 latency провайдера. Для OpenAI с типичной задержкой 2-3 секунды используйте reset_timeout 60 секунд, для локальной модели — 10-15 секунд.
Типичные ошибки при проектировании
- Одинаковый timeout для всех провайдеров. Разные LLM API имеют разный p99 latency. Настраивайте индивидуальные таймауты.
- Сброс circuit breaker при каждом успехе. После открытия цепи делайте только один пробный запрос через reset_timeout, иначе провайдер будет нагружаться ошибками.
- Отсутствие деградации. Если все провайдеры упали, агент должен сообщить об этом, а не висеть в бесконечном цикле.
Наш многолетний опыт: мы реализовали отказоустойчивые AI-агенты для 20+ проектов — от чат-ботов e-commerce до систем автоматизации складов. Получите консультацию по вашей архитектуре: свяжитесь с нами для аудита за 1 день. Закажите отказоустойчивость, и ваши агенты перестанут бояться сбоев.







