Что такое рекомендательная система🔗
Рекомендательная система - это инструмент, основанный на методах машинного обучения, для показа пользователю наиболее релевантного для него товара. Система позволяет решать различные бизнес-задачи: увеличивать количество продаж, повышать конверсию, средний чек, удержание, LTV и другие показатели игрового проекта.
В зависимости от набора рекомендованных предметов и таргетной аудитории, рекомендательная система может:
- подбирать персональные офферы и наборы
- показывать рекомендованные предметы без скидки для увеличения продаж неакционных предметов
- настраивать игровой магазин по релевантности для каждого пользователя
- создавать баланс между показами рекламы и продажей предметов в зависимости от профиля игрока
- противодействовать оттоку игроков путем подбора оптимального предложения или подарка в нужный момент времени.
Алгоритм работы с командой RecSys🔗
Шаги🔗
- Формализация бизнес-задачи.
- Проверка различных моделей и выбор лучшей из них.
- Проведение A/B теста модели.
- Оценка экономической эффективности.
- Запуск в Production.
- Поддержка и мониторинг после внедрения.
Что будет нужно от проекта🔗
- Инициализирующая встреча: выбор задачи, целевой аудитории, бизнес метрики.
- Формирование списка предметов для рекомендаций, при необходимости заведение предметов в платежной системе.
- Интеграция игры и API рекомендательной системы.
- Консультация по логам игры и механикам.
- В некоторых случаях требуется добавление логирования дополнительных событий (например, показ и покупка оффера).
Данные🔗
Игровые логи🔗
Для составления профилей по пользователям и предметам для создания рекомендаций используются игровые логи, хранящиеся в едином хранилище - игровом DWH.
Игровые логи, хранящиеся в игровых базах в DWH в свободном формате, преобразуются силами команды RecSys в унифицированный формат данных, понятный рекомендательной платформе.
Базово, используются следующие игровые события:
- Лог траты/получения игровой валюты
- Лог добавления/удаления предмета из инвентаря
- Лог получения уровня
- Лог боя
- Лог показа/клика/покупки предложения/предмета от Рекомендательной системы
Также потребуются следующие логи, если не было интегрировано MRGS SDK:
- Лог логина/логаута
- Лог регистрации в игре
- Лог покупки
Общий формат логов🔗
| Поле | Тип | Обязательное | Типовой | Кастомный | Комментарии |
|---|---|---|---|---|---|
| eventName | string | Y | задается по умолчанию | определяется создателем лога | название лога |
| eventTime | unixtime | Y | обязательное поле | - | может быть клиентским или серверным |
| userId | string | Y | обязательное поле | - | id персонажа из игры |
| mygamesUserId | string | - | - | ||
| sessionId | string | - | - | id игровой сессии (нужно договориться с аналитиками как ее определять) | |
| deviceId | string | - | - | ||
| ip | string | - | - | ||
| idfa | string | - | - | ||
| idfv | string | - | - | ||
| systemVersion | string | - | - | ||
| country | string | Y | обязательное поле | - | |
| language | string | Y | обязательное поле | - | |
| level | int | - | - | ||
| abTestGroup | string | - | - | Группа при AB-тестировании | |
| serverName | string | - | - | Сервер к которому подключен игрок | |
| environment | string | - | - | "dev-стенд, тестовый, игровой" | |
| appVersion | string | Application version | |||
| mrgsSdkVersion | string | ||||
| appBuild | string | Application build number | |||
| value | float | Какое-нибудь значение | |||
| objectId | string | Логи currencyChange, inventoryChange | |||
| transactionId | string | Логи currencyChange, inventoryChange | |||
| amt | float | Лог currencyChange | |||
| currency | string | Лог currencyChange | |||
| cnt | int | Лог inventoryChange | |||
| stage | string | Лог playMatch | |||
| result | string | Лог playMatch | |||
| offerId | string | Логи recsysOfferView, recsysOfferClick, recsysOfferPurchase | id предмета/пака в офере | ||
| eventType | string | Логи recsysOfferView, recsysOfferClick, recsysOfferPurchase | Тип лога recsysOffer | ||
| customString_1 | string | кастомные данные | Кастомный лог | ||
| customString_2 | string | кастомные данные | Кастомный лог | ||
| customString_3 | string | кастомные данные | Кастомный лог | ||
| customString_4 | string | кастомные данные | Кастомный лог | ||
| customString_5 | string | кастомные данные | Кастомный лог | ||
| customString_6 | string | кастомные данные | Кастомный лог | ||
| customString_7 | string | кастомные данные | Кастомный лог | ||
| customString_8 | string | кастомные данные | Кастомный лог | ||
| customString_9 | string | кастомные данные | Кастомный лог | ||
| customString_10 | string | кастомные данные | Кастомный лог | ||
| customString_11 | string | кастомные данные | Кастомный лог | ||
| customString_12 | string | кастомные данные | Кастомный лог | ||
| customString_13 | string | кастомные данные | Кастомный лог | ||
| customString_14 | string | кастомные данные | Кастомный лог | ||
| customString_15 | string | кастомные данные | Кастомный лог | ||
| customString_16 | string | кастомные данные | Кастомный лог | ||
| customString_17 | string | кастомные данные | Кастомный лог | ||
| customString_18 | string | кастомные данные | Кастомный лог | ||
| customString_19 | string | кастомные данные | Кастомный лог | ||
| customString_20 | string | кастомные данные | Кастомный лог | ||
| customInt_1 | int | кастомные данные | Кастомный лог | ||
| customInt_2 | int | кастомные данные | Кастомный лог | ||
| customInt_3 | int | кастомные данные | Кастомный лог | ||
| customInt_4 | int | кастомные данные | Кастомный лог | ||
| customInt_5 | int | кастомные данные | Кастомный лог | ||
| customInt_6 | int | кастомные данные | Кастомный лог | ||
| customInt_7 | int | кастомные данные | Кастомный лог | ||
| customInt_8 | int | кастомные данные | Кастомный лог | ||
| customInt_9 | int | кастомные данные | Кастомный лог | ||
| customInt_10 | int | кастомные данные | Кастомный лог | ||
| customFloat_1 | float | кастомные данные | Кастомный лог | ||
| customFloat_2 | float | кастомные данные | Кастомный лог | ||
| customFloat_3 | float | кастомные данные | Кастомный лог | ||
| customFloat_4 | float | кастомные данные | Кастомный лог | ||
| customFloat_5 | float | кастомные данные | Кастомный лог | ||
| customFloat_6 | float | кастомные данные | Кастомный лог | ||
| customFloat_7 | float | кастомные данные | Кастомный лог | ||
| customFloat_8 | float | кастомные данные | Кастомный лог | ||
| customFloat_9 | float | кастомные данные | Кастомный лог | ||
| customFloat_10 | float | кастомные данные | Кастомный лог |
Лог траты/получения игровой валюты🔗
когда пишется: в момент списания/начисления внутриигровой валюты
eventName: "currency_change"
endpoint:
топик:
Специальные поля:
| поле | тип | обязательное | комментарий |
|---|---|---|---|
| objectId | string | Y | объект взаимодействия, цель траты валюты - название/id, купленного предмета, если пишется в момент траты, если в момент получения, то поле отвечает за источник получения валюты |
| transactionId | string | N | |
| amt | float | Y | (количество потраченной валюты, отрицательное, если происходит списание, положительное, если начисление |
| currency | string | Y | тип потраченной валюты: "gold", "crystals" |
| custom* | при необходимости |
Лог добавления/удаления предмета из инвентаря🔗
когда пишется: в момент добавления/удаления предмета в инвентарь персонажа
eventName: "inventory_change"
endpoint:
топик:
Специальные поля:
| поле | тип | обязательное | комментарий |
|---|---|---|---|
| objectId | string | Y | id добавляемого/удаляемого предмета |
| cnt | int | Y | количество таких предметов, положительное, если добавляется предмет, отрицательное, если удаляется |
| custom* | при необходимости |
Лог получения уровня🔗
когда пишется: в момент обновления уровня персонажа
eventName: "level_gain"
endpoint:
топик:
Специальные поля:
| поле | тип | обязательное | комментарий |
|---|---|---|---|
| level | int | Y | какой уровень получен |
| custom* | при необходимости |
Лог боя🔗
когда пишется: в момент старта/окончания либо другого важного момента боя
eventName: "play_match"
endpoint:
топик:
Специальные поля:
| поле | тип | обязательное | комментарий |
|---|---|---|---|
| stage | string | N | примеры: start, end - момент боя, в котором этот лог пишется |
| result | string | Y | примеры: win, lose, leave, draw - результат боя |
| custom* | при необходимости |
Лог показа/клика/покупки предложения от Рекомендательной системы🔗
когда пишется: в момент просмотра баннера с рекомендациями/ клика по баннеру с рекомендациями и переходе к покупке/ в момент покупки
eventName: "recsys_offer"
endpoint:
топик:
Специальные поля:
| поле | тип | обязательное | комментарий |
|---|---|---|---|
| offerId | string | Y | id предложения из конфига с офферами/предметами, которые участвуют в рекомендациях (offerId) |
| eventType | string | Y | тип события view, click или purchase |
| custom* | при необходимости |
Предметы🔗
Чтобы решать задачу рекомендации, нужно определиться со списком предметов, доступных для рекомендательной системы.
Возможны 2 сценария:
- Проект сам определяет список предметов, доступных рекомендательной системе.
- Команда RecSys проводит исследование и выбирает набор предметов либо предлагает набор сгенерированных предметов (бандлы либо базовые предметы с немного измененными характеристиками).
Сбор данных для обучения🔗
В некоторых случаях, когда запускаются новые предметы или предметы были показаны ранее на специфической аудитории, и накопленные исторические логи показов не могут быть использованы для решения текущей задачи, первое время происходят рандомные показы предметов с целью накопить достаточно статистики по показам для обучения. Обычно этот период занимает от 1 до 3 недель и осуществляется через стандартную схему интеграции с проектом.
Тестирование🔗
Тестирование функционала🔗
Тестирование функционала может быть разделено на несколько частей:
- Тестирование получения ответа рекомендаций по пользователю от API RecSys - на стороне проекта. Базовый подход: на стороне RecSys заводятся в тестовом или продовом API тестовые id пользователей и тестовые рекомендации, которые могут быть рандомными предметами из списка либо покрывать определённые use cases, по договорённости с проектом. Проект запрашивает рекомендации по данным тестовым пользователям и проверяет ответы на соответствие ожиданиям.
- Тестирование показа/начисления офферов внутри игры - проводится на стороне проекта, RecSys проверяет логирование показов и покупок в хранилище.
- Тестирование раздачи A/B тестов, модели - на стороне команды RecSys.
A/B🔗
Разбиение пользователей на группы для A/B тестирования осуществляется на стороне RecSys. Механизм раздачи A/B теста позволяет раскатить эксперимент на разный объем аудитории и гибко перезапускать эксперименты, а также проводить несколько экспериментов одновременно. Все данные по разбиениям сохраняются в исторические таблицы, что позволяет при необходимости легко воспроизвести результаты. Все решения о внедрении рекомендательной системы осуществляются через статистику путем расчета результатов A/B теста и наблюдения статистически значимых изменений.
Интеграция🔗
Получение рекомендаций для пользователя🔗
GET /projects/{project_id}/user/{user_id}/recommendations
Параметры запроса:
| Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
| project_id | string | да | Идентификатор проекта для которого происходит загрузка рекомендаций |
| user_id | string | да | Id пользователя |
Пример запроса и ответа:
Запрос
GET /projects/test/user/1605632/recommendations
Ответ
200 OK
{
"items:": [
{
"id": "123",
"is_mrgs_show": true,
"is_transaction": false,
"images": {
"landscape" : {
"img_2400" : "https://localhost/image%20test_2400.png",
"img_800" : "https://localhost/image%20test_800.png",
"hash_2400" : "abcdefghigklmnopqrstuvwxyz123456",
"hash_800" : "abcdefghigklmnopqrstuvwxyz123456"
},
"portrait" : {
"img_2400" : "https://localhost/image%20test_2400.png",
"img_800" : "https://localhost/image%20test_800.png",
"hash_2400" : "abcdefghigklmnopqrstuvwxyz123456",
"hash_800" : "abcdefghigklmnopqrstuvwxyz123456"
}
}
}
]
}
Авторизация🔗
Чтобы осуществлять запросы на получение рекомендаций к API, нужна базовая авторизация для проекта/студии. Без авторизации API на все запросы будет отвечать 422 ошибкой.
Чтобы авторизоваться, нужно добавить в запрос пользователя и пароль, которые передаются игровому проекту разработчиками Рекомендательной системы. Для тестового и продового API имя пользователя и пароль могут отличаться.
Адреса API🔗
Игровой сервер напрямую обращается к продакшн или тестовому API Рекомендательной системы.
Production API: https://recsys.my.games
QA API: https://api-test.recsys.pvt
Формат ошибок🔗
Общий формат ответа для 422 ошибок:
| Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
| code | string | да | Код ошибки |
| text | string | нет | Описание ошибки |
| context | array | нет | Дополнительная информация по конкретной ошибке |
Ответ
HTTP/1.1 422 Unprocessable Entity for url: https://api-test.recsys.pvt/projects/test/user/105240266041/recommendations/feature/0
Connection: keep-alive
Content-Length: 180
Content-Type: application/json
Keep-Alive: 5
{
"code": "recommendation_not_found",
"text": "Recommendation for 105240266041 not found",
"context": {
"user_id": "105240266041"
}
}
В случае непредвиденной ошибки вернется ответ со статусом 500.
Интеграция на клиенте🔗
Для отправки необходимых событий нужно воспользоваться классом MRGSRecommendations в одноименном модуле MRGS. Все объекты событий можно создать через фабрику MRGSRecSysEvent, либо через стандартный конструктор класса, отвечающего за событие.
Примеры отправки событий:
// Log of spending/receiving game currency
var currencyChangeEvent = MRGSRecSysEvent.CurrencyChange("item_id", "transaction_id", 123.4f, "gold");
// Event sending
MRGSRecommendations.Instance.TrackEvent(currencyChangeEvent);
// Log of adding/removing an item from inventory
MRGSRecommendations.Instance.TrackEvent(MRGSRecSysEvent.InventoryChange("item_id", -123));
// Log of getting level
MRGSRecommendations.Instance.TrackEvent(MRGSRecSysEvent.LevelGain(555));
// Battle log
MRGSRecommendations.Instance.TrackEvent(MRGSRecSysEvent.PlayMatch("start", "win"));
// Log of showing/click/purchase of the offer of the recommendation system
MRGSRecommendations.Instance.TrackEvent(MRGSRecSysEvent.OfferAction("some_offer_id", MRGSRecSysEvent.ActionTypeView));
MRGSRecommendations.Instance.TrackEvent(MRGSRecSysEvent.OfferAction("some_offer_id", MRGSRecSysEvent.ActionTypeClick));
MRGSRecommendations.Instance.TrackEvent(MRGSRecSysEvent.OfferAction("some_offer_id", MRGSRecSysEvent.ActionTypePurchase));
// Log of spending/receiving game currency
MRGSRecSysEvent* ev = [MRGSRecSysEvent currencyChangeWithObjectId:@"item_id" transactionId:@"transaction_id" amount:123.4f currency:@"gold"];
// Event sending
[[MRGSRecommendations sharedInstance] trackEvent:ev];
// Log of adding/removing an item from inventory
[[MRGSRecommendations sharedInstance] trackEvent:[MRGSRecSysEvent inventoryChangeWithObjectId:@"item_id" count:-123]];
// Log of getting level
[[MRGSRecommendations sharedInstance] trackEvent:[MRGSRecSysEvent levelGain:555]];
// Battle log
[[MRGSRecommendations sharedInstance] trackEvent:[MRGSRecSysEvent playMatchWithStage:@"start" result:@"win"]];
// Log of showing/click/purchase of the offer of the recommendation system
[[MRGSRecommendations sharedInstance] trackEvent:[MRGSRecSysEvent offerActionWithOfferId:@"some_offer_id" actionType:kMRGSRecSysOfferActionView]];
[[MRGSRecommendations sharedInstance] trackEvent:[MRGSRecSysEvent offerActionWithOfferId:@"some_offer_id" actionType:kMRGSRecSysOfferActionClick]];
[[MRGSRecommendations sharedInstance] trackEvent:[MRGSRecSysEvent offerActionWithOfferId:@"some_offer_id" actionType:kMRGSRecSysOfferActionPurchase]];
import games.my.mrgs.recsys.MRGSRecSysEvent;
import games.my.mrgs.recsys.MRGSRecSysEvents;
import games.my.mrgs.recsys.MRGSRecommendations;
// Log of spending/receiving game currency
final MRGSRecSysEvent currencyChangeEvent = new MRGSRecSysEvents.CurrencyChangeEvent("item_id", "transaction_id", 123.4f, "gold");
// Event sending
MRGSRecommendations.getInstance().trackEvent(currencyChangeEvent);
// Log of adding/removing an item from inventory
MRGSRecommendations.getInstance().trackEvent(new MRGSRecSysEvents.InventoryChangeEvent("item_id", -123));
// Log of getting level
MRGSRecommendations.getInstance().trackEvent(new MRGSRecSysEvents.LevelGainEvent(555));
// Battle log
MRGSRecommendations.getInstance().trackEvent(new MRGSRecSysEvents.PlayMatchEvent("start", "win"));
// Log of showing/click/purchase of the offer of the recommendation system
MRGSRecommendations.getInstance().trackEvent(new MRGSRecSysEvents.OfferActionEvent("some_offer_id", MRGSRecSysEvent.ACTION_TYPE_VIEW));
MRGSRecommendations.getInstance().trackEvent(new MRGSRecSysEvents.OfferActionEvent("some_offer_id", MRGSRecSysEvent.ACTION_TYPE_CLICK));
MRGSRecommendations.getInstance().trackEvent(new MRGSRecSysEvents.OfferActionEvent("some_offer_id", MRGSRecSysEvent.ACTION_TYPE_PURCHASE));
Дополнительные параметры к событиям🔗
С версии MRGS 6.4.0 можно добавить дополнительные параметры к событиям. Вы можете добавлять к ним поля из унифицированных логов, такие как customString1-20, level, customFloat и другие.
import games.my.mrgs.recsys.MRGSRecommendations;
import games.my.mrgs.recsys.MRGSRecSysEvent;
import games.my.mrgs.recsys.MRGSRecSysEvents;
final MRGSRecSysEvent event = MRGSRecSysEvents.LevelGainEvent(555);
event.putParam("test_param_1", "testStr");
event.putParam("customFloat_1", 123.456f);
MRGSRecommendations.getInstance().trackEvent(event);
Дата создания: 2020-12-15