Есть класс задач, где привычная связка «оптимизируем по конверсии на сайте» ломается тихо и незаметно. Не выдаёт ошибку, не показывает падение метрик — наоборот, отчёты выглядят всё лучше, а денег становится меньше.

Это услуги с офлайн‑исполнением и разрывом между конверсией и фактом оказания услуги: салоны, клиники, автосервисы, SPA, репетиторы, любые записи «на время». Разберу механику проблемы и то, как я её решаю. Примеры и цифры — с проекта в бьюти‑сегменте, но задача не отраслевая.

В чём, собственно, проблема

Стандартная цепочка выглядит так:

показ → клик → конверсия на сайте (заявка/запись) → ??? → выручка

Рекламные системы видят цепочку до третьего звена включительно. Всё, что правее, для них не существует. Оптимизация — по конверсии на сайте.

Теперь добавим реальность: часть записавшихся не приходит. В нише, о которой пойдёт речь, доля недоходов на старте составляла 38%. Это не аномалия, для записи на услугу это типичный диапазон 25–40%.

Дальше начинается интересное. Обозначим:

  • C — стоимость конверсии на сайте (то, что оптимизирует система)

  • r — доля дошедших до визита

  • C_real = C / r — фактическая стоимость привлечённого клиента

Пока r одинаков для всего трафика, задача сводится к масштабированию: минимизируя C, вы минимизируете и C_real. Оптимизация работает корректно.

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

Иначе говоря: чем дешевле сегмент, тем чаще он не доходит. Это не совпадение, а следствие. Дешёвый клик — это низкоконкурентный запрос, а низкая конкуренция в коммерческой нише почти всегда означает низкое коммерческое намерение.

Численный пример

Два сегмента, оба дают конверсии на сайте.

Сегмент A — “записаться на массаж”, «массаж рядом со мной». Стоимость конверсии 1 200 ₽, доходимость 0,85.
C_real = 1200 / 0,85 ≈ 1 410 ₽

Сегмент B — “массаж спины цена”, «сколько стоит массаж». Стоимость конверсии 600 ₽, доходимость 0,42.
C_real = 600 / 0,42 ≈ 1 430 ₽

По метрике, доступной рекламной системе, сегмент B вдвое эффективнее. По факту они равны. А если учесть, что в B выше доля разовых визитов и ниже средний чек, B хуже.

Теперь запустите автостратегию с оптимизацией по конверсиям. Она честно перераспределит бюджет в сторону B — потому что видит только числитель. Каждую неделю отчёт будет показывать снижение CPA, и каждую неделю фактическая стоимость клиента будет расти.

Это не сбой алгоритма. Алгоритм решает ровно ту задачу, которую ему поставили. Задача поставлена неверно.

Почему нельзя просто загрузить офлайн‑конверсии

Первый очевидный ответ: передавать в Директ факты визитов через офлайн‑конверсии и оптимизировать по ним. API это позволяет, механика описана в документации.

На практике упирается в объём данных. Стратегии Директа требуют порядка 10 конверсий в неделю на кампанию для выхода из обучения, и это нижняя граница — на ней стратегия работает неустойчиво. Если у вас 40–60 визитов в месяц на несколько кампаний, вы физически не набираете статистику: стратегия либо не обучается, либо переобучается на шуме.

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

Я пробовал этот путь. На объёмах проекта он не заработал.

Рабочее решение: перенести фильтрацию в структуру

Раз качество трафика нельзя отдать алгоритму, его нужно заложить в структуру кампаний — до того, как начнётся обучение.

1. Кластеризация по интенту, а не по частотности

Ядро разбивается на три группы по признаку «что человек собирается сделать прямо сейчас»:

  • транзакционные: «записаться на X», “X сегодня”, «X рядом»;

  • коммерческие с уточнением: «X для расслабления», «X для двоих»;

  • информационные: «виды X», “чем полезен X”, «как делать X».

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

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

2. Отдельные группы под каждую услугу

Одна группа объявлений на несколько услуг усредняет и CTR, и конверсию, и доходимость. Разделение даёт возможность считать r по сегментам, а не по кампании целиком.

3. Отсечение до клика

Часть фильтрации выносится в текст объявления: конкретная услуга, район, формат. Это единственный тип фильтра, который экономит деньги до списания, а не после.

Как считать r, когда данных мало

Основной инструмент — таблица сопоставления. Из CRM или журнала записи выгружается факт визита с идентификатором записи, из Метрики — конверсии с UTM‑метками и ClientID. Джойн по идентификатору, группировка по сегменту.

Даже без полноценной сквозной аналитики это делается выгрузкой в две таблицы и одним merge в pandas. На объёмах в сотни строк этого достаточно.

Дальше считается C_real по каждому сегменту, и решения принимаются по нему.

Про статистическую значимость. При 40–60 визитах в месяц доверительные интервалы для r широкие. Разница в доходимости 0,80 против 0,75 на таких объёмах не значима, и делать вид, что она о чём‑то говорит, не стоит. Что действительно видно на малых выборках — это разница в разы: 0,85 против 0,42 видна уже на нескольких десятках наблюдений. С такими различиями и нужно работать, остальное — шум.

Накопление данных здесь важнее скорости реакции. Первый месяц уходит на сбор, второй — на решения.

Результат и оговорки

За два месяца работы: конверсия в запись 12,3%, стоимость заявки снизилась на 41%, число записей выросло в 1,9 раза, средний чек — на 22%.

Что здесь нужно держать в уме.

Изолировать вклад отдельных изменений я не могу. Параллельно менялась структура кампаний, семантика и посадочные страницы. Сплит‑теста не было — на таком трафике он не набрал бы значимости за разумный срок. Так что «41%» — это результат совокупности изменений, а не эффект какого‑то одного.

Два месяца — короткий период для ниши с сезонностью. Часть динамики объясняется сезонным фактором, отделить его на такой выборке невозможно.

Проценты без абсолютных значений — слабая форма доказательства, понимаю. Абсолютные цифры раскрывать не могу.

Я бы читал эти числа как «направление верное», а не как «эффект равен ровно столько‑то».

Что не сработало

Офлайн‑конверсии как цель оптимизации — описано выше, не хватило объёма данных.

Корректировка ставок по доходимости через сегменты Метрики. Идея была завести сегмент «пришёл» и делать повышающие корректировки на похожие аудитории. На практике сегменты не набирали минимального размера для применения.

Прогноз недохода по времени записи. Гипотеза: чем дальше дата визита от даты записи, тем выше вероятность недохода. Корреляция обнаружилась, но слабая и неустойчивая — на имеющемся объёме использовать её для принятия решений было нельзя.

Выводы

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

  2. Разброс доходимости между сегментами — это не погрешность, а основной фактор. Игнорируя его, вы оптимизируете не ту функцию.

  3. При объёме конверсий, недостаточном для машинного обучения, фильтрация закладывается в структуру кампаний. Автостратегии не решают задачу качества трафика, если событие оптимизации одинаково для целевых и нецелевых пользователей.

  4. Малые данные не повод не считать. Они повод работать с различиями в разы, а не в проценты.

Отдельно интересно мнение тех, кто работал с офлайн‑конверсиями на реально малых объёмах — десятки событий в месяц. Удавалось ли вывести стратегию из обучения, и на какой выборке это начинало давать устойчивый результат?