
Привет, Хабр! На фоне ажиотажа вокруг нейросетей все чаще встает вполне приземленный вопрос — сколько стоит содержать собственную LLM.
Современные ИИ-агенты уровня Claude, ChatGPT и DeepSeek уже давно перестали быть «чатами для развлечения». Это сложные системы, которые перед тем как выдать ответ, тратят десятки тысяч токенов на внутренние рассуждения, вызывают внешние функции, взаимодействуют с MCP-серверами и даже работают напрямую с интерфейсом ОС.
В продакшене — особенно при использовании нескольких агентов, собственных инструментов и фоновых задач — потребление токенов растет лавинообразно. При плотной нагрузке счет за API легко превращается в постоянную и плохо прогнозируемую статью расходов, от которой уже сложно отмахнуться.
В статье я покажу практичный компромисс: как развернуть собственную облачную LLM, которая укладывается в 16 ГБ видеопамяти, поддерживает инструменты и вызов функций, интегрируется с MCP-серверами и может использоваться как полноценный API-сервис для бэкенд-задач.
Используйте навигацию, если не хотите читать целиком:
Как сократить расходы на использование LLM, не потеряв в качестве
Самый очевидный ответ — развернуть собственную нейросеть на сервере и использовать ее как облачный API.
Однако здесь нас ждет стоп-фактор. Большинство мощных open-source языковых моделей требуют десятки гигабайт видеопамяти. 24, 48, а иногда и 80 ГБ VRAM — а это уже совсем другой бюджет на инфраструктуру, который подходит далеко не всем. Вся архитектура будет максимально прагматичной и ориентированной на продакшен.
Из каких компонентов состоит решение
Прежде чем писать команды и поднимать сервер, нужно зафиксировать, что именно мы строим. Важно понимать: речь идет не о запуске «локального чата», а о сборке небольшой, но полноценной инфраструктуры.
В итоговом варианте решение представляет собой несколько логических компонентов. В основе лежит LLM-сервер, отдельный сервис, отвечающий исключительно за инференс модели: прием запросов, генерацию ответов, а также работу с инструментами и MCP.
MCP (Model Context Protocol) — открытый протокол, позволяющий подключать к агенту внешние сервисы как инструменты. Вместо того чтобы писать обертки для каждого API вручную, можно использовать готовые MCP-серверы.
Между моделью и внешним миром расположен инференс-движок — слой, отвечающий за производительность, управление видеопамятью и API. Именно на этом уровне используется vLLM, подробнее о нем чуть ниже.
Для корректного доступа к сервису извне применяется reverse proxy. Он отвечает за SSL, доменное имя и создает задел для будущего масштабирования.
Поверх этого располагается FastAPI-приложение — отдельный бэкенд-слой с прикладной логикой. Здесь сосредоточено управление входящими запросами, обработка вызовов инструментов, интеграция MCP-серверов, а также контроль доступа и ограничений.
Такое разделение позволяет воспринимать LLM как обычный бэкенд-сервис, а не как монолит, в который положили все подряд.
Какой стек будем использовать
Возьмем приземленный и знакомый большинству Python-разработчиков стек:
Linux-сервер с GPU;
инференс-движок для LLM, vLLM;
Nginx, FastAPI как основной бэкенд;
MCP-серверы и инструменты — в качестве внешних или встроенных компонентов.
Никаких специфичных ML-фреймворков или редких зависимостей — все легко поддерживается и масштабируется.
Почему именно vLLM, а не классический запуск через transformers?
Если вы когда-либо поднимали LLM локально через transformers, то знаете, что такой подход хорошо подходит для экспериментов, но плохо масштабируется в сервисном режиме.
Если к модели обратятся сразу несколько человек, видеопамять начнет тормозить и вам придется вручную прикручивать API и придумать, как не «уронить» сервер при высокой нагрузке.
vLLM создан как раз для таких задач. Он хорошо держит несколько одновременных запросов, а также API, совместимый с OpenAI-стилем. Он справляется со сложными функциями и позволяет засунуть серьезные модели даже в обычную видеокарту на 16 ГБ VRAM. Проще говоря, vLLM — это инференс-движок, заточенный под продакшен.
Подготовка инфраструктуры
Переходим к практике. На этом этапе арендуем VPS с GPU и сразу привязываем к нему домен. Это позволит дальше не тратить время на настройку DNS.
Даже если вы никогда раньше не работали с VPS и доменами — ничего страшного. Повторяя шаги ниже, вы без проблем справитесь.
Домен
Чтобы наша система могла работать как полноценный «персональный ИИ-инстанс», нам необходимо открыть возможность отправки внешних запросов к нашему Qwen3.
При этом обращаться напрямую к IP-адресу с портом — не лучшая идея. Вместо этого мы будем использовать доменное имя с HTTPS. Без шифрования ваши промты и API-ключи будут лежать в сети в открытом виде, а современные браузеры и сервисы просто откажутся работать с «небезопасным» IP. Плюс, если сервер переедет, вам не придется переписывать конфиги во всех приложениях.
Шаги по регистрации домена:
Регистрируйте аккаунт в панели управления, если у вас его еще нет. Открывайте вкладку Продукты, далее Домены. Переходите в раздел регистрации доменных имен и нажимайте кнопку Зарегистрировать домен.

