В 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 за всеки потребител в полунощ.
Кодова последователност:
Всеки ден в полунощ:
- (веднъж) Генериране на списък с най-популярно съдържание, top_content всеки ден (на базата на последните 30 дни)
- (веднъж) Генериране на списък с ново съдържание new_content (съдържание, където резултат = null и posted_at › now() — интервал ’24 часа’)
- За всеки потребител вземете user_list = top_content — user_seen_last_30
- За всеки потребител направете mixed_list = f(last_seen_at, shuffle(new_content)[0..K]) + user_list
- За всеки потребител, complete_list = space_items_by_category(mixed_list)
- Запазете този пълен_списък, за да можем да го прочетем в API!
Бъдещи подобрения
Какво искаме да правим в бъдеще, но все още не днес:
- Персонализиране въз основа на интереси - за днешната проста версия, нашето предположение е, че най-доброто съдържание може да се ползва от всички потребители. Ние не правим никакви препоръки на ниво потребителски интереси.
- Машинно обучение за системата за точкуване. Днес системата за оценяване на съдържанието е ръчна и опростена. Ниско висящ плод би бил линейна регресия на теглата на характеристиките на съдържанието.