Инструменты и Автоматизация

Как мы построили AI-чат с квалификацией лидов на локальной LLM

Михаил Шувалов
27 июля 2026
~9 мин чтения

Типичный бизнес-сайт — это витрина. Посетитель пришёл, почитал, ушёл. Если повезло — заполнил форму. Но между «посетитель на сайте» и «лид в CRM» — пропасть: кто-то должен ответить ночью, кто-то — выяснить что нужно, кто-то — квалифицировать и передать продажнику. Мы зашили все эти «кто-то» в одну систему из четырёх компонентов: 1 397 строк PHP, 602 строки JS, 451 строка NLP-движка, и 935 строк CRM-адаптера. Работает 24/7 на локальной LLM без единого внешнего API.

Почему не OpenAI / DeepSeek / облачные API

Первое что спрашивают: «Почему не взяли OpenAI API и не закончили за день?» Ответ — три причины:

  1. 152-ФЗ. Персональные данные клиентов (имя, телефон) не должны покидать периметр компании. Облачные API — это передача PII третьей стороне. Юридически — серый зона. Технически — риск.
  2. Стоимость. 1000 диалогов в день через OpenAI API = $50–150/сутки. Выделенный GPU-сервер на Selektel = фиксированная стоимость в месяц, независимо от нагрузки.
  3. Контроль. System-prompt, температура, контекст — на своём сервере ты контролируешь всё. Никаких «модель обновилась и теперь отвечает по-другому».
Локальная LLM на выделенном сервере Selektel — это когда ты платишь за железо, а не за токены. И данные клиентов не пересекают границу РФ.

Архитектура: четыре компонента

Система спроектирована под shared hosting — никаких Docker, Redis, WebSocket. Файловые сессии, PHP 8.2 без фреймворка, JSON как БД. Четыре компонента:

1. chat_widget.js — 602 строки чистого JS

IIFE-паттерн, ни одной зависимости. Подключается на любую страницу одной строкой: <script src="chat_widget.js"></script>. Инжектит CSS (injectCSS), строит DOM чата, управляет состоянием.

Три режима работы (стейт-машина): aiwaiting_humanhuman. Polling каждые 3 секунды в режиме ожидания оператора. Auto-reconnect при обрыве соединения. Персистентность через localStorage — перезагрузка страницы не теряет сессию.

2. chat_api.php — 1 397 строк, 12 endpoints

Точка входа для виджета. Маршрутизация через ?action=...:

3. kb_engine.php — 451 строка, NLP-поиск

Движок базы знаний. Токенизирует запрос пользователя (униграммы + биграммы), фильтрует русские стоп-слова, ищет совпадения по 10 секциям KB. Веса: keyword match (×4), title (×3), content (×1), FAQ question (×2).

Он же управляет стадиями разговора и извлекает BANT-сигналы из ответов пользователя — budget, authority, timeline, need_clarity. И он же считает lead_score.

4. crm_adapter.php — 935 строк, dual-write в Twenty + Bitrix24

Единый PHP-мост к GraphQL API Twenty CRM. Транслирует 9 методов Bitrix24 (crm.lead.add, crm.deal.add, crm.timeline.comment.add, tasks.task.add, и др.) в GraphQL-мутации Twenty. Режим CRM_MODE=dual — параллельная запись в обе CRM с авто-fallback.

6 стадий разговора

Это не чат-бот «вопрос-ответ». Это диалоговая машина с памятью состояния. Каждая стадия имеет:

Стадия Триггер перехода Форсирование
1. Discovery ≥2 проблемных ключевика 6 сообщений
2. Qualification ≥3 BANT-сигналов 12 сообщений
3. Solution Matching Возражение или интерес
4. Objection Handling Возражение отработано
5. Consultation Offer Новый вопрос → рестарт
6. Human Handoff «позови оператора»

BANT-скоринг — автоматическая квалификация

Пока пользователь общается с ботом, kb_engine в фоне извлекает сигналы из каждого ответа. Никаких «оцените лид от 1 до 10» — система сама понимает кто перед ней.

Формула score:

// kb_engine.php:366 — функция kb_calculate_score()
$score = 0;
if (!empty($signals['budget_indicators']))   $score += 3;  // бюджет
if (!empty($signals['authority_indicators'])) $score += 3;  // ЛПР
if (!empty($signals['timeline_indicators']))  $score += 2;  // срочность
$score += ($signals['need_clarity'] === 'high') ? 2 : 0;
$score += ($signals['need_clarity'] === 'medium') ? 1 : 0;

if ($score >= 7) return 'hot';
if ($score >= 4) return 'warm';
return 'cold';

Паттерны для детекции бюджета ищут не только цифры с «₽», но и косвенные признаки: «бесплатный пилот», «недорого», «эконом». Authority детектит «директор», «я принимаю решение», «собственник», «руководитель отдела». Timeline ловит «срочно», «вчера», «на этой неделе».

Human Handoff — когда бот зовёт человека

Самое тонкое место любого AI-чата — понять когда заткнуться и позвать оператора. Мы сделали двухуровневую систему.

Уровень 1 — триггеры пользователя:

Уровень 2 — защита от ложных срабатываний:

Перед проверкой триггеров — regex-фильтр негации: «не надо оператора», «без менеджера», «не нужен человек». Если негация найдена — handoff не активируется. Простая но критичная деталь, которую забывают 90% чат-ботов.

После handoff оператор заходит в chat_admin.html (888 строк, SPA) — видит все активные сессии, полную историю разговора, AI-подсказки ответов, флаги (good/bad/missing_knowledge).

Интеграция с CRM — лид не теряется

Каждый захваченный контакт мгновенно создаётся в двух CRM одновременно:

  1. Twenty CRM (основная) — Person с телефоном/email, Opportunity для горячих лидов, Notes с историей стадий
  2. Bitrix24 (legacy fallback) — Lead с кастомными полями (UF_CRM_LEAD_SCORE, UF_CRM_STAGE)

Дедупликация: перед созданием — поиск по телефону (local digits, последние 7–10 цифр) и email. Если контакт уже есть — добавляется Note «Повторное обращение через чат» вместо создания дубля.

Стадии разговора и изменения lead_score синхронизируются в реальном времени: каждый переход этапа → Note в Twenty с деталями. Рост score → Note с историей. Hot/Warm + consultation_offer → автоматическое создание Opportunity.

Почему PHP и файлы, а не нормальная БД

Хостинг Nichost — shared, без PostgreSQL, без Redis. Всё состояние системы — JSON-файлы с LOCK_EX. Это накладывает ограничения, но и даёт плюсы:

Минус — нет конкурентного доступа. Но для сценария «один пользователь — одна сессия» это не проблема.

Что дальше

Сейчас система работает в режиме dual-write (Twenty + Bitrix24). Следующий шаг — отключить Bitrix24 совсем и оставить только Twenty. Дальше — голосовой интерфейс (звонок на сайт → AI-оператор) и исходящие цепочки (бот сам инициирует диалог после заявки).

Главное что мы поняли за время разработки: AI-чат на сайте — это не «прикрутить ChatGPT и готово». Это инженерная задача: стейт-машина, NLP-поиск, BANT-квалификация, CRM-интеграция, комплаенс. Каждый слой по отдельности простой. Собранные вместе — система которая реально заменяет человека на первой линии.