Статья

Ловушка гибридных резонеров

Гибридный checkpoint упрощает выбор режима, но длинные reasoning-запросы и короткие ответы конкурируют за scheduler, KV cache и токенный бюджет. Разбираю, когда нагрузку нужно делить, а когда один инстанс разумнее.

Автор — Сергей Нотевский

Опубликовано

Гибридный резонер предлагает очень удобную сделку. Один checkpoint умеет рассуждать и отвечать напрямую. Сложную задачу отправляем в thinking, чат или извлечение полей запускаем без него. Веса уже загружены, API один, продуктовой команде не приходится выбирать модель.

Кажется, что и разворачивать всё это можно одним инстансом. Ловушка начинается именно здесь.

Гибридность модели и смешивание нагрузок в одном runtime отвечают на разные вопросы. Первая определяет, как модель умеет отвечать. Вторая решает, какие запросы будут одновременно делить GPU, scheduler и KV cache.

Как Qwen объединил, разделил и снова объединил режимы

В апреле 2025 года вышел Qwen3. Thinking и non-thinking впервые для этой линейки жили в одном checkpoint. Режим переключался через enable_thinking, а в диалоге можно было использовать /think и /no_think.

На словах всё прекрасно. Сложной задаче даём время подумать, простой не разрешаем расходовать токены на рассуждение.

Через три месяца Qwen выпустил модели 2507 раздельно. Instruct-2507 работал только в non-thinking, Thinking-2507 только в thinking. Я тогда подумал: ну вот и ответ. Видимо, гибрид оказался удобнее в API, чем при обучении.

Но это был слишком быстрый вывод. Вместе с разделением checkpoints команда меняла post-training, instruction following, знания и работу с длинным контекстом. Вендорские бенчмарки выросли, однако контролируемого сравнения «тот же training run, только без гибридности» не было.

Дальше история сделала ещё один поворот. В Qwen3.5 переключение вернулось: thinking включён по умолчанию, но его можно отключить на уровне запроса. Правда, soft switch через команды в самом диалоге модель уже официально не поддерживала.

Qwen3.8 окончательного ответа тоже не дал. Открытый Qwen3.8-27B снова умеет отвечать в обоих режимах. Thinking включён по умолчанию, его глубина регулируется через reasoning_effort, а для прямого ответа режим можно выключить. Открытый Qwen3.8-2.4T-A95B устроен иначе: это thinking-only модель. Отключить рассуждение у неё нельзя. Non-thinking для этого варианта есть в hosted Qwen3.8-Max.

В одной линейке одновременно остались гибридный и специализированный подходы. Так что релизная история Qwen не доказывает ни победу, ни провал гибридов. Зато она хорошо отделяет выбор checkpoint от выбора serving-архитектуры.

В чём ловушка одного инстанса

Для клиента thinking часто выглядит как один флаг. Для inference runtime это запросы разной формы.

Чат, классификация, суммаризация или извлечение полей обычно заканчиваются коротким ответом. Там ценятся небольшой TTFT, предсказуемая задержка и высокая пропускная способность.

Reasoning-запрос способен генерировать намного больше токенов и заметно дольше оставаться в decode. Всё это время он занимает место в KV cache, живёт в scheduler и конкурирует за токенный бюджет батча. Пока нагрузка маленькая, никто ничего не замечает. Под нагрузкой длинные рассуждения начинают задерживать короткие ответы. Средняя утилизация GPU при этом может выглядеть отлично, а p95 TTFT быстрого чата уже уехал.

Один checkpoint поддерживает два режима. Из этого не следует, что оба режима нужно обслуживать одним inference-инстансом.

Современный vLLM умеет смягчать конфликт. Chunked prefill дробит длинные входы и совмещает их обработку с decode. Через max_num_batched_tokens можно двигать компромисс между TTFT, ITL и throughput. Есть priority scheduling, который позволяет пропускать срочные запросы вперёд.

Бесплатного решения тут нет. Вытеснённый reasoning-запрос может потерять KV cache и повторно пройти часть вычислений. Маленький токенный бюджет батча нередко помогает ITL, большой бывает выгоднее для TTFT и общего throughput. Настройка, удачная для длинных reasoning-сессий, не обязана подходить быстрому чату.

Как не попасть в ловушку

Самый прямой вариант: разные checkpoints и разные пулы. Быструю Instruct-модель держим под короткие запросы, thinking-модель под задачи с рассуждением. Контракт получается ясным, но обе модели приходится отдельно оценивать, обновлять и держать в памяти.

Можно оставить один гибридный checkpoint и развернуть его дважды. В одном пуле thinking выключен, настройки подобраны под короткие ответы. Во втором разрешены длинные рассуждения и действует другой latency-класс. Память под веса дублируется, зато режимы не мешают друг другу.

Перед пулами нужен gateway или другой слой маршрутизации. Там запрос получает workload-класс, лимит reasoning, timeout и fallback. Клиент по-прежнему работает с одним API, а инфраструктура уже знает, какой профиль нагрузки ей достался.

У Qwen3.8 появился ещё один полезный слой контроля. reasoning_effort задаёт модели уровень усилия, хотя жёсткого потолка по токенам не гарантирует. В актуальном vLLM есть thinking_token_budget, который принудительно завершает reasoning после заданного числа токенов. Такие лимиты особенно важны в общем пуле: один неожиданно длинный ответ не должен бесконечно занимать его ёмкость.

Лимит не заменяет изоляцию. Он лишь делает хвост распределения короче и предсказуемее. Если быстрый и reasoning-классы живут под разными SLO, измерять их всё равно придётся отдельно.

Когда один инстанс действительно лучше

Разделение тоже стоит денег. Два пула держат две копии весов и могут по очереди простаивать. Если thinking-запрос приходит несколько раз в час, отдельный GPU под него выглядит не как архитектура, а как дорогая мебель.

Общий инстанс имеет смысл, когда у него остаётся запас ёмкости даже во время reasoning-запросов, а быстрый сценарий не живёт под жёстким p95 latency. Рассуждение должно быть редким или ограниченным по токенам. Для разработки, пилота, внутреннего инструмента и offline-задач этого часто достаточно.

Есть и продуктовый плюс. Команда получает один API и один checkpoint, а свободная ёмкость не запирается внутри двух недогруженных пулов. Позже gateway сможет развести режимы по разным deployments без изменения клиентского контракта.

Проверять границу лучше на своём workload. Нужны распределения входных и выходных токенов, доля thinking-запросов, concurrency и характер прихода нагрузки. Я бы смотрел на TTFT и ITL отдельно по классам, длину очереди, preemption и использование KV cache. Среднее по общему потоку здесь почти бесполезно. Подробнее об этом я писал в заметке «Workload shape важнее названия модели».

Гибридный резонер хорошо упрощает выбор режима. Ловушка появляется, когда вместе с режимом ему молча отдают ещё и выбор инфраструктуры.

Один инстанс может оказаться лучшим решением. Но это должен показать профиль нагрузки, а не наличие enable_thinking в документации модели.

Источники

Связанные материалы