Как мы построили ИИ‑аналитику, которая не галлюцинирует

от автора

Я руковожу командой аналитиков. Мы работаем с данными брендов на маркетплейсах. Начну не с технологии, а с проблемы, из которой всё выросло.

Каждый день мы разбираем, где у селлера утекают деньги. С данными у нас все было в порядке. Витрины были нормальные, дашборды наглядные, цифры под рукой. Дело было не в данных, а в людях. Дашборд показывает, что произошло, а разобраться, что с этим делать, должен уже аналитик.

Тут я и заметила неприятную вещь. Один и тот же дашборд три моих аналитика читают по‑разному. Дело не в том, что кто‑то работает хуже. Просто у каждого своя логика, свой порядок проверки, своя глубина разбора.

Один аналитик формулирует вывод точно. Он объясняет, что реклама третью неделю льется в карточку с нулевым стоком, и называет сумму утечки. Другой ограничивается общей фразой о том, что выручка просела и команда разбирается. Кто‑то находит проблему за десять минут, кто‑то за полдня, а кто‑то не находит вовсе. Качество разбора зависело от того, кто сегодня сидит за дашбордом.

Хуже всего то, что весь этот анализ живет в голове одного человека. Его нельзя проверить, нельзя передать другому, нельзя масштабировать. Уйдет сильный аналитик, и вся экспертиза уйдет вместе с ним. Настоящей болью оказалась не нехватка данных, а неравномерность и невоспроизводимость анализа.

Мне захотелось систематизировать процесс, чтобы качество разбора не зависело от того, кто именно его делает. Сейчас все советуют один и тот же ход. Возьми выгрузку, загрузи ее в ChatGPT, попроси найти, где утекают деньги, и модель сама все посчитает и объяснит.

Я попробовала. Не сработало. Дело было не в слабой модели и не в том, что нужен GPT помощнее. Причина оказалась архитектурной.

Разбор этой поломки привел меня к схеме, ради которой я и пишу эту статью. Схема отвечает на два вопроса. Первый вопрос про то, где должна проходить граница между детерминированным кодом и языковой моделью. Второй вопрос про то, почему расчеты нельзя отдавать LLM.

Почему выгрузка в ChatGPT не работает

Я поймала четыре причины на реальных выгрузках. Все они воспроизводимы.

Модель не знает бизнес‑логику. Она не в курсе, какую выручку брать в расчет, какие расходы учитывать, что такое склейка и почему ДРР по артикулу и по склейке это разные показатели. Правильный расчет держится на доменных правилах, а модель их из общих соображений не выведет. В нашей практике накопилось четыре таких примера:

  • процент возвратов считается в штуках, а не в рублях, потому что в рублях он врет из‑за разной цены возвращенных и проданных позиций;

  • ДРР считается от суммы заказов, а не от выручки, это отдельное бизнес‑определение, и оно отличается от простой формулы «расходы делить на выручку»;

  • эквайринг нельзя складывать с комиссией, потому что он уже сидит внутри комиссии, а сложение дает двойной счет.

СПП, скидка покупателя, не расход продавца, ее компенсирует площадка, и включать ее в расходы маркетплейса нельзя.

Ни одно из этих правил модель не подставит сама. Их зашивают в код и проверяют на сходимость до рубля. Контекст задает человек, а не угадывает LLM.

Большой отчет молча обрезается. Выгрузки на маркетплейсах это миллионы строк, а окно модели ограничено. Модель не скажет «я взял только половину», она просто ответит по куску, который влез в контекст. Аналитик при этом решит, что получил вывод по всему отчету.

Ответ зависит от формулировки вопроса. Другой аналитик, другая формулировка того же вопроса, другой предыдущий контекст в чате, и результат уже другой. Для генерации текста это нормально, а для отчетности, на которую опираются в решениях, это недопустимо. По сути, это та же беда, с которой я начинала. Непредсказуемость разбора никуда не делась, она просто переехала из головы аналитика в чат с моделью.

Четвертая причина в невоспроизводимости. Тот же вопрос завтра дает другую цифру. На отчет, который нельзя повторить, нельзя и опереться.

Поверх всего этого модель почти никогда не говорит «мне не хватает данных» или «здесь противоречие». Она уверенно заполняет пробел правдоподобной догадкой. Быстрый неверный отчет опаснее медленного верного, потому что по нему решение принимают раньше, чем успевают проверить.

Где проходит граница между кодом и моделью

Вывод для меня был не в том, чтобы отказаться от ИИ, а в том, чтобы поставить его на одно узкое место конвейера, объяснение результата. Все остальное делает код. Схема строится из пяти шагов, и порядок здесь важен.

