Реализация AI-персонализации ленты контента в мобильном приложении
Лента — это сердце контентных приложений: новостных агрегаторов, социальных сетей, медиаплатформ. Хронологическая лента убивает engagement, потому что большинство публикаций пользователю неинтересны. Персонализированный ранжировщик решает задачу сортировки кандидатов — он не создаёт контент, а расставляет приоритеты.
Как работает ранжирование ленты
Две стадии: retrieval и ranking
Персонализация ленты — двухступенчатый пайплайн. Первый этап — retrieval: из миллионов публикаций быстро отбираем несколько сотен кандидатов (подписки + похожий контент через ANN). Второй этап — ranking: поверх кандидатов запускаем более тяжёлую модель, которая учитывает сотни признаков и выдаёт финальный порядок.
Разделение этапов принципиально: ranking-модель слишком медленная для всего каталога, retrieval-модель слишком грубая для финального упорядочения.
Признаки для ранжирования
Хорошая ранжирующая модель использует три группы признаков:
Пользовательский контекст: время суток, день недели, текущий сеанс (cold session vs продолжение), активность за последние 24 часа.
Характеристики контента: возраст публикации, engagement rate (лайки/просмотры), скорость набора просмотров в первый час, автор (follower count, исторический CTR постов автора).
Пересечение пользователя и контента: сходство с историей взаимодействий, тематическое пересечение с топ-интересами пользователя, знаком ли пользователь с автором.
# Feature vector для одного кандидата
@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 между профилем юзера и эмбеддингом поста
author_ctr_for_user: float # исторический CTR этого автора у этого юзера
# Context
hour_of_day: int
is_weekend: bool
session_depth: int # сколько постов уже просмотрено в сессии
Модель: LightGBM для продакшн-скорости
Нейросетевые ранжировщики дают лучшее качество, но LightGBM ранжирующая модель (LambdaRank objective) — быстрее в inference и проще в итерации. Типичный ранжировщик для ленты на 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 элементов.
Diversity: избегаем filter bubble
Если просто брать top-N по скору ранжировщика, лента превращается в эхо-камеру — один автор или одна тема занимают все позиции. Постобработка через Maximum Marginal Relevance (MMR) или простую эвристику: не более 2 постов одного автора в первых 10 позициях.
Процесс работы
Аудит текущих сигналов: что логируется, какое качество данных.
Проектирование feature pipeline и collection системы событий.
Обучение ранжирующей модели на исторических данных.
Разработка serving API + мобильный клиент с prefetch.
Метрики: CTR@10, средняя глубина скролла сессии, diversity score (энтропия тем в показанной ленте).
Ориентиры по срокам
LightGBM ранжировщик с базовыми признаками + API — 2–3 недели. Полная система с двухступенчатым retrieval+ranking, diversity-постобработкой и A/B тестированием — 6–10 недель.







