Когда платить за GPT-5 или Opus за каждый чих — роскошь, а не необходимость
Вы когда-нибудь считали, сколько уходит на API в месяц? Тысячи, десятки тысяч долларов... И львиная доля — на запросы вроде "перепиши это письмо вежливее" или "сделай summary этой статьи в 50 слов". Задачи, которые с лёгкостью решит модель в 10 раз дешевле. Но как понять, какой запрос "легкий", а какой требует полной мощности? Ручной выбор провайдера? Слишком медленно. Порог по длине промпта? Слишком тупо.
Здесь приходит роутинг на основе tool calling — техника, которая позволяет модели-роутеру самой решать, куда направить запрос. А vLLM 0.8 с флагом enable_auto_tool_choice делает это нативно и без костылей.
Идея: 95% пользовательских запросов — простые (генерация текста, базовый анализ, извлечение данных). Их ловит дешёвая MoE-модель (Hunyuan Hy3, ~$0.02 за шаг). Оставшиеся 5% (код, логика, сложный рерайт) летят на премиум-модель вроде Claude Opus 5 или GPT-5. Результат — средняя стоимость запроса падает в 5-10 раз.
Проблема: дорогой API ест бюджет, а качество не нужно везде
Допустим, ваш сервис обрабатывает 1 млн запросов в месяц. Каждый запрос к Opus стоит $0.01 (для примера). Это $10 000. Если 95% запросов можно отправить на Hy3 по $0.0002 — затраты снижаются до ~$1 000. Разница — $9 000. Но как определить, какой запрос "легкий"?
Подходы вроде "если prompt короче 1000 токенов — шли на Hy3" работают плохо. Короткий запрос может быть сложным ("напиши сортировку пузырьком на Go"). Длинный — банальным ("перескажи этот роман в трёх предложениях"). Нужен интеллектуальный роутинг.
Решение: vLLM как умный диспетчер с auto-tool-choice
В vLLm начиная с версии 0.8 появилась возможность включить автоматический выбор инструмента — enable_auto_tool_choice. Модель-роутер (тоже LLM, но маленькая и дешёвая) получает запрос и решает: либо вызвать инструмент "дешевый ответ" (и тогда запрос идёт на Hunyuan Hy3), либо "дорогой ответ" (Opus). Всё прозрачно для пользователя.
Установка: два инстанса vLLM — один локально с Hy3 (2xA100), второй как прокси к внешнему API (или тоже локально, если есть карты). Роутер работает на том же инстансе vLLM с минимальной моделью (например, Llama-3.2-1B-Instruct).
Важно: локальные модели могут быть выгоднее API при высоких объёмах — я писал об этом в статье API vs локальные модели в 2026. Но наш кейс гибридный: дешёвую модель держим локально, дорогую — через API.
1 Собираем инфраструктуру
Нам понадобится:
- Сервер с 2x A100 80GB (или аренда у облачного провайдера).
- vLLM версии 0.8.0 или новее (будем считать, что на май 2026 это стабильный релиз).
- Модель Hunyuan Hy3 (MoE 256x39B в квантизованном варианте, занимает ~80GB на двух картах).
- API ключ для Opus (или другой премиум-модели).
Устанавливаем vLLM:
pip install vllm==0.8.02 Запускаем инстансы vLLM
Первый инстанс — Hunyuan Hy3 на 2xA100 с tensor parallelism=2:
python -m vllm.entrypoints.openai.api_server \
--model Tencent/Hunyuan-Hy3-8B \
--tensor-parallel-size 2 \
--port 8001 \
--max-model-len 32768Второй инстанс — роутер на базе быстрой модели (например, Llama-3.2-1B-Instruct). Он будет принимать запросы от клиента и решать, какой инструмент вызвать. Запускаем на порту 8000:
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.2-1B-Instruct \
--port 8000 \
--enable-auto-tool-choice \
--tool-call-parser hermesФлаг --enable-auto-tool-choice включает автоматический выбор инструмента — роутер сам определит, нужно ли вызывать tool. А --tool-call-parser hermes использует парсер для models, обученных на формате tool calling (Hermes, Llama-3.1+).
3 Конфигурируем инструменты (tools)
Роутер должен знать о двух возможных действиях: вызвать дешёвую модель (Hy3) или дорогую (Opus). Для этого передаём в запросе список инструментов. Вот пример клиентского кода на Python:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="-")
tools = [
{
"type": "function",
"function": {
"name": "cheap_completion",
"description": "Отвечает на простой запрос, используя дешевую локальную модель. Подходит для суммаризации, перефразирования, извлечения данных, ответов на простые вопросы.",
"parameters": {
"type": "object",
"properties": {
"prompt": {"type": "string", "description": "Исходный запрос пользователя"}
},
"required": ["prompt"]
}
}
},
{
"type": "function",
"function": {
"name": "expensive_completion",
"description": "Отвечает на сложный запрос, требующий глубоких рассуждений, генерации кода или креативного письма. Использует премиум-модель.",
"parameters": {
"type": "object",
"properties": {
"prompt": {"type": "string"}
},
"required": ["prompt"]
}
}
}
]
response = client.chat.completions.create(
model="meta-llama/Llama-3.2-1B-Instruct",
messages=[{"role": "user", "content": "Напиши краткое резюме этого текста: ..."}],
tools=tools,
tool_choice="auto"
)Если роутер решает, что запрос простой — он вернёт вызов инструмента cheap_completion. Ваш код перенаправляет запрос на порт 8001 (Hy3). Если же выбран expensive_completion — шлёте запрос к внешнему API Opus.
Ловушка: роутер может ошибиться и отправить сложный запрос на дешёвую модель. Чтобы этого избежать, добавьте в описание инструмента expensive_completion ключевые слова-триггеры: "код", "алгоритм", "доказательство", "напиши функцию" и т.д. Подробнее о настройке таких правил — в обзоре библиотеки LLMRouter.
Как обрабатывать вызов инструмента и маршрутизировать
После получения ответа от роутера нужно распарсить tool_call и отправить запрос к соответствующему бэкенду. Вот пример обработчика:
import requests
def route_request(response):
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0]
func_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
prompt = arguments["prompt"]
if func_name == "cheap_completion":
# Отправляем на локальный Hy3
local_client = OpenAI(base_url="http://localhost:8001/v1", api_key="-")
result = local_client.chat.completions.create(
model="Tencent/Hunyuan-Hy3-8B",
messages=[{"role": "user", "content": prompt}]
)
return result.choices[0].message.content
elif func_name == "expensive_completion":
# Отправляем на внешний API Opus
opus_client = OpenAI(api_key="sk-opus-...", base_url="https://api.example.com/v1")
result = opus_client.chat.completions.create(
model="claude-opus-5-20260523", # пример названия
messages=[{"role": "user", "content": prompt}]
)
return result.choices[0].message.content
else:
# Роутер ответил напрямую (без вызова инструмента)
return response.choices[0].message.contentОбратите внимание: описание инструмента cheap_completion содержит подсказки для роутера, когда его вызывать. Экспериментируйте с формулировками — это критически влияет на точность.
Нюансы и грабли, на которые я наступил
- Задержки. Если роутер маленький (1B), он отрабатывает за 30-50 мс. А вот Hy3 на 2xA100 с бакетом 4K токенов выдаёт ответ за 200-300 мс — всё ещё приемлемо. Opus через API может тормозить до 2-3 секунд. Убедитесь, что ваш клиент асинхронный.
- Failover. Что если Hy3 упал? Хорошо бы в обработчике ошибок отправлять запрос на дорогую модель (или крутить обратно к роутеру с просьбой выбрать другой инструмент). vLLM 0.8 поддерживает graceful shutdown, но логику fallback пишите сами.
- Мониторинг затрат. Ведите лог: сколько запросов ушло на каждый инструмент. Сравнивайте с тем, что было бы без роутинга. Статья "Экономика AI: реальная стоимость запроса" поможет посчитать effective cost с учётом кэша.
- Tool calling не для всех моделей. Роутер должен уметь вызывать инструменты. Llama-3.2-1B-Instruct умеет, но в vLLM 0.8 нужно указать правильный
--tool-call-parser. Для Hermes —hermes, для Llama-3.1 —llama3.1.
Типичные ошибки и их решения
Ошибка 1: Роутер никогда не вызывает дорогой инструмент
Проверьте описание expensive_completion — вероятно, оно слишком узкое. Убедитесь, что в параметрах не указано "required": ["prompt"] — это ок, но само описание должно явно говорить, что этот инструмент для сложных задач. И обязательно передавайте tool_choice="auto".
Ошибка 2: Роутер вызывает дешёвый инструмент для сложного запроса, результат — мусор
Добавьте в cheap_completion описание с ограничениями: "Не используй для кода, математики, многошаговых рассуждений". Также можно в промпт к роутеру добавить системное сообщение: "You must select cheap_completion only for simple tasks". Пример:
messages = [
{"role": "system", "content": "If the user request requires complex reasoning, coding, or creative writing, use expensive_completion. Otherwise use cheap_completion."},
{"role": "user", "content": user_text}
]Ошибка 3: vLLM падает при параллельных запросах с tool calling
В vLLM 0.8 есть баг (на май 2026, возможно уже исправлен) с многопоточностью при enable_auto_tool_choice. Лечится увеличением --max-num-batched-tokens до 8192 и использованием --scheduling-policy=fcfs.
Сколько можно сэкономить? Реальные цифры
| Модель | Стоимость за 1М токенов | Доля запросов |
|---|---|---|
| Hunyuan Hy3 (локально) | $0.02 (только электричество + амортизация) | 95% |
| Claude Opus 5 / GPT-5 (API) | $15.00 (вход + выход) | 5% |
| Средняя стоимость без роутинга | $15.00 (все на Opus) | - |
| Средняя стоимость с роутингом | $0.02*0.95 + $15*0.05 = $0.769 | - |
Экономия ~95% на стоимости токенов. Плюс локальная модель не имеет задержек сети (если не считать API для Opus). Но учтите, что стоимость электричества и железа для Hy3 может отличаться — подробный ROI описан в статье про окупаемость железа.
FAQ: ответы на хейтеров
Не проще ли держать всё локально на одной большой модели?
Можно, но большая локальная модель (вроде 400B) потребует 8xA100 и всё равно не дотянет до Opus на сложных задачах. А Hy3 — MoE, активные параметры ~10B, что даёт отличное соотношение цена/качество для простых задач.
А как же модели-роутеры вроде LLMRouter?
LLMRouter — отдельная библиотека, упрощающая конфигурацию. Мы же используем нативный функционал vLLM, что даёт полный контроль и меньше зависимостей. Но для быстрого старта можно взять LLMRouter — там уже есть готовый роутинг по сложности.
Можно ли роутить не только по сложности, но и по длине контекста?
Да, добавьте третий инструмент для очень длинных контекстов (например, GPT-4-turbo с 128K). vLLM поддерживает любое количество инструментов.
Неочевидный совет: начните с off-policy оценки
Прежде чем внедрять роутинг в production, соберите датасет из 1000 реальных запросов, разметьте их на "простые" и "сложные", и прогоните через вашего роутера. Посчитайте accuracy: сколько раз он выбрал правильный инструмент. Только после этого настраивайте пороги. И помните: даже 90% точности сэкономит кучу денег, а 5% ошибок можно компенсировать тем, что вы просто дадите пользователю возможность переключить модель вручную.
Роутинг на vLLM — это не rocket science, но простор для оптимизации огромный. Идеально, если вы уже прочитали про замену трёх LLM на одну MoE 122B — там та же философия, но с другим акцентом.