Legan Studio
Все статьи
~ 15 мин чтения

Аналитика бота в MAX: какие метрики действительно важны

Какие метрики бота в MAX реально показывают эффективность, как настроить воронку, события и отчеты, и какие цифры — пустой шум.

  • MAX
  • аналитика

«Сколько у нас пользователей в боте?» — самый бесполезный вопрос про продукт. Большое число подписчиков ничего не говорит ни о выручке, ни о здоровье бота, ни о том, удерживает ли он аудиторию. Разберём, какие метрики действительно показывают, как работает бот в MAX (мессенджер VK Tech), как собирать события, строить воронки и когорты, и какие цифры — пустой шум, который только мешает принимать решения.

Большинство команд проходит три стадии зрелости аналитики: «считаем подписчиков» → «таблица событий + базовые воронки» → «полноценный продуктовый стек с когортами, A/B-тестами и алертами на бизнес-метрики». Перепрыгнуть стадии нельзя, но и задерживаться на первой дольше пары спринтов — значит работать вслепую.

Что вообще можно мерить в боте MAX

MAX как платформа даёт три уровня данных:

  1. Bot API — события, которые приходят от платформы: входящие сообщения, нажатия inline-кнопок (callback), подключение/отключение бота, изменения членства в чатах, события платежей. Это сырьё.
  2. Mini App / WebView — полноценная веб-аналитика внутри встроенного браузера: Yandex Metrika, Amplitude, PostHog. Здесь работают те же инструменты, что и в обычном вебе, плюс доступ к данным пользователя из MAX.
  3. Внешние системы — CRM (сделки, выручка), платёжный шлюз (refund, MRR), биллинг подписок, поддержка (тикеты, время ответа), 1С/ERP.

Хорошая аналитика склеивает все три уровня по user_id (для бота это ID пользователя в MAX) и даёт цельную картину: пришёл из рекламы → нажал /start → дошёл до чекаута → оплатил → продлил подписку через месяц.

Базовый принцип: воронка, а не счётчики

Любой осмысленный анализ бота — это воронка. Пользователь куда-то заходит, что-то делает, до чего-то доходит. На каждом шаге часть отваливается. Задача — измерить каждый шаг и понять, где теряется больше всего.

Минимальная универсальная воронка:

  1. Открытие бота (/start или клик по deeplink с UTM-меткой).
  2. Активация — сделал первое значимое действие (выбрал услугу, согласился на ПДн).
  3. Целевое действие — оставил заявку, оплатил, записался.
  4. Удержание — вернулся через 7/30 дней.

Каждый шаг — отдельная метрика. Соотношения между ними — ключевые показатели.

Список ключевых метрик

Минимальный набор для большинства ботов в MAX:

МетрикаЧто показываетКогда смотреть
DAU / WAU / MAUАктивная аудиторияЕжедневно
Retention D1 / D7 / D30Возвращаемость по когортамЕженедельно
Conversion воронкиУзкие местаПосле каждого релиза
Session lengthГлубина вовлеченияЕженедельно
Command frequencyКакие команды живыеЕжемесячно
CTR inline-кнопокКачество UXПосле A/B
NPS / CSATУдовлетворённостьРаз в квартал
ARPU / LTVДеньги на пользователяЕжемесячно
CAC по источникамЭффективность каналовЕженедельно
Доля заблокировавших ботаЗдоровье рассылокЕжедневно

DAU/MAU — базовая метрика, но без воронок она бесполезна: можно гнать трафик и держать DAU, теряя при этом 95% пользователей на первом шаге.

События, а не сообщения

Главная техническая ошибка — мерить «сколько сообщений отправлено». Это не метрика, а лог. Метрика — это событие (event), которое описывает поведение пользователя:

  • bot_opened — открыл бот (с UTM из deeplink);
  • consent_given — согласился на обработку ПДн;
  • service_selected — выбрал услугу (с параметром);
  • form_step_completed — прошёл шаг анкеты (с номером шага);
  • lead_submitted — отправил заявку;
  • payment_succeeded — оплатил (с суммой);
  • support_escalated — попросил оператора.

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

