Главная / Блог / Ассистент, который записывает: как мы добавили в GraphRAG клиентов, расписание и каналы Telegram, Max и сайт

Ассистент, который записывает: как мы добавили в GraphRAG клиентов, расписание и каналы Telegram, Max и сайт

Ассистент, который только отвечает, оставляет человеку самое трудоёмкое: записать, напомнить, позвать снова. Мы добавили в GraphRAG модуль «Клиенты» с услугами, расписанием и сценариями, подключили Telegram, Max и виджет на сайт, а для обычных аккаунтов компании написали отдельный шлюз. Рассказываем, какие решения приняли, что пришлось выбросить и где граница.

AI-агентзапись клиентовTelegramMaxMCPвиджет

Коротко. GraphRAG умел отвечать по базе знаний компании: регламенты, прайс, каталог. Для салона или клиники этого мало: после ответа клиент хочет записаться, а через месяц его надо позвать снова. Мы сделали модуль «Клиенты». Агент видит услуги и свободное время, создаёт заявку, менеджер её подтверждает, а сценарии напоминают о визите и зовут обратно. Каналов теперь четыре: виджет на сайт, боты Telegram и Max и внешний канал для всего остального. Под обычные аккаунты компании в мессенджерах мы написали отдельный шлюз. Настроить модуль под конкретный бизнес может и AI-агент по админской ссылке MCP.

Как GraphRAG ведёт клиента: каналы → агент, который отвечает и записывает → модуль «Клиенты» с услугами, слот-ресурсами, календарём, карточками и сценариями → менеджер и CRM; сценарии пишут первыми, настройка через админский MCP
Как GraphRAG ведёт клиента: каналы → агент, который отвечает и записывает → модуль «Клиенты» с услугами, слот-ресурсами, календарём, карточками и сценариями → менеджер и CRM; сценарии пишут первыми, настройка через админский MCP

Первая версия умела слишком много

Модуль мы начали 15 октября и за три дня собрали почти всё, что обычно бывает в CRM для услуг: клиенты, календарь, активность с оплатами и покупками, выручка, цели рассылок, вебхуки, аналитика. Получилось восемь вкладок, и половина настроек была текстовыми полями вида «id: Название». Стало непонятно, что в модуле главное, а главной болью владелец назвал как раз текстовые поля.

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

Время, которое нельзя продать дважды

Главный объект модуля мы назвали слот-ресурсом. Это то, на что записываются по времени: мастер, кабинет, стол. У каждого свой график и исключения, поверх лежит режим работы заведения. Свободное время считает программа: график минус занятые записи с перерывом между ними.

Агент не берёт время из текста базы знаний, только из инструмента booking_slots. Цены и длительность тоже только из справочника услуг. Раньше прайс лежал в .md, и модель могла процитировать устаревшую строку. Теперь справочник один, а страница «Услуги и цены» в базе собирается из него автоматически.

Двойную бронь закрывает база, а не модель. Проверка и запись идут в одной транзакции SQLite с блокировкой на запись, и занятый слот-ресурс не снимается ничем. В тесте четыре одновременные записи на один стол: проходит одна. Отказ возвращает ближайшее свободное время, и агент сразу предлагает его клиенту.

Сценарии: агент пишет первым

Напоминание о визите, вопрос «Ждём вас?», спасибо после визита, «пора снова» по ритму клиента, «давно не были», день рождения. Свободных рассылок в интерфейсе нет, только эти сценарии с настройками: решение владельца после того, как свободные цели в первой версии оказались слишком сложными. Ответ «нет» на «Ждём вас?» не меняет запись сам, а уходит менеджеру в Telegram.

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

Каналы: что оказалось сложнее, чем казалось

КаналКак подключаетсяНа чём споткнулись
Telegram-боттокен, вебхук с секретомс российского сервера api.telegram.org недоступен; запросы идут через прокси из окружения
Max-боттокен, подписка с секретомTLS падал с CERTIFICATE_VERIFY_FAILED; положили корневой сертификат Минцифры, проверку не отключали
Виджет на сайтодна строка <script>ключ виден в HTML; он даёт только вопросы этой базе, домены можно ограничить
Внешний каналURL и токен в обе стороныпервая версия была привязана к нашему шлюзу; переделали в общий механизм

Виджет живёт в Shadow DOM, поэтому стили сайта его не ломают. История диалога хранится на сервере по случайному id посетителя: страница не может подсунуть агенту выдуманный «прошлый разговор».

Отдельная история с кнопками. Когда агент предлагает выбрать услугу или окно, клиенту удобнее нажать, чем печатать «давайте второй». Мы не дали модели писать кнопки сама. Агент вызывает инструмент offer_choices и перечисляет варианты, а программа сверяет каждый с тем, что инструменты реально вернули в этом вопросе, и пишет подписи из данных. Выдуманное окно в кнопку не превратится. Нажатие приходит обычным сообщением с текстом варианта, поэтому все каналы работают одинаково, а внешний канал показывает тот же выбор списком с номерами. В Max старую клавиатуру не убрать, поэтому в данных кнопки есть метка набора вариантов, и кнопка вчерашнего ответа не выберет окно из сегодняшнего.

