Бот отвечает на вопросы о продажах цифрами: шесть метрик, и расчёты перепроверили ИИ-агенты
Владелец бренда товаров для активного отдыха из США спросил бота в Telegram, сколько продали по странам за два года, и цифр не получил. Теперь бот отвечает живыми числами из двух магазинов на Shopify: страны, годы, топ товаров по деньгам и по штукам, штаты США по каналам продаж. Все расчёты делает один общий модуль — ядро метрик. Через час после выпуска четыре ИИ-агента проверили код и нашли в расчётах четыре ошибки.
Бот был подключён к Shopify и всё равно отвечал без цифр
Бренд товаров для активного отдыха из США, два магазина на Shopify. Владелец задаёт вопросы боту в мессенджере между делами. К этому моменту у бота уже была база знаний по переписке команды. Был и небольшой модуль, который брал живые данные из магазина. Он умел считать две метрики: выручку за период и соотношение основных товаров и аксессуаров.
В августе владелец спросил: «сколько продали в 2025 и 2026 годах и в какие страны, кроме США и Канады». Ответ пришёл из базы знаний — текстом, без единой цифры. Я проверил: доступ к магазину работал, «выручка за 30 дней» считалась сразу. Географии среди метрик просто не было. Причина лежала глубже: модуль отчётов вообще не запрашивал у Shopify адрес доставки. Страны в данных не было, хотя Shopify её отдаёт.
На той же неделе нашлась вторая ошибка. На вопрос «какой топ товаров по выручке» бот отвечал общей выручкой магазина: слово «выручка» срабатывало раньше. Ответ звучал уверенно, но относился к другому вопросу. Такой ответ опасен: владелец ему верит и принимает по нему решения.
Шесть метрик, один общий расчёт: числа считает код
Бот определяет вопрос по ключевым словам и выбирает подходящую метрику. Считает код — тем же ядром, которое собирает официальный отчёт по магазинам. Каждая цифра посчитана по строгим правилам; если посчитать нельзя, цифры в ответе нет. Нераспознанный вопрос, как и раньше, уходит в базу знаний.
Владелец пишет боту по-русски или по-английски: «топ товаров за 30 дней», «продажи по штатам за 2026»
Ключевые слова и строгий порядок проверок: штаты раньше стран, топ раньше выручки, годы раньше дней
Отдельные модули для стран, топа товаров и штатов по каналам. Период, статусы и возвраты они берут у ядра
Единое правило, какой заказ считать, возвраты по каждой позиции, распределение скидок, часовой пояс магазина. Одна арифметика для бота и отчёта
Числа показываются в деньгах и штуках. Вопрос, на который не нашлось цифры, попадает в журнал без имени и номера чата
Почему языковая модель не считает цифры
Эта часть бота ценна тем, что его цифрам верят. Модель со свободным доступом к данным рано или поздно выдаёт правдоподобное число из воздуха. Один такой случай в переписке с владельцем подорвёт доверие ко всем остальным отчётам. Поэтому решение принимает код: ключевые слова на двух языках, фиксированный набор метрик, вызов ядра.
Если вопрос не распознан, модуль метрик молчит, и бот отвечает из базы знаний. Я сознательно выбрал точность: бот называет цифру только там, где её можно проверить.
Чтобы метрики добавлялись по реальному спросу, вопросы, на которые не нашлось цифры, складываются в журнал. Там хранится только текст вопроса, очищенный от секретов, без имени, логина и номера чата. Сводка за две недели показывает, чего боту не хватает.
Бот различает три вида сбоев
Если модуля метрик нет, бот отвечает из базы знаний. Если модуль есть, но Shopify недоступен, бот прямо пишет: «не смог достать живые данные». Если модуль и ядро перестали понимать друг друга, потому что формат данных разошёлся, пользователь тоже видит сообщение. Тихий сбой однажды уже погубил функцию: модуль ждал данные в формате, которого на деле не было, молча уходил в базу знаний и выглядел рабочим. Ошибка, которую никто не видит, может жить годами.
География: заказ без адреса остаётся в общем итоге
Страна берётся из адреса доставки — туда на самом деле везли товар. Заказы без адреса (цифровые товары, самовывоз) входят в общий итог, но ни одной стране не приписываются. Иначе они незаметно попали бы в США и исказили картину. Годы бот распознаёт раньше дней, поэтому «за 2025» даёт календарный год, а «кроме США и Канады» убирает две страны из ответа.
Теперь бот отвечает ровно на то, что спросил владелец: заказы, выручка и число стран по каждому году, отдельной строкой — продажи за рубеж. В 2025 году заказы ушли в 21 страну, 19 из них — за пределами США и Канады.
Топ товаров: деньги и штуки рядом
В этом бизнесе две метрики сильно расходятся: аксессуары дают около 22% денег и около 63% штук. Одна колонка подтолкнула бы к неверному выводу об ассортименте. Поэтому у каждой позиции показаны обе доли, а отдельный блок называет товары, которых продают много, хотя в денежный топ они не входят.
Товары группируются по идентификатору из каталога. Название у одного товара бывает разным, а артикул пуст примерно у 87% позиций. Проверку топа я поставил раньше проверки выручки — это первое из двух исправлений порядка.
Штаты США по каналам продаж
Владелец попросил чистые продажи по штатам, общие и отдельно по маркетплейсу, за вычетом возвратов и скидок. Чистые продажи здесь — позиции заказа после скидок минус возвраты, без доставки и налогов. Ядро сверяет по тому же правилу, поэтому сумма по штатам обязана сходиться с общим отчётом.
Канала продажи в данных ядра раньше не было. Я добавил в запрос источник заказа и теги и сбросил старый кэш. Владелец получает таблицу CSV и ответ бота, собранные из одних и тех же чисел.
Вопрос «по штатам США» содержит слово «США», и при неправильном порядке бот уводил его в разбивку по странам. Второе исправление порядка: штаты проверяются раньше стран.
Все расчёты в одном модуле, остальные только собирают данные
Разбор вопроса, модули-сборщики, ядро метрик и отправка ответа — всё на одном языке
Заказы с позициями, возвратами, адресом доставки, источником и тегами канала. Доступ только на чтение
Единое правило, какой заказ считать, возвраты по позициям, распределение скидок, период и часовой пояс магазина
Русский и английский, строгий порядок проверок: штаты → страны, топ → выручка, годы → дни
Вопрос и ответ в личных сообщениях. Тот же бот отвечает по базе знаний
Вопросы, на которые не нашлось цифры: текст без секретов и без данных о человеке. Сводка за 14 дней
Четыре отдельные проверки свежего кода через час после выпуска; пять проверок с разных сторон перед отчётом по штатам
Сверка возвратов с итоговым платежом Shopify: продажи за рубеж сошлись до цента, итог года — в пределах 0,1%
5 защитных тестов на расчёты, 27 на разбивку по штатам, полный прогон — 1312
Пять модулей на Python — разбор вопроса, сборщики стран, топа товаров и штатов, журнал вопросов — это около 1,2 тыс. строк и ещё около 600 строк тестов. Каждый сборщик умещается в 150–300 строк, потому что расчёты он берёт у ядра.
Четыре агента через час после выпуска, пять проверок с разных сторон перед отчётом по штатам. Сверка на живых данных за 30 дней: 324 заказа во всех трёх отчётах, расхождение по товарной выручке — ноль. Полный прогон: 1312 тестов пройдено.
Четыре ошибки через час после выпуска и ноль расхождений после исправлений
добавились страны, годы, топ по деньгам и по штукам, штаты по каналам
сверка на живых данных за 30 дней: 324 заказа во всех трёх отчётах
заказы со скидкой 100% за два года: подарки отделены от продаж
Через час после выпуска: четыре агента, четыре ошибки
Свежий код проверили четыре ИИ-агента, каждый отдельно. Что они нашли:
- Топ товаров вообще не вычитал возвраты, и товарная выручка была завышена на 8,54%.
- У модуля-сборщика был свой список статусов оплаты, и два статуса ядра в него не попали. Отсюда 324 заказа в отчёте против 323 у метрик.
- Группировка шла по полю «тип товара», которого в позициях заказа нет: тип хранится в каталоге. Колонка типа всегда была пустой.
- География считала возвратом отмену предварительной авторизации платежа. Таких сумм тогда не было, и ошибка проявилась бы при первой отменённой авторизации.
У всех четырёх ошибок одна причина: сборщик считал сам. Теперь все расчёты идут через ядро: одно правило, какой заказ считать, возвраты по позициям, распределение скидок. География больше не делает собственный запрос: адрес доставки добавлен в данные ядра. Кэш пришлось сбросить, иначе шесть часов читались бы старые данные без этого поля. Сверка после исправлений на живых данных за 30 дней: 324 заказа в отчёте, географии и топе, расхождение по товарной выручке — 0,00.
Пять тестов закрепляют исправления
Цифру, посчитанную почти правильно, глазами не поймать. Поэтому совпадение с ядром закреплено тестами. География и топ считают ровно те же заказы, что ядро. Полностью возвращённый заказ не добавляет выручки. Отмена авторизации возвратом не считается. Отдельный тест разбирает исходный код и проверяет, что ни у одного сборщика нет собственного отбора по статусу оплаты. Если такой отбор появится, тесты при сборке не пройдут.
Перед отчётом по штатам: ещё четыре находки
Отчёт по штатам владелец попросил вечером и получил в тот же вечер. Перед отправкой цифры проверили пять раз, каждый раз с другой стороны:
- Черновик и маркетплейс — разные признаки. Фраза «заказы маркетплейса помечаются черновиками» подталкивала к отбору по черновику. Я проверил на данных за семь недель. В одном магазине из 106 черновиков к маркетплейсу относился 81. Во втором было 12 черновиков и ни одного заказа маркетплейса. Отбор по черновику приписал бы маркетплейсу 25 чужих заказов и придумал бы 12 там, где маркетплейса нет. Канал определяется тегом заказа, черновики показаны отдельной контрольной строкой.
- Подарки блогерам считались продажами. 264 заказа за два года со скидкой 100%, выставленной вручную: денег не поступало, но статус стоял «оплачен», и заказ попадал в счётчик каждого штата и каждой страны. Теперь оплаченные и бесплатные заказы разделены. Подарок, который с самого начала стоил ноль, отличается от продажи, которая обнулилась после возврата.
- Возврат хранится в Shopify в двух видах, и ни один не покрывает всё. Учитывался один — годовая выручка была завышена примерно на 2%. Второй вид исправлял США и Канаду, но ломал продажи за рубеж: там в платёж входят доставка и налог. Решение — по каждому заказу отдельно: есть платёж — берётся он, нет — позиции. Контрольный запрос подтвердил: продажи за рубеж сошлись до цента за оба года, итог года — в пределах 0,1%.
- Подпись «полный год» стояла на 7,6 месяцах. Shopify без ошибки принимает дату окончания в будущем. Рядом с полным прошлым годом рост на треть выглядел как падение на 17%. Теперь текущий год заканчивается сегодняшней датой.
27 новых тестов закрепляют ровно эти находки; полный прогон — 1312 пройденных тестов.
Что изменилось в работе
Владелец задаёт вопрос про продажи в мессенджере и получает живые числа из двух магазинов: страны, годы, топ по деньгам и по штукам, штаты и каналы. Отчёт по штатам ушёл ему в CSV и в ответе бота, и оба собраны из одних и тех же чисел. Вопросы без цифры копятся в журнале, и новые метрики добавляются по реальному спросу.
Где ещё работает тот же подход
За кейсом стоит типовая проблема: «три отчёта дают три разные выручки». Она возникает везде, где одну и ту же цифру считают в нескольких местах: в боте, на странице аналитики, в выгрузке для бухгалтера.
- → Интернет-магазины на любой платформе: владелец спрашивает в мессенджере и получает цифру, посчитанную тем же кодом, что и официальный отчёт
- → Продажи в нескольких каналах и на маркетплейсах: канал определяется признаком, проверенным на данных; черновики и подарки отделены от продаж
- → ИИ-помощники с доступом к учётной системе: модель формулирует ответ и ищет по документам, цифры считает код; нераспознанный вопрос попадает в журнал
- → Сводки для руководителя: возвраты, отмены и скидки вычитаются в одном месте, подпись периода совпадает с данными
- → Команды с несколькими отчётами: защитные тесты держат все модули на одном ядре, и расхождение ловится при сборке
Какой канал приносит заказы, показывает сквозная атрибуция: она следит за путём от клика до заказа. Здесь другой вопрос: сколько продали, кому и где, и можно ли этой цифре верить.
- Разбор вопроса по ключевым словам со строгим порядком проверок и тестами, которые ловят, если вопрос ушёл не в ту метрику
- Единое ядро метрик: правило, какой заказ считать, возвраты, скидки и период в одном месте; сборщики только складывают
- Защитный тест, который по исходному коду запрещает сборщику заводить свой отбор по статусу
- Проверка кода отдельными агентами: каждый ищет ошибки в свежем коде, пока цифры не ушли заказчику
- Журнал вопросов без ответа как план следующих метрик
Три отчёта дают три разные выручки? Их можно свести к одному расчёту
Начать стоит с одного ядра метрик и одной сверки с официальным отчётом до цента. Бот с живыми цифрами появляется, когда расчёты сошлись, и каждая следующая метрика строится на том же ядре.
Похожие кейсы
ИИ-помощник по базе знаний компании в Telegram
Бот отвечает по рабочим чатам, письмам, голосовым и документам и к каждому ответу прикладывает цитаты из…
Автономные боты: как сделать их надёжными и честно проверять на истории
Десяток сервисов принимает решения по потоку внешних событий. Данные приходят по WebSocket в момент события…
Избирательная маршрутизация трафика через собственный сервер
Часть внешних сервисов идёт через мой арендованный сервер, остальной интернет — напрямую. Маршруты заданы по…
Начните с аудита за 5 000 ₽: письменный отчёт и расчёт окупаемости
Расскажу, что автоматизировать в первую очередь, когда это окупится и нужен ли вашей задаче ИИ вообще. Иногда он не нужен.
Или просто задайте вопрос. В рабочее время отвечаю в течение 2 часов