У вас есть LangGraph-агент — он умеет вызывать инструменты, ходить в базы, слать сообщения в Slack. Но как с ним общаться? Терминал? Хардкор, но не для продакшна. Xотите, чтобы агент выглядел как ChatGPT? Тогда без UI не обойтись. И не абы какого, а такого, который показывает мысли агента, вызовы инструментов, поддерживает человеческий цикл (human-in-the-loop). Встречайте связку: vLLM (быстрый инференс), MCP (единый протокол инструментов), LangGraph Server (железный бэкенд) и agent-chat-ui (готовый React-интерфейс). Сейчас соберём это всё на облачном GPU за час.
Не просто чат-болванка: что внутри
Классическая архитектура ChatGPT — это монолит. Вы шлёте запрос, OpenAI отвечает. В нашем случае каждый элемент — отдельный сервис, и это даёт хакерский контроль. Схема такая:
[Next.js (agent-chat-ui)] ↔ [LangGraph Server (FastAPI)] ↔ [vLLM (OpenAI API)]
↕
[MCP Server #1] [MCP Server #2]
Пользователь пишет сообщение в UI. UI отправляет POST-запрос к LangGraph серверу. Тот запускает граф: вызывает LLM через vLLM, получает tool-calls, срабатывает MCP-инструменты, возвращает ответ с историей вызовов. UI отрисовывает всё в стиле ChatGPT — с карточками мыслей, логами инструментов, деревом вызовов. И да, вы можете в любой момент нажать «Подтвердить» или «Отклонить» действие — человеческий цикл не выключен.
Шаг 1. Поднимаем GPU и vLLM
В теории можно использовать любой LLM, но vLLM — максимально эффективная обёртка для Transformer на CUDA. Берём облачную GPU: минимум A10G (24 ГБ) для моделей 7B-13B, L40 или A100 — если хочется 70B. Запускаем инстанс на RunPod или Vast.ai, ставим Docker и контейнер vLLM:
# Установите nvidia-docker2 и запустите vLLM
sudo docker run --rm -d --gpus all \
-p 8000:8000 \
vllm/vllm-openai:latest \
--model NousResearch/Meta-Llama-3.1-8B-Instruct \
--gpu-memory-utilization 0.95 \
--max-model-len 8192
Проверяем:
curl http://localhost:8000/v1/chat/completions -d '{"model":"NousResearch/Meta-Llama-3.1-8B-Instruct","messages":[{"role":"user","content":"Hi!"}]}'
Не советую экономить на model-len: 4096 для агентов — больно. 8192 — минимум для того, чтобы вместить историю вызовов инструментов.
Если у вас Tesla P40 или старая архитектура — придётся попотеть. Для таких случаев есть отдельный гайд: Tesla P40 против vLLM: как взломать фреймворк для работы с устаревшей архитектурой. Но мы идём по простому пути — современные карты.
Шаг 2. MCP-серверы: инструменты по единому протоколу
Без MCP вы будете лепить костыли для каждого вызова API. MCP — это протокол, который превращает разрозненные инструменты в стандартизированный список. Ставим готовые серверы или пишем свои.
Пример с популярными серверами:
# Файловая система — чтобы агент читал логи
npx @anthropic/mcp-server-filesystem --path /var/logs &
# Slack — чтобы отправлять уведомления
npx @anthropic/mcp-server-slack --slack-bot-token "xoxb-..." &
# Redis — для работы с очередями
npx @anthropic/mcp-server-redis --host 127.0.0.1 --port 6379 &
Внимание: MCP-серверы могут быть опасны. Не давайте агенту доступ к чувствительным ресурсам без ограничений. Используйте файрволлы и изолируйте серверы хотя бы на уровне контейнеров.
Каждый MCP-сервер — это отдельный процесс, который общается через stdio или HTTP. Они предоставляют схемы в JSON Schema — агент их запрашивает один раз при старте.
Шаг 3. LangGraph-агент с MCP-клиентом
Теперь склеиваем LLM и инструменты. Создаём граф. Ключевой компонент — MCPClient из пакета langgraph-mcp. Он подключается к MCP-серверам и регистрирует их инструменты как инструменты LangGraph.
from langgraph_mcp import MCPClient
from langgraph.graph import StateGraph, MessagesState
mcp = MCPClient()
mcp.connect_servers([
{"url": "http://localhost:3100/sse"}, # файловая система
{"url": "http://localhost:3101/sse"}, # Slack
])
tools = mcp.get_tools()
builder = StateGraph(MessagesState)
builder.add_node("llm", lambda state: {"messages": llm.invoke(state["messages"], tools=tools)})
builder.add_node("tools", ToolNode(tools))
builder.set_entry_point("llm")
builder.add_conditional_edges("llm", should_continue, {"continue": "tools", "end": END})
builder.add_edge("tools", "llm")
agent = builder.compile()
Заметьте: здесь LLM вызывается с параметром tools. vLLM (если версия >= 0.6.3) поддерживает tool calling нативно. Если нет — обновляйте: pip install vllm==0.7.2 (на дату статьи актуальна версия 0.7.2).
Для человеческого цикла добавляем узел подтверждения:
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.types import Command
builder.add_node("human_approve", lambda state: Command(resume=True))
builder.set_entry_point("human_approve")
builder.add_conditional_edges("human_approve", ...)
agent = builder.compile(checkpointer=SqliteSaver.from_conn_string("checkpoints.sqlite"))
Теперь агент будет ждать подтверждения перед вызовом любого инструмента. На UI это выглядит как кнопка «Подтвердить выполнение».
Готовый граф нужно выставить как FastAPI-сервер. Самый быстрый путь — langgraph serve:
# Установите langgraph-cli
pip install langgraph-cli
langgraph serve --host 0.0.0.0 --port 8123
Он сам сгенерирует конечные точки: /chat/completions (как у OpenAI), /runs, /threads.
Подробнее про развёртывание LangGraph Server читайте в отдельной статье: Построение графовой инфраструктуры LLM: LangGraph Server, LangSmith и SDK для локальных моделей.
Шаг 4. agent-chat-ui — ChatGPT-интерфейс за 10 минут
Берём готовый проект от LangChain: agent-chat-ui. Это Next.js приложение с Convex для хранения истории чатов и стриминга.
Клонируем и настраиваем:
git clone https://github.com/langchain-ai/agent-chat-ui
cd agent-chat-ui
cp .env.example .env
# Редактируем .env:
# LANGGRAPH_API_URL=http://<ваш-сервер>:8123
# OPENAI_API_KEY=sk-... (если используете OpenAI fallback)
# CONVEX_DEPLOYMENT=...
npm install
npm run dev
Или проще — через Docker Compose (репозиторий включает docker-compose.yaml). Запускается за 2 команды.
Интерфейс из коробки:
- Поле ввода с поддержкой Shift+Enter
- Список сообщений с маркдауном и синтаксис-подсветкой
- Отображение tool calls: в виде серых блоков «Searching files...», «Sending message to Slack...»
- Кнопка «Подтвердить» (если агент запросил аппрув)
- Dark mode, темы, смена модели (если указано несколько эндпоинтов)
UI полностью асинхронный — ответ стримится через Server-Sent Events. Агент может думать несколько секунд, а пользователь видит живые логи инструментов.
Живой пример: поиск ошибки и уведомление в Slack
Представьте: вы — разработчик на дежурстве. Пишете агенту: «Найди в последнем логе ошибку «TimeoutError» и отправь сообщение в канал #alerts». Что происходит под капотом?
- UI отправляет сообщение на LangGraph сервер.
- Агент вызывает LLM через vLLM. LLM решает: использовать инструмент search_files.
- MCP-сервер filesystem находит файлы, потом LLM парсит их и находит строку с ошибкой.
- Агент решает отправить сообщение в Slack. UI показывает карточку «Отправить сообщение в #alerts» с текстом.
- Если включён human-in-the-loop, пользователь нажимает «Подтвердить».
- MCP-сервер Slack постит сообщение. Возвращается результат.
- LLM генерирует финальный ответ: «Ошибка найдена, уведомление отправлено».
Вся трассировка видна на экране — каждое действие, каждый вызов инструмента. Да, это уже не чёрный ящик.
Альтернативы и почему ваш вариант лучше
Что ещё можно было использовать?
| Вариант | Минусы |
|---|---|
| Ollama + Open WebUI | Нет нативной поддержки tool calling, нет человеческого цикла, сложнее интегрировать MCP. |
| FastChat + Chatbot UI | Сваггер вместо интерфейса для агентов. Не отображает вызовы инструментов. |
| Свой React-интерфейс с нуля | Много работы: стриминг, отмена запросов, карточки инструментов, управление состоянием. agent-chat-ui уже сделал это за вас. |
Наша связка — единственная, которая из коробки понимает MCP, LangGraph и vLLM. Плюс вы контролируете данные: модель лежит на вашем GPU, инструменты не уходят за пределы контура.
Кому это вообще нужно
В первую очередь — тем, кто строит внутренние AI-ассистенты для компании. Например:
- Дэвопс, который хочет, чтобы агент сам перезапускал сервисы через Kubernetes API (MCP-сервер для kubectl).
- Сепорт-команда, где агент смотрит в базу знаний и отвечает клиентам, но требует подтверждения перед действием.
- Разработчик, который устал таскать токены в OpenAI и хочет окупить свои 8×3090.
Если у вас GPU-ферма из гибридных видеокарт, не забудьте заглянуть в статью Гибридные GPU-связки для LLM: как объединить видеопамять Nvidia и AMD/Intel — там показано, как собрать кластер под эту архитектуру.
Подводные камни, которые я не хочу скрывать
1) vLLM tool calling не идеален. Иногда модель игнорирует инструкции, путает имена инструментов. Помогает system prompt с примером схемы вызова. Включите её в конфигурацию LangGraph.
2) MCP-серверы могут упасть. Не забудьте про health-checks, перезапуск через Docker restart: always. Лучше запускать их как отдельные микросервисы, а не в подпроцессе агента.
3) agent-chat-ui не тянет тысячи юзеров. Convex — отличная БД, но если планируете 500+ одновременных сессий, поднимите свой backend для хранения чатов (например, на Postgres). Или используйте LLMeQueue для балансировки запросов.
Не делайте так: не пытайтесь засунуть все инструменты в один MCP-сервер. Перегруженный сервер — это лаги и потерянные вызовы. Лучше один сервер на домен: файлы отдельно, Slack отдельно, БД отдельно.
Вместо титров: что дальше
Через полгода-год подобные UI станут стандартом для корпоративных AI-агентов. LangGraph уже движется к этому: в roadmap на 2026 — встроенный стриминг через WebRTC, рендеринг на стороне клиента для tool calls. Советую уже сейчас подписываться на репозиторий agent-chat-ui и следить за обновлениями vLLM — скоро они добавят поддержку KV Cache Sharing для multi-agent сценариев, что ещё вдвое ускорит работу (об этом писали здесь).
Если хочется увидеть, как такой стенд падает под нагрузкой — добро пожаловать в наш следующий текст про отказоустойчивость. А пока — запускайте, ломайте, дорабатывайте. Полноценный ChatGPT на собственных GPU — это реальность, и она уже в ваших руках.