Схема таблицы событий

Минимальная схема в PostgreSQL/ClickHouse — event_name, user_id, timestamp, properties JSON. Расширения добавляются по мере роста: session_id, device, app_version, experiment_id.

CREATE TABLE events (
  id           BIGSERIAL PRIMARY KEY,
  event_name   TEXT NOT NULL,
  user_id      BIGINT NOT NULL,
  session_id   UUID,
  ts           TIMESTAMPTZ NOT NULL DEFAULT now(),
  source       TEXT,
  properties   JSONB NOT NULL DEFAULT '\{\}'::jsonb
);

CREATE INDEX events_user_ts_idx  ON events (user_id, ts);
CREATE INDEX events_name_ts_idx  ON events (event_name, ts);
CREATE INDEX events_props_gin    ON events USING GIN (properties);

В коде бота заводится единственный helper track(event, props), который пишет строку в БД (или шлёт в очередь). Прямые INSERT-ы из бизнес-логики запрещены — иначе схема расползётся за месяц, и никто не вспомнит, что значит step_3_done.

async function track(
  userId: number,
  eventName: string,
  properties: Record<string, unknown> = {}
) {
  await db.query(
    `INSERT INTO events (user_id, event_name, source, properties)
     VALUES ($1, $2, $3, $4)`,
    [userId, eventName, properties.source ?? null, properties]
  );
}

bot.command("start", async (ctx) => {
  const utm = parseStartPayload(ctx.match);
  await upsertUser(ctx.from.id, { utm });
  await track(ctx.from.id, "bot_opened", {
    utm,
    lang: ctx.from.language_code,
  });
  await ctx.reply("Привет! Это бот MAX-студии.");
});

Где хранить и обрабатывать данные

Несколько рабочих вариантов:

  • PostgreSQL. Своя таблица events (user_id, event_name, ts, properties JSONB). Простой, гибкий, отчёты строятся SQL-запросами в Metabase/Superset. Подходит до десятков миллионов событий.
  • ClickHouse. Когда событий миллионы в сутки, и PostgreSQL начинает тормозить на агрегациях. Колоночная СУБД, секундные ответы на годовых данных.
  • Yandex Metrika. Бесплатно, готовые отчёты, удобная воронка. Подходит для Mini App MAX — внутри WebView обычная веб-аналитика.
  • Amplitude / Mixpanel. Продуктовая аналитика «из коробки» — воронки, когорты, ретеншн без SQL. Дорого, данные хранятся вне РФ (учитывайте 152-ФЗ).
  • PostHog (self-hosted). Open-source альтернатива Amplitude. Можно развернуть в РФ, бесплатно для своих данных.

Для большинства проектов оптимум — PostgreSQL + Metabase + Yandex Metrika для Mini App. Этого хватает на месяцы и закрывает 80% задач без больших затрат.

Сравнение аналитических систем

ИнструментСильные стороныМинусыДля кого
PostgreSQL + MetabaseПолный контроль, дёшевоНадо строить рукамиВсе, у кого есть бэкенд
ClickHouse + SupersetМиллионы событий в деньСложнее эксплуатацияHigh-load
AmplitudeГотовые воронки, когортыДорого, данные вне РФЗрелый продукт
MixpanelГибкие отчётыДорого, данные вне РФПродуктовые команды
PostHog (self-hosted)Open-source, можно в РФНадо администрироватьКоманды с DevOps
Yandex MetrikaБесплатно, цели, вебвизорТолько для Mini AppMini App с веб-частью
Yandex DataLensРоссийский SaaS, есть freeТолько дашбордыКоманды в РФ
Power BI / TableauКорпоративный сегментДорогоEnterprise

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

UTM-метки в /start — единственный способ разделить источники