Шаг 0. Данные. Это собранная, структурированная база, сверенная с личными кабинетами продавца. Сверка здесь не про то, что «данные похожи», а про совпадение итоговой суммы, которую посчитал код, с фактической выплатой кабинета. Отчеты площадок с разными моделями выплат приводятся к единой методологии P&L, а расхождение по SKU помечается как аномалия, а не сглаживается. Это фундамент и примерно 90% всей системы. Без базы дальше идти незачем, коду не из чего считать, а модели нечего объяснять.

Большинство историй «внедрили ИИ, эффекта нет» это провалы нулевого шага, а не модели. По отраслевым оценкам около 80% времени аналитика уходит не на анализ, а на подготовку данных. Пока это так, любой ИИ поверх работает на мусоре.

Шаг 1. Правила. Аналитик задает бизнес‑логику. Он определяет, как считать показатели, где искать потери и потенциал для роста, какие ограничения есть у каждой площадки. Здесь же живет знание про склейки, СПП, комиссии и возвраты. Именно на этом шаге решилась моя исходная боль. Логику разбора мы формализовали один раз, и дальше она применяется одинаково, а не по‑разному у каждого аналитика.

Шаг 2. Расчет. Python считает по заданным правилам. Один и тот же вопрос всегда дает один и тот же ответ, потому что расчет детерминированный. На этом же шаге проходит проверка на аномалии, агрегация и сведение показателей по SKU, причём средневзвешенно, а не средним арифметическим.

Шаг 3. Объяснение. Только теперь подключается LLM, в нашем случае облачная модель через Claude API. На вход она получает не сырые строки, а компактный срез. В срезе уже посчитанные кодом цифры и найденные сигналы. Модель переводит их на человеческий язык и отвечает на три вопроса. Что важно прямо сейчас, почему это важно и что с этим делать.

Пересчитывать что‑либо самой ей запрещено промптом, да и попросту нечем, сырых данных на входе у нее нет. Поэтому и галлюцинировать по числам не на чем, они приходят уже готовыми.

Технически это устроено так. Модель не ходит в базу произвольным SQL, потому что это риск и инъекций, и тяжелого запроса вида SELECT *. Ей доступен закрытый реестр из полутора десятков типизированных инструментов, по сути параметрических запросов, каждый к одной витрине с фиксированными фильтрами. Модель выбирает инструмент и бизнес‑параметры, например период или сравнение с прошлым, но не знает имен таблиц, не пишет WHERE и не ставит LIMIT. Все, где можно ошибиться, вынесено в детерминированный код, а модели остается только выбор «что посмотреть» и объяснение результата.

Отсюда и природа воспроизводимости в этой схеме. Она касается цифр, а не формулировок. Числа считает детерминированный код, и они повторяются один в один. Объяснение это текст, и если его формулировка немного плывет от версии модели к версии, на сами цифры и диагноз это не влияет.

Шаг 4. Решение и развитие метода. Человек проверяет вывод, добавляет контекст и принимает решение. После решения цикл замыкается обратно на правила. Команда дорабатывает формулы и добавляет новые ветки анализа. Развивается здесь набор правил и алгоритм на шагах 1 и 2, а не арифметика внутри модели, считать мы ей так и не даем. Метод не статичен, но граница между кодом и LLM не двигается.

Ключевая мысль всей схемы простая. LLM не считает, а объясняет уже посчитанное. Как только это разделение нарушается, возвращаются все четыре поломки из первого раздела.

Есть и третья причина не пускать расчет через LLM

Галлюцинации и невоспроизводимость мы уже разобрали, детерминированный код закрывает обе проблемы по построению. Но есть еще один довод, который всплывает только на реальных объемах данных. Это стоимость.

Каждое обращение к модели это оплата за обработку входа. На малых объемах она незаметна, а на миллионах строк выгрузки уже нет. Если гонять LLM по всей сырой выгрузке на каждый вопрос, компания платит за то, чтобы модель медленно и с ошибками пересчитывала то, что код делает мгновенно, детерминированно и почти бесплатно. Правильная схема устроена иначе. Данные считаются заранее, в модель уходит компактный срез, и она только объясняет.

Отсюда правило, которым я теперь пользуюсь. Если задачу можно решить без ИИ, ее нужно решать без ИИ. И отсюда же пропорция результата. Данные и код дают 90%, а интерпретация моделью дополняет оставшиеся 10%.

Точки роста ищет код, а не ИИ

Здесь живет самое частое заблуждение про такие системы. Кажется, что «где я теряю деньги» находит нейросеть. На самом деле это не так. Утечки денег находит код по фиксированным алгоритмам, а LLM подключается в самом конце, чтобы объяснить уже найденное. Каждый сигнал это условие на данных, а не суждение модели. Оно либо выполнилось, либо нет, и завтра на тех же данных сработает точно так же.

