Реализация AI-рекомендательной системы контента (Content-Based) в мобильном приложении
Представьте: вы запустили новостное приложение с тысячами статей, но у новых пользователей нет истории чтения. Необходимо подобрать каждому релевантный контент без данных о других. Content-Based Filtering (основанный на атрибутах) решает эту задачу с первого дня. Мы анализируем метаданные каждой статьи — теги, категории, авторов, текст — и строим профиль пользователя на основе того, с чем он взаимодействовал. Для приложений, где конфиденциальность важна, CB позволяет работать полностью локально, без отправки данных на сервер. Это особенно актуально для финансовых или медицинских приложений. В этой статье мы разберём ключевые компоненты CB-системы: от эмбеддингов до on-device рекомендаций, на примерах кода для iOS и Python. On-device CB снижает затраты на облачные вычисления до $1000 в месяц, а для каталога из 10 000 айтемов с эмбеддингами размерностью 384 индекс занимает всего ~15 МБ. Профиль пользователя весит 1.2 КБ — рекомендации вычисляются за 5 мс на iPhone 12.
Когда Content-Based лучше Collaborative Filtering?
Три сценария, где CB предпочтительнее:
-
Нишевой контент с уникальными метаданными. Статьи, рецепты, туристические маршруты — каждый айтем имеет богатый набор атрибутов (теги, категории, авторы, локации). CF опирается на сигнал «пользователи похожи», но для нишевого контента таких пользователей может быть слишком мало, особенно на старте.
-
Privacy-first архитектура. CB может работать полностью на устройстве — профиль пользователя хранится локально, рекомендации строятся без отправки данных на сервер. Это критично для приложений с чувствительными данными.
-
Длинный хвост контента. Новая статья, опубликованная час назад, не имеет истории взаимодействий для CF. CB рекомендует её сразу, как только метаданные проиндексированы.
Как on-device CB снижает затраты?
On-device CB исключает серверные вычисления. Для каталога из 10 000 айтемов с эмбеддингами размерностью 384 индекс занимает ~15 МБ. Профиль пользователя — 1.2 КБ. Рекомендации вычисляются за 5 мс на iPhone 12. Средняя экономия на облачных вычислениях после внедрения on-device CB составляет порядка $500–$1000 в месяц для каталога из 10 000 айтемов. При этом конфиденциальность данных обеспечивается автоматически.
Как Content-Based работает на устройстве?
Для небольших каталогов (до 50K айтемов) весь CB-поиск можно вынести на устройство. Профиль пользователя хранится в UserDefaults, эмбеддинги контента загружаются при старте приложения (JSON ~20 МБ для 50K айтемов × 384d float32). Рекомендации вычисляются локально — без сетевых запросов, без задержек.
Код: on-device CB-поиск на Swift
class OnDeviceRecommender {
private let userProfileKey = "user_embedding_v2"
private var itemIndex: [(id: String, embedding: [Float])] = []
func loadItemIndex(from url: URL) {
let data = try! Data(contentsOf: url)
itemIndex = try! JSONDecoder().decode([(id: String, embedding: [Float])].self, from: data)
}
func getRecommendations(count: Int) -> [String] {
guard let profileData = UserDefaults.standard.data(forKey: userProfileKey),
let profile = try? JSONDecoder().decode([Float].self, from: profileData)
else { return popularItemIds(count: count) }
return itemIndex
.map { item in (item.id, cosineSimilarity(profile, item.embedding)) }
.sorted { $0.1 > $1.1 }
.prefix(count)
.map { $0.0 }
}
private func cosineSimilarity(_ a: [Float], _ b: [Float]) -> Float {
zip(a, b).map(*).reduce(0, +)
}
}
Эмбеддинги индекса обновляются при старте или по расписанию. Мы используем предвычисленные векторы с сервера, что минимизирует нагрузку на устройство.
Ядро системы: TF-IDF и эмбеддинги текста
Для статей, описаний, новостей — два подхода: TF-IDF для скорости, sentence embeddings для качества. На практике используем sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 — 278 МБ, поддерживает русский. Каждый айтем превращается в вектор 384 измерений. Согласно TF-IDF, частота слов и обратная частота документов дают простую, но эффективную меру релевантности.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
def embed_article(article: Article) -> np.ndarray:
text = f"{article.title}. {article.description}. {' '.join(article.tags)}"
return model.encode(text, normalize_embeddings=True)
def similarity(v1: np.ndarray, v2: np.ndarray) -> float:
return float(np.dot(v1, v2))
Профиль пользователя — скользящее среднее
Профиль пользователя — взвешенное среднее эмбеддингов контента, с которым он взаимодействовал. Недавние взаимодействия весят больше (экспоненциальное затухание с коэффициентом 0.9):
def update_user_profile(profile: np.ndarray, new_item_embedding: np.ndarray,
interaction_weight: float, decay: float = 0.9) -> np.ndarray:
updated = decay * profile + (1 - decay) * interaction_weight * new_item_embedding
return updated / np.linalg.norm(updated)
Структурированные метаданные: не только текст
Для каталога товаров текстовые эмбеддинги дополняются категориальными признаками: категория, бренд, ценовой диапазон, цвет. Финальный вектор — конкатенация нормализованного текстового эмбеддинга и one-hot/ordinal признаков с весами 0.7 и 0.3 соответственно:
def build_item_vector(item: Product) -> np.ndarray:
text_emb = embed_text(f"{item.name} {item.description}")
cat_features = encode_categorical({
'category': item.category_id,
'brand': item.brand_id,
'price_range': bucket_price(item.price)
})
return np.concatenate([text_emb * 0.7, cat_features * 0.3])
Сравнение подходов: Content-Based vs Collaborative
| Параметр | Content-Based | Collaborative Filtering |
|---|---|---|
| Требуется история пользователей | Нет | Да |
| Работает с новым контентом | Сразу | Только после взаимодействий |
| Конфиденциальность | Локально | Требует сервера |
| Качество для нишевого контента | Хорошее | Плохое (разреженность) |
| Вычислительная нагрузка | Низкая (на устройстве) | Средняя (на сервере) |
| Экономия на инфраструктуре | До $1000/мес | Зависит от масштаба |
Как реализовать on-device CB на Swift: пошаговая инструкция
- Индексация контента. На сервере вычисляем эмбеддинги для каждого айтема с помощью sentence-transformers. Сохраняем в JSON-файл с полями id и embedding.
- Загрузка индекса. При запуске приложения загружаем JSON в массив OnDeviceRecommender. Для ускорения можно использовать бинарный формат.
- Сбор взаимодействий. Каждый раз, когда пользователь открывает или лайкает контент, сохраняем эмбеддинг айтема в UserDefaults с меткой времени.
- Обновление профиля. При каждом взаимодействии пересчитываем скользящее среднее с экспоненциальным затуханием.
- Генерация рекомендаций. Вызываем getRecommendations при показе ленты или в фоне.
Что входит в работу
Мы предоставляем:
- Анализ структуры контента и доступных метаданных
- Выбор оптимальной модели эмбеддингов под язык и домен
- Построение индекса и механизма обновления профиля
- Реализация on-device или серверного решения
- Интеграция с существующим бэкендом
- Документация и обучение команды
Наш опыт: более 10 лет в мобильной разработке, 30+ проектов с AI-рекомендациями. Гарантируем поддержку после внедрения. Свяжитесь с нами для оценки вашего проекта — мы подберём оптимальную архитектуру под ваш кейс. Закажите разработку рекомендательной системы под ваш проект.
Ориентиры по срокам
| Сценарий | Сроки |
|---|---|
| Серверный CB с готовыми эмбеддингами и API | 1–1,5 недели |
| On-device вариант для iOS/Android с локальным индексом | 2–3 недели |
| Гибрид с частичной on-device обработкой | 3–4 недели |
Получите консультацию: расскажите о своём контенте и пользовательских сценариях, и мы предложим решение.