Проверьте доступность доменного имени, если имя свободно — нажимайте Оформить.

Заполните контактные данные администратора домена и завершите регистрацию. После этого доменное имя переходит в ваше распоряжение.

Далее переходите в раздел Доменные зоны и нажимайте Добавить зону.

Ожидайте, пока домен получит статус «Делегирован». Изменения вносятся в реестр за один рабочий час, но для полной работы DNS-серверов может потребоваться до 24 часов.

Этот статус означает, что мы можем полноценно управлять DNS-записями домена.
На этом этапе этих шагов более чем достаточно. К доменному имени мы еще вернемся позже — когда будем настраивать Nginx на VPS-сервере и привязывать домен к нашему vLLM-проекту, превращая его в полноценную точку входа для удаленных запросов.
VPS сервер с GPU 16 ГБ
В рамках статьи я сознательно буду показывать запуск проекта на бюджетной конфигурации с GPU 16 ГБ. Этого более чем достаточно для демонстрации, тестирования и личного использования.
Если же вы планируете использовать модель в продакшене с высокой или средней нагрузкой, большим количеством одновременных запросов и длинным контекстом — в этом случае стоит сразу смотреть на более мощное железо. Но логика настройки при этом останется той же.
Сейчас нам нужно арендовать VPS-сервер с GPU, получить к нему доступ и подготовить его к дальнейшей настройке.
Я остановился на следующем варианте конфигурации облачного сервера:
процессор — четыре виртуальных ядра;
оперативная память — 32 ГБ;
накопитель — 100 ГБ SSD;
графический ускоритель — одна видеокарта NVIDIA Tesla T4 с 16 ГБ видеопамяти.
Это сбалансированная и доступная конфигурация, которая отлично подходит для запуска Qwen3-14B в квантованном виде и работы через vLLM.
Как по шагам создать свой VPS
Переходим в в панель управления, открываем раздел Инфраструктура → Облачные серверы.

Кликаем на Создать сервер.

В качестве операционной системы выбираем Ubuntu 24.04. Она лучше всего адаптирована под работу с GPU, драйверами NVIDIA и современным ML-стеком (и идет с установленными драйверами на видеокарту).

Выбираем конфигурацию. Я возьму для тестов GPU с Tesla T4 (16 ГБ).

Добавим к стандартным 20 ГБ SSD диска еще 80 ГБ, чтобы хватило места для модели, кэша Hugging Face, а также для тяжелых зависимостей vLLM в виртуальном окружении.

Добавим SSH-ключ для входа на сервер без ввода пароля.

Создание SSH-ключа — инструкция для всех ОС
Инструкция для Windows:
Для современных Windows 10/11 лучше всего использовать встроенный клиент:
# Встроенный OpenSSH (Win 10/11) ssh-keygen -t ed25519 -C "ваш@email.com" # Или через PowerShell New-Item -ItemType Directory -Force ~/.ssh ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
Альтернатива: PuTTYgen (скачать с putty.org) → Generate → Save public/private key.
Инструкция для macOS / Linux:
Здесь все стандартно, используем терминал:
# Самый безопасный (рекомендую) ssh-keygen -t ed25519 -C "ваш@email.com" # Или RSA (совместимость со старыми системами) ssh-keygen -t rsa -b 4096 -C "ваш@email.com"
Где лежат ключи:
публичный (его мы копируем в панель облака):
~/.ssh/id_ed25519.pub;приватный (это ваш пароль, его нельзя никому показывать):
~/.ssh/id_ed25519.
Как быстро скопировать ключ в буфер обмена:
# macOS pbcopy < ~/.ssh/id_ed25519.pub # Linux (xclip) xclip -sel clip < ~/.ssh/id_ed25519.pub # Windows PowerShell Get-Content ~/.ssh/id_ed25519.pub | Set-Clipboard
Готово. Теперь вставляем публичный ключ в панель управления облака.


На этом этапе сервер готов к работе.
Связываем доменное имя с VPS сервером
Если на данном этапе у вас есть доменное имя и активирована доменная зона, то процесс связки доменного имени с сервером займет пару минут.
Переходим в Доменные зоны.

Проваливаемся внутрь записи и привязываем публичный IP-адрес сервера к домену, как указано ниже.

