Как устроен AI-менеджер на этом сайте

Собственное внедрение · опубликовано 30 августа 2026 г. · Механика Лунгу

Разбор собственного внедрения: как чат на сайте превращает свободный текст в структурированные факты, где работает AI, а где детерминированная логика, и как диалог передаётся человеку.

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

Ниже — разбор того, как он устроен. Это собственное внедрение, а не клиентский проект: все технические утверждения на этой странице соответствуют коду, который работает в проде. Цифр по конверсии, количеству заявок или экономии времени здесь нет — мы их не измеряли публично и не выдумываем.

Задача: чат, который понимает, а не гоняет по кнопкам

Посетитель приходит с формулировкой на своём языке: «здрасте, почём бот?», «а можно это с 1С связать?», «у нас менеджер всё руками заносит». От чата нужно, чтобы он:

  • понял суть по свободному тексту, без дерева кнопок;
  • задавал по одному осмысленному вопросу, а не анкету из десяти пунктов;
  • честно отвечал на прямой вопрос про цену или сроки — и не называл цифры наугад;
  • сам понял, когда разговор дозрел до человека, и аккуратно передал его вместе с контекстом.

Почему обычного сценарного бота недостаточно

Сценарный бот ведёт человека по заранее нарисованному дереву. Он предсказуем, но ломается на первом же сообщении не по сценарию: «а если у нас две точки?», «нам не в вотсап, а в телегу», «да я вообще не знаю, что мне нужно». Каждая такая ветка — это либо новый узел в дереве, либо тупик.

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

Как устроена система: три шага на каждое сообщение

Каждое сообщение клиента проходит один и тот же конвейер: извлечение → решение → формулировка, и только потом результат записывается.

  1. Сообщение клиента — свободный текст
  2. 1
    Извлечение фактов — обращение к модели

    Свободный текст превращается в список фактов вида {slot, value, stated | inferred} плюс тип прямого вопроса. Строгий JSON, температура 0, явный запрет придумывать данные.

  3. 2
    Движок решения — без модели

    Чистая функция: текущие факты и стадия диалога → одно действие, один уместный вопрос и жёсткие запреты для ответа. Ничего не пишет в базу.

  4. 3
    Формулировка ответа — модель под проверкой

    Модель пишет ответ внутри решения движка. Детерминированная проверка ловит лишние вопросы, названные цены и обещания — иначе повтор с подсказкой или запасной шаблонный ответ.

  5. Запись — apply_decision

    Единственное место, где меняются факты и стадия диалога. Переход на более позднюю стадию — только при реальном основании.

  6. Ответ клиенту  ·  при наличии контакта — заявка Артёму в Telegram

Модель вызывается дважды за ход: на входе (понять) и на выходе (сформулировать). Между ними — код, который решает, что делать. Модель может предложить, но не может ничего записать: состояние диалога, лида и стадию меняет только движок.

Как система понимает свободный текст

Первый вызов модели — это экстрактор. Он не отвечает клиенту. Он получает последнее сообщение, немного предыдущего контекста и уже известные факты, а возвращает строгий JSON:

  • список фактов: slot (тип бизнеса, канал, текущий процесс, боль, объём…), value, и origin — сказал клиент прямо (stated) или это вывод модели (inferred);
  • флаг needs_confirmation для важных или неоднозначных выводов;
  • тип прямого вопроса, если он есть: price, timeline, integration, feasibility.

Ответ модели — это черновик, а не истина. Он проходит через схему: неизвестные поля отбрасываются, а часть полей (бюджет, объём, сроки, сам контакт) модели запрещено выводить — их можно взять только если клиент назвал их явно. Это защита ровно от той галлюцинации, которая тут опаснее всего. Названия каналов («в телеге», «с сайта») дополнительно ловятся простым регулярным выражением — на случай, если модель их пропустила.

Где AI, а где детерминированная логика

Граница проходит чётко: модель работает со смыслом текста, всё остальное — код.

Делает модель

  • Понимание свободного текста → структурированные факты
  • Семантическая классификация прямого вопроса клиента
  • Формулировка финального ответа — внутри готового решения движка

Делает код, без модели

  • Какое действие допустимо и какое выбрано
  • Задавать ли вопрос и какой именно — ровно один
  • Все переходы между стадиями диалога
  • Правила «никогда не обещать цену, срок, кейс, эффект»
  • Слияние фактов, разрешение противоречий, отказ от неотвечаемых вопросов
  • Гейты передачи человеку, доставка заявки, идемпотентность

