Реализация AI-рекомендаций товаров на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация AI-рекомендаций товаров на сайте
Сложная
~1-2 недели
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Реализация AI-рекомендаций товаров на сайте

Рекомендации товаров — одна из наиболее измеримых инвестиций в e-commerce: по данным McKinsey, 35% выручки Amazon генерируется через рекомендации. Технически задача сложнее, чем для контента: нужно учитывать категории, атрибуты товаров, сезонность, ценовые диапазоны, наличие на складе.

Типы рекомендаций в e-commerce

  • «Похожие товары» — товары с близкими атрибутами (карточка товара)
  • «Часто покупают вместе» — complementary items (корзина, чекаут)
  • «Персональные рекомендации» — на главной, в разделе (история покупок + просмотры)
  • «Альтернативы» — если товар отсутствует, предложить замену
  • «После покупки» — upsell, аксессуары, расходники

Структура данных товара для эмбеддинга

function buildProductText(product) {
  return [
    product.name,
    product.brand,
    product.category + ' > ' + product.subcategory,
    product.description?.slice(0, 500),
    product.tags?.join(', '),
    Object.entries(product.attributes || {})
      .map(([k, v]) => `${k}: ${v}`)
      .join(', '),
  ].filter(Boolean).join('\n');
}

// Индексирование
async function indexProduct(product) {
  if (!product.active || product.stock === 0) return; // не индексируем неактивные

  const text = buildProductText(product);
  const { data: [{ embedding }] } = await openai.embeddings.create({
    model: 'text-embedding-3-small',
    input: text,
  });

  await db.query(`
    INSERT INTO product_embeddings (product_id, embedding, updated_at)
    VALUES ($1, $2::vector, NOW())
    ON CONFLICT (product_id) DO UPDATE
    SET embedding = $2::vector, updated_at = NOW()
  `, [product.id, JSON.stringify(embedding)]);
}

Похожие товары

async function getSimilarProducts(productId, options = {}) {
  const { limit = 8, minPrice, maxPrice, inStockOnly = true } = options;

  const result = await db.query(`
    WITH source AS (
      SELECT pe.embedding, p.price, p.category_id
      FROM product_embeddings pe
      JOIN products p ON p.id = pe.product_id
      WHERE pe.product_id = $1
    )
    SELECT
      p.id, p.name, p.slug, p.price,
      p.main_image, p.rating, p.reviews_count,
      1 - (pe.embedding <=> source.embedding) AS similarity
    FROM product_embeddings pe
    JOIN products p ON p.id = pe.product_id
    CROSS JOIN source
    WHERE pe.product_id != $1
      AND p.active = true
      AND ($2::boolean IS FALSE OR p.stock > 0)
      AND ($3::numeric IS NULL OR p.price >= $3)
      AND ($4::numeric IS NULL OR p.price <= $4)
    ORDER BY pe.embedding <=> source.embedding
    LIMIT $5
  `, [productId, inStockOnly, minPrice || null, maxPrice || null, limit]);

  return result.rows;
}

Ассоциативные правила: "часто покупают вместе"

Market Basket Analysis через Apriori или FP-Growth на истории заказов:

# Python: периодическое обновление (cron раз в сутки)
from mlxtend.frequent_patterns import fpgrowth, association_rules
import pandas as pd

def compute_frequently_bought_together():
    # Загружаем заказы
    orders = fetch_orders_last_90_days()  # [(order_id, product_id)]

    # Создаём матрицу заказ-товар
    basket = orders.groupby(['order_id', 'product_id'])['product_id'] \
        .count().unstack().fillna(0)
    basket = basket.map(lambda x: 1 if x > 0 else 0)

    # FP-Growth
    frequent_sets = fpgrowth(basket, min_support=0.005, use_colnames=True)
    rules = association_rules(frequent_sets, metric='lift', min_threshold=1.5)

    # Сохраняем в БД
    for _, rule in rules.iterrows():
        antecedent = list(rule['antecedents'])[0]
        consequent = list(rule['consequents'])[0]
        save_association(antecedent, consequent, rule['confidence'], rule['lift'])
// Node.js: получение ассоциаций
async function getFrequentlyBoughtTogether(productId, limit = 4) {
  const result = await db.query(`
    SELECT
      p.id, p.name, p.slug, p.price, p.main_image,
      ar.confidence, ar.lift
    FROM association_rules ar
    JOIN products p ON p.id = ar.consequent_id
    WHERE ar.antecedent_id = $1
      AND p.active = true AND p.stock > 0
    ORDER BY ar.lift DESC
    LIMIT $2
  `, [productId, limit]);

  return result.rows;
}

