vLLM оптимизация: Qwen 3.5 Thinking и Non-Thinking без двойной VRAM | AiManual
AiManual Logo Ai / Manual.
28 Фев 2026 Гайд

Оптимизация vLLM: как обслуживать Qwen 3.5 в Thinking и Non-Thinking режимах без двойной загрузки в VRAM

Гайд по обслуживанию Qwen 3.5 в двух режимах через один экземпляр vLLM. Экономим видеопамять на 50% с помощью chat template kwargs.

Проблема: две модели в памяти, один результат

Представьте ситуацию: вы развертываете Qwen 3.5 в продакшене. Пользователи хотят два режима - обычный (Non-Thinking) и "размышляющий" (Thinking), где модель ведет внутренний диалог перед ответом. Вы запускаете два экземпляра vLLM с разными chat templates. И тут понимаете, что каждый экземпляр загружает свою копию весов модели. Для Qwen 3.5 32B это около 64 ГБ VRAM в FP16. Две копии - 128 ГБ. А если у вас только 80 ГБ на карте? Вы встали.

Эта проблема не теоретическая. На февраль 2026 года, когда модели становятся больше, а VRAM дорожает, каждая гигабайта на счету.

Почему так происходит? Потому что vLLM по умолчанию загружает модель один раз на процесс. Если вы запускаете два отдельных сервера - это два процесса, две загрузки. Но есть способ лучше.

Что такое Thinking режим в Qwen 3.5 и почему он съедает память

Qwen 3.5 (особенно версии 32B и выше) поддерживает режим "Thinking" - это не отдельная модель, а специальный формат промпта. Модель получает инструкцию "Think step by step" внутри запроса, и её chat template добавляет метки для внутреннего диалога.

Например, в Non-Thinking режиме промпт выглядит так:

system: You are a helpful assistant.
user: What is the capital of France?

А в Thinking режиме так:

system: You are a helpful assistant. Think step by step.
user: What is the capital of France?
assistant: Let me think... The capital of France is Paris.

Но ключевое отличие - в chat template. Для Thinking режима используется другой шаблон, который добавляет служебные токены. И если в vLLM вы задаете chat template при запуске, он фиксирован для всего сервера.

Отсюда и проблема: два разных шаблона - два сервера.

Решение: один vLLM, два режима через chat_template_kwargs

Начиная с vLLM 0.5.7 (актуально на 28.02.2026), появилась возможность передавать дополнительные аргументы в chat template через параметр --chat-template-kwargs. Это позволяет динамически менять поведение шаблона без перезагрузки модели.

Идея: создать универсальный chat template, который в зависимости от переданного параметра решает, использовать Thinking или Non-Thinking формат.

💡
Это похоже на то, как вы передаете аргументы в функцию. Chat template в vLLM - это по сути Jinja2 шаблон, который может принимать параметры.

Таким образом, мы запускаем один экземпляр vLLM, а при запросе через API передаем параметр, например, thinking_mode=True. Шаблон интерпретирует это и форматирует промпт соответственно.

1 Шаг 1: Создание кастомного chat template

Сначала нужно создать файл chat template для Qwen 3.5, который поддерживает оба режима. Сохраним его как qwen_thinking.jinja:

{% if thinking_mode %}
{{- bos_token -}}
{%- for message in messages %}
    {%- if message['role'] == 'system' %}
        {{- 'system: ' + message['content'] + ' Think step by step.' + eos_token -}}
    {%- elif message['role'] == 'user' %}
        {{- 'user: ' + message['content'] + eos_token -}}
    {%- elif message['role'] == 'assistant' %}
        {{- 'assistant: ' + message['content'] + eos_token -}}
    {%- endif %}
{%- endfor %}
{{- 'assistant: Let me think...' -}}
{% else %}
{{- bos_token -}}
{%- for message in messages %}
    {%- if message['role'] == 'system' %}
        {{- 'system: ' + message['content'] + eos_token -}}
    {%- elif message['role'] == 'user' %}
        {{- 'user: ' + message['content'] + eos_token -}}
    {%- elif message['role'] == 'assistant' %}
        {{- 'assistant: ' + message['content'] + eos_token -}}
    {%- endif %}
{%- endfor %}
{{- 'assistant:' -}}
{% endif %}

Этот шаблон проверяет переменную thinking_mode. Если True, добавляет "Think step by step." к системному сообщению и инициирует ответ с "Let me think...". Если False, работает как обычный шаблон.