Оставляем имя группы пустым, если хотите, чтоб вызов нейросети шел по домену второго уровня.
Настройка VPS-сервера
Перед запуском LLM-модели необходимо подготовить VPS-сервер: установить базовые зависимости, проверить драйверы NVIDIA и поставить окружение для запуска vLLM. Приведем сервер в рабочее состояние.
Подключение к серверу
Ввод пароля не требуется, так как мы на предыдущем этапе уже связали свою локальную машину с VPS сервером через публичный ключ.

Обновление системы и установка базовых зависимостей
Для начала обновим пакеты и установим необходимый минимум:
sudo apt update && sudo apt install -y \ nginx \ certbot \ python3 \ python3-pip \ python3-venv \ python3-certbot-nginx

Что здесь важно:
Python 3 + venv — основа для запуска vLLM;
Nginx и certbot понадобятся позже для HTTPS и проксирования.
Мы закладываем инфраструктуру сразу, без необходимости возвращаться к этому этапу.
Драйверы NVIDIA
Если вы выбирали выделенный сервер с Ubuntu 24 в панели управления Selectel, то необходимости в отдельной установке драйверов на видеокарту у вас не будет.
Убедимся, что драйверы установлены, командой: nvidia-smi.
Если команда выводит информацию о Tesla T4 — значит, драйвер установлен и видеокарта готова к работе.

Дополнительно установим CUDA Toolkit из репозитория Ubuntu/Debian. Нам это нужно для компиляции CUDA-кода, работы с GPU и выполнения операций на видеокарте.
apt install -y nvidia-cuda-toolkit

Облачная инфраструктура для ваших проектов
Виртуальные машины в Москве, Санкт-Петербурге и Новосибирске с оплатой по потреблению.
Подготовка проекта и виртуального окружения
Создадим рабочую директорию под наш проект и разместим ее в домашнем каталоге:
cd ../home mkdir vllm_qwen3 cd vllm_qwen3
Дальше создаем виртуальное окружение Python и активируем его:
python3 -m venv .venv source .venv/bin/activate
Использование виртуального окружения позволяет:
изолировать зависимости проекта;
избежать конфликтов с системными пакетами;
упростить дальнейшее сопровождение сервиса.
Устанавливаем инференс-движок vLLM: pip install vllm.

Запуск Qwen3 через vLLM на Tesla T4
Мы запускаем Qwen3-14B-AWQ на доступной Tesla T4 (16 ГБ VRAM) с 4-битной квантовкой AWQ и аккуратно подобранными лимитами памяти и контекста.
Цель — получить продакшн-совместимый OpenAI-API сервер и не прибегнуть к производительным A100/H100.
Генерация API-ключа
Для защиты API потребуется токен, получим его командой: openssl rand -hex 32.
Токен будет выглядеть примерно так: c8a9f7d2e4b1a6c3f9d8e7b2a4c6f1d3e5b7a9c2f4d6e8b1a3c5f7d9e2b4a6c8
Этот ключ потребуется передавать в заголовке:
Authorization: Bearer ВАШ_КЛЮЧ
Команда запуска
vllm serve Qwen/Qwen3-14B-AWQ \ --host 0.0.0.0 \ --port 8002 \ --gpu-memory-utilization 0.96 \ --max-model-len 8000 \ --max-num-seqs 1 \ --dtype half \ --enforce-eager \ --trust-remote-code \ --enable-prefix-caching \ --enable-chunked-prefill \ --quantization awq \ --served-model-name qwen3-14b \ --api-key "ВАШ_КЛЮЧ" \ --disable-log-requests \ --enable-auto-tool-choice \ --tool-call-parser hermes \ --chat-template-content-format string