Движок — чистая функция без обращений к сети и базе. Это делает его поведение воспроизводимым и покрываемым тестами: на одинаковые вход и состояние он всегда выдаёт одинаковое решение.

Как хранится состояние диалога

У разговора два независимых носителя состояния:

  • Стадия (current_state) — единственный источник правды о том, где сейчас диалог: intake, diagnosis, problem_confirmation, solution, value_check, scope, handoff, no_sale.
  • Модель задачи (TaskModel) — компактное структурированное понимание задачи клиента: около двадцати возможных полей, каждое с пометкой stated/inferred и флагом «нужно подтверждение». Числовых «уверенностей» нет — только эти пометки.

Модель задачи после двух реплик — так она и лежит в DialogueState.data

{
  "facts": {
    "channels":        { "value": ["instagram", "whatsapp"], "origin": "stated" },
    "current_process": { "value": "клиенты пишут в директ, администратор ведёт запись в тетради и календаре", "origin": "stated" },
    "pain_points":     { "value": ["confusion", "double_booking"], "origin": "stated" },
    "goal":            { "value": "приём заявок и запись без ручного переноса", "origin": "inferred", "needs_confirmation": true }
  },
  "notes": {}
}

Записывает сюда только функция apply_decision — и только после того, как ответ клиенту уже сформирован. Переход на позднюю стадию охраняется: например, handoff требует не только «согласия», но и реального контакта; scope — предложенного решения и положительной оценки ценности.

Как выбирается следующий шаг

Движок не идёт по стадиям как по рельсам. Он смотрит на всю картину и выбирает самое продвинутое обоснованное действие:

  1. Слить новые факты в модель (правило: stated побеждает inferred; inferred не перетирает то, что клиент сказал сам; изменённый ответ клиента — это «обновление»).
  2. Определить набор допустимых действий по гейтам. Гейт проблемы открыт, когда известны процесс и боль. Гейт решения — когда вдобавок есть цель. Передача человеку доступна, только когда есть причина и есть что передавать.
  3. Выбрать действие по приоритету: прямой вопрос клиента отвечается всегда; закрытие и передача человеку достижимы из любой точки; иначе — самый продвинутый обоснованный шаг.
  4. Решить, нужен ли вопрос и насколько он обязателен: blocking (без ответа дальше никак), useful (помог бы, максимум один) или не задавать вовсе.

Результат — «решение движка»: действие, одно поле для вопроса, цель подтверждения и список жёстких ограничений для формулировщика (no_fabricated_price, no_final_timeline, one_question_max, no_unsolicited_question, handoff_needs_contact_first и другие).

Что происходит с непонятными и «непродуктивными» ответами

Отдельная логика — на ответы, которые не двигают разговор: «не знаю», «без понятия», эмоциональный выброс, мат, или повтор своего же пожелания вместо ответа на вопрос. Такой ответ распознаётся детерминированными маркерами, и спрошенное поле откладывается: его больше не переспрашивают и оно перестаёт блокировать прогресс. То же происходит, если один и тот же факт спросили дважды подряд без результата — движок переключается на следующую полезную ось разговора.

Грубость и мат при этом не повод для нотаций и не повод отказать в услуге — это прямо зафиксировано и в промпте, и в тестах. Если клиент после резкого сообщения снова спрашивает по делу, консультация спокойно продолжается с сохранённого контекста.

Когда система просит контакт и как передаёт диалог человеку

Контакт запрашивается не сразу, а когда клиент согласился, чтобы с ним связались, или хочет расчёт — и при этом уже есть что передавать (обсуждённое направление решения или рамки). Телефон, @ник или e-mail вычленяются и моделью, и регулярным выражением; поле контакта — из тех, что берутся только со слов клиента.

Когда контакт есть, отдельный сервис после фиксации хода:

  • заводит ровно одного лида на разговор;
  • отправляет Артёму в Telegram карточку — структурированные факты плюс ограниченная по объёму выдержка из переписки;
  • фиксирует факт доставки: delivered или failed.

Важная деталь: фразу «передал Артёму» формирует бэкенд, а не модель. Пока контакта нет, формулировщику запрещено даже намекать, что заявка ушла. Если Telegram недоступен — система честно говорит, что автоматически уведомить не получилось, и не обещает, что «менеджер скоро свяжется». Передача человеку при этом не завершает диалог: клиент может продолжать писать, и новые важные факты уходят дополнением к тому же лиду.

Пример диалога

Ниже — как это выглядит в переписке. Значения условные, механика — реальная.

Клиент

здравствуйте, сколько стоит чат-бот для записи клиентов?

