Как я реализовал передачу диалога из ИИ-консультанта сайта в MAX

от автора

Всё началось с простого вопроса: почему малый бизнес теряет клиентов ночью?

Клиент заходит на сайт в 23:00, пишет в чат — и уходит. Потому что менеджер спит. Утром менеджер видит сообщение, перезванивает — а клиент уже купил у конкурента.

Стандартное решение — чат-бот. Но обычный чат-бот либо ограничен заранее заданными сценариями, либо требует интеграции с крупными облачными LLM. (отвечает по скриптам), либо слишком дорогой (GPT-4 за десятки тысяч в месяц), либо хранит данные за рубежом, что не всегда удобно для российского бизнеса.

Я решил сделать иначе.

Проблема переключения между ИИ и оператором

Главная сложность оказалась не в интеграции с моделью, а в сохранении непрерывности диалога. После передачи клиента оператору необходимо было сохранить единый контекст разговора, не меняя интерфейс для пользователя. Для этого relay-сервер хранит состояние сессии и динамически маршрутизирует сообщения либо в vLLM, либо в MAX, в зависимости от текущего режима обработки.

Что в итоге получилось

ИИ-консультант на базе Qwen3 30B отвечает клиентам на сайте. Когда клиент хочет поговорить с живым человеком — оператор получает уведомление в MAX с кратким резюме всего диалога. Отвечает прямо из мессенджера. Клиент видит один непрерывный разговор и не знает что канал переключился.

Вся установка — один тег перед </body>:

<script src="https://llmcod.ru/widget/ВАШ_ID.js"></script>

Как это работает технически

Архитектура состоит из трёх частей:

1. Виджет на сайте — обычный JS чат, подключается через WebSocket к relay-серверу. Никаких iframe, никаких внешних зависимостей.

2. Relay-сервер (FastAPI + WebSocket) — центральный компонент. Держит сессию пользователя, проксирует сообщения в vLLM или в MAX в зависимости от режима. Хранит историю диалога и маппинг session_id ↔ MAX chat_id.

Ещё одна проблема возникла при переключении между режимами. Если оператор подключается слишком рано, ИИ не успевает собрать достаточный контекст. Если слишком поздно — клиент может уйти. Поэтому порог эскалации сделан настраиваемым и определяется количеством сообщений в диалоге.

3. MAX бот — получает уведомления об эскалации. Оператор видит резюме диалога и отвечает прямо в мессенджере.

Схема передачи сообщений:

Клиент → WebSocket → Relay → vLLM (AI режим)                           ↓ (при эскалации)                     MAX webhook ← MAX бот ← Оператор

При эскалации relay вызывает vLLM для суммаризации истории диалога и отправляет оператору готовое резюме. Оператор не читает весь чат — видит только суть.

Почему MAX, а не Telegram

Несколько причин:

  • MAX — российский мессенджер, данные хранятся в РФ

  • API MAX позволяет получать webhook и отвечать от имени бота

  • MAX предоставляет API для ботов и webhook-механизм, достаточный для реализации сценария передачи диалога оператору.

Ключевой момент — резюме диалога

Это главное отличие от обычного «перевода на оператора». Когда клиент нажимает кнопку — оператор получает не просто уведомление, а готовый контекст:

🔔 Клиент просит оператораСайт: ООО Ромашка📋 Резюме:Клиент интересуется доставкой в регионы. Уточнял сроки и стоимость. Хочет заказать на следующей неделе.💬 Последнее сообщение:А скидки на первый заказ есть?✏️ Ваш ответ → придёт клиенту как сообщение оператора.

Оператор сразу в контексте. Не нужно читать историю, не нужно переспрашивать клиента «а что вы хотели?».

Мультитенантность

Система изначально спроектирована для нескольких клиентов. Каждый клиент получает:

  • Свой MAX бот с отдельным токеном

  • Свой webhook endpoint: /relay/{client_id}/webhook

  • Свой виджет: /widget/{client_id}.js

  • Свой системный промпт с описанием бизнеса

Добавить нового клиента — один API вызов:

curl -X POST "https://llmcod.ru/admin/clients" \  -H "X-Admin-Token: ..." \  -d '{    "name": "ООО Ромашка",    "bot_token": "...",    "operator_user_id": 12345678,    "system_prompt": "Ты ассистент магазина цветов..."  }'

В ответ приходит готовый <script> тег для вставки на сайт.

Как формируется контекст модели

При подключении клиент описывает свой бизнес в свободной форме — прайс, режим работы, частые вопросы, тон общения. Это становится системным промптом модели.

Дополнительно модель знает текущее время (московское) — может сообщить клиенту рабочее ли сейчас время, когда будет доступен оператор.

Контекст диалога — последние 6 сообщений. Этого достаточно для большинства сценариев поддержки.

Инфраструктура

  • Модель: Qwen3 30B A3B Instruct (Q4_K_M GGUF)

  • Инференс: vLLM с GGUF квантизацией

  • GPU: NVIDIA Tesla V100 32 ГБ

  • CPU: 2 х Intel Xeon Gold 6244

  • RAM: 128 ГБ

  • сервер Dell T440

  • В зависимости от длины контекста скорость генерации составляет до ~98 токенов/с.

  • Контекст: 32K токенов

  • Инференс модели и хранение диалогов выполняются на сервере, расположенном в России.

nvidia-smi

nvidia-smi

Модель запущена через OpenAI-совместимый API — тот же chat/completions endpoint. Это важно для интеграции с любым кодом который уже работает с OpenAI SDK.

Что можно настроить

Через личный кабинет клиент управляет:

  • Цветом виджета

  • Системным промптом (до 10 000 символов)

  • Порогом эскалации (после скольких сообщений предлагать оператора)

  • Подпиской и продлением

Демонстрационный стенд

Для тестирования системы был развёрнут демонстрационный стенд, на котором можно проверить работу виджета, генерацию ответов и переключение между ИИ и оператором.


Если интересно как устроен relay-сервер или вопросы по интеграции MAX API — отвечу в комментариях.

ссылка на оригинал статьи https://habr.com/ru/articles/1045412/