Разбор параметров — что и за что отвечает
Модель Qwen/Qwen3-14B-AWQ
Как я уже говорил, используется уже квантованная версия модели в формате AWQ. На T4 это позволяет:
уместить 14B модель в 16 ГБ VRAM;
сохранить приемлемое качество;
получить хорошую скорость инференса.
Сетевые параметры
--host 0.0.0.0— сервис доступен извне (не только localhost);--port 8002— порт OpenAI-совместимого API.
Память и производительность
--gpu-memory-utilization 0.96
vLLM может использовать до 96% VRAM. Позволяет максимально эффективно задействовать T4, не доводя до OOM.
--max-model-len 8000
Максимальный контекст — 8 000 токенов.
На T4 это практически верхняя безопасная граница при 4-битной модели. Больше — риск нехватки памяти при длинных диалогах.
--max-num-seqs 1
Одновременно обрабатывается один активный запрос. Это стабилизирует потребление памяти, исключает резкие скачки VRAM и подходит для личного сервера или low-load продакшена.
Если планируется многопользовательская нагрузка — параметр, как и мощность железа, нужно будет пересматривать.
--dtype half
Используется FP16 для вычислений (где это применимо). Оптимальный баланс скорости и стабильности для T4.
--enforce-eager
Принудительный eager-режим. На T4 это часто работает стабильнее, чем полностью компилируемый граф.
Оптимизации скорости
--enable-prefix-caching
Кэширует общий префикс запроса и сильно ускоряет диалоги, повторяющиеся системные промты, агентные сценарии.
--enable-chunked-prefill
Разбивает длинный ввод на части при чтении запроса. Позволяет избежать пикового потребления памяти при больших промтах.
Безопасность и интеграция
--api-key "..."
Ограничивает доступ к серверу. Без ключа запросы будут отклоняться.
--disable-log-requests
Не логирует тексты запросов в stdout. Важно для приватности и продакшн-развертываний.
Инструменты (Tool Calling)
--enable-auto-tool-choice
Позволяет модели автоматически выбирать инструмент.
--tool-call-parser hermes
Используется Hermes-парсер для корректной обработки tool-calls.
Это важно, если планируется:
LangChain;
агентные сценарии;
вызов внешних функций.
Прочее
--trust-remote-code
Разрешает загрузку кастомного кода модели с HuggingFace. Необходим для корректной работы Qwen.
--served-model-name qwen3-14b
Имя, которое будет отображаться в API. Именно его будут видеть клиенты OpenAI-формата.
--chat-template-content-format string
Формат передачи контента в шаблон чата. Обеспечивает совместимость с OpenAI-стилем сообщений.
Что происходит после запуска
Скачивание модели происходит автоматически (8–9 ГБ, один раз), после чего ее загрузка в VRAM занимает около 20–40 секунд. Первый запрос может выполняться до 1–2 минут из-за нормального процесса компиляции графа. Однако все последующие ответы будут приходить практически мгновенно.
Не пугайтесь долгого первого старта, это стандартное поведение.

Что мы получили:
14B модель;
4-битная квантовка;
8 000 токенов контекста;
OpenAI-совместимый API;
Полная работа на одной Tesla T4.
Без A100, без 80 ГБ VRAM и без космического бюджета.
Теперь модель доступна по адресу:
http://SERVER_IP:8002
Быстрый тест API
Когда сервер поднялся, проверим, что все работает. Для этого необходимо открыть новый терминал и там выполнить вход на VPS сервер.
Если мы введем команду nvidia-smi, то увидим, что наша модель заняла практически весь размер видеопамяти:

Выполним простой запрос через curl:
curl http://localhost:8002/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer z64bf3f7f93601wZez6a621381axqfagzaaa0fd79c589aX2eaaadsr1a0cdd0491a2ea" \ -d '{ "model": "qwen3-14b", "messages": [ { "role": "user", "content": "Привет! Кто тебя создал такого?" } ], "max_tokens": 256 }'

Сразу видим, что модель из коробки поддерживает режим Thinking. То есть кроме обычного ответа и доп данных, мы можем видеть процесс размышления модели. Бывает очень полезно в некоторых сценариях.
Если вы не хотите тратить время на получение размышлений, то можно легко эти самые размышления отключить.
Пример запроса:
curl http://localhost:8002/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer z64bf3f7f93601wZez6a621381axqfagzaaa0fd79c589aX2eaaadsr1a0cdd0491a2ea" \ -d '{ "model": "qwen3-14b", "messages": [ { "role": "user", "content": "Привет! Кто ты?" } ], "max_tokens": 256, "chat_template_kwargs": { "enable_thinking": false } }'

Ответ получили значительно быстрее.
Запуск через systemd
Для постоянной работы сервиса запускать его вручную — плохая идея. Поэтому сразу оформим все через systemd.
Создаем сервисный файл командой: sudo nano /etc/systemd/system/vllm-qwen3.service.
Пример конфигурации:
[Unit] Description=vLLM Qwen3-14B Server After=network.target StartLimitIntervalSec=0 [Service] Type=simple User=root WorkingDirectory=/home/vllm_qwen3 Environment="PATH=/home/vllm_qwen3/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" Environment="CUDA_VISIBLE_DEVICES=0" ExecStart=/home/vllm_qwen3/.venv/bin/python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen3-14B-AWQ \ --host 0.0.0.0 \ --port 8002 \ --gpu-memory-utilization 0.96 \ --max-model-len 8000 \ --max-num-seqs 1 \ --dtype half \ --enforce-eager \ --trust-remote-code \ --enable-prefix-caching \ --enable-chunked-prefill \ --quantization awq \ --served-model-name qwen3-14b \ --api-key ВАШ_ТОКЕН\ --disable-log-requests \ --enable-auto-tool-choice \ --tool-call-parser hermes \ --chat-template-content-format string Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=vllm-qwen3 [Install] WantedBy=multi-user.target
Активируем сервис:
# Перезагрузить systemd sudo systemctl daemon-reload # Включить автозапуск sudo systemctl enable vllm-qwen3.service # Запустить сервис sudo systemctl start vllm-qwen3.service # Проверить статус sudo systemctl status vllm-qwen3.service # Посмотреть логи sudo journalctl -u vllm-qwen3.service -f

