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

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація AI-рекомендацій товарів на сайті
Складна
~1-2 тижні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

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

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

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

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

Структура даних товару для embedding

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 embedding працює одразу, колаборативна фільтрація підтягнеться через 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 тижні