Любая ссылка на бот MAX — это deeplink с параметром start. Параметр идеально подходит для UTM-меток, но на полноценную CSV-строку места не хватает: payload ограничен длиной и алфавитом [A-Za-z0-9_-]. Поэтому используют либо короткие коды, либо base64url-кодирование JSON.

import { Buffer } from "node:buffer";

type Utm = {
  source?: string;
  medium?: string;
  campaign?: string;
  content?: string;
};

function encodeUtm(utm: Utm): string {
  const json = JSON.stringify(utm);
  return Buffer.from(json).toString("base64url").slice(0, 60);
}

function parseStartPayload(payload?: string): Utm {
  if (!payload) return {};
  try {
    const json = Buffer.from(payload, "base64url").toString("utf8");
    return JSON.parse(json) as Utm;
  } catch {
    return { campaign: payload };
  }
}

const link = `https://max.ru/your_bot?start=${encodeUtm({
  source: "vk_ads",
  medium: "cpc",
  campaign: "brand_ru_jan",
})}`;

При первом контакте бот декодирует payload и сохраняет UTM в профиль пользователя (users.first_utm). Дальше любая метрика разворачивается по источникам — это позволяет считать CAC и ROMI по каждому каналу, а не «в среднем по больнице».

Без UTM весь маркетинг — это «верим, что работает», но проверить нельзя.

Воронки — главное оружие

Воронка показывает, какой процент пользователей проходит через цепочку шагов. Базовые воронки для бота в MAX:

  • Активация: bot_openedconsent_givenemail_or_phone_providedfirst_meaningful_action.
  • Покупка: bot_openedcatalog_viewedcart_item_addedcheckout_startedpayment_succeeded.
  • Подписка: bot_openedplan_selectedpayment_succeededsubscription_activesubscription_renewed.
  • Поддержка: support_requestedbot_answeredsolved или support_escalated.

Когда видна структура, понятно, где «обрыв». Если 40% пользователей открывают чекаут, но только 5% платят — проблема в чекауте, а не в трафике. Если 80% соглашаются на ПДн, но только 10% доходят до телефона — проблема в шаге сбора телефона.

Когорты и ретеншн

Когортный анализ показывает, что происходит с пользователями со временем. Стандартные когорты:

  • по дате регистрации (пришли в одну неделю);
  • по источнику (рекламный канал, реферал);
  • по сегменту (новый, активный, спящий);
  • по первому действию (купил сразу vs только смотрел).

Главная метрика — ретеншн на день N (доля пользователей, вернувшихся в бот через 1, 7, 14, 30 дней). Для подписочных продуктов считается ещё MRR-ретеншн и net revenue retention.

Простой SQL-запрос для D7-ретеншна по неделе регистрации:

WITH cohorts AS (
  SELECT
    user_id,
    DATE_TRUNC('week', MIN(ts))::date AS cohort_week
  FROM events
  WHERE event_name = 'bot_opened'
  GROUP BY user_id
),
returns AS (
  SELECT
    c.cohort_week,
    c.user_id,
    MAX(CASE
      WHEN e.ts BETWEEN c.cohort_week + INTERVAL '7 day'
                    AND c.cohort_week + INTERVAL '8 day'
      THEN 1 ELSE 0
    END) AS returned_d7
  FROM cohorts c
  LEFT JOIN events e USING (user_id)
  GROUP BY c.cohort_week, c.user_id
)
SELECT
  cohort_week,
  COUNT(*)                                AS users,
  ROUND(AVG(returned_d7)::numeric, 3)     AS retention_d7
FROM returns
GROUP BY cohort_week
ORDER BY cohort_week DESC;

Главное правило когорт: смотреть не средние числа, а строки таблицы. Средний ретеншн часто врёт — новые когорты «вытягивают» статистику, пока старые отваливаются.

A/B-тесты на пользователях бота