Шлюз gate: когда нужен не бот, а аккаунт

Многие компании пишут клиентам не от бота, а с обычного аккаунта в Telegram или Max. Бот такого не заменит, а держать сессии чужих аккаунтов внутри GraphRAG мы не хотели. Поэтому шлюз gate стал отдельным сервисом. Каждая учётка работает в своём процессе под пользователем ОС своей компании, сессии и ключи зашифрованы, историю переписки шлюз не хранит, а входящие доставляет с подписью и повторами до суток. Для Telegram это Telethon, для Max неофициальная библиотека PyMax; Max при входе с нового устройства требует включённую двухфакторную защиту.

Ошибку мы сделали в связке. Сначала GraphRAG подключался именно к нашему шлюзу, кодом в один клик. Потом владелец попросил не привязываться к gate, и мы сделали внешний канал с одним правилом: адрес и токен выдаёт тот, кто принимает запросы. После переделки пришла жалоба: «подключено, а ничего не работает». Разбор показал две разные причины. В те минуты таймаутил шлюз к модели, а свой сервис отвечал 403, потому что gate слал в него запросы со своей подписью. Теперь вверху вкладки есть схема, где у каждой стороны видно последнее событие и причину отказа словами.

Настройка под бизнес через MCP

У коллекции есть админская ссылка MCP, и у модуля «Клиенты» на ней 31 инструмент. Подключённый AI-агент, например Claude, может завести услуги пакетом (services_bulk: всё или ничего, режим «ровно этот список» отправляет лишнее в архив), слот-ресурсы с графиками, включить сценарии и проверить свободное время. Логику ответа агент правит в харнесе с тестами, как мы описывали в статье про харнес и 300 вопросов. Новую версию харнеса включает человек. Тонкие настройки, вроде привязки Telegram менеджера и «отправить сейчас», по решению владельца доступны только в интерфейсе.

Честные ограничения

  • Логин в веб-чате и виджете не подтверждён. Поэтому агент видит телефон клиента только последними цифрами.
  • Первым агент пишет в Telegram, внешний канал и веб-чат. Max-бот в сценарии пока не входит, а посетителю виджета напомнить нечем: его может не быть на сайте.
  • Календарь свой. С внешними системами записи сейчас связь через вебхуки; адаптеров, которые читают их расписание напрямую, ещё нет.
  • Max-учётки держатся на неофициальной библиотеке. Если Max поменяет протокол, шлюз придётся обновлять.
  • Планировщик и доставка вебхуков работают потоками одного процесса. Для одного сервера это нормально, для нескольких воркеров нужно включать их в одном.

FAQ

Агент может записать клиента на занятое время? Нет. Время он берёт только из расписания, а запись проверяется в транзакции базы. Занятый слот-ресурс получает отказ с ближайшим свободным временем.

Нужен ли модуль «Клиенты», чтобы работал виджет на сайте? Нет. Без модуля виджет отвечает по базе знаний. Кнопки выбора и запись появляются, когда модуль и бронирование включены.

Можно ли писать клиентам с обычного номера, а не от бота? Да, через шлюз gate или свой сервис, подключённый как внешний канал. Ответы и напоминания уходят тем же путём.

Кто подтверждает записи, сделанные агентом? Менеджер, в админке. О «перезвонить» и об ответе «не приду» он узнаёт в Telegram.

→

Читать дальше

Все статьи →

Агент вместо сценария: как мы описали логику AI-ассистента данными и проверили его на 300 вопросах

Ассистента по базе знаний обычно собирают конвейером «роутер → поиск → ответ», и каждый новый тип вопроса добавляет ветку. В GraphRAG мы заменили конвейер агентом с восемью инструментами, а всё, что должно происходить всегда, вынесли в JSON-харнес с версиями и тестами. Как это устроено, что показало сравнение на 300 вопросах и где агент пока проигрывает.

Читать →

Локальный AI-монтажёр: как ноутбук разбирает медиатеку и собирает ролик под музыку по текстовому запросу

После поездки осталась сотня роликов с телефона и десяток треков, а монтаж откладывался неделями. Написал приложение, которое само разбирает медиатеку локальной VL-моделью, размечает музыку по тактам и по одной фразе собирает черновой клип на таймлайн. Что умеет, на какие грабли наступил и чего оно делать не будет.

Читать →

Включить домашний ПК из отпуска: ESP32 за $2, Wake-on-LAN и MQTT

Уехал в долгий отпуск, а дома стоит ПК с RTX 5090, который время от времени нужен для AI. Держать его включённым круглые сутки не хотелось. Собрал «будильник» на ESP32 за два доллара: с телефона жму кнопку, плата будит компьютер по Wake-on-LAN, а после загрузки с ним можно делать что угодно, вплоть до запуска локальной Ollama через reverse SSH.

Читать →