Персональные рекомендации через матричную факторизацию

# Обучение модели на implicit feedback
import implicit
from scipy.sparse import csr_matrix

def train_product_model(events):
    # events: user_id, product_id, weight
    # weight: view=1, add_to_cart=3, purchase=10, review=8
    users_idx = {u: i for i, u in enumerate(events['user_id'].unique())}
    items_idx = {p: i for i, p in enumerate(events['product_id'].unique())}

    matrix = csr_matrix((
        events['weight'],
        (events['user_id'].map(users_idx), events['product_id'].map(items_idx))
    ))

    model = implicit.als.AlternatingLeastSquares(factors=64, iterations=30)
    model.fit(matrix.T)  # item-user

    return model, users_idx, items_idx

# Получение рекомендаций для пользователя
def get_personal_recs(user_id, model_data, n=12):
    model, users_idx, items_idx = model_data
    items_idx_rev = {v: k for k, v in items_idx.items()}

    if user_id not in users_idx:
        return []  # cold start — вернём trending

    user_items = get_user_item_matrix_row(user_id, users_idx, items_idx)
    ids, scores = model.recommend(users_idx[user_id], user_items, N=n)

    return [{'product_id': items_idx_rev[i], 'score': float(s)} for i, s in zip(ids, scores)]

Cold Start: новые пользователи и товары

Новый пользователь — показываем бестселлеры с персонализацией по UTM/категории входа:

async function getNewUserRecs(entryCategory, limit = 8) {
  return db.query(`
    SELECT p.*, ps.views_7d, ps.purchases_7d
    FROM products p
    JOIN product_stats ps ON ps.product_id = p.id
    WHERE p.active = true AND p.stock > 0
      AND ($1::text IS NULL OR p.category_slug = $1)
    ORDER BY ps.purchases_7d DESC, ps.rating DESC
    LIMIT $2
  `, [entryCategory || null, limit]);
}

Новый товар — content-based эмбеддинг работает сразу, коллаборативная фильтрация подтянется через 50–100 событий.

Разнообразие рекомендаций (Diversity)

Блок из 8 одинаковых ноутбуков — плохо. Нужно разнообразие:

function diversify(recommendations, diversityFactor = 0.3) {
  const selected = [recommendations[0]];
  const remaining = recommendations.slice(1);

  while (selected.length < 8 && remaining.length > 0) {
    // Находим наименее похожий на уже выбранные
    const scores = remaining.map(candidate => {
      const maxSimilarity = Math.max(
        ...selected.map(s => categorySimilarity(s, candidate))
      );
      return {
        item: candidate,
        score: candidate.score * (1 - diversityFactor * maxSimilarity),
      };
    });

    scores.sort((a, b) => b.score - a.score);
    selected.push(scores[0].item);
    remaining.splice(remaining.indexOf(scores[0].item), 1);
  }

  return selected;
}

function categorySimilarity(a, b) {
  if (a.category_id === b.category_id) return 1;
  if (a.parent_category_id === b.parent_category_id) return 0.5;
  return 0;
}

A/B тестирование алгоритмов

async function getRecommendations(userId, productId) {
  const variant = await getABVariant(userId, 'recs-algorithm');

  switch (variant) {
    case 'content-based':
      return getSimilarProducts(productId);
    case 'collaborative':
      return getPersonalRecs(userId);
    case 'hybrid':
      return getHybridRecs(userId, productId);
    default:
      return getSimilarProducts(productId);
  }
}

// Трекинг конверсии рекомендаций
async function trackRecommendationClick(userId, productId, position, algorithm) {
  await db.query(`
    INSERT INTO rec_events (user_id, product_id, position, algorithm, event_type, created_at)
    VALUES ($1, $2, $3, $4, 'click', NOW())
  `, [userId, productId, position, algorithm]);
}

Сроки

  • Content-based похожие товары через pgvector — 3–4 дня
  • "Часто покупают вместе" через ассоциативные правила — плюс 2–3 дня
  • Персональные рекомендации (ALS) + Python-сервис — плюс 4–5 дней
  • Полная система (все типы рекомендаций + A/B + аналитика) — 3–4 недели