Зазначимо: коли 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 день. Замовте відмовостійкість, і ваші агенти перестануть боятися збоїв.







