Роботи / Агенти і боти

AI-консультант для мережі магазинів техніки ТехноТочка: відповідає покупцям у чатах і не вигадує ціни

Що було

Покупці техніки пишуть у месенджер у будь-який час, а менеджерів уночі немає. Питання в чаті майже завжди про конкретику: чи є модель, скільки коштує доставка в моє місто, чи можна в розстрочку, де моє замовлення. Помилитися дорого: бот, який упевнено назвав не ту ціну, створює магазину проблему, а не економить час. Одна покупчиня із замовленням на 32 300 грн чекала відповіді 12 годин 34 хвилини і написала, що скасує його.

Що зробили

Поставили в чати магазину AI-консультанта на Claude. Він шукає по локальній базі каталогу tt.ua з фільтрами сайту, розпізнає фото товару, рахує доставку і занос за правилами магазину, питає 1С про статус замовлення і передає менеджеру готову заявку. Кожна відповідь перед відправкою проходить набір перевірок у коді: ціна без даних, «є в наявності», вигадані дії, місто доставки.

Що отримав клієнт

Бот відповідає покупцям щодня, вночі веде всі чати сам, а менеджера не перебиває. Чотири дні поспіль нуль вигаданих цін і характеристик при звірці з живим сайтом. 8 059 тестів зелені на кожній викладці, на кожну викладку тег відкату. Витрати на API Claude близько 56 $ на місяць.

ЗамовникТехноТочка, tt.ua, мережа магазинів побутової техніки, 5 філій
Моя рольАрхітектура, код, промпт, захист відповідей, інтеграції з SendPulse, 1С і Новою поштою, прилади і щоденні розбори діалогів
СтатусПрацює в чатах магазину, відповідає покупцям щодня
Результат8 059 тестів зелені на кожній викладці в прод
Відхилено: бот назвав 10 449 грн, на сайті було 14 900. Ціни в даних не було, шаблон у промпті вимагав форму «Назва - ЦІНА грн» і мовчав, що робити без ціни. Реконструкція за журналом розбору, не реальна переписка
Відхилено: бот назвав 10 449 грн, на сайті було 14 900. Ціни в даних не було, шаблон у промпті вимагав форму «Назва - ЦІНА грн» і мовчав, що робити без ціни. Реконструкція за журналом розбору, не реальна переписка
Прийнято: ціна тільки з даних магазину, а коли її немає, бот каже «ціну підтвердить менеджер». У коді окрема перевірка на ціну без джерела. Реконструкція за журналом розбору, не реальна переписка
Прийнято: ціна тільки з даних магазину, а коли її немає, бот каже «ціну підтвердить менеджер». У коді окрема перевірка на ціну без джерела. Реконструкція за журналом розбору, не реальна переписка

Завдання

ТехноТочка продає велику техніку: холодильники, пральні машини, плити, бойлери. Покупці пишуть у Telegram і Viber магазину, і питання майже завжди про конкретику. Габарити, кількість конфорок, доставка із заносом на поверх, розстрочка від банків, статус замовлення. У робочий час на це відповідають менеджери, вночі та у вихідні відповідати нема кому, а покупець, який не дочекався відповіді, йде в інший магазин.

Скільки саме людей чекає, ми поміряли ще до нічного правила. За 14 діб у чати прийшло 133 звернення поза робочим часом: 18 людей чекали понад годину, 3 не отримали відповіді взагалі. Один випадок розібрали окремо: покупчиня із замовленням на 32 300 грн чекала 12 годин 34 хвилини і написала, що скасує замовлення. Саме після цього заміру з’явилося правило: вночі всі чати веде бот.

Завдання звучало просто: зняти з менеджерів однакові питання і не втрачати людей поза робочим часом. Складність в іншому. Бот, який упевнено назвав не ту ціну або пообіцяв «є в наявності», створює магазину проблему. Тому головне було не навчити бота відповідати, а зробити так, щоб він не казав того, чого не знає.

Навіщо і яка ідея

Ідея в тому, що відповідь моделі не йде клієнту напряму. Між моделлю і чатом стоїть шар коду: локальна база каталогу з фільтрами сайту, правила доставки магазину, таблиця гарантій за брендами, запити до 1С. Модель складає відповідь з цих даних, а потім набір перевірок у коді дивиться на готовий текст. Ціна без джерела, твердження про наявність, вигадана дія на кшталт «я оформив», номер телефону, список не в тому форматі: усе це не проходить. Модель переписує відповідь із підказкою, а якщо не впоралась, клієнт отримує безпечний текст.

