Як я автоматизував техпідтримку StabX, розробивши AI-агента в 5 месенджерах

💡 Усі статті, обговорення, новини про AI — в одному місці. Приєднуйтесь до AI спільноти!

Привіт, DOU. Мене звати Павло Шпагін, до цього працював у Grammarly, зараз — співзасновник Academia Tech. Ми розробили StabX — систему стабілізації для дронів, яку використовують понад 700 військових підрозділів. Наше ком’юніті в Signal налічує 1300+ людей: оператори, військові, виробники, представники компаній.

Я хочу розповісти, як із завалу схожих питань у чаті підтримки виріс AI-агент supportbot.info, який зараз відповідає 350+ разів на місяць, і як із внутрішнього інструменту він став окремим продуктом на п’ять месенджерів. Та сама архітектура зараз працює і в 1-на-1 чатах, і в телефонній підтримці — бо задача однакова скрізь: знайти правильну відповідь у купі старих розмов і документів, цитуючи їх.

Проблема

Уявіть собі групові чати на 1300+ людей. Щодня хтось питає про оновлення прошивки, хтось не може під’єднати контролер, а хтось уточнює сумісність із конкретним дроном. І на кожне з цих питань хтось із нашої команди вже відповідав — тиждень, два або місяць тому. Але ці відповіді просто «потонули» в нескінченному потоці повідомлень, і тепер нам доводиться прописувати все заново.

Наша команда підтримки витрачала на це години щодня. Причому це не якісь складні кейси — у 90% випадків відповідь вже була або в історії чату, або в глибинах документації. Просто ніхто не міг її знайти.

FAQ ми пробували — не працює. FAQ покриває десяток базових сценаріїв, а реальні питання користувачів — це суцільні edge cases, специфічні конфігурації, нестандартні ситуації. Цього ніколи не буде в жодному FAQ. І навіть якби було — люди все одно не читають FAQ. Вони відкривають чат і пишуть питання. З цим немає сенсу боротися.

Тому я вирішив підійти інакше — написати бота, який сам знаходить відповідь з минулих розмов або документації і видає її прямо в чат. Звучить просто: взяти RAG, нагодувати історією — і готово. Але груповий чат — це не акуратна тікетна система. Тут одночасно йдуть три розмови, хтось жартує, хтось відповідає на повідомлення годинної давності, а хтось просто кидає скріншот без контексту. Класичний RAG на такому хаосі галюцинує.

А галюцинації в техпідтримці — неприпустимо. Людина може взяти «пораду» бота, щось зробити не так і зламати пристрій. Тому з самого початку я закладав принцип zero-trust: бот не повинен бути оракулом, якому вірять на слово. Він повинен цитувати конкретне джерело і давати посилання, щоб людина перевірила сама і прийняла рішення на основі першоджерела, а не висновку моделі.

Це джерело — кейс. Кейс у нашому контексті — структурована трійка «проблема + рішення + історія розмови», яку бот автоматично виділяє з живих розмов у чаті. Хтось описав проблему з калібрацією, скинув скріншот помилки, інша людина відповіла як вирішити, автор підтвердив що спрацювало — бот фіксує це як кейс із повним контекстом і додає в базу знань. Все мультимодально: зображення, відео, аудіо, файли — бот розуміє і зберігає. Медія зберігається в Cloudflare R2, а самі кейси хостяться на сайті і доступні за прямим посиланням — щоб коли бот цитує кейс у відповіді, людина могла одразу перейти і перевірити контекст. Ось приклад реального кейсу. Наступного разу, коли хтось задає схоже питання — бот знаходить цей кейс і цитує його.

Чому почали з Signal

Перший месенджер, де я це побудував — Signal. І саме він був найскладнішим з точки зору інтеграції, тому почну з нього. Signal — месенджер, який вважається одним із найбезпечніших у світі.

Але Signal — закрита екосистема. Тут немає Bot API. Немає вебхуків. Немає документації для розробників ботів. До мого проєкту розумних ботів у Signal практично не існувало.

Signal шифрує все. Повідомлення зберігаються локально на пристрої у зашифрованій SQLCipher базі — і назовні їх просто так не витягнеш. Щоб бот взагалі міг бачити що відбувається в групі, мені довелося підняти headless Signal Desktop на сервері — по суті, безголовий клієнт Signal, який тихо сидить у групі, зчитує базу і віддає повідомлення моєму бекенду.