Теперь LLM будет автоматически подниматься вместе с сервером и переживать перезапуски.
Настраиваем Nginx и HTTPS
Превратим наш локальный LLM-сервис в настоящую облачную нейросеть, к которой можно подключаться с любого места — точно так же, как к ChatGPT или DeepSeek.
На данный момент у нас уже должно быть доменное имя, к которому привязана А-запись на домен второго уровня с привязкой к IP-адресу нашего сервера.

Конфигурация Nginx
Создадим конфигурационный файл:
nano /etc/nginx/sites-available/qwen3-vllm.ru
Содержимое:
server { listen 80; server_name ВАШ ДОМЕН (в моем случае qwen3-vllm.ru); # Важно для долгих ответов и стриминга proxy_read_timeout 600s; proxy_connect_timeout 600s; proxy_send_timeout 600s; # Для больших промтов client_max_body_size 50M; location / { proxy_pass http://127.0.0.1:8002; proxy_http_version 1.1; # WebSocket для стриминга proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # Отключаем буферизацию для SSE proxy_buffering off; proxy_cache off; } }
Активация конфига и получение SSL сертификата:
# Активируем сайт ln -s /etc/nginx/sites-available/qwen3-vllm.ru /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default # Проверяем конфиг nginx -t # Перезапускаем Nginx systemctl restart nginx

Подключаем HTTPS
Устанавливаем certbot (если еще не установлен):
sudo apt install -y certbot python3-certbot-nginx
Получаем SSL-сертификат
certbot --nginx -d ВАШ ДОМЕН --non-interactive --agree-tos --email your-email@example.com
Например:
certbot --nginx -d qwen3-vllm.ru --non-interactive --agree-tos --email ivanov_invan@gmail.com
Certbot автоматически включит HTTPS, настроит редирект и добавит автообновление сертификата.

Проверяем, что все работает
Открываем в браузере https://ВАШ_ДОМЕН/docs. В моем случае это: https://qwen3-vllm.ru/docs.

Так как vLLM использует FastAPI под капотом, вы сразу увидите Swagger-документацию. Это отличный знак — значит сервис полностью доступен извне.
На этом этапе у нас есть полностью рабочая облачная LLM с HTTPS-доступом и API, совместимым с OpenAI. Прежде чем двигаться дальше и добавлять прикладную бизнес-логику, имеет смысл проверить, как эта модель ведет себя в реальных сценариях.

ML Impact — про ML и AI без хайпа
Все кругом говорят про ML, но многие ли понимают его настоящую пользу для бизнеса? Мы запустили ресурс, который поможет во всем разобраться.
Практика: тестируем LLM как API-сервис
Все примеры ниже выполняются на локальном компьютере. GPU, Docker и сложная инфраструктура не требуются — мы работаем с LLM как с обычным внешним API.
Подготовка локального окружения
Создадим отдельное виртуальное окружение для клиентского кода на своей локальной машине:
mkdir llm-client cd llm-client python3 -m venv .venv source .venv/bin/activate
Установим минимальный набор зависимостей:
pip install langchain langchain-openai langgraph langchain-mcp-adapters pydantic-settings httpx
Или, если вы клонировали репозиторий: pip install -r requirements.txt.
Далее начинается достаточно много примеров кода на Python. Для того, чтобы было проще ориентироваться по тексту далее, можете клонировать репозиторий, в котором находится полный исходник.
Конфигурация
Для подключения к LLM нам нужны три параметра: токен, адрес сервера и название модели. Хранить их в коде — плохая практика, поэтому вынесем все в .env-файл:
CLIENT_TOKEN=ВАШ_ТОКЕН CLIENT_BASE_URL=https://qwen3-vllm.ru LLM_MODEL_NAME=qwen3-14b
А считывать будем через pydantic-settings — это дает валидацию и типизацию из коробки: from pydantic_settings import BaseSettings, SettingsConfigDict.
class Settings(BaseSettings): model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False, ) client_token: str client_base_url: str llm_model_name: str settings = Settings()
Если забыть указать любую из переменных — приложение падает при старте с понятной ошибкой валидации, а не где-то посреди работы с None вместо токена.
Пример-1: чат-агент
Начнем с минимального варианта — агент без инструментов, просто разговор с моделью. Это позволяет убедиться, что подключение работает, модель отвечает адекватно, а задержка приемлемая.
import asyncio from langchain_openai import ChatOpenAI from langchain.agents import create_agent from config import settings def create_model( temperature: float = 0.7, max_tokens: int = 1024, enable_thinking: bool = False, ) -> ChatOpenAI: base_url = settings.client_base_url.rstrip("/") extra_body = None if not enable_thinking: extra_body = { "chat_template_kwargs": {"enable_thinking": False} } return ChatOpenAI( model=settings.llm_model_name, api_key=settings.client_token, base_url=f"{base_url}/v1", temperature=temperature, max_completion_tokens=max_tokens, extra_body=extra_body, ) async def main(): model = create_model() agent = create_agent( model=model, tools=[], system_prompt="Ты полезный ассистент. Отвечай кратко и по делу на русском языке.", ) print("Простой агент запущен. Введите 'exit' для выхода.\n") while True: user_input = input("Вы: ") if user_input.strip().lower() in ("exit", "quit", "выход"): break result = await agent.ainvoke( {"messages": [{"role": "user", "content": user_input}]} ) messages = result.get("messages", []) for msg in reversed(messages): if msg.type == "ai" and msg.content: print(f"\nАссистент: {msg.content}\n") break if name == "__main__": asyncio.run(main())
Ключевой момент — ChatOpenAI из LangChain работает с любым OpenAI-совместимым API. Мы просто подставляем base_url нашего vLLM-сервера, и все подключение готово. Никаких специальных клиентов или SDK не нужно.
Параметр extra_body с enable_thinking: False отключает «режим размышления» у моделей, которые его поддерживают (например, QwQ). Для простого чата он не нужен, а ответы приходят быстрее.
Запускаем через команду: python -m example.simple_agent.

