Когда успешный запрос всё равно означает сбой
Представьте, что вы уже несколько недель общаетесь с одним персонажем. Его голос знаком, язык стабилен, а ответы кажутся точными и живыми.
И вдруг, хотя вы не меняли ни персонажа, ни модель, что-то становится не так.
Следующий ответ начинается только через десять секунд. Функция, зависящая от структурированного вывода, внезапно перестаёт работать. Персонаж начинает говорить бессвязно, вставляет фрагменты на другом языке или отвечает так, словно его заменили гораздо более слабой моделью.
С точки зрения сервера ошибки нет. API вернул 200 OK, токены пришли, запрос был оплачен.
С точки зрения пользователя модель только что стала хуже.
Именно из-за этого разрыва мы создали для Reverie систему канареечных проверок провайдеров и карантина маршрутов.
Одно название модели, множество сред инференса
OpenRouter — наш основной шлюз к моделям. Одно из его преимуществ в том, что одну модель могут обслуживать сразу несколько независимых инференс-провайдеров. Если один недоступен, запрос принимает другой. Это даёт больше вычислительных ресурсов, конкурентные цены и лучшую устойчивость, чем зависимость от единственного endpoint.
Но вместе с этим появляется скрытая переменная.
Название модели остаётся тем же, а инфраструктура, которая её запускает, может меняться от запроса к запросу. У разных провайдеров могут отличаться движок инференса, оборудование, уровень квантования, parser, шаблон чата, очередь и дополнительный слой политик.
В документации OpenRouter по маршрутизации провайдеров сказано, что стандартный алгоритм учитывает недавнюю доступность и цену, оставляя других провайдеров в качестве fallback. Это важные сигналы, но endpoint может быть доступным и дешёвым, при этом выдавая неприемлемый для конкретного продукта результат.
OpenRouter также публично рассказывал об измеримых различиях между провайдерами одной и той же модели. В теории одинаковые веса при одинаковой точности должны вести себя похоже. На практике промышленный инференс большой модели сложен, поэтому различия возникают.
Что мы называем «деградацией модели»
«Деградация модели» — не единый стандартизированный диагноз. Она также не означает автоматически, что провайдер намеренно подменил модель меньшей.
Мы используем операционное определение: инференс-endpoint деградировал, если на репрезентативных prompts он больше не сохраняет поведение, возможности или интерактивную производительность, ожидаемые от запрошенной модели, даже когда запрос формально завершился успешно.
Для Reverie наиболее заметны следующие формы:
- Деградация задержки — соединение установлено, но первый токен приходит настолько поздно, что ощущение живого разговора исчезает.
- Деградация возможностей — endpoint, от которого ожидается структурированный вывод, возвращает повреждённый текст или перестаёт соответствовать schema.
- Поведенческая деградация — ответы повреждаются, теряют связность, неожиданно повторяются или явно переходят на другой язык.
- Несовпадение политик — хост добавляет собственную фильтрацию и превращает поддерживаемую Reverie сцену в отказ, пустой или оборванный ответ.
Причин может быть много. Квантование с более низкой точностью способно ухудшать результат на сложных prompts — это отмечают и документация OpenRouter, и опубликованные исследования квантования. Но квантование не объясняет всё: не менее важны ошибки движка инференса и tool-parser, проблемы tokenizer или шаблона чата, перегруженные очереди и middleware провайдера. OpenRouter наблюдал, что при вызове инструментов parser чаще становится причиной расхождений, чем сама точность.
Важный вопрос не в том, «какая причина звучит подозрительнее», а в том, «можем ли мы изолировать endpoint и воспроизвести ошибку».
Два случая, которые сделали проблему очевидной
Для нас это не было теоретическим риском.
В сравнительном тесте с закреплёнными провайдерами один из них испортил начало 19 из 20 ответов, добавляя знаки препинания или фрагмент постороннего токена. На четырнадцати других хостах той же модели проблема встретилась 0 раз в 49 ответах. В ролевом чате первый символ часто является Markdown-маркером выделения, поэтому один лишний byte мог нарушить форматирование всего ответа.
В другом тесте один endpoint выдал сильное смешение португальского и испанского в 3 генерациях из 3. Тот же prompt оставался на португальском у официального хоста модели и у других проверенных хостов того же класса точности. Объяснение «модель просто непоследовательна» перестало работать: дефект следовал за endpoint.
Такие сбои незаметны для обычного мониторинга, потому что не обязательно вызывают исключение. Система проверки uptime видит здоровый API. Пользователь видит персонажа, который внезапно разучился нормально говорить.
Наш ответ: проверять каждый маршрут, а не только модель
Каждые тридцать минут канареечная система получает список активных провайдеров стандартной чат-модели Reverie. Затем она отправляет каждому небольшой набор синтетических запросов, закреплённых только за этим провайдером, с отключёнными fallback.
Закрепление критически важно. Если проверка может переключиться на fallback, второй здоровый провайдер скроет сбой первого. Нам необходимо точно знать, какая среда произвела результат.
Система проверяет четыре узких контракта, которые можно объективно оценить кодом.
1. Время до первого токена
В потоковом разговоре throughput — лишь половина опыта. Первый токен определяет, сколько пользователь смотрит на индикатор загрузки.
Мы измеряем это непосредственно в stream. Если провайдер не выдаёт текст в течение десяти секунд, он не проходит проверку задержки. Порог намеренно щедрый, и одного медленного раунда недостаточно для удаления хоста: временная нагрузка на очередь случается.
2. Фильтрация и тихие отказы
Мы отправляем фиксированное продолжение ролевой сцены, репрезентативное для поддерживаемого Reverie трафика. Проверка анализирует причину завершения, высоконадёжные шаблоны отказа и пустой вывод.
Она также запускается на заведомо фильтрующих хостах, используемых как положительный контроль. Если такой контроль неожиданно проходит тест, мы не объявляем всех остальных здоровыми, а помечаем саму проверку как слабую. Тест, который больше не видит известный дефект, ничего не доказывает.
3. Структурированный вывод
Мы запрашиваем небольшой объект по фиксированной schema и проверяем результат. Транспортная ошибка и повреждённый вывод оцениваются по-разному: разрыв соединения говорит о недоступности провайдера, а завершённая генерация, не соответствующая schema, свидетельствует о регрессии возможностей.
«Endpoint заявляет поддержку параметра» и «endpoint надёжно соблюдает параметр» — не одно и то же обещание.
4. Целостность языка
Reverie поддерживает 17 языков интерфейса, поэтому smoke test только на английском пропустил бы часть проблем, с которыми действительно сталкиваются пользователи.
Канарейка по очереди проверяет все поддерживаемые языки с помощью синтетических ролевых prompts. Локальный детерминированный детектор ищет уверенный переход всего ответа на другой язык, устойчивое смешение языков и явное повреждение кодировки. Реальные разговоры не используются, а субъективная оценка другой модели не запрашивается.
Неоднозначный или слишком короткий вывод считается неопределённым, а не ошибочным. При обнаружении языковой аномалии система сразу делает ещё две попытки и требует большинства. Тот же язык повторяется в следующем раунде, чтобы провайдер не обошёл правило последовательных раундов лишь из-за ротации.
Мы спроектировали систему так, чтобы она сомневалась в себе
Автоматическое удаление инференс-мощности полезно, но ложное срабатывание способно вызвать более серьёзный сбой, чем исходный дефект. Поэтому у канарейки есть несколько ограничителей:
- Транспортные ошибки не считаются проблемой качества. Timeout, rate limit и ответы 5xx замораживают счёт, а не продвигают серию к блокировке.
- Одного плохого раунда недостаточно. Провайдер должен провалить два последовательных раунда, запускаемых раз в полчаса.
- Наблюдение отделено от применения. Систему можно запустить в режиме наблюдения, записать потенциальные блокировки, уведомить администраторов и измерить ложные срабатывания до включения автоматики.
- У мощности есть нижняя граница. Канарейка не блокирует провайдера, если после этого останется меньше двух рабочих хостов. Вместо этого она уведомляет администратора.
- Восстановление тоже проверяется. Заблокированные провайдеры продолжают получать probes. Два полностью успешных раунда возвращают хост досрочно.
- Повторные сбои усиливают меру постепенно. Первая блокировка длится один день, следующая — три, дальнейшие — семь. После четырнадцати дней без новой блокировки история забывается, и лестница начинается заново.
Цель не в наказании провайдера. Мы хотим убрать пользовательский трафик с воспроизводимо нездорового маршрута и оставить понятный путь назад после исправления проблемы.
Три уровня защиты
Финальное решение о маршруте объединяет три типа исключений:
- Проверенные встроенные исключения для уже измеренных дефектов или несовпадений политик, которые нельзя случайно вернуть.
- Оперативные исключения при инциденте, которые администратор добавляет сразу, не дожидаясь нового deployment.
- Временные канареечные блокировки для провайдеров, пересёкших автоматический порог ошибок.
При создании запроса эти списки объединяются в настройке OpenRouter provider.ignore. Пользователю не нужно повторять запрос, пока случай не выберет лучший провайдер: нездоровый маршрут исключается из кандидатов.
Все автоматические решения попадают в журнал аудита, администраторы получают уведомления о важных изменениях состояния, а блокировку при необходимости можно снять вручную.
Что система гарантирует — и чего не гарантирует
Область оценки канарейки намеренно узкая. Кодом можно проверить задержку, валидность schema, высоконадёжную фильтрацию, целостность языка и кодировки. Система не делает вид, будто способна определить, достаточно ли персонаж забавен, эмоционально проницателен или верен сложной личности.
Для таких широких вопросов всё ещё нужны оценки на репрезентативных prompts, человеческая проверка, производственная telemetry и, главное, сообщения пользователей.
Система также не означает, что любой странный ответ доказывает деградацию. Генеративные модели вероятностны. В длинном разговоре может накопиться противоречивый контекст, а в описании персонажа — конфликтующие инструкции. Иногда странный ответ — просто странный ответ.
Изменилось другое: фраза «это та же модель» больше не завершает расследование. Теперь мы можем изолировать маршрут, воспроизвести объективную ошибку и отвести от него дальнейший трафик.
Надёжность — это сохранение опыта
Маршрутизация между несколькими провайдерами остаётся ценной. Она даёт Reverie ресурсы и устойчивость, чтобы разговоры продолжались даже при отказе отдельных endpoints.
Но устойчивость — не просто получение HTTP-ответа. Ответ, который начинается слишком поздно, теряет нужную структуру, отказывает в поддерживаемом контенте или внезапно меняет язык, не становится здоровым лишь потому, что в счёте запрос отмечен успешным.
Для продукта с ИИ-персонажами надёжность означает нечто более человеческое: какая бы машина ни говорила за персонажа, он должен оставаться самим собой.
Именно этот стандарт защищают наши новые механизмы маршрутизации.
Если вы заметили внезапное изменение качества или языка ответа, отправьте нам сообщение с названием модели и примерным временем. Это поможет связать ваш опыт с конкретным маршрутом, который его обслужил.

