Одна модель на клиенте: шлюз делит уровни автоматически
Уберите выбор модели из интерфейса — клиент показывает одно имя модели, планирование идёт на сильной модели, а исполнение попадает на экономичную, с разделением на том переходе, который запрос и так совершал.
На что на самом деле жалуется сообщество
«У всех в настройках разные модели, и один и тот же промпт у двух людей даёт разный результат», «самый частый вопрос в первую неделю — какую модель выбрать», «вывод модели из эксплуатации означает правку переменных окружения на каждой машине разработчика». Выбор модели передан людям с наименьшей информацией для этого решения: они не видят своих расходов и не могут знать, заслуживает ли этот конкретный запрос сильной модели.
Переведите всех на дешёвую модель — и работа деградирует: отдайте планирование и архитектурные решения небольшой модели, и переделки обойдутся дороже экономии. Разделение, которое действительно важно, — что делает этот запрос: планирует или исполняет, — а клиент никогда не сообщал этого шлюзу.
Как это делает GateLLM
Клиент сохраняет одно имя модели; сквозное сопоставление (from «*») на шлюзе нормализует любое запрошенное имя, поэтому правка личных настроек больше не меняет место назначения запроса. Объяснять, какую модель выбрать, больше нечего.
Сигнал для разделения уже есть в диалоге: режим планирования Claude Code вставляет Plan mode is active при входе и Exited Plan Mode при выходе. Скрипт в слоте before сканирует назад от последнего сообщения, берёт последний встреченный маркер для определения текущего состояния и вызывает context.useModel() для переключения маршрутизации. Решение — сравнение позиции в строке, без отдельного вызова классификации и без лишнего сетевого перехода.
Разделение должно жить в слоте before: слот after видит уже транслированное тело upstream и не может прочитать маркеры, вставленные клиентом, а useModel в слоте after переключает только внутри того же протокола, тогда как переход от планирования к исполнению — это смена маршрута между моделями. Правила request_payload (prompt_cache_key и т. п.) должны быть настроены на обеих моделях: входная модель сохраняет свои, пока идёт планирование, а целевая применяет свои после переключения.
5 рычагов, которые действительно работают
- Одно имя на входе: клиент показывает одно имя модели, а сквозное сопоставление from «*» нормализует каждое запрошенное имя к одной целевой модели
- Автоматическое разделение: скрипт в слоте before берёт последний маркер при обратном сканировании — маркер входа навсегда остаётся в истории, поэтому проверка «содержит» навсегда читает состояние как режим планирования
- Защита протокола: скрипт сначала проверяет context.sourceProtocol и завершается раньше для трафика не-Anthropic, поэтому одна входная модель, обслуживающая несколько клиентов, никогда не маршрутизируется неверно
- Наблюдаемость: метка SW в колонке модели лога означает переключение скриптом, а MM — сопоставление по идентичности; статистика и биллинг атрибутируются по итоговому имени модели, поэтому каждое решение о разделении аудируемо
- Нулевая раскатка на клиентах: изменения конфигурации не требуют правки на каждой машине, а сопоставления перезагружаются на лету (единственное исключение — лимит кучи скрипта SCRIPT_MEMORY_LIMIT_MB, требующий перезапуска)