Все небанальные изменения проверяются A/B-тестом: новый текст приветствия, перестановка кнопок в меню, изменение последовательности вопросов. Расщепление обычно делается по hash от user_id — это даёт стабильную группу для каждого пользователя на всё время эксперимента.

import { createHash } from "node:crypto";

function bucket(userId: number, experiment: string, variants = 2): number {
  const hash = createHash("sha256")
    .update(`${experiment}:${userId}`)
    .digest();
  return hash.readUInt32BE(0) % variants;
}

function variant(userId: number) {
  const v = bucket(userId, "welcome_copy_v3", 2) === 0 ? "A" : "B";
  // фиксируем exposure — без него тест нечестный
  void track(userId, "experiment_exposure", {
    experiment: "welcome_copy_v3",
    variant: v,
  });
  return v;
}

Без события experiment_exposure (фиксации показа) тест нечестный: посчитать конверсию можно только по тем, кто реально увидел вариант. Подробнее про методологию A/B в боте — в отдельной статье; здесь важно зафиксировать обязательный минимум: гипотеза, метрика успеха, заранее посчитанный размер выборки и exposure-событие.

Дашборды и BI

Куда складывать графики:

  • Metabase — самый простой старт, SQL-редактор, бесплатная open-source-версия.
  • Apache Superset — мощнее Metabase, но сложнее в эксплуатации.
  • Grafana — хороша для технических метрик (latency, ошибки), хуже для продуктовых.
  • Yandex DataLens — российский SaaS, бесплатный лимит, удобен для команд в РФ.
  • Power BI / Tableau — корпоративный сегмент, дорого.

Базовый набор дашбордов: «Главная» (DAU/MAU, конверсия, выручка), «Воронки», «Когорты», «Источники», «Ошибки». Каждый дашборд должен помещаться на один экран — иначе им никто не пользуется.

Пример SQL-запроса для дашборда «Воронка активации за вчера»:

WITH funnel AS (
  SELECT
    COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'bot_opened')        AS s1_opened,
    COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'consent_given')     AS s2_consent,
    COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'phone_provided')    AS s3_phone,
    COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'lead_submitted')    AS s4_lead
  FROM events
  WHERE ts >= CURRENT_DATE - INTERVAL '1 day'
    AND ts <  CURRENT_DATE
)
SELECT
  s1_opened,
  s2_consent, ROUND(100.0 * s2_consent / NULLIF(s1_opened, 0), 1) AS cr_consent,
  s3_phone,   ROUND(100.0 * s3_phone   / NULLIF(s1_opened, 0), 1) AS cr_phone,
  s4_lead,    ROUND(100.0 * s4_lead    / NULLIF(s1_opened, 0), 1) AS cr_lead
FROM funnel;

ClickHouse, когда событий очень много

Когда поток превышает несколько миллионов событий в сутки, PostgreSQL начинает тормозить на агрегациях. Стандартный путь — складывать сырые события в ClickHouse через Kafka или прямую запись батчами.

CREATE TABLE events
(
  event_date    Date          DEFAULT toDate(ts),
  ts            DateTime64(3),
  event_name    LowCardinality(String),
  user_id       UInt64,
  source        LowCardinality(String),
  properties    String CODEC(ZSTD(3))
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_name, user_id, ts)
TTL event_date + INTERVAL 18 MONTH;

LowCardinality для имени события и источника даёт 5–10× экономии места, ZSTD сжимает JSON-properties. Запросы на годовых данных отвечают за секунды вместо часов.

Алерты на бизнес-метрики

Технические алерты (5xx, latency) обычно уже есть. Для бизнеса нужны отдельные:

  • Активация упала ниже 35% за последний час → проблема с шагом согласия или регистрации.
  • Конверсия в оплату упала на 30% относительно недели → сломался платёжный шлюз.
  • Доля ошибок отправки сообщений выросла → массовая блокировка после рассылки или проблема на стороне MAX Bot API.
  • DAU ниже скользящего среднего на 2σ → внешний инцидент или баг.