Вот несколько сигналов из алгоритма, чтобы была видна механика:

  • реклама на товар без остатка, когда по SKU есть рекламные расходы за период, а остаток ниже дневного темпа продаж или в нуле, деньги идут на показы без продаж, позиция в выдаче падает, получается двойной убыток, и код считает потери как рекламный расход за дни отсутствия плюс просадку по позиции;

  • OOS топового товара, когда артикул входит в топ по заказам, а остаток ушел в ноль, потерю за неделю код считает по формуле «оборот за 4 недели, деленный на 28, умноженный на дни дефицита», при этом уходит и выручка, и позиция в выдаче, которую потом отыгрывают рекламой;

  • рост выручки при падении маржи, сумма заказов идет вверх, а маржа вниз, и код раскладывает, за счет чего это происходит, выросла логистика, реклама съела маржу или продажи сместились в дешевые SKU, в итоге рост оборота уходит в убыток.

Это три сигнала из большего набора. Хранение, кластеры, возвраты и воронка проверяются точно так же, по условиям. Пороги и формулы заданы заранее человеком на шаге правил, а не придуманы моделью в моменте.

Как это выглядит на данных

Короткий разбор нужен, чтобы алгоритмы не остались просто схемой на бумаге. Цифры ниже изменены для обезличивания, а механика реальная.

По одному клиенту за неделю сигналы сложились в ощутимую упущенную выручку. Часть потерь дали топовые артикулы с нулевым остатком, часть дала просадка конверсии в воронке, а часть дала просадка средней цены. Интереснее не итоговая сумма, а то, как код отделяет один корень проблемы от другого. Возьмем две карточки с одинаковым симптомом.

Карточка А. Симптом простой. Упала конверсия. Наивный вывод на бегу звучит так, «плохая карточка, надо переделывать фото». Алгоритм проверяет остатки раньше, чем контент, и показывает другую картину.

Алгоритм поймал сигнал, ИИ объяснил причину по конкретной карточке и предложил план, решение остается за человеком

Алгоритм поймал сигнал, ИИ объяснил причину по конкретной карточке и предложил план, решение остается за человеком

Остаток около 16 штук при расходе примерно 200 штук в день, ключевые размеры в нуле, покупатель не находит свой размер и уходит. Причина не в конверсии, а на складе, рекламу трогать не нужно, потому что CR по ней даже вырос. Сигнал сработал по условию «остаток ниже дневного темпа», а не по догадке.

Карточка Б. Симптом тот же. Заказы упали. Но остатки здоровые, товара хватает. Зато выросли отмены, примерно вдвое, и рекламный CR просел кратно. Трафик идет, но не превращается в заказ, здесь сработал другой сигнал, проблема в контенте и релевантности, а не в остатках.

Заказы упали. Причина – не сток, а отмены

Заказы упали. Причина — не сток, а отмены

Один симптом и два разных корня. Шаблонный ответ «переделайте карточку» верен в одном случае и вреден в другом. Разводит их фиксированный порядок проверки условий в коде, а не формулировка промпта.

Галлюцинации здесь про причинность, а не про арифметику

Вот важный поворот. Мы вынесли из‑под модели все расчеты, но галлюцинации никуда не делись, они просто сменили природу. Даже когда модель не считает ни одного рубля, она легко придумывает причинно‑следственные связи, которых в данных нет. Это опаснее ошибки в числе. Число можно сверить, а красивое ложное объяснение звучит убедительно.

Поэтому промпт объяснения я строю как дисциплину, слой за слоем, и каждое правило родилось из конкретного разбора.

Вот живые примеры.

  1. Разделяй время, модель объясняла падение заказов прошлой недели свежим отсутствием товара, которого на той неделе еще не было и который кончился позже, поэтому пришлось явно научить модель сверяться с состоянием на конец разбираемой недели и подавать дефицит как срочное действие, а не как причину прошлого падения.

  2. Срез цены это риск маржи, а не успех, во время промо цена упала на 30%, объем вырос в 4 раза, и модель радостно объявляла это успехом, а потом советовала закрепить цену, но при срезе цены фикс‑косты на единицу, логистика, приемка, хранение, съедают бóльшую долю, и маржа сжимается быстрее цены, поэтому срез цены теперь считается первоочередным риском, а рост объема при отрицательной марже означает умноженный убыток.

  3. Лаг выкупа, сумма к перечислению отстает от заказов на 5–14 дней, делить одно на другое и говорить о противоречии нельзя, потому что это не аномалия, а обычный лаг.

  4. Санитарные границы, базовая установка простая, модель ищет бизнес‑причину, а не баг в выгрузке, но физически невозможные значения, выкуп или конверсия больше 100%, отрицательный остаток, ДРР меньше 0%, все равно считаются ошибкой выгрузки, и строить на них вывод нельзя, модель такие случаи помечает, а не сочиняет для них причину.

  5. Корреляция не равна причинности, включили рекламу, выросли продажи, но мог сыграть роль сезон, поэтому модель обязана называть альтернативные объяснения, а не хвататься за первый красивый вывод.

  6. Вывод должен быть в деньгах, не «минус 8% маржи», а конкретная сумма, которую компания теряет в месяц, потому что решение принимают по деньгам, а не по процентам.

