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