Друга частина ідеї: не чекати скарг. Щодня окремий прогін розбирає всі діалоги з реплікою бота і ставить кожному оцінку, тож класи помилок видно наступного ранку. Щодня за розкладом знімок фільтрів каталогу звіряється з живим сайтом. Для кожного приладу є число, яке можна перевірити, а не відчуття «ніби стало краще».

Кілька рішень приймав власник магазину, і вони зафіксовані в коді. Бот не каже ні «є», ні «немає» про наявність на складі, бо доступу до залишків у нього немає, і перевірка в коді симетрична. На пряме питання «ти бот?» відповідає чесно, що це AI-консультант. Номер телефону питає один раз і більше не повертається до цього.

Хід роботи

Етап Що відбувалося
Етап 1 Перша версія: Telegram-бот, парсинг сайту, SQLite, Claude Sonnet 4.5
Етап 2 Каскадний пошук по каталогу, розпізнавання фото, передача менеджеру, перші перевірки відповідей у коді. Третій консиліум про продаж бота іншим магазинам: поки ні, спочатку регресійний набір і автоматичні сигнали
Етап 3 Перехід із чистого вебхука SendPulse на їхній API. Прямий Telegram-бот вимкнено: за 14 діб через нього не прийшов жоден живий клієнт, через SendPulse ішло 328 повідомлень на добу
Етап 4 Аудит із 5 частин, 76 знахідок виправлено за день. Розбір Viber: ланцюжок у SendPulse не публікувався, правки до клієнтів не доїжджали
Етап 5 Перший регулярний аудит за своїм промптом із 13 кроків: 32 коміти і дві викладки за день, закрито 24 знахідки з 34. Бойова модель переключена на Sonnet 5 після заміру на 10 живих питаннях
Етап 6 Старт щоденного розбору живих діалогів. Нічне правило: поза робочим часом усі чати веде бот, але якщо менеджер писав останні 20 хвилин, бот мовчить
Етап 7 7 і 10 викладок за добу за живими кейсами. 1С почала віддавати номер накладної Нової пошти, перевірено на 50 живих замовленнях
Етап 8 Знайдено корінь розходження між історією в базі і тим, що бачив клієнт. Вилку розстрочки збирає код, а не модель. Набір тестів 8 059 з 8 059

Набір тестів ріс разом із проєктом: 3 766, потім 5 260, 7 154 і 8 059. На момент кожної викладки всі зелені. Викладка тільки з окремого клона, після зеленого набору і по слову власника, на кожну викладку тег відкату.

Чим робили

Канали: Telegram і Viber магазину підключені через SendPulse. SendPulse викликає наш вебхук, відповідь іде назад у чат. Окремий процес доганяє повідомлення, які до вебхука не дійшли, і шле відповіді через API SendPulse.

Моделі: відповіді клієнтам пише Claude Sonnet 5. Дешеві задачі на Claude Haiku 4.5: визначити намір клієнта, категорію товару, розібрати фото. Промпт складається з ядра з жорстким лімітом довжини і розділів, які підключаються за темою розмови: кредит, уцінка, акції, самовивіз. Ядро кешується, кеш прогрівається кожні 15 хвилин, за розрахунком це економить близько 31 $ на місяць.

Дані: локальна база каталогу tt.ua з фільтрами сайту (після правки бот знає 5 283 фільтри, покриття розділів сайту 90%), карта категорій, таблиця гарантій за брендами, правила доставки. Зберігання в SQLite: історія діалогів, окрема таблиця номерів клієнтів, журнал відправленого. Інтеграції: 1С магазину по HTTP для статусів замовлень, відкритий трекінг Нової пошти.

Де крутиться: Linux VPS, дві служби systemd, вебхук і доганяючий процес. Ключі в .env, не в коді. Перед push стоїть гачок, який шукає телефони, IBAN у логах маскується.

Як приймали рішення. На кожній великій розвилці п’ять AI-радників (скептик, архітектор, візіонер, людина збоку, прагматик) незалежно розбирають ситуацію, рецензують один одного, голова зводить вердикт. На одному з аудитів перехресна рецензія спростувала 16 тверджень самого аудитора. Перед викладкою код-ревю: одного дня два ревю знайшли 11 проблем у правках, іншого ревю знайшло 3 справжні помилки в правці словника скарг, і вона не пішла в прод. Усі правила лежать у реєстрі «не повторювати», нумерація дійшла до 390 плюс 262 ранніх без номерів. Перед кожною правкою бота реєстр відкривається першим.

Результат

