Бот отвечает цифрами по продажам: шесть метрик и состязательная проверка арифметики
Владелец бренда товаров для активного отдыха из США спросил бота в Telegram про продажи по странам за два года и цифр не получил. Теперь бот отвечает живыми числами из двух магазинов на Shopify: страны, годовые окна, топ товаров по деньгам и по штукам, штаты США в разрезе каналов. Вся арифметика идёт через единое ядро метрик. Через час после выпуска четыре независимых агента-проверяющих нашли четыре ошибки — каждая меняла смысл ответа.
Живой доступ к платформе был, цифр в ответе не было
Бренд товаров для активного отдыха из США, два магазина на Shopify, владелец, который задаёт вопросы боту в мессенджере между делами. К этому моменту у бота уже была база знаний по переписке команды и тонкий живой слой поверх торговой платформы с двумя метриками: выручка за период и соотношение основных товаров и аксессуаров.
В августе владелец спросил: «сколько продали в 2025 и 2026 годах и в какие страны, кроме США и Канады». Ответ пришёл из базы знаний — текстом, без единой цифры. Проверка показала, что живой доступ к платформе работает: «выручка за 30 дней» отвечалась сразу. Географии в наборе метрик просто не существовало. Корень лежал глубже: отчётный модуль вообще не запрашивал у платформы адрес доставки. Страны в наших данных не было в принципе, хотя API её отдаёт.
Вторая находка той же недели: на вопрос «какой топ товаров по выручке» бот отвечал общей выручкой магазина — слово «выручка» перехватывало намерение раньше. Ответ выглядел уверенно и отвечал на другой вопрос. Такой ответ опаснее молчания: владелец принимает его за ответ и идёт с ним принимать решение.
Шесть метрик через одно ядро — языковая модель вне контура чисел
Вопрос из мессенджера распознаётся по ключевым словам, попадает в фиксированный набор метрик и считается кодом через единое ядро аналитики — то же, которым собирается официальный отчёт по магазинам. Цифра либо посчитана детерминированно, либо её нет: нераспознанный вопрос уходит в базу знаний, как и раньше.
Владелец пишет боту по-русски или по-английски: «топ товаров за 30 дней», «продажи по штатам за 2026»
Ключевые слова и явный порядок проверок: штаты раньше стран, топ раньше выручки, годы раньше дней
Отдельные модули для стран, топа товаров и штатов в разрезе каналов; окно, статусы и возвраты они берут у ядра
Единый фильтр считаемого заказа, возвраты построчно, скидки по распределениям, часовой пояс магазина — одна арифметика на бота и отчёт
Числа печатаются деньгами и штуками; вопрос, оставшийся без живой цифры, попадает в журнал без имени и идентификатора чата
Почему в контуре чисел нет языковой модели
Ценность всего контура в том, что числам верят. Модель со свободным доступом к API рано или поздно выдаёт правдоподобное число из воздуха, и один такой случай в переписке с владельцем обнуляет доверие ко всем остальным отчётам. Поэтому решение принимает код: ключевые слова на двух языках, фиксированный набор метрик, вызов ядра. На нераспознанный вопрос слой возвращает пустой результат, и бот отвечает из базы знаний. Молчание здесь честнее догадки: полнота осознанно принесена в жертву точности.
Чтобы набор метрик рос по реальному спросу, числовые вопросы без живой цифры складываются в журнал: только текст вопроса после вычистки секретов, без имени, логина и идентификатора чата. Сводка за две недели показывает, чего боту не хватает.
Три вида отказа различаются осознанно
Модуля нет — слой отключён, бот отвечает из базы знаний. Модуль есть, платформа недоступна — честное «не смог достать живые данные». Контракт с ядром разошёлся — тоже видимое сообщение. Тихий отказ здесь уже один раз убил функцию: слой писался против выдуманного контракта, молча уходил в базу знаний и выглядел рабочим. Видимая ошибка чинится, невидимая живёт годами.
География: заказ без адреса остаётся в итоге
Страна берётся из адреса доставки — туда реально везли товар. Заказы без адреса (цифровые товары, самовывоз) входят в общий итог и ни одной стране не приписываются: иначе они молча утекли бы в США и исказили картину. Годы распознаются раньше дней, поэтому «за 2025» даёт календарный год; «кроме США и Канады» вычитает две страны из выдачи. Вопрос владельца теперь отвечается дословно: заказы, выручка и число стран по каждому году, отдельной строкой — заграница. В 2025 году заказы уехали в 21 страну, 19 из них — за пределами США и Канады.
Топ товаров: деньги и штуки печатаются вместе
На этом бизнесе две метрики расходятся: аксессуары дают около 22% денег и около 63% штук. Одна колонка подтолкнула бы к ложному выводу по ассортименту, поэтому у каждой позиции печатаются обе доли плюс отдельный блок «много штук, мимо денежного топа». Ключ агрегации — идентификатор товара из каталога: название у одного товара плавает, артикул пуст примерно у 87% строк позиций. Проверка топа переставлена раньше проверки выручки — первое из двух исправлений порядка распознавания.
Штаты США в разрезе каналов продаж
Просьба владельца звучала так: чистые продажи по штатам, общие и по маркетплейсу, за вычетом возвратов и скидок. База «чистых» — позиции заказа после скидок минус возвраты по позициям, без доставки и налогов. Этот же инвариант сверяет ядро, поэтому сумма по штатам обязана сходиться с общим отчётом. Канал продажи в выборке ядра до этого отсутствовал: в поля запроса добавлены источник заказа и теги, версия кэша поднята. Владелец получает CSV и ответ бота, собранные из одних чисел.
Вопрос «по штатам США» содержит слово «США» и без приоритета уезжал в разрез по странам — второе исправление порядка: штаты проверяются раньше географии.
Арифметика живёт в одном месте, сборщики только агрегируют
Распознавание намерения, сборщики, ядро метрик и доставка ответа — один язык на весь контур
Заказы с позициями, возвратами, адресом доставки, источником и тегами канала; доступ только на чтение
Единый фильтр считаемого заказа, возвраты построчно, скидки по распределениям, окно и часовой пояс магазина
Русский и английский, явный порядок приоритетов: штаты → страны, топ → выручка, годы → дни
Вопрос и ответ в личных сообщениях; тот же бот отвечает по базе знаний
Числовые вопросы без живой цифры, текст после вычистки секретов, без личности; сводка за 14 дней
Четыре независимых разбора свежего кода через час после выпуска; пять линз перед отчётом по штатам
Сверка возвратов с чистым платежом платформы: заграница до цента, итог года в пределах 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.
Пять защитных тестов поверх находок
Цифра, посчитанная почти так же, выглядит нормально и глазами не ловится. Поэтому сверка с единым источником прибита тестами: география и топ считают ровно те же заказы, что ядро; полностью возвращённый заказ не добавляет выручки; отмена авторизации возвратом не считается; разбором исходного кода проверяется, что ни у одного сборщика нет собственного фильтра по статусу оплаты. Появится второй фильтр — упадёт CI.
Перед отчётом по штатам: ещё четыре находки
Отчёт по штатам владелец попросил вечером и получил в тот же вечер. Перед отправкой цифры прошли проверку пятью независимыми линзами:
- Черновик и маркетплейс — разные признаки. Формулировка «заказы маркетплейса помечаются черновиками» напрашивалась на фильтр по черновику. Замер за семь недель: в одном магазине из 106 черновиков к маркетплейсу относился 81, во втором — 12 черновиков и ни одного заказа маркетплейса. Фильтр по черновику приписал бы маркетплейсу 25 чужих заказов и выдумал бы 12 там, где его нет. Канал определяется тегом заказа, черновик показывается отдельной контрольной строкой.
- Раздачи блогерам сидели в продажах. 264 заказа за два года с ручной скидкой 100%: платёж не принимался, статус стоял «оплачен», и фильтр считаемого заказа их пропускал — в счётчик каждого штата и каждой страны. Теперь оплаченные и бесплатные разделены; подарок с нулём с рождения отличается от продажи с нулём после возврата.
- Возврат живёт в платформе в двух представлениях, ни одно не покрывает всё. Учитывалось одно — годовая выручка завышалась примерно на 2%. Замена на второе чинила США и Канаду и ломала заграницу, где в транзакцию входят доставка и налог. Решение позаказное: есть транзакция — берётся она, нет — позиции. Сверка с контрольным запросом: заграница сошлась до цента за оба года, итог года — в пределах 0,1%.
- Подпись «полный год» стояла на 7,6 месяцах. Платформа принимает будущую границу окна без ошибки, и рядом с полным прошлым годом рост на треть читался как падение на 17%. Конец текущего года теперь прижимается к сегодняшней дате.
27 новых тестов закрепляют ровно эти находки; полный прогон — 1312 зелёных.
Что изменилось в работе
Вопрос про продажи задаётся боту в мессенджере и возвращается живыми числами из двух магазинов: страны, годы, топ по деньгам и по штукам, штаты и каналы. Отчёт по штатам ушёл владельцу в CSV и в ответе бота из одних и тех же чисел. Вопросы без цифры копятся в журнале, и следующие метрики добавляются по реальному спросу.
Где ещё ложится та же методология
За кейсом стоит типовая боль «три отчёта дают три разные выручки». Она есть везде, где одну и ту же цифру считают в нескольких местах: в боте, на странице аналитики, в выгрузке для бухгалтера.
- → Интернет-магазины на любой платформе — вопрос владельца в мессенджере отвечается цифрой из API, посчитанной тем же кодом, что и официальный отчёт
- → Мультиканальные продажи и маркетплейсы — канал определяется признаком, проверенным замером; черновики и раздачи отделены от продаж
- → AI-помощники с доступом к учётной системе — модель формулирует и ищет по документам, арифметику считает код; нераспознанный вопрос попадает в журнал спроса
- → Сводки для руководителя — возвраты, отмены и скидки вычитаются в одном месте, подпись периода соответствует данным внутри
- → Команды с несколькими отчётами — защитные тесты держат все сборщики на едином ядре, расхождение ловится в CI
Какой канал приносит заказы — вопрос сквозной атрибуции, она живёт вокруг пути клика. Здесь вопрос другой: сколько продали, кому и где, и можно ли этой цифре верить.
- Распознавание намерения по ключевым словам с явным порядком приоритетов и тестами на перехват
- Единое ядро метрик: фильтр считаемого заказа, возвраты, скидки и окно в одном месте; сборщики только агрегируют
- Защитный тест, который разбором исходного кода запрещает сборщику заводить свой фильтр по статусу
- Состязательная проверка свежего кода несколькими независимыми агентами до отправки цифр заказчику
- Журнал вопросов без ответа как план по следующим метрикам
Если три отчёта дают три разные выручки и никто не знает, какой верить — это решается
Начинаем с одного ядра метрик и одной сверки с официальным отчётом до цента. Бот с живыми цифрами появляется после того, как арифметика сошлась, и каждая следующая метрика встаёт на то же ядро.
Похожие кейсы
AI-помощник по внутренней базе знаний (RAG) в Telegram
BM25 → SQLite FTS5 со своим ранжированием, расшифровка голосовых, OCR, уровни доступа и MCP-сервер поверх…
Автономные боты и проверка на истории: инженерия надёжности
Десяток сервисов, принимающих решения по потоку внешних событий. Данные приходят по WebSocket в момент…
Избирательная маршрутизация трафика через собственный сервер
Статические маршруты по AS-диапазонам, точечные DNS-привязки, настройка автоматизирована через HTTP API…
Аудит за 9 900 ₽ — с конкретным отчётом и сметой
Расскажу что внедрить в вашем бизнесе в первую очередь, какая будет окупаемость, и нужен ли вообще AI для вашей задачи (иногда — нет).
Или просто напишите свой вопрос — отвечу в течение 2 часов