Как тестировать рекомендации через внешний A/B-инструмент
Если вы уже распределяете пользователей в стороннем A/B-инструменте, используйте его группу для выбора selector Gravity Field. Для группы A запрашивайте одну рекомендательную кампанию, для B — другую; обе показывайте в одном месте сайта или приложения.
Внешний инструмент назначает и сохраняет группу, собирает данные эксперимента и рассчитывает его результат. Gravity Field подбирает рекомендации выбранной кампании и принимает события для своей аналитики. Ваша интеграция связывает эти части: выбирает selector, отображает контент и отправляет события каждой системе.
1. Подготовьте две кампании в GF
Создайте две API-кампании с рекомендациями или две мобильные inline-кампании, если используете готовый inline-компонент SDK.
Имена условные: согласуйте собственные selector с разработчиком. Используйте отдельный selector для каждой кампании, без других активных кампаний с тем же именем в секции.
Для обеих кампаний:
- Настройте одинаковые placement, контекст, аудиторию, расписание и число товаров. Меняйте только то, что сравнивает тест, например рекомендательную стратегию; UI и правила обработки ошибок оставьте одинаковыми.
- В сценарии оставьте один вариант с 100% трафика. Выберите ручное распределение, не включайте «Автопилот» и дополнительную контрольную вариацию. Внешние 50/50 относятся к выбору между кампаниями; внутри каждой кампании повторное 50/50 не нужно.
- Проверьте все ограничения охвата: таргетинг, дополнительные условия распределения, расписание и лимиты показов. У двух кампаний они должны соответствовать одному дизайну теста. Selector выбирает кампанию, но не отменяет её условия и внутренний выбор вариации.
- Если проект использует глобальную контрольную группу, заранее определите её участие. Например, исключите её из аудитории этого эксперимента до назначения A/B. Не меняйте общую контрольную группу проекта ради теста без согласованного решения.
- Опубликуйте сценарий/вариант и активируйте обе кампании. Проверьте выдачу каждой на тестовом пользователе.
В одной кампании могут использоваться рекомендации Gravity Field, в другой — клиентские, если такая стратегия уже подключена. Способ распределения пользователей и вызова selector остаётся тем же.
2. Закрепите внешнюю группу за пользователем
Создайте эксперимент в вашем A/B-инструменте и задайте доли A/B. Получайте сохранённое назначение для стабильного идентификатора пользователя. Не вычисляйте случайную группу заново при каждом просмотре, запросе или перезапуске приложения.
Согласуйте поведение при входе в аккаунт, смене устройства и для анонимных посетителей. Группа должна следовать правилам внешнего инструмента; идентификацию в GF продолжайте выполнять обычным способом. Не создавайте новый GF-профиль ради выбора A или B.
Пока внешнее назначение неизвестно, не запрашивайте обе кампании и не назначайте группу случайно локально. Используйте заранее согласованный сценарий ожидания или базовый интерфейс вне теста.
3. Выберите один selector и запросите контент
Пример логики интеграции: assignedGroup приходит из вашего A/B-инструмента. Это переменная приложения, а не новое поле API Gravity Field.
function selectorForGroup(assignedGroup) {
if (assignedGroup === "A") return "home-recs-a";
if (assignedGroup === "B") return "home-recs-b";
return null; // Назначения нет: блок эксперимента пока не загружаем.
}
Передайте выбранный selector стандартному пути вашей интеграции.
В запросе /choose передайте один элемент data[]. Ниже фрагмент для группы A; для B заменяется только selector:
{
"data": [{ "selector": "home-recs-a" }],
"options": {
"isBuildEngagementUrl": true,
"isImplicitImpression": false
}
}
Остальные поля (sec, user, ctx, device) заполните по стандартной интеграции. Сохраняйте пользователя и сессию, передавайте одинаковый контекст реального placement для обеих групп. Просмотр страницы отправляйте через /visit или isImplicitPageview один раз, без дублирования. Здесь isImplicitImpression: false отключает учёт impression при выдаче: приложение отправит его при отображении блока через URL из ответа.
После получения группы, внутри coroutine:
val selector = when (assignedGroup) {
"A" -> "home-recs-a"
"B" -> "home-recs-b"
else -> null
}
if (selector != null) {
val response = GravitySDK.instance.getContentBySelector(
org.json.JSONObject.quote(selector), pageContext
)
// Передайте response существующему рендереру вашего приложения.
}
В Android 1.0.4 кодируйте обычное имя selector через JSONObject.quote ровно один раз.
Сигнатура и структура ответа: Android SDK.
После получения группы, в async-контексте:
let selector: String? = assignedGroup == "A" ? "home-recs-a"
: assignedGroup == "B" ? "home-recs-b" : nil
if let selector = selector {
let response = await GravitySDK.instance.getContentBySelector(
selector: selector, pageContext: pageContext
)
// Передайте response существующему рендереру вашего приложения.
}
Сигнатура и структура ответа: iOS SDK.
Если используете встроенный inline-компонент, передайте ему выбранный selector вместо ручного запроса; подключение и tracking описаны в inline-гайде. Выберите один способ загрузки. Запросы обеих кампаний «для сравнения», общий group selector или переключение на selector B при пустом ответе A смешивают условия теста. При пустой выдаче/ошибке сохраняйте внешнюю группу и применяйте согласованный fallback.
4. Передавайте показы, клики и конверсии
Назначение A/B, получение ответа и фактический показ блока — разные события. Заранее договоритесь, что означает exposure во внешнем инструменте и какая аудитория входит в расчёт. Не отмечайте внешний exposure только потому, что /choose вернул ответ. В SDK isImplicitImpression по умолчанию включён: GF может учитывать impression уже при выдаче. Для ручного учёта impression при показе настройте Options(isImplicitImpression = false) на Android или Options(isImplicitImpression: false) на iOS через setOptions(...), сохранив остальные настройки интеграции. Эти опции действуют на последующие запросы SDK, поэтому согласуйте режим для приложения целиком.
- В GF: при ручном рендеринге отправляйте content/product engagement с tracking-данными именно полученного ответа. В SDK используйте
sendContentEngagement(...)/sendProductEngagement(...)с исходнымиcampaign,content,slot; встроенный UI берёт часть tracking на себя. При клике на свою товарную карточку отправьте product click и выполните навигацию приложения. - В GF: продолжайте отправлять просмотры, добавления в корзину и покупки через стандартные события или
trackView(...)/triggerEvent(...)SDK. Сохраняйте идентичность пользователя, контекст и SKU из фида. Настройте одинаковые цели и атрибуцию для двух кампаний. - Во внешний инструмент: отдельно передавайте назначение, exposure и выбранные метрики средствами этого инструмента. Сохраняйте связь «эксперимент → пользователь → группа → selector/кампания» в своей аналитике. GF не отправляет результаты стороннему A/B-инструменту автоматически.
Отчёты GF покажут работу каждой кампании. Итог A/B между двумя внешними группами рассчитывайте в выбранной системе экспериментов с её правилами аудитории и метрик; это отдельный эксперимент от встроенного A/B внутри одного сценария GF.
5. Проверьте тест перед запуском
- Тестовый пользователь A запрашивает только
home-recs-a, B — толькоhome-recs-b; повторное посещение сохраняет группу. - Обе кампании дают ожидаемые товары в одном placement и контексте, без второго случайного распределения и конфликтующего таргетинга.
- Фактические показы, товарные клики и покупки учитываются без дублей; группа во внешней аналитике совпадает с выбранным selector.
- Пустые ответы, ошибки и fallback не переводят пользователя между A и B. Отдельно видны назначенные пользователи и реально показанные блоки.