Клієнт пише звичайними словами, українською чи російською: «холодильник Samsung до 15 тисяч», «плита 50 на 50», «чорний бойлер, не білий». Бот шукає по базі каталогу з фільтрами сайту, показує позиції з посиланнями і цінами з картки, пропонує аналог, якщо потрібної моделі немає. Присилає скріншот товару: бот розпізнає модель і шукає її в каталозі, за замір у два тижні розібрано 32 фото з 34.

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

Вночі всі чати веде бот, навіть закріплені за менеджером, але живу людину не перебиває. Вікно в 20 хвилин вибране за заміром: з 20:00 до 22:00 менеджери ще пишуть, з 23:00 до 07:00 нуль. Правило виросло із заміру за два тижні до нього: зі 133 нічних звернень 112 отримали відповідь протягом години, 18 чекали довше, 3 так і не дочекалися. Правило з’явилося саме заради цих 21.

Що показали прилади. Весь живий трафік іде через SendPulse: 328 повідомлень на добу проти нуля через прямий Telegram-бот за ті самі 14 діб, тому прямого бота вимкнули і лишили один канал. Чотири дні поспіль нуль вигаданих цін і характеристик при звірці з живим сайтом, по 7-10 цін на день. Прилад звірки історії з відправленим після правки: 41 відправлено, 41 в історії, розходжень 0. Витрати на API Claude за замір у 14 діб 26,25 $, тобто близько 56 $ на місяць, одна відповідь клієнту близько 0,025 $. Перехід на Sonnet 5 на 10 живих питаннях дав економію 21%.

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

Помилки і що винесли

Захист стояв на одних дверях із двох.У бота два шляхи назовні: тіло відповіді на вебхук і відправка через API. Сітка перевірок стояла тільки на одному, і відповіді про ПДВ, пільги, неробочий час ішли клієнту без перевірок. Сторож був зелений, бо перевіряв саму сітку, а не те, що її викликають. Тепер захист перевіряємо на кожному виході, а сторож має довести виклик, а не наявність коду.
Бот дивився на мініатюру 41x90 замість фото 589x1280.Клієнтка прислала скріншот, бот відповів про інший Kenwood. У коді повний кадр запитувався з API тільки якщо у вебхуку не було посилання, а за посиланням SendPulse віддає мініатюру в 1 146 байт. За логами 10 фото йшли через мініатюру, 19 через API. Перевіряти треба, що саме бачить модель, а не що «картинка прийшла».
Своя перевірка зарізала правильну ціну і зірвала продаж.Одного дня перевірка ціни шість разів вирізала нашу ж комісію післяплати 20 грн, двічі не впоралась і покликала менеджера. Покупчиня скасувала замовлення на 14 299 грн. Це був четвертий випадок того самого роду: рішення «порівнювати з числами з конфігу магазину» було записане раніше, але не зроблене. Перевірка не може не знати про дані магазину, а хибні спрацьовування тепер окремий прилад на кожен день.
Хибна тривога «каталог недоступний».Я заявив, що сайт віддає 403 і бот не бачить жодного товару, і власнику запропонували просити магазин про білий список. Аварії не було: бот шукає по локальній базі. Перевіряв доступність адреси сторонньою утилітою замість самої функції бота, а падіння 4 тестів прийняв за доказ, хоча це був вичерпаний ліміт після моїх же прогонів. Перевіряти функцію, якою користується бот, а тривогу міряти за її результатом.
Гроші на API скінчилися вночі, бот стояв 12 годин.Сторож балансу спрацював через 3 хвилини, але писав тільки у файл на сервері. Два клієнти отримали аварійний текст. Тривога, яку ніхто не бачить, не тривога. Плюс гілка повторів витрачала 3 спроби на помилку балансу, яку повтор не лікує.
Історія в базі не збігалася з тим, що бачив клієнт.Функція збереження підміняла список у кеші, і відповідь дописувалась у «мертвий» список. Клас спливав у щоденних розборах чотири рази, перша правка не допомогла. Після знайденого кореня прилад звірки показує 0 розходжень. Якщо правка не прибрала симптом, корінь не знайдений, і це не привід для другої латки.
Тести писали в бойову базу.Із 569 записів про спрацьовування перевірок 535 виявились нашим тестовим трафіком, живих було близько 34. Одного дня половина діалогів вікна, 22 з 44, теж були нашими прогонами. Тести тільки з окремою базою, інакше прилад міряє власний слід.

Інструменти

PythonClaude Sonnet 5Claude Haiku 4.5Anthropic APISendPulseSQLite1СНова поштаsystemdClaude Code