Пример-2: агент с инструментами
Простой чат — это хорошо, но настоящая сила агентов в том, что они могут использовать инструменты. Модель сама решает, когда и какой инструмент вызвать, на основе запроса пользователя.
Определим несколько инструментов через декоратор @tool:
from langchain_core.tools import tool import httpx from datetime import datetime, timezone, timedelta @tool def get_current_time() -> str: """Возвращает текущую дату и время по Москве (UTC+3).""" moscow_tz = timezone(timedelta(hours=3)) now = datetime.now(moscow_tz) return now.strftime("%d.%m.%Y %H:%M:%S (МСК)") @tool async def get_random_joke() -> str: """Возвращает случайный анекдот/шутку на английском языке.""" async with httpx.AsyncClient(timeout=10) as client: resp = await client.get("https://official-joke-api.appspot.com/random_joke") resp.raise_for_status() data = resp.json() return f"{data['setup']}\n— {data['punchline']}" @tool async def get_random_quote() -> str: """Возвращает случайную вдохновляющую цитату.""" async with httpx.AsyncClient(timeout=10) as client: resp = await client.get("https://zenquotes.io/api/random") resp.raise_for_status() data = resp.json()[0] return f'«{data["q"]}»\n— {data["a"]}' @tool async def get_random_fact() -> str: """Возвращает случайный интересный факт.""" async with httpx.AsyncClient(timeout=10) as client: resp = await client.get( "https://uselessfacts.jsph.pl/api/v2/facts/random?language=en" ) resp.raise_for_status() data = resp.json() return data["text"]
Обратите внимание: docstring каждого инструмента — это не просто документация для разработчика. Именно по этому описанию модель понимает, что делает инструмент и когда его стоит вызвать. Чем точнее описание, тем лучше модель выбирает нужный инструмент.
Теперь передаем инструменты агенту:
async def main(): model = create_model() tools = [get_current_time, get_random_joke, get_random_quote, get_random_fact] agent = create_agent( model=model, tools=tools, system_prompt=( "Ты полезный ассистент с доступом к инструментам:\n" "- get_current_time — узнать текущее московское время\n" "- get_random_joke — получить случайную шутку\n" "- get_random_quote — получить случайную цитату\n" "- get_random_fact — получить случайный интересный факт\n\n" "Используй инструменты когда это уместно. " "Данные от API приходят на английском — переводи их на русский в ответе." ), ) tool_names = [t.name for t in tools] print(f"Агент с инструментами запущен: {', '.join(tool_names)}") print("Введите 'exit' для выхода.\n") while True: user_input = input("Вы: ") if user_input.strip().lower() in ("exit", "quit", "выход"): break result = await agent.ainvoke( {"messages": [{"role": "user", "content": user_input}]} ) messages = result.get("messages", []) for msg in messages: if msg.type == "ai" and hasattr(msg, "tool_calls") and msg.tool_calls: for tc in msg.tool_calls: print(f" ? Вызов: {tc['name']}({tc['args']})") if msg.type == "tool": print(f" ? Результат: {msg.content[:200]}") for msg in reversed(messages): if msg.type == "ai" and msg.content and not msg.tool_calls: print(f"\nАссистент: {msg.content}\n") break
Логика процесса устроена так: LangChain через LangGraph строит цикл, «модель → инструмент → модель». Когда пользователь спрашивает «Который час?», модель не пытается угадать время — она возвращает вызов get_current_time(). LangGraph выполняет функцию, передает результат обратно модели, и та формирует финальный ответ.
Запуск: python -m example.agent_with_tools.
Попробуйте спросить: «Расскажи шутку и интересный факт» — модель вызовет оба инструмента и скомбинирует результаты в один ответ.

