Model routing: как выбирать LLM по задаче, риску и цене
Сначала допустимость, затем рейтинг
Качество измеряется на конкретной задаче
Авторская схема ROUTE-GATE-7
Политика, бюджет, fallback и проверка результата

$ route --contract ROUTE-GATE-7
> bind: task / data class / budget
> filter: eligible models / regions
> rank: quality / latency / cost
> route: accept / fallback / reviewModel routing выбирает модель для конкретного запроса по явному контракту качества, задержки, стоимости и безопасности. Это не просто отправка каждого вызова самому дешёвому провайдеру. Решение имеет смысл, если приложение может объяснить выбор, обнаружить непригодный результат и восстановиться без скрытой смены требований.
Здесь разбираем узкий вопрос маршрутизации LLM. Более широкие задачи описаны на страницах prompt engineering, AI-автоматизации и AI-специалиста в Армении. Смежные темы: структурированный вывод и версионирование промптов.
Проблема и требования
Представим поддержку: есть короткая классификация обращений, резюме по документам и чувствительные черновики для эскалации. Одна дорогая модель для всего увеличивает расходы; одна малая модель для всего может не выполнить требования к качеству или данным. Маршрутизатору нужны класс задачи, чувствительность данных, схема ответа, предельная задержка, бюджет, список допустимых моделей и правило fallback. Эти поля задаёт доверенное приложение, а не случайный документ или ответ модели.
Цель — не абстрактное место модели в рейтинге, а проверяемый критерий для конкретной задачи. Ответ должен соответствовать схеме и источникам, уложиться в согласованную задержку и бюджет. Иногда правильный маршрут — не вызывать модель до уточнения прав или ручной проверки.
Собственная архитектура ROUTE-GATE-7
Семь этапов ниже — иллюстративная схема, а не измеренный результат клиентского проекта:
- Привязать запрос. Проверить пользователя, зафиксировать класс задачи, организацию, доступные данные, срок и максимальный расход.
- Применить жёсткую политику. Исключить модели и адреса, которым нельзя передавать данные или которые не поддерживают нужный формат. Низкая цена не отменяет эту проверку.
- Прочитать версионированный реестр. Для каждого кандидата хранить провайдера, версию, регион, возможности, основу тарификации, лимиты и ограничения. Устаревшие записи выводить из обращения осознанно.
- Оценить допустимых кандидатов. Использовать результаты тестов по типу задачи и наблюдаемые распределения задержки и стоимости. Оценка относится к конкретному набору тестов, а не ко всем задачам.
- Вызвать с лимитами. Ограничить время, токены и повторы; присвоить стабильный ID. Не превращать неизвестный исход внешней записи в автоматическое второе действие.
- Проверить вывод. Вне модели сверить схему, обязательные ссылки на источники, политику и критерии приёмки маршрута.
- Направить результат. Ответить, ограниченно повторить на совместимом кандидате, уточнить вопрос либо передать человеку. Сохранить решение и версию без лишнего чувствительного текста.
Схема зависимостей: доверенный запрос → фильтр политики → реестр → выбор → ограниченный вызов → валидатор → ответ или проверка. Политикой и валидацией управляет приложение. Модель не может сама разрешить себе обработку данных или подтвердить собственный результат.
Минимальный пример решения
Возьмём три условных профиля: fast для меток, balanced для обычных резюме с источниками и reviewed для сложных черновиков. Это роли, а не результаты замеров конкретных брендов. Если обращение помечено sensitive=true, допустимы только профили с разрешением на такой класс данных. При отсутствии кандидатов маршрут — hold, даже если запрос срочный.
const request = bindTask(actor, input, { deadlineMs: 2500, maxCost: 0.02 });
const eligible = registry.current().filter(model =>
policy.allows(model, request.dataClass) &&
model.supports(request.schema) &&
model.estimatedCost(request) <= request.maxCost
);
if (eligible.length === 0) return holdForReview("no_eligible_model");
const candidate = rankByTaskEvidence(eligible, request.taskClass)[0];
const output = await callWithBudget(candidate, request);
if (!validate(output, request.schema, request.evidence))
return routeCompatibleFallbackOrReview(request, candidate);
return recordDecisionAndRespond(output, candidate.version);Число в maxCost — условный предел для демонстрации контракта, а не совет по цене. Тарифы, задержки и поведение моделей меняются. В реальном проекте их берут из версионированного реестра и измеряют на собственном потоке задач.
Компоненты и контракты
Классификатор задачи. Лучше опираться на детерминированные признаки: endpoint, поле формы, этап процесса. Если класс определяет модель, проверяйте её метку и оставляйте маршрут unknown. Ошибки классификации учитывайте отдельно от ошибок ответа.
Политика допустимости. До ранжирования проверяйте регион и правила обработки данных, права, инструменты, размер контекста и поддержку формата. Нельзя посылать чувствительный текст кандидату только для того, чтобы узнать, подходит ли он.
Тестовый набор. Нужны обычные, пограничные, многоязычные, длинные и атакующие примеры. У каждого случая должны быть ожидаемый маршрут, схема, источники и запрещённые последствия. Сравнивайте кандидата с базовой версией на одинаковых редакциях случаев.
Эксплуатационные сигналы. Записывайте маршрут, версию кандидата, число токенов при наличии, задержку, результат проверки, причину fallback и конечный исход. Группируйте по классу задачи и данных. Успешный ответ API или HTTP 200 не доказывает выполнение задачи пользователя.
Ошибки и восстановление
Маршрутизатор только по цене может выбрать модель, которая ломает цитаты или формат, и повторы обойдутся дороже подходящего первого вызова. Выбор только по задержке может дать быстрый, но непригодный ответ. Изменение версии модели делает старые оценки сомнительными, даже если её имя выглядит прежним. Сбой провайдера или ограничение скорости может привести к fallback с другими правилами обработки данных. Ошибка проверки иногда связана с плохим источником или отсутствием прав; более мощная модель этого не исправит.
Резервный кандидат обязан пройти ту же жёсткую политику и тот же контракт вывода. После неясного исхода действия инструмента сначала сверяйте систему назначения по ID операции. Для важных решений или пустого списка допустимых кандидатов нужен понятный маршрут на ручную проверку. Храните последнюю рабочую версию политики, чтобы откатить неудачные веса или реестр независимо от выпуска модели.
Тестирование и production-чек
Начните с офлайн-прогона версионированных случаев. Проверяйте и выбор маршрута, и итоговую приёмку: допустимую модель, схему, ссылки на источники, бюджет, поведение при дедлайне и запрет обработки недоступных данных. Добавьте пустые источники, неверный формат, таймаут, rate limit, ошибку провайдера, старый реестр и смену версии модели. Цепочку fallback тестируйте без реальных внешних записей.
Затем сравните ограниченный теневой или канареечный поток, если он разрешён для этих данных. Смотрите ошибки качества, p50/p95 задержки и фактически оплаченные токены по классам задач. Пороги нужно задать до анализа результата и сравнивать с базовой версией на одинаковых входах. Не публикуйте выдуманный процент экономии или точности. Выпускайте изменение после согласованных проверок и испытанного отката.
Рабочий контракт состоит из версии политики и измеримых свидетельств. Маршрутизатор полезен, когда выбирает допустимую модель, возвращает принятый ответ в пределах ограничений и делает сбои понятными. Для разбора такой схемы в вашем процессе можно запросить архитектурный аудит.
const task = bindTask(actor, input, limits);
const eligible = registry.filter(model => policy.allows(model, task));
if (!eligible.length) return holdForReview();
const output = await callWithBudget(rank(eligible, task)[0], task);
return validate(output, task) ? accept(output) : fallbackOrReview();