Есть и честный урок про такой промптинг. Каждое новое правило «проверь» или «помечай» удлиняет ответ модели. В какой‑то момент раздел «что произошло» превратился в стену текста, и мне прямо сказали, что букв слишком много и фокус размывается. Пришлось ввести простой принцип. Все правила остаются чек‑листом для анализа, а в текст ответа попадает только то, что реально сработало, и один главный вывод впереди.

Безопасность это свойство архитектуры

Здесь обычно возникает стоп‑фактор. Кажется, что нельзя отдавать данные в чужую нейросеть. Осторожность верная, но отдавать все и не нужно, это снова вопрос того, где проходит граница. Вот три принципа, которые я считаю обязательными для любой такой системы:

  • минимизация данных, в модель уходит не вся база, а готовый агрегированный срез, продажи, реклама, остатки, контактных данных в срезе нет в принципе;

  • обезличивание, названия компаний заменены на условные метки, а бренды в срез не попадают, цифры есть, но кому они принадлежат, со стороны не видно;

  • расчеты и хранилище на серверах в РФ, за периметр уходит только обезличенный срез, а сырые данные остаются внутри.

Модель видит цифры без привязки к конкретному бизнесу. Безопасность ИИ‑аналитики это не вопрос «внедрять или нет», а вопрос «как встроить».

Что это в итоге изменило

Ради этого все и затевалось. Вот что реально поменялось, когда система заработала.

  1. Глубина разбора перестала зависеть от того, кто сегодня смотрит на дашборд, это была исходная боль, а теперь опыт лучшего аналитика зашит в правила и пороги и доступен всей команде в одном и том же виде, при этом дашборд по‑прежнему показывает, что произошло, а система отвечает, что с этим делать.

  2. Рутинный разбор занимает минуты вместо часов, вопрос «почему просела карточка» теперь это несколько минут чтения готового ответа в формате «что произошло, главный фактор, что делать», а не полдня ручной сборки, по нашим прикидкам это примерно в десять раз быстрее.

  3. Фокус аналитика сместился вверх, раньше день уходил на то, чтобы собрать показатели и найти, где утекают деньги, теперь эту рутину делает код, а внимание человека освободилось для главного, для того, чтобы подумать, как помочь клиенту решить его задачу.

  4. Экспертиза осталась в системе, а не ушла с человеком, логика расчетов и методики теперь живут в правилах, а не только в голове ключевого сотрудника, если он уволится, метод останется, и новый человек войдет в работу быстрее.

Каждый из этих пунктов решает часть той боли, с которой начиналась эта история.

Честно про эксплуатацию

Есть и то, о чем обычно молчат. Запустить решение и показать демо это легкая половина работы. Основное сопротивление приходит уже на поддержке. Модель нужно дообучать, ловить баги, чинить инфраструктуру под меняющиеся площадки, а команду нужно приучать доверять результату. Если система работает нестабильно, люди тихо возвращаются к старым таблицам. Примерно половина всей работы приходится на период после запуска, и это стоит закладывать сразу, а не подавать ИИ как разовый проект.

За все время работы я ни разу не разбирала инцидент «бот посчитал неправильно», потому что бот не считает. Разбирала только случаи «объяснил неубедительно», а это лечится правилами промпта и доменной логикой, а не пересчетом. Граница «код считает, LLM объясняет» оказалась не догмой ради чистоты, а практической защитой от целого класса ошибок.

Если свести все к одной мысли, качество ИИ‑аналитики держат не веса модели, а данные под ней и архитектура вокруг нее. Код считает, LLM объясняет, человек решает. Для меня это не лозунг, а точное описание того, кто на каком шаге что делает. Именно так я вылечила ту самую боль, с которой начинала. Теперь разбор не зависит от того, какой аналитик за него взялся.

Если тема заинтересует, во второй части я могу подробнее разобрать, как сигналы потерь и точек роста формализуются в пороги и формулы. Или отдельно показать устройство правил промпта, которыми мы держим LLM в рамках «объясняет, а не сочиняет». Пишите в комментариях, что интереснее.

ссылка на оригинал статьи https://habr.com/ru/articles/1066650/