Чек-лист подготовки LLM API к продакшену: таймауты, повторные запросы, circuit breaker и graceful degradation
Единый чек-лист для подготовки интеграций LLM API к продакшену: бюджеты таймаутов, retry-политики с jitter, паттерн circuit breaker, цепочки graceful degradation, обработка rate limit, надёжность стриминга и маршрутизация на основе health check.
Подготовка LLM API к продакшену означает выстраивание пяти слоёв защиты, каждый из которых закрывает свой класс отказов. Таймауты предотвращают блокировку приложения зависшими соединениями. Retry с jitter восстанавливают работоспособность после временных сбоев без создания retry-штормов. Circuit breaker останавливают трафик к неисправному провайдеру до того, как каскадный отказ обрушит всю систему. Цепочки graceful degradation маршрутизируют на более дешёвые или кешированные альтернативы, когда основная модель недоступна. Health-check маршрутизация делает всё это проактивным. Этот чек-лист объединяет опыт, накопленный нами при эксплуатации OpenAI-совместимой маршрутизации через несколько провайдеров.
OpenAI-совместимость означает, что провайдер предоставляет endpoint chat-completions, чей контракт запроса и ответа достаточно близок к API OpenAI, чтобы немодифицированный вызов OpenAI SDK работал после замены трёх значений: API key, base URL, название модели. Минимальная поверхность на практике —POST /v1/chat/completions с messages, model и потоковым ответом в форме OpenAI.
Зачем нужен единый чек-лист
По отдельности каждая тема надёжности хорошо задокументирована. OpenAI публикует руководство по rate limit. Anthropic описывает коды ошибок и retry-поведение SDK. DeepSeek перечисляет коды ошибок с рекомендуемыми действиями. Мы отдельно разбирали таймауты, retry и идемпотентность и fallback-маршрутизацию.
Не хватает единого чек-листа, который связывает все эти элементы в целостный продакшен-деплоймент. На практике retry борются с circuit breaker, таймауты ломают логику fallback, а health check мониторят не те сигналы. Это руководство собирает фрагменты воедино.
Чек-лист: общий вид
Распечатайте эту таблицу, вставьте в deployment runbook и проверяйте каждый пункт перед выкаткой.
| # | Категория | Проверка | Приоритет |
|---|---|---|---|
| 1 | Таймауты | Connect таймаут установлен (5-10 с) | Критический |
| 2 | Таймауты | Read таймаут по сценарию (30-120 с non-streaming) | Критический |
| 3 | Таймауты | Total таймаут ограничивает end-to-end latency | Критический |
| 4 | Таймауты | Per-chunk deadline для streaming | Высокий |
| 5 | Retry | Повторяются только 429 и 5xx | Критический |
| 6 | Retry | Exponential backoff + full jitter | Критический |
| 7 | Retry | Retry-After учитывается | Критический |
| 8 | Retry | Максимум 3-5 попыток, retry budget <10% трафика | Высокий |
| 9 | Retry | Retry только на одном уровне | Высокий |
| 10 | Circuit breaker | Порог failure rate настроен (20-30% за 60 с) | Высокий |
| 11 | Circuit breaker | В open-состоянии мгновенный fail fast | Высокий |
| 12 | Circuit breaker | Half-open пробы тестируют восстановление | Средний |
| 13 | Degradation | Цепочка деградации моделей определена | Высокий |
| 14 | Degradation | Кеш/очередь при полном отказе | Средний |
| 15 | Rate limit | RPM + TPM одновременно на уровне приложения | Высокий |
| 16 | Rate limit | Предварительная оценка токенов | Средний |
| 17 | Streaming | Обработка partial response и reconnect | Средний |
| 18 | Мониторинг | p50/p95/p99 latency по провайдерам | Высокий |
| 19 | Мониторинг | Алерты на error rate и смену состояний circuit breaker | Высокий |
| 20 | Мониторинг | Трекинг cost per request и детекция аномалий | Средний |
Стратегия таймаутов
Таймауты — первая линия обороны. Без них единственный медленный или недоступный провайдер бессрочно блокирует соединения, потоки и внимание пользователя.
LLM API заметно медленнее обычных REST API. Один запрос генерации обычно занимает 5-60 секунд в зависимости от размера модели, длины ввода и вывода. Стандартные значения по умолчанию HTTP-клиента (часто 30 секунд total) вызывают ложные таймауты на легитимных запросах с длинной генерацией. Полное отсутствие таймаутов означает, что сбой провайдера тихо заблокирует ваше приложение.
Connect таймаут защищает фазу TCP/TLS handshake. Установите 5-10 секунд. Если провайдер не может принять соединение за это время, endpoint вероятно недоступен.
Read таймаут (для streaming — first-byte таймаут) защищает интервал между отправкой запроса и получением первого байта ответа. Для non-streaming 30-120 секунд разумно. Для streaming первый chunk должен прийти за 10-30 секунд; далее — per-chunk deadline 15-30 секунд.
Total таймаут ограничивает весь жизненный цикл запроса. Для non-streaming обычно 60-180 секунд. Если запрос не завершился в пределах общего бюджета, завершайте его независимо от частичного прогресса.
import httpx
# Продакшен-конфигурация таймаутов для LLM API
client = httpx.Client(
timeout=httpx.Timeout(
connect=5.0, # TCP/TLS handshake
read=60.0, # Ожидание первого байта / следующего chunk
write=10.0, # Отправка тела запроса
pool=10.0, # Ожидание соединения из пула
)
)
Рекомендации по провайдерам
| Провайдер | Connect | Read (non-streaming) | Read (streaming, первый chunk) | Примечания |
|---|---|---|---|---|
| OpenAI | 5-10 с | 60-120 с | 15-30 с | Модели с длинным контекстом (GPT-5.5) могут потребовать больший read |
| Anthropic | 5-10 с | 60-120 с | 15-30 с | Extended thinking длится дольше; сервер возвращает 504 при таймауте |
| DeepSeek | 5-10 с | 60-90 с | 10-20 с | V4 Flash обычно быстрый; V4 Pro reasoning может потребовать больше времени |
| DashScope | 5-10 с | 60-90 с | 10-20 с | OpenAI-совместимый endpoint; логика таймаутов та же |
Retry-политика с exponential backoff и jitter
Retry восстанавливают работу после временных сбоев. Наивные retry приносят больше вреда, чем сам сбой.
Что повторять:
- 429 (rate limit) с учётом
Retry-After - 500 (internal server error) с backoff
- 503 (server overloaded) с backoff
- Connection reset, DNS таймаут, сбой TLS handshake
Что никогда не повторять:
- 400 (bad request) — каждый раз упадёт так же
- 401 (authentication error) — надо исправить API key
- 402 (billing/balance) — надо пополнить баланс
- 403 (permission error) — надо менять конфигурацию
- 413 (request too large) — надо уменьшить запрос
Exponential backoff с full jitter предотвращает retry-штормы. Чистый exponential backoff без jitter синхронизирует все клиенты на повторную попытку в один момент, воссоздавая thundering herd на каждой итерации.
import random
import time
def retry_with_jitter(func, max_attempts=3, base_delay=1.0, max_delay=32.0):
for attempt in range(max_attempts):
try:
return func()
except RetryableError as e:
if attempt == max_attempts - 1:
raise
# Учитываем Retry-After от провайдера
retry_after = getattr(e, 'retry_after', None)
if retry_after:
delay = float(retry_after)
else:
# Full jitter: случайное значение от 0 до экспоненциального потолка
exp_delay = min(max_delay, base_delay * (2 ** attempt))
delay = random.uniform(0, exp_delay)
time.sleep(delay)
Retry budget предотвращает ситуацию, когда один деградировавший endpoint поглощает всю ёмкость. Глобальное ограничение: retry не должны превышать 10% от общего трафика. При превышении бюджета — мгновенный fail fast и переключение на fallback вместо продолжения запросов к деградировавшему провайдеру.
Retry только на одном уровне предотвращает мультипликативный взрыв. Три retry на каждом уровне пятислойной цепочки сервисов = 3^5 = 243 бэкенд-вызова на один пользовательский запрос. Выберите один уровень — обычно внешний слой приложения или routing gateway.
При использовании routing-слоя типа TheRouter настраивайте retry на уровне маршрутизатора, а не в коде приложения. Маршрутизатор видит все endpoint провайдеров и принимает более разумные решения о том, когда повторить запрос, а когда переключиться на альтернативный маршрут.
Паттерн circuit breaker для LLM-провайдеров
Retry обрабатывают временные сбои. Circuit breaker обрабатывает системные сбои.
Circuit breaker отслеживает failure rate в скользящем окне и имеет три состояния.
Closed (нормальная работа): все запросы проходят к провайдеру. Breaker ведёт статистику успехов и отказов.
Open (сработал): failure rate превысил порог. Все запросы немедленно получают fast fail без обращения к провайдеру. Это даёт провайдеру время восстановиться.
Half-open (зондирование): после cooldown breaker пропускает несколько тестовых запросов. Если они успешны — breaker закрывается. Если нет — снова открывается.
Рекомендуемые пороги для LLM API
| Параметр | Рекомендуемое значение | Обоснование |
|---|---|---|
| Скользящее окно | 60 секунд | Достаточно для детекции, достаточно коротко для быстрой реакции |
| Порог failure rate | 20-30% запросов | Выше, чем для типичных микросервисов — LLM API имеют базовый уровень ошибок |
| Последовательные отказы | 5-10 | Альтернативный триггер для endpoint с низким трафиком |
| Cooldown | 30-60 секунд | Достаточно для восстановления провайдера |
| Half-open пробы | 1-3 запроса | Минимальный трафик для проверки |
| Сброс при успехе | 2-3 последовательных успеха в half-open | Подтверждение стабильного восстановления |
Дополнительные триггеры, специфичные для LLM
- Деградация latency: срабатывание, когда p95 latency превышает базовый уровень в 3 раза
- Аномалия стоимости: срабатывание, когда cost per request превышает установленный порог — ловит runaway agent loops
- Частота разрыва streaming: срабатывание, когда более 30% streaming-ответов обрываются до финального chunk
Цепочки graceful degradation
Когда circuit breaker срабатывает, приложению нужен fallback-путь от основной модели через всё более дешёвые или простые альтернативы, вплоть до кешированных ответов.
Пример цепочки для coding assistant
| Уровень | Модель | Триггер | Влияние на пользователя |
|---|---|---|---|
| L0 (основная) | DeepSeek V4 Pro | Нормальная работа | Полные возможности |
| L1 (fallback) | DeepSeek V4 Flash | L0 circuit open или latency >45 с | Чуть менее точно, значительно быстрее |
| L2 (бюджетная) | Qwen3.8 Flash через DashScope | L0 + L1 в open | Другая модель, сопоставимый уровень |
| L3 (кеш) | Кешированные ответы для частых запросов | Все live-провайдеры деградированы | Возможно устаревшие, но доступные |
| L4 (деградация) | Сообщение об ошибке с ETA | Все fallback исчерпаны | Честное уведомление о недоступности |
Ключевые решения
Кросс-провайдерный fallback устойчивее, чем fallback внутри одного провайдера. Если OpenAI лежит, переключение на другую модель OpenAI может не помочь. Переключение на Anthropic или DeepSeek через маршрутизацию fallback в TheRouter обходит отказы на уровне провайдера.
Деградация модели часто лучше, чем ожидание таймаута. Ответ от меньшей модели за 2 секунды полезнее, чем 30 секунд ожидания таймаута от большой модели. Настраивайте пороги деградации по latency, а не только по ошибкам.
Кешированные ответы требуют политики свежести. Для FAQ-запросов 1 час кеша обычно приемлем. Для данных реального времени кеширование неуместно.
Честная деградация укрепляет доверие. Когда все fallback отказали, сообщите пользователю, что произошло и когда ожидается восстановление.
Обработка rate limit
Rate limit — не ошибка, а управление потоком. Продакшен-система должна поглощать rate limit, а не обрабатывать как сбой.
Двухосевой трекинг: LLM-провайдеры ограничивают по RPM (запросы в минуту) и TPM (токены в минуту) одновременно. Запросы с длинным контекстом легко превышают TPM, оставаясь в пределах RPM. Отслеживайте оба показателя на уровне приложения.
Предварительная оценка токенов предотвращает внезапное превышение TPM. Используйте tokenizer (tiktoken для OpenAI-совместимых API) для оценки количества токенов перед отправкой. Если расчётное количество превысит остаток TPM-бюджета — ставьте запрос в очередь, а не отправляйте.
Всегда указывайте max_tokens для ограничения длины вывода. Без этого модель, генерирующая необычно длинный ответ, тихо исчерпает ваш TPM-бюджет на одном запросе.
Поглощение пиков через очередь: вместо отбрасывания запросов сверх лимита ставьте их в очередь с ограниченным временем ожидания. Очередь на Redis или в памяти с максимальным ожиданием 5-10 секунд сглаживает пиковый трафик без потери запросов.
Надёжность streaming
Streaming (SSE) ответы создают проблемы надёжности, которых нет у batch-запросов.
Обработка partial response: streaming-ответ может оборваться. Приложение должно отслеживать полученный объём и наличие маркера завершения ([DONE] в OpenAI-совместимых API или message_stop у Anthropic). Partial response без стоп-маркера надо отметить как неполный.
Стратегия reconnect: не переподключайтесь слепо с тем же prompt. Провайдер мог частично обработать запрос и выставить счёт. Пометьте partial response как неполный и либо покажите его пользователю с предупреждением, либо начните новый запрос с нуля.
Per-chunk таймаут: установите deadline для каждого SSE chunk, а не только для первого байта. Провайдер, отправляющий первый chunk за 2 секунды, но потом зависающий на 60 секунд между chunk, фактически деградирован.
Health check и проактивная маршрутизация
Реактивная устойчивость (retry после сбоя, circuit breaker после ошибок) — базовый уровень. Проактивная устойчивость (маршрутизация от деградированного провайдера до того, как пользователи пострадают) — следующая ступень.
Синтетические health-пробы: отправляйте лёгкие тестовые запросы каждому провайдеру раз в 30-60 секунд. Используйте маленькую быструю модель и короткий prompt. Если latency пробы провайдера превышает базу в 2 раза или пробы начинают падать — снижайте вес маршрутизации до того, как пользовательский трафик пострадает.
Мониторинг status pages провайдеров: программно отслеживайте status.openai.com, status.claude.com, status.deepseek.com. При объявлении деградации или major outage — превентивно перенаправляйте трафик.
Мониторинг response headers: OpenAI возвращает x-ratelimit-remaining-requests и x-ratelimit-remaining-tokens в каждом ответе. Используйте их для предсказания момента достижения лимита и проактивного throttling.
При использовании TheRouter health-check маршрутизация встроена в конфигурацию model fallback. Маршрутизатор отслеживает здоровье провайдеров по всему проходящему трафику и обходит деградированные endpoint без кода health check на уровне приложения.
Мониторинг и определение SLO
Невозможно hardening то, что не измеряешь. Определите SLO для интеграции LLM API и алертите при нарушениях.
Ключевые метрики
| Метрика | Измерение | Пример SLO |
|---|---|---|
| Успешность запросов | 1 - (error_count / total_count) | >99.5% за 5-минутные окна |
| Latency p50 | Медианное end-to-end время | <5 с для flash, <15 с для flagship |
| Latency p95 | 95-й перцентиль | <15 с для flash, <45 с для flagship |
| Latency p99 | 99-й перцентиль | <30 с для flash, <90 с для flagship |
| Время open circuit breaker | Общее кол-во секунд open в час | <300 с/час |
| Cost per request | Общие расходы / общие запросы | Ниже бюджетного порога |
| Retry rate | retry_count / total_count | <5% устойчиво, <10% пиковое |
| Streaming completion rate | complete_streams / started_streams | >99% |
Условия алертов:
- Успешность ниже SLO два окна подряд
- Любой circuit breaker перешёл в open
- Cost per request превысил 2x от 7-дневного скользящего среднего
- Retry rate выше 10% более 5 минут
Как всё работает вместе
Защитные слои взаимодействуют. Порядок обработки запроса:
- Запрос поступает в приложение
- Предварительная проверка: оценка токенов, проверка бюджета rate limit. Если превышен — в очередь или отброс.
- Выбор маршрута: health-check маршрутизация выбирает лучший доступный провайдер/модель
- Применение таймаутов: connect + read + total обёртывают API-вызов
- Обработка сбоя: если запрос упал — классифицировать ошибку
- Решение о retry: повторяемые ошибки проходят exponential backoff с jitter в рамках retry budget
- Проверка circuit breaker: если circuit breaker провайдера в open — сразу в fallback
- Fallback-маршрутизация: если retry исчерпаны или circuit breaker открыт — маршрутизация к следующей модели в цепочке деградации
- Режим деградации: если все провайдеры лежат — кешированные ответы или честная ошибка
- Телеметрия: логирование каждой точки принятия решения для мониторинга и SLO
Строить всё это с нуля необязательно. LLM routing-слой обрабатывает шаги 3, 4, 6, 7, 8 на уровне инфраструктуры, позволяя коду приложения сосредоточиться на шагах 1, 2, 5, 9, 10.
FAQ
Какой таймаут ставить для extended thinking / reasoning моделей?
Модели типа DeepSeek R1 или Claude с extended thinking могут генерировать 60-180 секунд. Установите read таймаут минимум 120 с. Используйте streaming для visibility в прогресс. Total таймаут — 3x от ожидаемого времени генерации для данной модели и длины prompt.
Повторять ли tool calls и function calls?
Только если приёмник исполнения инструмента идемпотентен. Если tool call отправляет email, создаёт тикет или пишет в базу данных, retry может дублировать побочный эффект. Проверьте, выполнился ли предыдущий вызов, перед повтором. Используйте idempotency keys, если провайдер их поддерживает.
Как обрабатывать rate limit при нескольких API key?
Отслеживайте RPM и TPM per key, а не per application. Каждый ключ имеет свои лимиты. Rate limiter должен отслеживать каждый ключ независимо и маршрутизировать к ключам с оставшейся ёмкостью.
В чём разница между retry на уровне приложения и на уровне gateway?
Retry на уровне приложения происходят в вашем коде. Retry на уровне gateway — в routing-слое (например, TheRouter). Используйте одно или другое, но не оба. При двойном retry возникает мультипликативный retry-шторм. Gateway retry обычно предпочтительнее — gateway видит весь трафик и принимает более разумные решения.
Как часто настраивать пороги circuit breaker?
Пересматривайте ежемесячно. Характеристики надёжности провайдеров меняются. Подходящий порог при 99.5% uptime может быть слишком чувствительным при 99.9% или слишком мягким при деградации. Используйте данные мониторинга для тюнинга.
Когда использовать routing-слой, а когда писать retry и fallback самостоятельно?
Если вы вызываете одного провайдера с одной моделью — retry в коде приложения достаточно. Если несколько провайдеров, несколько моделей, circuit breaker и health-check маршрутизация — специализированный routing-слой убирает существенную сложность из кода приложения и централизует логику устойчивости.
Источники:
- OpenAI rate limits (получено 2026-09-22)
- Anthropic Claude API errors (получено 2026-09-22)
- DeepSeek error codes (получено 2026-09-22)
- LLM API Resilience in Production — tianpan.co (получено 2026-09-22)
- Retries, fallbacks, and circuit breakers in LLM apps — Portkey (получено 2026-09-22)
- Circuit Breaker Pattern — AWS Prescriptive Guidance (получено 2026-09-22)