Fallback между LLM: как не получить корректный, но неверный ответ
Fallback между языковыми моделями может сохранить JSON, но изменить смысл результата. Разбираем, как разделить retry и fallback, проверить семантику и контролировать стоимость.
В обычном backend fallback выглядит просто: основной исполнитель недоступен, запрос передаётся резервному, а клиент получает ответ по тому же контракту. Для языковых моделей эта схема работает не всегда. Две модели могут вернуть одинаково валидный JSON, соблюсти все обязательные поля и при этом по-разному понять задачу.
Проблема не в формате ответа, а в его семантике. Резервная модель способна не нарушить технический контракт, но изменить классификацию, приоритет, тон рекомендации или вывод, на котором строится следующий шаг системы.
Поэтому fallback для LLM нужно рассматривать не как простую замену исполнителя, а как управляемую деградацию качества, смысла и стоимости.
Почему валидный ответ может быть неправильным
Представим запрос на классификацию обращения клиента. Сервис ожидает такой ответ:
{
"category": "billing",
"priority": "high",
"needs_human": true
}
Резервная модель может вернуть тот же набор полей и допустимые значения, но определить обращение как техническую проблему с низким приоритетом. Парсер не сообщит об ошибке: JSON корректен, схема соблюдена. Однако бизнес-логика получит другое решение.
Причины расхождений обычно связаны с несколькими факторами:
- модели по-разному интерпретируют неоднозначные формулировки;
- одна модель лучше удерживает длинный контекст, другая сильнее в коротких инструкциях;
- отличаются склонность к осторожным ответам и порог уверенности;
- модели могут по-разному следовать приоритетам системной и пользовательской инструкции;
- разная токенизация и лимиты контекста влияют на доступную информацию;
- одинаковые настройки температуры не делают модели эквивалентными.
Из этого следует важный вывод: проверка JSON подтверждает только структурную совместимость. Она не доказывает, что резервный ответ равнозначен основному.
Retry и fallback — разные механизмы
Retry предназначен для повторения того же запроса в надежде устранить временную техническую проблему. Например, запрос завершился тайм-аутом, соединение разорвалось или сервис вернул временную ошибку. В этом случае можно повторить вызов той же модели с ограниченным числом попыток.
Fallback переключает запрос на другую модель. Это уже изменение исполнителя, а значит, потенциально меняются качество, стиль, содержание и цена ответа.
Разделяйте эти сценарии в коде и в мониторинге:
- определить, была ли ошибка технической;
- выполнить ограниченный retry для временных сбоев;
- при исчерпании попыток решить, допустим ли fallback;
- отдельно зафиксировать факт переключения и модель, которая сформировала ответ.
Не стоит делать fallback на любую ошибку подряд. Если модель вернула некорректный JSON, можно повторить запрос с теми же параметрами или применить исправляющий вызов. Если же ответ синтаксически корректен, но не прошёл проверку смысла, автоматическая замена модели может как помочь, так и добавить ещё один непредсказуемый результат.
Контракт нужно проверять на трёх уровнях
1. Структура
Проверьте JSON Schema или аналогичную схему:
- обязательные поля присутствуют;
- типы данных соответствуют ожиданиям;
- значения входят в допустимый набор;
- нет неожиданных полей, если они запрещены;
- числа, даты и идентификаторы имеют правильный формат.
Это базовый слой. Он отсекает ответы, которые нельзя безопасно передать в программу.
2. Бизнес-ограничения
Структура может быть правильной, но значения — противоречивыми. Например, поле needs_human равно false, хотя priority установлено в critical. Такие правила нужно проверять отдельно:
def validate_business_rules(result: dict) -> bool:
if result["priority"] == "critical" and not result["needs_human"]:
return False
if result["category"] == "billing" and not result.get("account_id"):
return False
return True
Набор проверок зависит от задачи. Для извлечения данных это могут быть сверка сумм и дат. Для классификации — допустимые сочетания категорий. Для генерации SQL — запрет операторов изменения данных.
3. Семантика
Самый сложный уровень — понять, соответствует ли ответ исходной задаче. Здесь помогают эталонные примеры, правила предметной области и автоматические проверки по критичным признакам.
Для важных сценариев полезно хранить не только итоговый ответ, но и служебные данные:
- идентификатор модели;
- версию промпта;
- параметры запроса;
- причину переключения;
- результат структурной и бизнес-проверки;
- время и стоимость вызова.
Так можно выяснить, где именно началась деградация и действительно ли fallback спасает систему.
Как выбирать резервную модель
Резервная модель должна подбираться не по принципу «самая дешёвая», а по допустимому риску. Для простых задач можно переключаться на более экономичный вариант. Для решений, влияющих на деньги, доступы или коммуникацию с клиентом, лучше заранее определить границу, за которой система возвращает честную ошибку.
В Протоке на дату публикации доступны, например, следующие котировки за 1 млн токенов:
- GPT-5.6 Sol — 13,60 ₽ за входные и 85,29 ₽ за выходные токены;
- GLM-5.3 — 20,47 ₽ и 64,34 ₽ соответственно;
- GPT-5.6 Luna — 8,57 ₽ и 51,26 ₽;
- GLM-5.2 — 3,66 ₽ и 11,50 ₽;
- GLM-5.3 Flash — 11,76 ₽ и 39,18 ₽.
Цена зависит не только от выбранной модели, но и от объёма входа и выхода. Если fallback получает большой контекст после неудачной попытки основной модели, итоговая стоимость может вырасти. Поэтому в расчёте учитывайте оба вызова и отдельно измеряйте среднюю стоимость успешного результата.
Практическая схема может выглядеть так:
- основная модель используется для сложного анализа;
- резервная — для задач с ограниченным риском и коротким контекстом;
- для критичных решений fallback запрещён без дополнительной проверки;
- при невозможности подтвердить смысл система возвращает ошибку, а не догадку.
Пример вызова через совместимый API
В примерах параметр model — это slug модели латиницей из котировок, а не отображаемое название:
from openai import OpenAI
client = OpenAI(
api_key="",
base_url="https://dualchat.pro/v1"
)
response = client.chat.completions.create(
model="gpt-5.6-sol",
messages=[
{
"role": "system",
"content": "Верни только JSON по заданной схеме. Не меняй значения перечислений."
},
{
"role": "user",
"content": "Классифицируй обращение и укажи, нужна ли проверка сотрудником."
}
],
temperature=0
)
Если основной вызов завершился временной ошибкой, приложение может повторить запрос, а затем передать его резервной модели. Но после переключения нужно запускать тот же набор проверок, а не считать ответ безопасным только потому, что он успешно распарсился.
Когда лучше вернуть ошибку
Честная ошибка предпочтительнее «успешного» ответа в нескольких случаях:
- результат запускает финансовую операцию;
- ответ меняет права доступа или состояние учётной записи;
- ошибка может привести к юридическим или репутационным последствиям;
- нет автоматической проверки семантики;
- резервная модель не прошла предварительное сравнение на рабочих примерах.
Для менее критичных задач можно вернуть ответ с явным признаком деградации: указать, что использовалась резервная модель, и направить результат на последующую проверку. Главное — не скрывать переключение от системы и пользователя там, где это влияет на интерпретацию ответа.
Как внедрять fallback без сюрпризов
Начните с набора контрольных запросов, отражающих реальные сценарии. Для каждой модели измерьте не только долю технически успешных ответов, но и:
- точность по эталонным результатам;
- долю нарушений бизнес-правил;
- согласованность с основной моделью;
- задержку;
- стоимость одного принятого системой ответа.
Затем задайте политики для разных классов задач. Один fallback может быть допустим для суммаризации, но запрещён для определения возврата денег. Такой подход лучше универсального правила «если ошибка — используем другую модель».
Вывод
Fallback между LLM не гарантирует сохранения смысла. Корректный JSON — только первый уровень проверки; за ним должны следовать бизнес-правила и контроль семантики. Retry повторяет вызов того же исполнителя, а fallback меняет модель и потенциально меняет результат, задержку и стоимость.
В Протоке можно выбрать модель под требования конкретной задачи, сравнить котировки и подключить API через https://dualchat.pro/v1. Создайте ключ в кабинете в разделе «Ключи», настройте ограниченный retry, заранее протестируйте резервный сценарий и не заменяйте подтверждённую ошибку непроверенным ответом.