Алерты живут в Grafana / Yandex Monitoring / Prometheus Alertmanager. Главное правило: алерт обязан вести к действию. Если на него нечего ответить — это не алерт, а шум, его надо удалить.

Связка с CRM и деньгами

Метрики бота особенно ценны, когда привязаны к деньгам. Это требует связки с CRM:

  • сделка из CRM → событие deal_won или deal_lost;
  • сумма сделки → revenue по пользователю;
  • LTV считается по CRM, а не по «событию покупки в боте»;
  • refund из платёжки → payment_refunded, корректирует MRR.

Без такой связки маркетинговая аналитика обманывает: можно гнать дешёвый трафик в плохих лидов и считать его «выгодным» по числу заявок.

Privacy и 152-ФЗ

Аналитика — это работа с персональными данными. Минимальные требования для российских проектов:

  • Согласие на обработку ПДн до сбора phone, email, любых ID, кроме обезличенного user_id MAX.
  • Уведомление РКН об обработке ПДн (если собирается что-то кроме обезличенного).
  • Уведомление о трансграничной передаче, если используется Amplitude/Mixpanel/GA4 (серверы вне РФ).
  • Право на удаление: должен быть механизм /forget или ручка в админке, которая удаляет пользователя из events и users.
  • Агрегирование: для большинства дашбордов хватит count, avg, sum без сырых ID.

Для Mini App в MAX дополнительно — баннер cookies, если используется Yandex Metrika (она ставит ID в localStorage).

Анти-паттерны

Что точно не делать:

  • Tracking всего подряд без модели — данных много, выводов нет, schema превращается в data swamp.
  • Неконсистентные имена событийcart_add, addToCart, add_to_cart живут в одной таблице, склеить нельзя.
  • Нет dictionary событий — спустя полгода никто не помнит, что значит step_3_done.
  • Полагаться только на встроенную аналитику конструктора — закрытая, не разворачивается на BI.
  • Считать «количество подписчиков» как KPI — это vanity-метрика, ничего не предсказывает.
  • Делать выводы на 1–2 неделях данных без когорт.
  • Хранить ПДн в properties без необходимости — ставит под удар при утечке.
  • A/B-тест без exposure — приходится верить, что 50/50 действительно увидели обе версии.

Roadmap внедрения

Прагматичный порядок работ:

  1. Неделя 1–2: таблица events, helper track(), базовые события (bot_opened, ключевые шаги воронки, payment_succeeded). Поднять Metabase, нарисовать первые 3 графика.
  2. Неделя 3–4: UTM в /start, дашборд по источникам, базовый ретеншн D1/D7.
  3. Месяц 2: связка с CRM, LTV/CAC, алерты на ключевые бизнес-метрики.
  4. Месяц 3: A/B-инфраструктура, dictionary событий, регулярные продуктовые ревью раз в неделю.
  5. Месяц 6+: ClickHouse при росте, когортные дашборды, прогнозы LTV, ML-сегментация.

Перепрыгивать пункты дорого: без воронок не имеет смысла строить ML, без dictionary — внедрять A/B.

Итого

Аналитика бота в MAX — это связка модели событий, воронок, когорт, ретеншна и UTM-разреза. Минимум — собственная таблица событий + Metabase + связка с CRM. Без аналитики невозможно понять, где рвётся воронка и какие каналы окупаются. Срок внедрения базового слоя — 1–3 недели, а отдача проявляется уже после первого месяца наблюдений. Главное — не тащить в трекинг всё подряд, а явно описать события, которые отвечают на вопросы продукта, и не считать «подписчиков» KPI.

Частые вопросы

Какие метрики бота в MAX действительно важны?