2 Шаг 2: Запуск vLLM с кастомным шаблоном и параметрами

Запускаем vLLM сервер с указанием нашего шаблона и передаем аргументы для шаблона:

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen3.5-32B-Instruct \
    --chat-template ./qwen_thinking.jinja \
    --chat-template-kwargs thinking_mode=false \
    --tensor-parallel-size 2 \
    --gpu-memory-utilization 0.9

Обратите внимание на --chat-template-kwargs thinking_mode=false. Это значение по умолчанию. Но мы можем переопределить его при запросе через API.

3 Шаг 3: Отправка запросов с разными режимами

При отправке запроса к API, мы можем передать дополнительные параметры в поле chat_template_kwargs (если используем OpenAI-совместимый API) или через другие методы.

Пример запроса для Non-Thinking режима (значение по умолчанию):

curl http://localhost:8000/v1/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "Qwen/Qwen3.5-32B-Instruct",
        "prompt": "What is the capital of France?",
        "max_tokens": 50
    }'

Для Thinking режима нужно передать chat_template_kwargs:

curl http://localhost:8000/v1/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "Qwen/Qwen3.5-32B-Instruct",
        "prompt": "What is the capital of France?",
        "max_tokens": 100,
        "chat_template_kwargs": {"thinking_mode": true}
    }'

В ответе модель будет использовать Thinking шаблон.

Важно: vLLM передает chat_template_kwargs непосредственно в шаблон, так что вам не нужно менять код модели. Это чисто конфигурационное решение.

Альтернатива: модификация модели под два шаблона

Если по какой-то причине chat_template_kwargs не работает (например, в более старых версиях vLLM), можно модифицировать код модели, чтобы она принимала параметр thinking в запросе. Но это требует изменений в коде и перезагрузки модели.

Однако, на 28.02.2026, vLLM 0.5.7 и выше поддерживает chat_template_kwargs, так что этот способ предпочтительнее.

Нюансы, которые взорвут ваш сервер, если не знать

  • Кэширование промптов: vLLM кэширует промпты для ускорения. Если вы отправляете одинаковые промпты с разными thinking_mode, они будут закэшированы отдельно. Это хорошо, но может занять память. Учитывайте это при настройке --block-size и --enable-prefix-caching.
  • Производительность: Thinking режим генерирует более длинные ответы (из-за внутреннего диалога), что может снизить throughput. Настройте --max-num-seqs и --max-model-len соответственно.
  • Ошибки памяти: Если вы используете Thinking режим для очень длинных контекстов, следите за памятью. Включите --gpu-memory-utilization и мониторьте использование VRAM. В крайнем случае, используйте CPU Offloading.

Частые ошибки и как их избежать

Ошибка Причина Решение
Шаблон не применяется, всегда один режим chat_template_kwargs не передаются в шаблон Убедитесь, что в запросе передается chat_template_kwargs, а в шаблоне используется правильная переменная.
Утечка памяти при переключении режимов Кэш промптов растет без ограничений Настройте --enable-prefix-caching и ограничьте --block-size.
Низкая скорость обработки в Thinking режиме Модель генерирует больше токенов Увеличьте --max-num-batched-tokens и используйте более быстрые GPU, например, через облачные сервисы вроде Lambda Labs.

FAQ: коротко о главном

Вопрос: Этот метод работает только для Qwen 3.5?

Ответ: Нет, он работает для любой модели, где chat template можно параметризовать. Например, для Llama 3.1 или Mistral. Но шаблон нужно адаптировать.

Вопрос: Сколько VRAM я экономлю?

Ответ: Вместо двух загрузок модели - одна. Для Qwen 3.5 32B в FP16 это экономия около 64 ГБ VRAM. Если использовать квантование, как в статье "Можно ли запустить локальную LLM на 10 ГБ видеопамяти?", то экономия еще значительнее.

Вопрос: Что если я хочу больше двух режимов?

Ответ: Добавьте больше параметров в chat_template_kwargs. Например, {"thinking_mode": true, "detailed": false}. Шаблон Jinja2 может обрабатывать несколько переменных.

Вопрос: А если vLLM не поддерживает chat_template_kwargs?

Ответ: Обновитесь до версии 0.5.7 или выше. Если не можете, придется модифицировать код модели или запускать два экземпляра с общими весами через техники оптимизации vLLM, например, используя общую память для весов.

Итог: больше не нужно жертвовать режимами из-за нехватки VRAM. Один vLLM, два режима - и все довольны.

Подписаться на канал