Пример-3: агент с MCP-серверами
В этом примере мы подключаем два MCP-сервера:
fetch — загрузка и парсинг веб-страниц по URL;
filesystem — чтение, запись и навигация по файловой системе.
И комбинируем их с нашими кастомными инструментами из предыдущего примера.
from langchain_mcp_adapters.client import MultiServerMCPClient MCP_SERVERS = { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"], "transport": "stdio", }, "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"], "transport": "stdio", }, }
MCP-серверы запускаются как дочерние процессы и общаются с клиентом по stdio. Библиотека langchain-mcp-adapters автоматически превращает все доступные операции MCP-сервера в LangChain-инструменты:
async def main(): mcp_client = MultiServerMCPClient(MCP_SERVERS) mcp_tools = await mcp_client.get_tools() custom_tools = [get_current_time, get_random_joke, get_random_quote, get_random_fact] all_tools = mcp_tools + custom_tools model = create_model() agent = create_agent( model=model, tools=all_tools, system_prompt=( "Ты ассистент с доступом к инструментам.\n\n" "MCP инструменты:\n" "- fetch — загрузка и чтение веб-страниц по URL\n" "- filesystem — чтение, запись, поиск файлов и навигация по каталогам\n\n" "Кастомные инструменты:\n" "- get_current_time — текущее московское время\n" "- get_random_joke — случайная шутка\n" "- get_random_quote — случайная цитата\n" "- get_random_fact — случайный интересный факт\n\n" "Отвечай на русском языке. Данные от API на английском — переводи." ), ) …
Запуск командой: python -m example.agent_with_mcp.
Теперь агент может, например, прочитать веб-страницу и пересказать ее содержание, или найти файл на диске и показать его содержимое — все в рамках одного диалога.
Попробуем с запросом: «прочитай эту статью https://ru.wikipedia.org/wiki/Qwen и сделай ее саммари с основными тезисами в файл summary.md».


Заключение
Мы убедились, что LLM доступна по API, корректно отвечает на вопросы, умеет вызывать инструменты и работать с внешними сервисами через MCP. Все это — с локального компьютера, GPU и без сложной настройки.
Следующий логичный шаг — обернуть эту логику в полноценный бэкенд на FastAPI, чтобы агент стал доступен не только из терминала, но и через HTTP-эндпоинты для фронтенда или других сервисов, чем мы и займемся в следующей части.
Поделитесь в комментариях: используете ли вы квантованные модели в продакшене и, если да, какое семейство моделей (Qwen, Llama, Mistral) лучше всего показало себя в ваших задачах?
Комментарии (16)

evgeniynet
06.03.2026 12:35Благодарю за статью, локальные (пусть здесь сказано облачная, но по сути это твоя on-device) llm это будущее. Просто Goggle, Apple называют их on-device (offline). Это однозначно целая ниша, которая будет развиваться и жить.
Очень будет интересно связаться с вами и обсудить несколько моментов.
Евгений

Triton5
06.03.2026 12:35"А 16 гиговая GPU для подобных вещей стоит относительно недорого. " - я бы даже сказал, сопоставимо с затратами за 1 месяц на генерацию в облаке:) А то и меньше в разы.

Areso
06.03.2026 12:35Ну так и возможности модели Qwen3-14B сильно ниже фронтиерных в облаках.
У меня qwen3-coder:30b на ноутбуке, и мне есть с чем сравнивать.

rocoss
06.03.2026 12:35Как вы валидируете, что квантованная модель + сокращённый контекст не теряют важные нюансы при извлечении фактов? Есть ли у вас практический чеклист или бенчмарк для сравнения «полная модель в облаке» vs «AWQ-квантованная локально» по метрикам вроде faithfulness или factual consistency?

Sanitir
06.03.2026 12:35Нужность GPU сильно переоценена. В том же селектеле на аукционе можно словить за 19 тыщ - 128core × 3.0ГГц / 256 ГБ ARM. И можно запускать qwen3.5 122b или gpt-oss 120b. Вполне быстро работают. А на 20b моделях ничего полезного не получается сделать.
Кто не согласен, пишите в комменты что же оно такое хорошее вам сделало.

diffnotes-tech
06.03.2026 12:358000 токенов с включённым MCP впритык. Схемы fetch + filesystem это десяток tool definitions которые целиком идут в контекст при каждом запросе. Плюс системный промпт. На диалог остается тысяч 5. А fetch одной веб-страницы легко возвращает 3-4k - контекст забит за один ход