Бизнес-метрики: conversion rate (доля открывших бота, дошедших до целевого действия), cost per lead и cost per acquisition при платном трафике, average revenue per user и LTV, retention D7 и D30, drop-off rate по шагам воронки. Метрики качества продукта: time to first value, доля сессий с обращением к оператору, доля «застрявших» пользователей, CSAT через прямой опрос. Бесполезны: количество отправленных ботом сообщений, число подписчиков в моменте без когорт, среднее время ответа бота (если это не SLA-метрика).

Где хранить аналитику событий бота MAX?

Четыре рабочих варианта. Своя PostgreSQL — таблица events (user_id, event_name, timestamp, properties JSONB), отчёты через SQL, простая и гибкая схема для большинства проектов. Yandex Metrika — бесплатно, готовые отчёты, удобная воронка для Mini App MAX. ClickHouse плюс Grafana или Superset — если событий миллионы и нужны быстрые агрегаты. Amplitude/Mixpanel/PostHog — готовые продуктовые дашборды, но первые два дорогие и хранят данные вне РФ. Для большинства проектов оптимум — события в своей БД и дашборды в Metabase или Superset.

Как правильно измерять воронку в боте MAX?

Через события (events), а не «количество сообщений». Минимальная универсальная воронка из четырёх шагов: открытие бота (/start или клик по deeplink с UTM), активация (первое значимое действие — согласие на ПДн, выбор услуги), целевое действие (заявка, оплата, запись), удержание (возврат через 7 и 30 дней). Каждый шаг — отдельная метрика, соотношения между шагами — ключевые показатели. Drop-off rate показывает, где отваливается больше всего пользователей и куда направить оптимизацию.

Зачем нужны UTM-метки в ссылках на бот MAX?

UTM-метки (utm_source, utm_medium, utm_campaign) в deeplink на бот позволяют считать конверсию по каждому каналу трафика, сравнивать качество разных источников, перераспределять рекламный бюджет в пользу работающих. Параметр start в deeplink ограничен длиной и алфавитом, поэтому UTM кодируют либо короткими кодами (start=vk_jan), либо base64url JSON. При первом контакте бот декодирует payload и сохраняет UTM в профиль пользователя — дальше любая аналитика разрезается по источнику. Без UTM весь маркетинг превращается в «верим, что работает».

Как делать A/B-тесты в боте MAX?

Технически — функция-распределитель, которая по hash от user_id (sha256(experiment:user_id) % N) определяет вариант для каждого пользователя, и параметр «вариант эксперимента» добавляется в каждое событие этого пользователя. Обязательно фиксировать exposure — событие experiment_exposure с experiment_id и variant в момент показа: без него тест нечестный, считать конверсию можно только по тем, кто реально увидел вариант. Через 1–2 недели накапливаются данные, и группы сравниваются по целевой метрике. Минимальный размер выборки считается заранее по ожидаемому эффекту.

Что такое когортный анализ и зачем он боту?

Когорта — группа пользователей, пришедших в один период (например, неделю). Когортный анализ показывает, как себя ведёт каждая группа со временем — D1, D7, D14, D30, D60 retention. Стандартный отчёт: таблица, где по строкам — недели регистрации, по столбцам — дни с момента регистрации, в клетках — доля пользователей, которая ещё активна. Это показывает, действительно ли бот удерживает аудиторию, или просто «прокручивает» новый трафик. Простые цифры «всего пользователей» здесь бесполезны — средний ретеншн часто врёт.

Как соблюсти 152-ФЗ при сборе аналитики бота?

Минимальные требования. Согласие на обработку ПДн до сбора телефона, email и любых идентификаторов, кроме обезличенного user_id MAX. Уведомление РКН об обработке ПДн (если собирается что-то кроме обезличенного). Уведомление о трансграничной передаче, если используется Amplitude, Mixpanel или GA4 (серверы вне РФ). Право на удаление — механизм /forget или ручка в админке, удаляющая пользователя из events и users. Агрегирование данных в дашбордах через count/avg/sum без сырых ID. Для Mini App с Yandex Metrika — баннер cookies, потому что метрика ставит идентификатор в localStorage.