Окрема історія — як «поглинути» існуючу історію групи. Signal не дає доступу до старих повідомлень через якийсь API. Тому я зробив механізм через QR-лінкування: адмін групи сканує QR-код, який прив’язує його акаунт до бота як додатковий пристрій. Після цього бот отримує доступ до історії повідомлень цього акаунту — і може витягнути весь архів групи, розпарсити його і побудувати початкову базу кейсів. Це одноразова процедура для кожної групи, але без неї бот стартував би з нуля, без жодного контексту.

Щодо безпеки: акаунт адміна прив’язується тимчасово під час імпорту, бот зчитує тільки обраний груповий чат, обробляє його — і повністю від’єднується. Жодні інші чати, контакти чи персональні дані не зчитуються. Детальніше — на supportbot.info/privacy.

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

І от що цікаво: коли я розібрався з Signal і почав дивитися на інші месенджери, виявилось, що для задачі «розумна підтримка в неструктурованих групових чатах» готових рішень не існує ніде. Не тільки в Signal — взагалі.

Перша версія і перший деплой

Для початку я обрав сім’ю LLM-моделей Gemini — найкращі моделі для мультимодальних задач, а в нас люди в чаті постійно кидають скріншоти, голосові, відео.

Перша робоча версія була досить простою: набір команд, за якими адмін міг скормити боту історію групи. Бот парсив її, намагався виділити пари «питання—відповідь», зберігав у векторну базу. Коли хтось задавав нове питання — шукав схожі кейси і відповідав.

Задеплоїли. І взагалі-то було непогано. Бот відповідав, кейси знаходив, базові речі закривав. Але коли почав працювати на живому потоці — вилізла купа нюансів, які я недооцінив.

Бот обробляв кожне повідомлення окремо — і коли в чат одночасно писали десять людей, алгоритм просто ламався. Він міг повторювати одну й ту саму відповідь кілька разів поспіль. Тегав не ту людину. Іноді відповідав без reply — і було незрозуміло кому він взагалі щось пише. Не розрізняв коли в групі паралельно йдуть дві розмови і змішував їх у кашу. І головне — галюцинував. Не катастрофічно, але достатньо щоб хтось міг зробити неправильний висновок.

Довелося заходити глибоко. Переписувати промпти — не один раз, десятки разів. Будувати симуляції живих чатів, щоб тестувати зміни не на продакшені. Писати judge-модель, яка оцінює якість кожної відповіді бота — і окремо калібрувати її, щоб її оцінки корелювали з тим, що реально добре чи погано в очах людини. Додавати перевірки на галюцинації для кожного повідомлення перед тим як воно піде в чат. По суті, кожен день — це був мікроцикл: знайти дефект → відтворити в симуляції → виправити → прогнати judge → задеплоїти.

Саме на цьому етапі я зрозумів, що робочий прототип і корисний продукт — це дві різні планети.

Як я це вирішував

Пройдуся по архітектурі, до якої прийшов — і як кожен дефект першої версії закривався окремим механізмом.

Починається все ще до першого повідомлення в реалтаймі. Коли адмін під’єднує групу через QR і імпортує історію (наприклад, 10 тисяч повідомлень), ці повідомлення не можна просто скормити моделі одним шматком. Тому історія розбивається на перетинні чанки (щоб не втрачати контекст на межах), кожен чанк аналізується окремо: Case Extractor витягує ланцюги повідомлень, які складаються в кейс — питання, уточнення, відповідь, підтвердження — і визначає статус (вирішено/рекомендація). Сусідні чанки часто знаходять одну й ту саму розмову, тому на виході є дедублікація: кейси з перетином по повідомленнях зливаються в один. Це і дає ключову властивість — бот не стартує «з нуля», а одразу після імпорту має робочу базу знань із сотнями готових кейсів.

Далі — робота в реалтаймі. Коли в чат приходить повідомлення, воно падає в буфер. Не аналізується одразу — просто зберігається. Далі працює debouncing таймер на 60 секунд тиші для кожної групи. Кожне нове повідомлення скидає цей таймер. Прийшло ще одне — таймер обнулився, і знову чекаємо 60 секунд. Це вирішило проблему з першої версії, коли бот відповідав тричі на одне питання, бо людина писала трьома повідомленнями.

