Реалізація AI-персоналізації ленти контенту в мобільних додатках
Лента — це серце контентних додатків: новинних агрегаторів, соціальних мереж, медіаплатформ. Хронологічна лента знищує залученість користувачів, тому що більшість публікацій їм нецікаві. Персоналізований рейтинг вирішує задачу сортування кандидатів — він не створює контент, а розставляє пріоритети.
Як працює рейтинг ленти
Два етапи: retrieval та ranking
Персоналізація ленти — двохступінчатий pipeline. Перший етап — retrieval: із мільйонів публікацій швидко вибираємо кілька сотень кандидатів (підписки + подібний контент через ANN). Другий етап — ranking: на топі кандидатів запускаємо більш важку модель, яка враховує сотні ознак і видає фінальний порядок.
Розділення етапів принципово: ranking-модель занадто повільна для всього каталогу, retrieval-модель занадто грубе для фінального упорядкування.
Ознаки для рейтингу
Хорошої рейтингової моделі використовують три групи ознак:
Контекст користувача: час доби, день тижня, поточний сеанс (новий vs. продовження), активність за останні 24 години.
Характеристики контенту: вік публікації, engagement rate (лайки/перегляди), швидкість набору переглядів у першу годину, автор (кількість підписників, історичний CTR постів автора).
Перетин користувача та контенту: схожість з історією взаємодій, тематичне перетинання з топ-інтересами користувача, чи знайомий користувач з автором.
# Вектор ознак для одного кандидата
@dataclass
class RankingFeatures:
# Content features
post_age_hours: float
engagement_rate_24h: float
viral_velocity: float # views_per_hour in first 2 hours
# User-content interaction
topic_affinity: float # cosine sim між профілем користувача та embedding поста
author_ctr_for_user: float # історичний CTR цього автора для цього користувача
# Context
hour_of_day: int
is_weekend: bool
session_depth: int # скільки постів уже переглянуто в сеансі
Модель: LightGBM для продакшн-швидкості
Нейромережеві рейтингувальники дають краще якості, але LightGBM рейтингова модель (LambdaRank objective) — швидша у виконанні та простіша у ітерації. Типовий рейтингувальник ленти на LightGBM видає скори для 200 кандидатів за 2–5 мс на сервері.
import lightgbm as lgb
model = lgb.LGBMRanker(
objective='lambdarank',
metric='ndcg',
ndcg_eval_at=[5, 10, 20],
n_estimators=500,
learning_rate=0.05,
num_leaves=63
)
model.fit(
X_train, y_train, # y — relevance labels: 0=ignored, 1=viewed, 2=liked, 3=shared
group=train_groups, # розмір груп запитів
eval_set=[(X_val, y_val)],
eval_group=[val_groups]
)
Мобільна частина: prefetch та безшовний скролінг
Користувач не повинен чекати завантаження ленти. Реалізуємо prefetch: коли користувач прогорнув до 70% поточного батчу, фоново завантажуємо наступні 20 постів.
// Android: Paging 3 з prefetch для персоналізованої ленти
class FeedPagingSource(
private val feedApi: FeedApi,
private val userId: String
) : PagingSource<String, FeedPost>() {
override suspend fun load(params: LoadParams<String>): LoadResult<String, FeedPost> {
return try {
val response = feedApi.getPersonalizedFeed(
userId = userId,
cursor = params.key,
pageSize = params.loadSize
)
LoadResult.Page(
data = response.posts,
prevKey = null,
nextKey = response.nextCursor
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
// ViewModel
val feed = Pager(
config = PagingConfig(pageSize = 20, prefetchDistance = 5),
pagingSourceFactory = { FeedPagingSource(feedApi, userId) }
).flow.cachedIn(viewModelScope)
prefetchDistance = 5 — Paging 3 почне завантажувати наступну сторінку, коли до кінця залишиться 5 елементів.
Різноманітність: уникаємо filter bubble
Якщо просто брати top-N за скором рейтингувальника, лента перетворюється на echo chamber — один автор або одна тема займають усі позиції. Постобробка через Maximum Marginal Relevance (MMR) або проста евристика: не більше 2 постів одного автора у першої 10 позиціях.
Процес роботи
Аудит поточних сигналів: що логується, яка якість даних.
Проектування feature pipeline та системи збирання подій.
Навчання рейтингової моделі на історичних даних.
Розробка serving API + мобільний клієнт з prefetch.
Метрики: CTR@10, середня глибина скролінгу сеансу, diversity score (ентропія тем у показаній ленті).
Орієнтири за часом
LightGBM рейтингувальник з базовими ознаками + API — 2–3 тижні. Повна система з двохступінчатим retrieval+ranking, diversity-постобробкою та A/B тестуванням — 6–10 тижнів.