AI-менеджер

Привет! Цена зависит от того, как сейчас устроена запись и что именно автоматизируем. Расскажите, как вы ведёте запись сейчас?

Прямой вопрос про цену. Движок: answer_direct_question + ограничение no_fabricated_price — отвечаем честно, цифру не называем, задаём один диагностический вопрос.

Клиент

клиенты пишут в директ инстаграма и вотсап, администратор записывает в тетрадь и календарь. постоянно путаница и двойные записи

AI-менеджер

Понятно, ручная запись в двух местах — отсюда и путаница. Правильно понимаю, что задача — принимать заявки из мессенджеров и вести запись в одном месте без ручного переноса?

Извлечены каналы, текущий процесс, боли. Гейт проблемы открылся → движок: confirm_problem. Формулировка — одно подтверждение, без второго вопроса.

Клиент

да, всё так. хочу чтобы менеджер потом со мной связался

AI-менеджер

Хорошо. Оставьте, пожалуйста, телефон или ник в Telegram — передам Артёму вместе с контекстом разговора.

Согласие связаться есть, контакта нет → ограничение handoff_needs_contact_first: движок просит контакт и запрещает говорить «передал».

Какие инженерные задачи пришлось решить

Разработка шла итерациями по реальному поведению. Самое заметное, что чинилось (и закрыто тестами — сейчас их 209):

  • Диалог-анкета. Ранние версии переспрашивали одно и то же поле бесконечно. Решение: гейт «нужен ли вопрос», лимит на повтор одного поля, откладывание неотвечаемых фактов, ограничение «один вопрос за ход».
  • Модель придумывала коммерческие факты — вилку цены, срок, «был такой кейс», оценку эффекта. Решение: движок выдаёт запреты-ограничения; формулировщик возвращает список своих утверждений, который детерминированно сверяется с запретами; отдельная сетка ловит валюту в тексте. Нарушил — повтор или шаблонный ответ.
  • Формулировщик протаскивал лишние вопросы. Право задать вопрос теперь даёт только явно выбранное движком поле, а не сам факт действия. Проверка считает «?» и ищет обороты-просьбы в тексте ответа.
  • Преждевременная и ложная передача человеку — «передал Артёму» до того, как появился контакт, или до реальной доставки в Telegram. Решение: ограничения handoff_needs_contact_first / handoff_delivery_pending, текст передачи от бэкенда, явный статус доставки.
  • Потеря состояния при обрыве связи и дубли отправки. Сообщение клиента и ход сохраняются до вызова модели; есть до-догон хода без ответа; пара «разговор + идентификатор сообщения» уникальна — повтор не создаёт второго сообщения и второго лида.
  • Проблема переспрашивалась каждый ход. Теперь она фиксируется при первом же вопросе на подтверждение, а переспрашивается только если клиент сообщил противоречащие сведения.
  • Одно резкое «не надо» закрывало продажу. Теперь no_sale требует подтверждённого отказа; предметный вопрос после резкого сообщения возвращает разговор к сохранённому контексту.

Что система умеет сейчас

  • Понимает свободный русский текст, включая разговорный стиль, опечатки и мат.
  • Двойной вызов модели с детерминированным движком между ними; движок покрыт тестами и воспроизводим.
  • Модель задачи из ~20 полей с пометками «сказано / выведено» и «нужно подтверждение»; откладывание неотвечаемых фактов.
  • Честные ответы на прямые вопросы о цене, сроках, интеграции, реализуемости — без выдуманных чисел.
  • Подтверждение проблемы и предложение направления решения (не сметы).
  • Передача диалога человеку через Telegram — со структурированной карточкой и правдивым статусом доставки.
  • Непрерывность диалога при обрыве связи; идемпотентный приём сообщений.
  • Провайдер модели заменяем: оффлайн-заглушка, Anthropic или OpenAI-совместимый шлюз; в проде работает компактная модель через такой шлюз.

Чего мы не заявляем

На этой странице нет и не будет цифр по конверсии, количеству лидов, ROI, экономии времени или росту продаж, отзывов клиентов и данных о внешних внедрениях. Это работающая система, и здесь описано, как она устроена, а не насколько она эффективна — такие метрики мы публично не измеряли.

Попробовать AI-менеджера

Откройте чат и напишите так, как написали бы живому менеджеру — свободным текстом, со своей задачей. Это тот же диалог, что разобран выше.

Обсудить похожую задачу

Если хотите такой же разбор процесса под свою задачу — это наше направление AI-агентов для бизнеса.