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