Коли тиша настала — бот дивиться на весь накопичений буфер і прогоняє його через Message Gate. Gate — це окрема модель (Gemini Flash), яка визначає: що тут взагалі є питаннями, а що — просто розмова, жарти, реакції. Калібрування Gate забрало купу роботи — різниця між «хм, не працює» (питання) і «хм, цікаво» (репліка) для моделі зовсім не очевидна.

Далі для кожного виявленого питання окремо запускається агент. Паралельно йдуть три пошуки (нижній рядок на діаграмі): Keyword Search по історії повідомлень, Dual Case RAG по базі кейсів (і по підтвердженим рішенням, і по рекомендаціям — це дві окремі колекції), і Docs Search по документації продукту. Docs Search рекурсивно обходить Google Docs — якщо в документі є посилання на інші документи, він іде по них і збирає повний контекст. Реальна документація — це не один файл, а дерево з перехресними посиланнями, і бот повинен орієнтуватися в ньому так само як людина. Вся зібрана документація кешується через Gemini context caching — тобто весь промпт з повним деревом документів зберігається на стороні API і не перевідправляється щоразу, що здешевлює і прискорює кожен запит. Плюс Google Search як fallback — якщо в кейсах і документації відповіді немає, бот шукає в інтернеті. Кандидати з усіх джерел потрапляють у Response Synthesizer — фінальну модель, яка складає цілісну відповідь із цитатами. Бот відповідає на кожне питання окремо, не одним суцільним полотном.

Якщо під час цього процесу в чат прилітає нове повідомлення — бот скасовує поточні результати, додає нове повідомлення в буфер і починає заново. Бо контекст змінився — може хтось уже відповів на питання, або додав уточнення. Краще зачекати ще 60 секунд і відповісти правильно, ніж видати щось застаріле.

Синтезатор не вигадує відповіді. Він обов’язково цитує конкретний кейс або документ і дає посилання — щоб людина перевірила сама. Якщо джерел немає або впевненості недостатньо — бот не вигадує, а тегає адміна (стрілка Tag на діаграмі). Це і є zero-trust: бот не оракул.

Case Extractor спрацьовує після кожного нового повідомлення або реакції — він аналізує буфер і шукає нові кейси. Хтось описав проблему, хтось відповів — створюється кейс зі статусом «рекомендація». Поки що це непідтверджена порада. Але потім автор питання ставить 👍 на відповідь — реакція потрапляє в буфер, тригерить Case Extractor знову, і він промоутить кейс із «рекомендація» до «вирішено». Те саме відбувається якщо автор пише щось типу «спасибі, допомогло» — текстове підтвердження працює так само як реакція. Два типи кейсів живуть у двох окремих колекціях Dual Case RAG, і бот чесно розрізняє їх у відповідях: «ось підтверджене рішення» проти «ось порада, але ще не підтверджена». Ніякого ручного введення — база знань росте сама з живих розмов.

Повний детальний опис усіх компонентів — на supportbot.info/how-it-works.

Результати

На момент написання статті бот відповідає 350+ разів на місяць на питання наших користувачів. Він майже повністю замінив живу техпідтримку StabX — ескалює на людину тільки тоді, коли дійсно бракує підтвердженої інформації. Галюцинації ми звели до мінімуму, бо бот не вигадує — він цитує існуючі кейси і документацію.

Наша команда підтримки змогла нарешті зайнятися тим, чим мала займатися з самого початку — розвитком продукту, роботою з партнерами, стратегічними питаннями. А не відповідями на одне й те ж питання по п’ять разів на день.

Але головне навіть не це. Головне — що бот будує живу базу знань ком’юніті. Кожна розмова, кожне вирішене питання стає кейсом, який допоможе наступному користувачу. Чим більше людей користуються продуктом і чатом — тим розумнішим стає бот. Це самопідсилювальний цикл.

Від внутрішнього інструменту до продукту

Коли я побачив, що система стабільно працює в Signal, виникла очевидна думка: це ж потрібно не тільки нам. Будь-який бізнес, який має групові чати з клієнтами, стикається з тією ж проблемою.

Тому я зробив наступний крок — створив повноцінний сайт з дашбордом і додав ще чотири месенджери: WhatsApp, Telegram, Discord, Slack.

