
Привет, Хабр! На фоне ажиотажа вокруг нейросетей все чаще встает вполне приземленный вопрос — сколько стоит содержать собственную 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) лучше всего показало себя в ваших задачах?