saionio
06.03.2026 12:35Gemma 3 12/27b и Qwen 3.5 27b/35b a3b — мои фавориты, в Q4 самое то, правда, для 24 ГБ. Хотя Геммки 12 и на 8 ГБ машинах более чем работоспособны!
Для помощи в составлении текста — идеально.
Любишь компы, но прогер из тебя как из утюга, и даже с командной строкой не просто на вы, а исключительно на ваше непостижимое величество? :D Локальные ИИ и здесь спешат на помощь, объяснил, где затупил, показал скрин, если надо, и вот тебя за ручку привели к нужным действам. И вовсе от такого не тупеешь, как часто в тырнетах пугают, а как раз напротив, за месяцы заполняешь пробелы, которые имел многие годы!
P. S. Пост найс. Но я, как ленивая жопа, особенно на первых парах, советую не заморачиваться и попробовать ЛМ Студио - просто, в меру функционально, и можно налегке заценить почти все имеющиеся модельки.

Johan_Palych
06.03.2026 12:35Если уж таким развлекаться - сборка llama.cpp под CUDA через cuBLAS и llama-swap
Performance of llama.cpp on Nvidia CUDA
CUDA Toolkit 13.1 Update 1 Downloads
cuda-toolkit · PyPI(cuda-toolkit 13.1.1)
Для начала:
Zero-Latency Local AI: Tuning Your Linux Kernel for LLM Inference
Testman2023
06.03.2026 12:35Почитал на reddit про фичи llama-swap - годно.
Есть "fork with vLLM sleep support: https://github.com/napmany/llmsnap"

MKreGGo
06.03.2026 12:35Учитывая практические возможности того, что можно запустить на 16Гб это скорее статья ради того, чтобы рассказать как пройти весь путь, но точно не о какой-то практической пользе. Стоимость аренды такого сервера перекроет покупку своего подобного GPU уже через 4-6 месяцев аренды, если не раньше, модели которые можно запустить крайне ограничены. Называть это именно "своим облачным" сервером тоже крайне сомнительно, скорее удаленная локалка, ибо в лучшем случае на модели размером 4b получится поддерживать 4-8 конкурентных потоков без значительной просадки TPS, далее уже кошмар начнется. Ну и если нет необходимости в реально приватности данных, то условная подписка будет в 10 раз дешевле аренды такого сервера и в сотни раз эффективнее по результатам.

yakvenalex Автор
06.03.2026 12:35В целом, вы правы. Но сама модель достаточно толковая. Если ее запустить на 32ГБ видео, у вас будет контекстное окно порядка 45к токенов, что позволит говорить про прод использование. В текущем виде - это, скорее, демка модели.

MKreGGo
06.03.2026 12:35Ну, у нас с вами, видимо, очень разные понимания "прод"а :)
Я вот собираю корпоративную агентную среду и моя RX7800XT позволяет содержать максимум 6 параллельных потоков с 32 000 контекста на Ministral3 3B в Q8.
И в моем понимании, с одной стороны это неплохой задел под параллельную работу (6 потоков явно лучше чем 1, где все пользователи будут стоять в очереди, а кто-то может дать сложную задачу и его все будут обязаны ждать), с другой я прекрасно понимаю как сильно ограничены возможности локальных моделей, так как что-то "вменяемое" начинается где-то от 20B+, а чтобы появились настоящие агентные навыки это 30B+, что на домашнем железе если и запустишь, то либо с частичной выгрузкой на CPU и RAM, либо с крайне ограниченным железом. И это явно не сценарий для какого-то "прод" решения. Это скорее личное пользование для тестов и экспериментов.
Вам же, для своих тестов, я бы порекомендовал попользовать GPT-OSS-20B, она намного быстрее Qwen3-14B работает, нативно обучена в 4-битном квантовании и позволит запустить ее даже с 120 000 контекста (другой вопрос, что для pp 120к понадобится минут 20 времени на запрос, но это уже другой вопрос).

Rastler
06.03.2026 12:35Статья, что бы заработать на "пробовальщиках", не более. Никакого практической пользы извлечь не получиться из комбинации vLLM, Qwen3-14B-AWQ и 8k токенов окна.

muradali
06.03.2026 12:35Вместо Ngnix используйте Caddy
Конфиг там 3 строчки.
example.com {
reverse_proxy localhost:3000
}Сразу https автоматом, сертификаты генерятся подтягиваются под капотом.

Moog_Prodigy
Не знаю даже кому нужно содержать облачную LLM. По безопасности - сразу пролетаем, доступность - под вопросом. Ну и зачем оно тогда, если можно просто покупать токены уже у настроенных готовых , мощных LLM с моделями размерами в терабайт и прикрутить к ним MCP и что угодно можно при желании.
Облачное - это всегда не твое. А 16 гиговая GPU для подобных вещей стоит относительно недорого. И всегда в твоем компе\сервере, за закрытым контуром, с охраной и прочими заборами с колючкой, если уж надо.
Для облачных применений видеокарт сюда лучше подходят картинко-видео-музыко генерилки. Собрал докером, пользуешься, не надо - образ забрал, отключился. Вот это уже имеет хоть какой-то смысл.
d3d12
Паблик модели - все ваши данные у владельца модели. Тогда уж сравнивать с моделью на своем балконном сервере.