Виявилося, що сама архітектура — Dual-RAG, gate, обробка неструктурованих чатів — переноситься на будь-яку платформу. Signal був найскладнішим, бо не мав API, а в інших месенджерах інтеграція була вже простішою частиною.

У дашборді можна під’єднувати групи, поглинати історії чатів, завантажувати документацію, дивитися аналітику, об’єднувати фрагментовані групи через drag-and-drop (в Signal, наприклад, максимум 990 учасників, тому фрагментація — це стандартна ситуація).

Нещодавно додав підтримку 1-на-1 чатів з ботом у месенджерах. Під капотом використовує схожий алгоритм, але тут є цікавий нюанс з роутінгом: один і той самий користувач може бути учасником кількох груп одночасно — наприклад, у чаті Нової Пошти та Monobank. Коли він пише боту в особисті, бот повинен зрозуміти з контексту якої саме групи питання, зібрати кейси і документацію з правильної бази знань, і відповісти з правильними джерелами. По суті — роутінг по семантиці, а не по хардкоду.

На практиці це працює так: створюєш групу (можна навіть порожню), активуєш 1-на-1, і ділишся посиланням або QR-кодом — або ж писати боту напряму можуть ті, хто вже в групі. Клієнти спілкуються з ботом у приваті, отримують відповіді з тієї самої бази знань що й у групі, а якщо бот не справляється — ескалація на живу людину. При ескалації бот не просто тегає адміна, а дає посилання на релевантну частину розмови, щоб адмін одразу отримав контекст і не витрачав час на з’ясування що сталося. І кейси з цих приватних розмов теж динамічно потрапляють у базу знань — бот вчиться на кожному вирішенні, так само як і в груповому чаті.

Окрема фіча — телефонний бот. У дашборді можна згенерувати номер телефону і поставити його на свій сайт або в контакти підтримки. На рівні реалізації — Telnyx для телефонії, при цьому справжній номер телефону підтримки анонімізується, клієнт бачить тільки згенерований номер. Коли хтось дзвонить, бот працює як голосовий проксі: слухає проблему, шукає відповідь в кейсах і документації в реальному часі, і намагається вирішити питання сам. Якщо не може — ескалює на живу підтримку або відділ продажів. Голосову частину тримає Gemini 3.1 Flash Live — це дає приблизно 3x приріст швидкості відповіді порівняно з класичним TTS-пайплайном, бо модель стрімить аудіо напряму без окремого етапу синтезу мовлення.

Чого я навчився

Кілька речей, які я б хотів знати на початку. Промпти — це набагато більше роботи, ніж здається. Алгоритм і архітектура — це фундамент, без якого нічого не працює, але коли фундамент є, саме формулювання промптів визначає, чи буде бот корисним, чи дратівливим. І на їхній тюнінг я витратив непропорційно багато часу порівняно з тим, скільки очікував на старті. Те саме з gate-моделлю і debouncing — я спочатку думав це деталі, а виявилося що без них бот у груповому чаті просто неюзабельний. Бо чат — це не тікетна система де є чітке питання і очікування відповіді. Тут хаос, паралельні розмови, жарти, емоції.

Ще один інсайт — довіра. Люди приймають AI-відповіді набагато краще, коли бачать джерело. «Ось підтверджений кейс, дивіться самі» працює в сто разів краще, ніж «відповідь згенерована штучним інтелектом». І коли бот не знає — він не вигадує, а чесно тегає адміна. Це теж довіра: люди бачать що бот знає свої межі. А автоматична екстракція кейсів — це те, що робить систему живою. Без цього база знань старіє і стає нерелевантною. З цим — вона росте разом із ком’юніті.

Що далі

Ми хочемо зробити SupportBot стандартом техпідтримки в месенджерах. Потреба є — ми це бачимо на своєму прикладі і в розмовах з іншими компаніями. Плануємо масштабувати, покращувати якість по фідбекам, та дати агенту нові інструменти, наприклад доступ AI-агента до комп’ютера підтримки та користувача за допомогою Computer Use.

Якщо вам цікаво спробувати або є питання по реалізації — реєструйтесь на supportbot.info і пишіть мені в Telegram. Буду радий поспілкуватися.

👍ПодобаєтьсяСподобалось26
До обраногоВ обраному9
LinkedIn
Ctrl + Enter
Ctrl + Enter

Дуже крутий кейс та впровадження.

Pure genius, Pavel, hats off to you and your team!

Підписатись на коментарі