В AppMonet предлагаме най-ангажиращите видео и дисплейно съдържание на милиони потребители ежедневно. Препоръката за съдържание е в основата на стека. Поради нашата уникална позиция в екосистемата, ние създадохме система за класиране на съдържанието, която е персонализирана за нашите случаи на употреба.

Нашият сценарий

Няколко интересни характеристики на нашите случаи на употреба:

  • Половината от нашето съдържание е чувствително към времето. Другата половина е „evergreen” и остават актуални до 3 месеца.
  • Искаме да представим възможно най-доброто свежо съдържание на потребителя всеки път, когато го посети, смесвайки вечнозелено и чувствително към времето съдържание.
  • Опитваме се да избягваме представянето на едно и също съдържание на потребителя два пъти.
  • Що се отнася до вечнозеленото съдържание, всеки ден има само няколко много добри. Искаме да сме сигурни, че потребителят няма да пропусне нито едно от последното си посещение.

Разлики между нас и други платформи

  • Reddit – Reddit разчита на потребителите да превключват между „горния“ и „горещ“ режим. Нито едно от тях не работи за нашия случай на смесване на чувствително към времето съдържание и вечнозелено съдържание.
  • Netflix – Netflix е повече за персонализиране въз основа на интереси и цялото им съдържание е вечнозелено (като говорим за онези стари филми!)
  • Instagram/Facebook Feed — Instagram може да е най-сходният. Ако потребител не е посещавал няколко дни, Instagram ще избере най-доброто съдържание за последните 3 дни. Въпреки това, тъй като Instagram има много повече съдържание от нас и издателите периодично публикуват вечнозелено съдържание, те могат да погледнат назад само за 3 дни. Трябва да погледнем назад 3 месеца.

Нашето решение

Стъпка 1: Генерирайте списък с най-популярно съдържание

Алгоритъмът за оценяване тук взема предвид всяка част от съдържанието:

  • честота на кликване
  • прекарано време
  • реакции в социалните медии (ако има такива)
  • Свежест: за чувствително към времето съдържание, свежестта постепенно пада под 1, когато съдържанието остарява.

По-долу е опростеният псевдокод за тръбопровода за данни за генериране на най-добрия списък със съдържание:

Стъпка 2: Проследете видяното съдържание по потребител

Това е трудната стъпка. За всеки потребител, видян през последните N дни (където N = 60), искаме да им предоставим най-доброто съдържание, минус всичко, което са видели през последните N дни

За да постигнем това, имаме нужда от лесен начин за търсене на това, което потребителят е видял. Ето изискванията:

  • бъдете много бързи за заявки
  • да е точен, поне от последното посещение на потребителя (тъй като може да посети по всяко време, то трябва да е много близо до текущото състояние)
  • лесно мащабируем (обслужва милиони потребители)

Ето няколко опции, които проучихме:

Вариант 1: Cloudflare работници + KV

  • pro: прост + мащабируем.
  • против: всъщност не знаем много за неговата издръжливост + не можем да го контролираме (резервни копия и т.н.), така че ще трябва да правим периодично архивиране. Не е ясно как ще се справим с това

Вариант 2. openresty (nginx) + redis

  • професионалист: просто + бързо, правех го преди и работеше добре
  • pro: вече имаме екземпляра „чудовище“. За най-добра производителност може да стартира redis на сокет на домейн с nginx на същата машина, вероятно може да обработва ~50k едновременни
  • против: трябва сами да управляваме инфраструктурата
  • против: мащабирането след 1 машина е малко неизвестно. Можем да клъстерираме redis и да поставим nginx зад LB.

Опция 3. kinesis + lambda + redis / PG
Това ще използва нашия съществуващ канал за събития, за да напише нов тип събитие в kinesis. Cleaner ще го постави в нов поток, който ще бъде прочетен от ламбда. Тази ламбда записва групираните събития в redis, PG или дори в конвейер 🙂

  • pro: използва съществуващата инфраструктура за конвейер на събития
  • професионалист: винаги може да промени слоя на DB (напр. стартиране на redis и превключване към конвейер)
  • pro: всички компоненти се хостват (ако използваме elasticache или RDS)
  • con: трябва да напишем наш собствен ламбда код
  • con: струва повече от CF работници + KV

Въз основа на нашия минал опит изглежда, че вариант 3 е най-добрият, тъй като има най-малко неизвестни и е най-добре документиран (все още се притесняваме, че работникът KV има проблеми, за които все още не знаем)

Стъпка 3: Извадете видяното съдържание за списъка с най-популярно съдържание на всеки потребител

Тази стъпка просто ще се изпълни след Стъпка 1 и 2 за всеки потребител в полунощ.

Кодова последователност:

Всеки ден в полунощ:

  1. (веднъж) Генериране на списък с най-популярно съдържание, top_content всеки ден (на базата на последните 30 дни)
  2. (веднъж) Генериране на списък с ново съдържание new_content (съдържание, където резултат = null и posted_at › now() — интервал ’24 часа’)
  3. За всеки потребител вземете user_list = top_content — user_seen_last_30
  4. За всеки потребител направете mixed_list = f(last_seen_at, shuffle(new_content)[0..K]) + user_list
  5. За всеки потребител, complete_list = space_items_by_category(mixed_list)
  6. Запазете този пълен_списък, за да можем да го прочетем в API!

Бъдещи подобрения

Какво искаме да правим в бъдеще, но все още не днес:

  • Персонализиране въз основа на интереси - за днешната проста версия, нашето предположение е, че най-доброто съдържание може да се ползва от всички потребители. Ние не правим никакви препоръки на ниво потребителски интереси.
  • Машинно обучение за системата за точкуване. Днес системата за оценяване на съдържанието е ръчна и опростена. Ниско висящ плод би бил линейна регресия на теглата на характеристиките на съдържанието.