Я не умел делать A/B-тесты. AI-агент построил аналитику и теперь сам её анализирует
У нас было три варианта нового экрана сканирования ценника.
Все три выглядели достаточно разумно. Claude Design подготовил макеты, я показал их коллегам и устроил голосование. Но проблема голосованием не решилась: явного лидера не оказалось.
Тогда я предложил:
Раз мы сами не можем решить, пусть решат пользователи.
Так в обычной Android-задаче неожиданно появился A/B/C-тест.
С этого момента для меня началась гораздо более интересная часть истории. Новый экран и все три варианта интерфейса AI-агент написал сам, но именно это оказалось самым простым.
Я никогда до этого не проводил A/B-тестирование.
Я читал статьи, смотрел видео и примерно представлял общую идею: делим пользователей на группы, показываем разные варианты, собираем метрики и сравниваем.
Но между этим описанием и реальным экспериментом в production лежит довольно большая пропасть.
Какие события отправлять?
Какие параметры у них должны быть?
Как отличить нормальную попытку сканирования от закрытого экрана?
Как измерять время?
Как понять, что код не распознался потому, что пользователь ушёл, а не потому, что приложение упало?
Как сравнивать магазины с разным количеством сканирований?
Как не сломать QR-сценарий, улучшая обычный штрихкод?
И наконец: как потом вообще анализировать всё это в Firebase?
Я решил не изучать ещё один набор абстрактных примеров, а попробовать пройти весь путь на своей реальной задаче вместе с агентом.
Первая моя идея оказалась неправильной
Когда я начал думать об аналитике сканера, моя первая интуиция была простой:
чем больше действий нужно анализировать, тем больше событий надо отправлять.
Я начал размечать много разных событий.
Логика казалась естественной: отдельно открытие, отдельно успешное сканирование, отдельно ошибка, отдельно «поднесите ближе», отдельно какие-то действия пользователя.
Агент предложил совершенно другую модель.
Для эксперимента ему в основном понадобились два события:
barcode_scan_scanner_session_startbarcode_scan_scanner_session_finish
Зато каждое событие получило контекст.
У сессии появился session_id, вариант интерфейса, магазин, устройство, источник запуска и другие параметры.
А при завершении — результат, тип кода, длительность сканирования, информация про TooFar, количество несовпадений при двойном подтверждении, использование фокуса, зума, фонарика, признак распознавания с первой попытки и другие характеристики.
Для меня это был первый важный результат эксперимента ещё до появления любых цифр.
Я бы сам так аналитику не спроектировал.
До этого я воспринимал аналитику скорее как набор событий: произошло действие — отправили event.
А здесь агент фактически спроектировал сущность «сессия сканирования».
События стали только способом передать начало и результат этой сессии.
И дальше уже можно было задавать вопросы не к отдельным кликам, а к попытке сканирования целиком.
Firebase оказался только источником данных
Следующая проблема обнаружилась довольно быстро.
Собирать события в Firebase Analytics несложно. Но анализировать такой эксперимент прямо в интерфейсе Firebase уже неудобно.
Start и finish — разные события.
Параметры лежат внутри вложенного event_params.
Нужно связывать события по session_id.
Нужно считать сессии, у которых есть start, но нет finish.
Нужно делать разрезы по варианту интерфейса, магазину, бизнес-юниту и модели устройства.
Нужны p50 и p75, а не только средние значения.
Нужно следить, чтобы сырые daily- и intraday-таблицы не дали дубли.
До этой задачи я никогда не работал с экспортом Firebase Analytics в BigQuery.
Агент объяснил, что для нормального анализа он нужен, а потом буквально провёл меня через настройку: куда зайти, что включить и что нажать.
Это был ещё один любопытный момент.
Обычно изучение новой технологии выглядит примерно так:
«почитать документацию → посмотреть пример → попробовать на тестовых данных → наконец применить в проекте».
Здесь всё происходило наоборот.
У меня уже была реальная задача, реальные события и реальные пользователи. А BigQuery я осваивал ровно в том объёме, который был нужен, чтобы агент мог двигаться дальше.
Потом агент предложил scanner_ab
Когда экспорт заработал, появилась следующая проблема.
Сырые Firebase events — не самый удобный интерфейс даже для агента.
Чтобы ответить на простой вопрос вроде:
Сколько успешных сессий было у варианта B?
нужно сначала извлечь параметры из event_params, найти start, найти соответствующий finish, связать их по session_id, обработать пропущенные завершения и только после этого считать метрику.
Если делать это в каждом запросе, огромный кусок SQL будет повторяться снова и снова.
Агент предложил отдельный dataset scanner_ab и подготовленный слой scanner_ab.sessions.
В нём одна строка соответствует одной сессии сканирования.
То есть между сырыми Firebase events и аналитическими вопросами появился промежуточный слой:
Android
↓
Firebase Analytics
↓
BigQuery events_*
↓
scanner_ab.sessions
↓
аналитические запросы
Именно здесь я начал понимать, зачем в аналитике вообще нужны такие подготовленные витрины.
Я сначала называл это для себя «нормированными событиями по сканеру», но точнее это аналитический слой данных.
Сырые события остаются сырыми событиями.
А scanner_ab.sessions превращает их в удобную для анализа бизнес-сущность — одну попытку сканирования.
Самое интересное началось после настройки
На этом месте можно было бы написать обычный отчёт:
«Мы подключили Firebase к BigQuery, написали несколько SQL-запросов и посчитали A/B/C».
Но именно здесь для меня началась самая интересная часть.
Я практически не пишу эти SQL-запросы.
Я задаю агенту вопросы обычным языком.
Например:
Сравни A/B/C за последние три дня.
Или:
Посмотри результаты в разрезе бизнес-юнитов.
Или:
Сделай анализ по моделям устройств.
Или:
Дай качественные показатели сканирования.
Или уже совсем инженерный вопрос:
В аналитике появился провал. Сравни его с Crashlytics.
Дальше агент сам определяет, какие данные нужны, строит SQL, выполняет запросы и возвращает результат в виде таблицы.
И вот тут особенно хорошо видна разница между пользовательским вопросом и тем, что происходит под капотом.
Один человеческий вопрос может превратиться в огромный SQL-запрос.
Там появляются:
- фильтрация периода;
- сборка сессий;
UNNEST(event_params);JOINstart и finish;- проверка пропущенных сессий;
- группировки по магазинам и устройствам;
- квантили;
- guardrail-метрики;
- разрезы по вариантам.
Если бы мне пришлось делать это самому, мне сначала пришлось бы довольно серьёзно изучить BigQuery и аналитический SQL.
А для агента этот SQL — просто промежуточный технический слой.
Он не показывает мне его, если я не прошу.
Я вижу уже таблицу.
Например условно:
| Вариант | Сессии | Success | Cancel | p75 | TooFar |
|---|---|---|---|---|---|
| A | … | … | … | … | … |
| B | … | … | … | … | … |
| C | … | … | … | … | … |
И могу задать следующий вопрос:
А теперь разложи B по моделям планшетов.
Или:
Покажи только два бизнес-юнита, где метрика ухудшилась.
Или:
Это массовая проблема или несколько конкретных магазинов?
По сути SQL перестал быть моим интерфейсом к данным.
Мой интерфейс — агент.
Но агенту недостаточно просто уметь писать SQL
Это, пожалуй, самый важный момент.
Если бы вся история сводилась к генерации длинных запросов, она была бы не очень интересной.
LLM умеют писать SQL уже давно.
Но здесь агент сначала сам спроектировал данные, которые потом собирался анализировать.
Сначала он определил, какая телеметрия нужна.
Потом внедрил её в Android-приложение.
Потом объяснил, как получить raw events в BigQuery.
Потом предложил отдельный аналитический dataset.
Потом начал использовать его для собственных запросов.
Получился замкнутый цикл:
продуктовый вопрос
↓
схема событий
↓
код приложения
↓
реальные пользователи
↓
Firebase
↓
BigQuery
↓
scanner_ab
↓
анализ агента
↓
инженерный вывод
Это уже совсем другая степень участия агента в разработке.
Он не просто пишет код по заданному ТЗ.
Он помогает построить систему, в которой результат этого кода можно измерить на реальных пользователях.
A/B-тест быстро превратился ещё и в инструмент диагностики
У продуктовой аналитики есть ещё одно полезное свойство: она показывает не только, какой вариант интерфейса быстрее.
Она показывает, когда что-то пошло не так.
В какой-то момент в данных появился заметный провал.
И вместо запроса «какой вариант победил?» возник совсем другой:
Почему ухудшились показатели?
Дальше можно было смотреть уже не только на A/B/C.
Разложить проблему по магазинам.
Проверить бизнес-юниты.
Посмотреть модели устройств.
Понять, это один продавец, один планшет или массовое изменение.
А затем сопоставить момент деградации с Crashlytics.
То есть тот же аналитический контур, который строился для продуктового эксперимента, стал частью технического расследования.
Это для меня особенно интересно.
Агент получил возможность сопоставлять сразу несколько типов контекста:
- код приложения;
- feature toggles;
- продуктовую телеметрию;
- BigQuery;
- Crashlytics;
- распределение проблемы по магазинам и устройствам.
И здесь его ценность уже не в том, что он «написал запрос».
Ценность в том, что он может пройти всю цепочку от жалобы пользователя до технической гипотезы.
Всё ли теперь можно отдать агенту?
Нет.
A/B-тест хорошо показывает границу.
Например, в нашем эксперименте вариант назначается магазину.
Это значит, что тысяча сканирований в одном магазине — не то же самое, что тысяча независимых участников эксперимента.
Если один крупный магазин делает намного больше сканирований, чем остальные, нельзя просто свалить все сессии в одну таблицу и объявить вариант победителем.
Именно поэтому анализ нужно делать сначала на уровне магазина, а потом уже сравнивать варианты.
А ещё есть внешние факторы:
- разные модели планшетов;
- разное освещение;
- разные продавцы;
- разная нагрузка;
- локальные технические проблемы.
Агент умеет находить эти ограничения и строить правильные разрезы.
Но финальный вывод всё равно нельзя превращать в магию:
AI сказал, что B лучше.
Человек должен понимать, как был устроен эксперимент и какой вывод данные действительно позволяют сделать.
Иначе можно получить очень красивую таблицу с неправильным смыслом.
Что для меня изменилось
Сам факт, что AI-агент способен работать в области, в которой я не специалист, для меня уже давно не новость.
Я постоянно использую агентов в разработке и давно перестал воспринимать их только как автодополнение кода.
Но продуктовая аналитика подвернулась впервые.
И здесь я увидел тот же эффект в совершенно другой области.
Раньше граница задачи выглядела примерно так:
Я не умею делать A/B-тесты
→ сначала надо изучить A/B-тесты
→ потом Firebase
→ потом BigQuery
→ потом SQL
→ потом можно начинать эксперимент
На практике получилось иначе:
Есть реальный продуктовый вопрос
→ ставлю его агенту
→ агент строит технический путь к данным
→ по дороге объясняет мне необходимые части
→ мы проверяем результат на реальном продукте
Я не стал специалистом по BigQuery.
И не стал аналитиком.
Но теперь могу работать с задачей, для которой раньше сначала потребовалось бы освоить несколько новых инструментов.
Наверное, именно это для меня и есть главное изменение роли разработчика при работе с AI-агентами.
Не «теперь можно ничего не знать».
Наоборот, вопросов становится больше.
Нужно уметь сформулировать проблему.
Нужно заметить, когда метрика выглядит подозрительно.
Нужно понимать ограничения эксперимента.
Нужно не принимать красивый ответ автоматически.
Но технический путь между вопросом и данными больше не обязательно проходить вручную самому.
В моём случае он выглядит довольно буквально:
Я работаю не с BigQuery через SQL.
Я работаю с агентом на языке инженерных вопросов.
А уже агент работает с BigQuery через SQL.
И три варианта экрана сканера оказались просто хорошим поводом это обнаружить.