Контролируемые сбои в LLM-инфраструктуре перед пиком
Контролируемые сбои в LLM-инфраструктуре помогают найти слабые места до пика спроса. Разберём проверки шлюза, провайдера, очередей и ретривера.

Что ломается перед пиком нагрузки
Спокойный день почти всегда обманывает. Трафик ровный, запросы похожи друг на друга, редкие ошибки теряются в общем фоне. Система выглядит устойчивой, хотя часть проблем просто не успевает проявиться.
Под пиком все меняется сразу. Пользователи приходят волнами, промпты становятся длиннее, параллельных вызовов больше, а время ответа начинает скакать даже там, где раньше было тихо. Задержка в 300 мс, которая в тестах казалась мелочью, в реальной нагрузке легко превращается в очередь на минуты.
Первыми обычно сдаются не самые заметные узлы. Чаще всего проблема начинается в одном из четырех мест:
- шлюз дольше держит соединения и копит таймауты
- провайдер режет лимиты или отвечает нестабильно
- очередь растет из-за повторных попыток
- ретривер медленнее ищет контекст и отдает хуже результаты
Один сбой редко остается один. Если провайдер замедлился, шлюз ждет дольше. Если шлюз ждет дольше, клиенты и воркеры начинают ретраи. Ретраи забивают очередь даже теми запросами, которые уже не нужны. Потом ретривер получает больше одновременных обращений, растет latency, а модель получает контекст позже или не получает его полностью.
Обычно все начинается скучно и заканчивается дорого. Перед распродажей команда замечает небольшой рост ошибок у одного LLM-провайдера. Она включает резервный маршрут, но не проверяет, как он ведет себя на длинных запросах. Новый маршрут отвечает медленнее, очередь пухнет, клиенты повторно отправляют запросы. Через 15 минут проблема уже не в провайдере, а во всей цепочке.
Именно поэтому контролируемые сбои нужны не для красивого отчета. Они показывают, где система ломается первой, что тянет за собой остальное и где у вас на самом деле нет запаса. Если команда работает через единый шлюз, он может пережить отказ одного провайдера. Но он не спасет, если очереди, таймауты и лимиты настроены вслепую.
С чего начать подготовку
Начинайте не с большого стресс-теста, а с 3-5 сценариев, где сбой сразу заметит пользователь или поддержка. Смысл таких прогонов не в проверке абстрактного стека, а в живом пути запроса: от клиента до ответа модели и обратно.
Обычно хватает чата в личном кабинете, суммаризации диалога для оператора, поиска по базе знаний, модерации текста и генерации ответа в CRM или help desk. Брать все сразу не стоит. Если команда попытается прогнать десять потоков, тест расползется, а спор потом уйдет в детали. Пяти сценариев достаточно, чтобы увидеть слабые места до пика.
Для каждого сценария заранее зафиксируйте две цифры: сколько пользователь готов ждать и какой процент ошибок вы готовы пережить без заметного ущерба. Формулировки должны быть прямыми. Внутренний поиск по документам может жить с задержкой 6-8 секунд, а подсказка оператору в чате уже нет. Если ответ приходит дольше 3 секунд, оператор просто идет дальше без него.
Дальше назначьте владельца на каждый узел цепочки. У шлюза, маршрутизации, очереди, ретривера и провайдера должен быть конкретный человек, который смотрит метрики и принимает решение во время прогона. Когда ответственность общая, в момент сбоя никто не знает, кто режет трафик, кто меняет маршрут на другого провайдера, а кто останавливает ретраи.
Хороший тест выглядит просто. У него есть окно запуска, лимит по времени и понятный план отката. Команда заранее решает, что делать, если растет очередь, провайдер отдает 5xx, ретривер тормозит или шлюз упирается в rate limit. Откат тоже должен быть простым: вернуть прежний маршрут, снизить нагрузку, выключить тяжелый сценарий флагом или перевести часть запросов на более быструю модель.
Если вы работаете через единый OpenAI-совместимый шлюз, проверьте это до теста: какие маршруты включены, где стоят лимиты по ключам, кто видит аудит-логи и кто может быстро сменить провайдера без правки кода. В схеме вроде AI Router это особенно удобно: команда меняет только base_url на api.airouter.kz и продолжает использовать тот же SDK, код и промпты. Перед пиком это экономит не часы, а иногда весь вечер команды.
Как проверить шлюз и маршрутизацию
Проверять нужно не сам факт отказа, а поведение всей цепочки. Шлюз должен переключать трафик быстро и предсказуемо. Если после отключения одного маршрута команда вручную правит конфиг, тест уже провален.
Сначала отключите один рабочий маршрут целиком. Это может быть один провайдер или одна модель в правилах роутинга. Отправьте тот же набор запросов и посмотрите, куда ушел трафик, как выросла задержка и не изменилась ли форма ответа для приложения. Если у вас OpenAI-совместимый шлюз, клиентский код в такой проверке лучше вообще не трогать. Иначе вы тестируете не маршрутизацию, а ручной обход проблемы.
Полный отказ ломает не так часто, как медленное умирание. Поэтому отдельно сымитируйте рост таймаутов у части моделей. Например, поднимите задержку с 2-3 секунд до 12-15 секунд для 20% запросов. Потом проверьте, ждет ли шлюз слишком долго, запускает ли лишние повторы и не забивает ли очередь запросами, которые уже потеряли смысл.
Во время прогона не смотрят на один график. Нужны сразу несколько метрик: сколько запросов ушло на запасной маршрут, как изменились p95 и p99, выросло ли число повторных попыток и отмен, не прыгнула ли стоимость запроса после переключения и не просело ли качество ответа на типовых сценариях.
Отдельно проверьте лимиты на уровне ключа. Один шумный клиент или внутренний сервис не должен съедать весь запас пропускной способности. Дайте одному ключу нагрузку выше лимита и убедитесь, что шлюз режет только этот поток, а не весь трафик. Если у вас несколько команд или продуктов на одном шлюзе, это всплывает очень быстро.
Логи и трассировка должны отвечать на простой вопрос: что случилось с конкретным запросом за 30 секунд поиска. У запроса нужен trace ID, выбранный маршрут, провайдер, модель, число повторов, причина переключения и причина отказа. Если компания обязана маскировать PII или ставить метки контента, эти события тоже должны быть видны в цепочке. Сверьте записи в приложении, шлюзе и очереди. Если trace ID не проходит через всю систему, разбор инцидента превратится в угадайку.
Хороший результат выглядит буднично. Один маршрут падает, часть моделей тормозит, один ключ упирается в лимит, а сервис все равно остается живым, а логи дают понятную картину.
Что проверить у провайдера
Перед пиком провайдер редко падает целиком. Чаще растет задержка, чаще приходят 429, а длинные ответы обрываются или едут заметно дольше обычного. Поэтому полезнее не общий стресс-тест, а набор коротких проверок по слабым местам провайдера.
Первый тест - фолбэк на второго провайдера. Он должен срабатывать не только при полном отказе, но и при частичных проблемах: серии 429, всплеске 5xx или резком росте времени ответа. Если основной провайдер тормозит 40 секунд, а запасной отвечает за 8, система должна переключиться сама, без ручного вмешательства и без поломки формата ответа.
Минимальный набор сценариев простой: основной провайдер отвечает 429 на каждый третий запрос, длинная генерация идет в 3-4 раза медленнее нормы, стриминг обрывается в середине, запасной провайдер возвращает тот же смысл в другой схеме полей, а квота у основного заканчивается посреди теста.
Команды чаще всего удивляют не сами ошибки, а лимиты, про которые никто не вспомнил заранее. Сверьте цену за входные и выходные токены, лимиты в минуту, лимиты по одновременным запросам и суточные квоты. Частая история: тест проходит на малом объеме, а на реальном трафике все упирается не в модель, а в rate limit аккаунта.
Отдельно проверьте совместимость SDK при смене base_url. Если вы используете OpenAI-совместимый шлюз, код не должен требовать переделки только из-за нового адреса API. Но это надо подтвердить тестом, а не предположением. Запросите обычный completion, streaming, tool calling и structured output, если он у вас есть. Иногда базовый вызов проходит, а стриминг или обработка ошибок ломаются уже в проде.
После такого прогона команда должна точно знать три вещи: кто примет трафик при 429, сколько это будет стоить и какие функции клиента переживут смену провайдера без сюрпризов.
Очереди, таймауты и повторные попытки
Пик трафика редко ломает систему в одном месте. Сбой обычно начинается с малого: очередь растет на 200-300 задач за минуту, один воркер зависает, ретраи раздувают нагрузку, а пользователи видят один и тот же ответ дважды. Поэтому проверку лучше начинать не с модели, а с пути запроса после приема.
Сначала дайте системе резкий всплеск и смотрите не только на среднюю задержку, но и на длину очереди по секундам. Если очередь растет быстрее, чем воркеры успевают ее разбирать, проблема уже есть, даже если ошибок пока не видно. Иногда 5-10 минут такого теста дают больше, чем спокойный час обычной нагрузки.
С ретраями ситуация еще опаснее. Повторная попытка должна спасать запрос, а не создавать дубли. Если клиент, шлюз и воркер повторяют один и тот же запрос независимо друг от друга, вы легко получаете три обращения к провайдеру вместо одного. Для задач с записью результата нужен idempotency key или хотя бы явная проверка, обрабатывали ли вы этот job раньше.
Потом сверяйте таймауты по всей цепочке: у клиента, у LLM-шлюза, у очереди и воркера, у ретривера и у провайдера. Если у клиента таймаут 15 секунд, у шлюза 30, а у воркера 60, запрос уже умер для пользователя, но бэкенд все еще тратит ресурсы. В системах с единым OpenAI-совместимым шлюзом, где запросы уходят к разным провайдерам, такой перекос встречается часто. Команда меняет только base_url, а старые лимиты и таймауты остаются от прошлой схемы.
Есть и очень простой прогон: остановите один воркер вручную во время нагрузки. Смотрите, как быстро остальные подхватят задачи, начнет ли очередь расти без остановки и не появятся ли повторные обработки. Хороший признак тут один: система замедляется, но не входит в цикл ретраев и не теряет задания.
Небольшой пример. Перед распродажей интернет-магазин выдерживает обычный поток, но падает на генерации карточек товара, когда ретривер отвечает на 2 секунды дольше, а очередь уже заполнена. Один лишний ретрай на каждом шаге быстро превращает 5 тысяч запросов в 9 тысяч. Это и нужно поймать на тесте, а не в день пика.
Ретривер под реальной нагрузкой
Ретривер часто ломается тише всех. Шлюз отвечает, модель формально жива, очередь движется, а итоговый ответ уже хуже, потому что поиск принес не те документы или не принес ничего. Это опаснее явной ошибки: команда видит нормальный статус, а пользователь получает слабый ответ.
Начните с плохих запросов. Не только с чистых формулировок, но и с пустых строк, обрывков фраз, опечаток, дублей, слишком длинных запросов и запросов с шумом. Люди пишут именно так, когда спешат. Если ретривер на таком входе возвращает случайные документы, модель уверенно додумает лишнее.
Хорошая проверка выглядит просто: смешайте нормальные запросы с мусорными хотя бы в одной нагрузочной серии. Потом смотрите не только на latency, но и на долю пустой выдачи, число нерелевантных документов и частоту, с которой ответ скатывается в общие фразы вместо точного ответа.
Задержки и деградация
Под нагрузкой поиск редко падает сразу. Сначала растет задержка в векторной базе, потом кэш перестает помогать, затем чтение упирается в лимиты, и только после этого проседает качество. Поэтому полезно искусственно добавить задержку на 100, 300 и 800 мс и посмотреть, что делает приложение: ждет, урезает top-k, берет устаревший кэш или идет в модель без контекста.
Маршрутизация между моделями тут не спасает сама по себе. Если ретривер отдает слабый контекст, пользователь видит плохой результат, а не причину. Поэтому отдельно проверьте кэш, дедупликацию, лимиты на чтение и поведение системы, когда поиск вернул ноль документов.
Когда падает качество
Самая частая ошибка на прогонах в том, что команда меряет только скорость. Этого мало. Заранее выберите набор вопросов с известным хорошим ответом и сравните результат до и после нагрузки. Для базового контроля хватит четырех метрик: доля ответов с верным документом в top-3, доля пустой выдачи, средняя задержка и число случаев, когда модель ответила без опоры на источник.
Небольшой пример: перед сезонной распродажей ритейлер проверяет поиск по базе доставки и возвратов. При 50 RPS все выглядит нормально. При 200 RPS база отвечает на 400 мс медленнее, кэш чаще промахивается, а дедупликация пропускает похожие карточки. Время ответа выросло всего на секунду, но точность просела заметно. Именно такой сдвиг и нужно ловить заранее.
Пример перед распродажей
За день большой акции чат поддержки обычно получает в 4-6 раз больше вопросов. Люди спрашивают про оплату, доставку, возвраты и зависшие заказы. Если бот на базе LLM отвечает медленно хотя бы 10 минут, операторы быстро уходят в ручной режим, а очередь начинает расти почти без пауз.
У такой команды часто есть основная модель, которая хорошо решает сложные вопросы, но в пиковые часы она начинает отвечать заметно дольше. Вместо обычных 2-3 секунд ответ приходит за 8-12. Для пользователя это уже сбой, даже если модель формально доступна.
За неделю до акции
Команда не ждет полного отказа. Она задает пороги заранее: при какой задержке шлюз переводит часть трафика на более быструю модель, при каком размере очереди бот отвечает короче, а при каком времени поиска по базе знаний вообще пропускает ретривер и отдает безопасный шаблонный ответ.
Если компания использует OpenAI-совместимый шлюз, такой прогон проще провести заранее. Например, в AI Router можно оставить тот же SDK и клиентский код, а потом проверить, как система ведет себя при смене провайдера и маршрута. Это удобно по очень простой причине: во время акции никто не хочет переписывать интеграцию на ходу.
В день распродажи
Представим обычную картину. Основная модель замедлилась, очередь выросла с 200 до 1800 запросов, а поиск по базе знаний вместо 300 мс занимает почти 2 секунды. Если правила не заданы заранее, пользователи получат смесь таймаутов, пустых ответов и странных повторов.
Если правила есть, система ведет себя предсказуемо:
- новые диалоги идут на запасную модель после заданной задержки
- длинные ответы режутся до кратких инструкций
- ретривер отключается для простых интентов вроде "где мой заказ"
- повторные попытки останавливаются после фиксированного лимита
Такой режим не делает сервис идеальным. Зато он держит поток и не дает одному узкому месту уронить весь контур. Для дня распродажи этого часто достаточно: пользователь все еще получает ответ, оператор видит понятную причину деградации, а команда понимает, какой порог сработал слишком поздно.
Ошибки на прогонах
Такие проверки легко дают ложное чувство готовности. Команда имитирует один аккуратный отказ, например отключает провайдера, видит переключение на резерв и считает прогон успешным. В пике почти никогда не ломается что-то одно. Провайдер отвечает медленнее, очередь растет, часть запросов уходит в таймаут, а ретривер приносит пустой контекст.
Одиночные тесты полезны только в начале. Дальше нужно проверять связки сбоев. Если шлюз переживает падение одного канала, но начинает метаться при росте задержки и частичных ошибках, проблема никуда не делась.
Средняя задержка тоже часто обманывает. p50 может выглядеть нормально, пока p95 и p99 уже уехали так далеко, что пользователи закрывают окно или жмут "отправить" второй раз. Для LLM важна не только полная длительность ответа, но и время до первого токена. Если интерфейс молчит первые 8-10 секунд, человек считает сервис сломанным, даже если ответ потом приходит.
Отдельная беда - ретраи без защиты от шторма. Когда каждый слой повторяет запрос сам по себе, нагрузка растет быстрее исходной. Один медленный провайдер быстро превращается в лавину дублей, забитую очередь и каскад таймаутов.
Перед прогоном зафиксируйте хотя бы три ограничения: сколько повторов можно делать на один запрос, когда система перестает стучаться в проблемный провайдер и какой лимит есть у очереди, после которого вы режете новые запросы или упрощаете сценарий.
Самый недооцененный промах виден не в графиках, а на экране пользователя. Команды проверяют метрики, но не смотрят, что получает человек в деградации. Пустой блок ответа, бесконечный спиннер и ошибка без объяснения бьют сильнее, чем честное сообщение о задержке. Лучше заранее решить, какой у вас запасной режим: короткий ответ без ретривера, переход на другую модель или сохранение запроса в очередь с понятным статусом.
Если после прогона у вас нет снимка пользовательского сценария, графика хвостовых задержек и лога повторных попыток по каждому слою, прогон прошел только наполовину.
Короткий чек-лист перед сезоном
За день до пика команда должна без споров ответить на пять простых вопросов. Если на любой из них нет ясного ответа, проверка отказов быстро превратится в обычную аварию.
- Переключение между провайдерами проходит на реальном тесте, а не только "на бумаге".
- Очередь растет предсказуемо и не копит тысячи задач до полного затора.
- Ретривер держит реальный объем, а не красивый лабораторный сценарий.
- Логи собирают одну историю запроса от входа до ответа модели.
- У отката есть конкретный хозяин, который принимает решение.
За каждым пунктом должны стоять простые проверки. Отключите основной маршрут и убедитесь, что шлюз уводит запросы на запасной вариант без правок в коде и без резкого роста ошибок. Проверьте лимит длины очереди, время жизни задач и правило, когда система режет поток. Посмотрите на задержку поиска, долю пустых ответов и качество выдачи на пиковом числе запросов, особенно если база обновляется в течение дня.
Логи тоже стоит проверить руками. По одному request ID команда должна видеть маршрут через шлюз, выбор провайдера, повторы, таймауты, обращение к ретриверу и причину отказа. Для команд из Казахстана это еще и вопрос аудита, если нужно показать, где случился сбой и какие данные система обработала.
Хороший признак - когда этот список проходит не только SRE или ML-инженер, но и дежурная смена. Тогда в момент нагрузки никто не тратит 20 минут на поиск ответственного и не спорит, считать ли всплеск временным.
Если вы используете единый OpenAI-совместимый шлюз, проверьте еще одну мелочь: одинаково ли ведут себя SDK, таймауты и коды ошибок после смены провайдера. На таких деталях часто срывается откат, хотя инфраструктура формально готова.
Что сделать дальше
Не откладывайте общий прогон на конец месяца. Один короткий тест на этой неделе полезнее, чем большой идеальный прогон, который снова перенесут. Часто хватает 30-40 минут, если команда заранее знает сценарий: отказ шлюза, медленный провайдер, рост очереди и пустой ответ ретривера.
После прогона фиксируйте не общие выводы, а пороги и действия. При какой задержке вы переключаете маршрут? Сколько 5xx подряд ждете до вывода провайдера из ротации? Когда отключаете дорогую модель и переводите трафик на запасной вариант? Эти числа лучше записать в одном месте и сразу привязать к роли: кто смотрит дашборд, кто меняет лимиты, кто сообщает продуктовой команде.
Полезное правило простое:
- если шаг повторяется в каждом прогоне, его пора автоматизировать
- если решение зависит от одного дежурного, нужен явный runbook
- если команда спорит о пороге во время сбоя, порог еще не определен
- если на переключение уходит больше 5 минут, схема слишком ручная
Особенно часто команды оставляют ручными мелочи, которые потом съедают время: включение запасного провайдера, смену маршрута для длинных запросов, снижение rate limit для одного клиента, отключение тяжелого ретривера. Такие действия лучше вынести в флаги, политики маршрутизации и готовые шаблоны алертов.
Если вы планируете такие прогоны регулярно, полезно сравнить и саму точку входа. Иногда проблема не в модели, а в том, что у вас слишком много отдельных интеграций, разных лимитов и ручных правил на каждом провайдере. Для команд в Казахстане, которым важны хранение данных внутри страны, маскирование PII, аудит-логи и лимиты на уровне ключа, имеет смысл посмотреть на единый шлюз вроде AI Router на airouter.kz. Смысл не в смене инструмента ради смены, а в том, чтобы сократить ручные переключения и сделать поведение системы предсказуемым перед пиком.
Если после следующего прогона у вас стало меньше ручных действий и меньше спорных решений, подготовка идет в правильную сторону.
Часто задаваемые вопросы
С чего начать подготовку к контролируемым сбоям?
Начните с 3–5 сценариев, где сбой сразу заметит человек: чат, поиск по базе знаний, суммаризация для оператора, модерация. Не берите весь стек сразу.
Сразу зафиксируйте порог ожидания и допустимый процент ошибок для каждого сценария. После этого назначьте одного владельца на шлюз, очередь, ретривер и провайдера, чтобы во время прогона никто не спорил, кто принимает решение.
Какие метрики смотреть во время прогона?
Смотрите не только на среднее время ответа. Быстрее всего проблему выдают p95, p99, время до первого токена, длина очереди по секундам, число повторных попыток и отмен.
Еще полезно видеть, сколько трафика ушло на запасной маршрут, выросла ли цена запроса и не стало ли больше пустых ответов от ретривера. Один ровный график почти всегда скрывает хвостовые задержки.
Как понять, что маршрутизация реально работает?
Отключите один рабочий маршрут целиком и отправьте тот же набор запросов. Код клиента не трогайте, иначе вы проверите ручной обход, а не саму маршрутизацию.
Потом сравните, куда ушел трафик, как выросла задержка и не сломался ли формат ответа. Если приложение после переключения начинает вести себя иначе, маршрут работает только на бумаге.
Когда включать запасного провайдера?
Не ждите полного отказа. Переключение стоит запускать и при серии 429, и при цепочке 5xx, и при заметном росте времени ответа.
Задайте пороги заранее. Например, если основной провайдер несколько раз подряд не укладывается в лимит, шлюз сам переводит часть трафика на запасной вариант. После такого шага команда должна понимать, сколько это стоит и какие функции клиента переживут смену без сюрпризов.
Почему таймауты часто ломают систему сильнее, чем сам отказ?
Потому что перекос таймаутов сжигает ресурсы даже после того, как пользователь уже ушел. Клиент может ждать 15 секунд, а воркер — еще минуту, и все это время система держит очередь и соединения.
Сведите таймауты по цепочке в один ряд: клиент, шлюз, очередь, воркер, ретривер, провайдер. Тогда запрос либо успеет закончиться вовремя, либо быстро освободит место для следующих.
Что делать с повторными попытками, чтобы не получить шторм?
Дайте право на повтор одному слою, а не всем сразу. Если клиент, шлюз и воркер повторяют один и тот же запрос независимо, вы быстро утроите нагрузку.
Ограничьте число попыток, добавьте паузу между ними и используйте idempotency key для задач с записью результата. Когда очередь растет или провайдер уже явно болеет, останавливайте повторы раньше, чем они забьют весь контур.
Как тестировать ретривер под реальной нагрузкой?
Гоняйте не только чистые запросы. Подмешайте пустые строки, опечатки, обрывки фраз, дубли и слишком длинный ввод, потому что люди так и пишут в спешке.
Во время нагрузки меряйте не одну задержку. Смотрите долю пустой выдачи, число нерелевантных документов в top-3 и случаи, когда модель отвечает без опоры на источник. Еще полезно искусственно добавить ретриверу 100, 300 и 800 мс и проверить, что делает приложение в каждом случае.
Что должно быть в логах для быстрого разбора инцидента?
По одному запросу вы должны за полминуты увидеть всю историю. Для этого в логах нужны trace ID, выбранный маршрут, провайдер, модель, число повторов, причина таймаута или переключения, а также обращение к ретриверу.
Если система маскирует PII или ставит метки контента, эти события тоже держите рядом. Когда trace ID теряется между приложением, шлюзом и очередью, разбор инцидента превращается в догадки.
Как подготовить откат перед пиком?
Нормальный откат выглядит коротко и ясно. Команда заранее решает, кто его запускает, в каком окне времени и по какому порогу.
Обычно хватает простых действий: вернуть прежний маршрут, снизить поток, выключить тяжелый сценарий флагом или перевести часть запросов на более быструю модель. Если на откат уходит больше нескольких минут и людям нужно править код на ходу, план слишком ручной.
Нужен ли единый OpenAI-совместимый шлюз перед высоким сезоном?
Да, если он убирает ручные переключения и не ломает текущую интеграцию. Перед пиком это особенно полезно: команда меняет base_url, а SDK, код и промпты оставляет как есть.
Но не верьте этому на слово. Проверьте обычный вызов, стриминг, tool calling, structured output, лимиты по ключам и коды ошибок после смены провайдера. В схеме вроде AI Router такой тест помогает быстро понять, где вы правда готовы к пику, а где только надеетесь на это.