
Если приложение обращается к LLM по API, успешный ответ — лишь один из возможных исходов. Запрос может завершиться таймаутом, получить HTTP 429 или оборваться после отправки, но до того, как клиент успеет прочитать ответ. В последнем случае приложение не всегда знает, выполнил ли провайдер запрос.
Проверка отказоустойчивости LLM помогает заранее увидеть, как клиент переживает эти ситуации: сколько раз повторяет запрос, укладывается ли в общий лимит времени и не создаёт ли дублирующие действия. Для этого не нужны реальные сбои на продакшене: достаточно задать контролируемые ответы тестовому HTTP-клиенту и проверить результат.
Что именно нужно проверить в отказоустойчивости LLM
Устойчивость — не просто способность «повторить запрос». Важно, чтобы приложение предсказуемо реагировало на разные классы ошибок.
- Временный сбой: например, HTTP 429 или ошибка 5xx. Повтор может помочь, но только с ограничением количества попыток.
- Постоянная ошибка: например, некорректный параметр в запросе. Повтор того же запроса обычно не исправит проблему.
- Таймаут: клиент перестал ждать ответа. Это не всегда означает, что сервер не обработал запрос.
- Ошибка чтения ответа: запрос мог дойти до провайдера, но клиент не получил результат.
- Отказ инструмента: если модель управляет внешним действием, например создаёт задачу или отправляет сообщение, повтор может привести к дублированию эффекта.
Документация OpenAI описывает отдельные коды ошибок и ограничения частоты запросов, но поведение приложения зависит также от клиентской библиотеки, собственного кода повторов и внешних инструментов. Поэтому тестировать нужно не только ответ провайдера, но всю цепочку: обработчик запроса, повтор, журналирование и итог для пользователя.
Разделите ошибки на повторяемые и постоянные
Начните с таблицы решений в коде или тестовой спецификации. Например, временные ошибки сервера и ограничения частоты можно считать кандидатами на повтор, а ошибки аутентификации и неверных параметров — поводом завершить запрос с понятной ошибкой.
| Ситуация | Ожидаемая реакция |
|---|---|
| HTTP 429 | Учесть задержку, если она указана, и повторить в пределах лимита |
| HTTP 500 или 503 | Ограниченный повтор с паузой |
| HTTP 400 | Не повторять без изменения запроса |
| HTTP 401 или 403 | Не повторять; проверить ключ и права доступа |
| Таймаут соединения до отправки | Повтор может быть безопасен, если лимит попыток соблюдён |
| Таймаут ожидания ответа | Считать исход неопределённым, особенно для запросов с побочным эффектом |
Это отправная точка, а не универсальная классификация: точные коды и поля ответа зависят от API. Сверяйте обработку с документацией своего провайдера и конкретной операции.
Важно отличать попытку подключения от ожидания ответа. Если соединение не удалось установить, запрос, вероятно, не дошёл до API. Если соединение оборвалось после отправки тела, клиенту может быть неизвестно, обработан ли запрос. Такая неопределённость требует особого внимания для платежей, публикаций, создания тикетов и вызовов инструментов.
Настройте повторы с пределом времени и случайной паузой
Повторные запросы способны усилить перегрузку. Если множество клиентов одновременно получают ошибку и тут же повторяют вызов, они могут снова обратиться к сервису в один момент. В рекомендациях AWS для распределённых систем описан экспоненциальный backoff с jitter — случайным разбросом задержки. Документация OpenAI также рекомендует учитывать лимиты запросов и не считать немедленные повторы решением для 429.
Пример задержки для попытки *n*:
text
delay = random(0, min(cap, base * 2^n))
Здесь `base` — начальная задержка, `cap` — верхний предел. Значения нужно выбрать под требования приложения и лимит времени операции: формула сама по себе не определяет подходящие параметры.
Для повторов задайте три ограничения:
Максимум попыток. Например, разрешить несколько попыток, а не цикл без конца.
Общий deadline. Время на все вызовы и ожидания не должно превышать бюджет запроса пользователя.
3. Ограничение параллелизма. После массового сбоя не запускайте бесконтрольную лавину повторов.
Если API возвращает информацию о времени ожидания, протестируйте, что клиент её учитывает в соответствии с документацией. Проверьте также, что один и тот же вызов не повторяется одновременно несколькими слоями — например, приложением и HTTP-библиотекой.
Воспроизведите сбой без обращения к настоящей модели
Для теста можно подменить HTTP-транспорт и задать последовательность ответов. Например: сначала вернуть 429, затем успешный ответ. Проверить нужно не только итоговый текст, но и число запросов, задержку, журнал событий и соблюдение общего времени.
Упрощённый пример на Python с `unittest.mock`:
python
from unittest.mock import Mock
responses = [
Mock(status_code=429, headers={«Retry-After»: «1»}),
Mock(status_code=200, json=lambda: {«output»: «Готово»}),
]
def fake_post(*args, kwargs):
response = responses.pop(0)
return response
Подключите fake_post вместо HTTP-транспорта клиента.
# Проверьте:
# 1. первый ответ обработан как временная ошибка;
# 2. выполнен не более чем один повтор;
# 3. итоговый ответ получен;
# 4. суммарное время не превысило заданный deadline.
Это иллюстрация сценария, а не готовый тест для конкретного SDK: интерфейс подмены транспорта зависит от библиотеки. Более надёжная проверка использует часы и функцию ожидания, которые можно подменить. Тогда тест не будет реально ждать секунду и сможет точно проверить запрошенную задержку.
Добавьте и отрицательный тест: на HTTP 400 клиент должен завершить операцию без повтора. Иначе ошибка валидации может породить лишние вызовы и скрыть первопричину.
Проверьте таймауты отдельно от повторов
Таймаут — не единый параметр. В зависимости от клиента могут отдельно настраиваться время установления соединения, чтения ответа и общий срок операции. Если задать только долгий таймаут чтения, короткий сбой сети способен надолго занять рабочий поток. Если общий срок меньше суммы ожиданий повторов, операция должна завершиться предсказуемо, а не превысить бюджет.
Сделайте тесты для нескольких точек отказа:
- соединение не установилось;
- ответ не пришёл до таймаута чтения;
- ответ пришёл после того, как пользовательский deadline уже истёк;
- приложение отменило запрос, пока ожидалась задержка перед повтором.
Для каждого сценария зафиксируйте ожидаемый статус, число попыток и поведение интерфейса. Например, отменённую пользователем задачу не следует отправлять повторно только потому, что завершился таймер backoff.
Защитите действия с побочными эффектами от дублей
Повтор запроса к модели и повтор действия агента — разные риски. Генерация текста обычно не меняет внешнее состояние. Но если после ответа выполняется отправка письма, создание заказа или публикация, повтор всей цепочки может выполнить действие дважды.
Разделите получение результата модели и выполнение действия. Для внешней операции используйте механизм идемпотентности, если его поддерживает целевая система, либо собственный идентификатор операции и проверку уже выполненного действия. При неопределённом исходе сначала выясняйте статус операции, а не запускайте её заново.
В тесте имитируйте случай, когда инструмент успешно выполнил действие, но клиент не получил подтверждение. Ожидаемый результат — повторная проверка статуса или безопасный отказ, а не второй вызов инструмента без защиты.
Считайте повторы и задержки, а не только ошибки
Метрики помогают заметить, что приложение формально продолжает работать, но тратит слишком много времени или запросов. Для каждой операции полезно собирать:
- число попыток и итоговый код;
- время до первого ответа и полную длительность;
- долю операций, завершённых после повтора;
- число таймаутов и отмен;
- ошибки по провайдеру и типу операции.
Не записывайте API-ключи, полные пользовательские запросы и секреты из контекста. Для диагностики обычно достаточно идентификатора операции, кода ошибки и обезличенных технических атрибутов.
Сравните поведение до и после изменения политики повторов на одном и том же наборе искусственных сценариев. Это покажет компромисс между успешностью, задержкой и количеством вызовов. Тестовая последовательность не доказывает, что клиент выдержит любой реальный сбой, но позволяет проверить конкретные гарантии реализации.
Перед выпуском проверьте четыре условия
Убедитесь, что постоянные ошибки не повторяются, временные повторы ограничены, общий deadline соблюдается, а внешние действия защищены от дублей. Затем проверьте документацию используемого API: коды ошибок, правила rate limit и поддержку идемпотентности могут отличаться у разных операций и провайдеров.
Для следующего шага возьмите журнал последних ошибок и превратите каждую повторяющуюся ситуацию в отдельный тест. Если в логах невозможно понять, сколько было попыток и где оборвалась операция, сначала добавьте безопасную техническую телеметрию — без неё оценка отказоустойчивости останется догадкой.










