Реалізація автоматичної категоризації товарів (AI)

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація автоматичної категоризації товарів (AI)
Середня
~3-5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • 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)

Коли каталог формується з кількох джерел — постачальники, XML-фіди, ручний ввід — товари приходять з різними структурами даних і часто без правильної категорії. Розставляти їх по розділам вручну при сотнях позицій на день нереально.

Автоматична категоризація через мовну модель працює інакше, ніж правила або regexp. Модель розуміє сенс, а не тільки ключові слова: «бездротові навушники з шумозаглушенням ANC» та «TWS earbuds noise cancelling» потраплять в одну категорію без явного маппінгу.

Два режими категоризації

Режим 1: класифікація в задане дерево категорій. Передаємо моделі список допустимих категорій та просимо вибрати найбільш підходящу. Детерміністичний результат, легко валідувати.

Режим 2: пропозиція нових категорій. Модель сама пропонує назву категорії на основі семантики товару. Використовується при первинному побудові каталогу або виявленні «осиротевших» товарів.

На практиці потрібен перший режим з fallback'ом на другий для товарів, що не потрапили в жодну категорію.

Класифікація в існуюче дерево

interface CategoryTree {
  id: string;
  name: string;
  path: string; // "Електроніка / Аудіо / Навушники"
  children?: CategoryTree[];
}

async function classifyProduct(
  product: RawProduct,
  categories: CategoryTree[]
): Promise<{ categoryId: string; confidence: number; reasoning: string }> {
  // Плоский список шляхів для промпту
  const categoryList = flattenCategories(categories)
    .map((c) => `${c.id}: ${c.path}`)
    .join("\n");

  const prompt = `
Класифікуй цей товар в найбільш підходящу категорію.

Товар:
- Назва: ${product.name}
- Опис: ${product.description?.slice(0, 300) ?? "—"}
- Бренд: ${product.brand ?? "—"}
- Категорія постачальника: ${product.supplierCategory ?? "—"}
- Атрибути: ${JSON.stringify(product.attributes ?? {}).slice(0, 200)}

Доступні категорії (id: path):
${categoryList}

Вихід JSON:
{
  "categoryId": "id зі списку вище",
  "confidence": 0.0-1.0,
  "reasoning": "одна фраза чому"
}

Якщо жодна категорія добре не підходить, використовуй найближчу батьківську категорію та встанови confidence нижче 0.5.
`.trim();

  const response = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: prompt }],
    response_format: { type: "json_object" },
    temperature: 0,
  });

  return JSON.parse(response.choices[0].message.content!);
}

temperature: 0 — для завдань класифікації потрібна відтворюваність, а не креативність.

Батч-обробка з розумним промптом

Для економії токенів та прискорення — класифікуємо кілька товарів за один запит:

async function classifyBatch(
  products: RawProduct[],
  categories: CategoryTree[]
): Promise<Map<string, ClassificationResult>> {
  const categoryList = flattenCategories(categories)
    .map((c) => `${c.id}: ${c.path}`)
    .join("\n");

  const productList = products
    .map(
      (p, i) =>
        `[${i}] "${p.name}"` +
        (p.brand ? ` від ${p.brand}` : "") +
        (p.supplierCategory ? ` (постачальник: ${p.supplierCategory})` : "")
    )
    .join("\n");

  const prompt = `
Класифікуй кожен товар в одну з категорій. Поверни JSON масив.

Категорії:
${categoryList}

Товари:
${productList}

Вихід: [{"index": 0, "categoryId": "...", "confidence": 0.0-1.0}, ...]
`.trim();

  const response = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: prompt }],
    response_format: { type: "json_object" },
    temperature: 0,
    max_tokens: 1000,
  });

  const results: Array<{ index: number; categoryId: string; confidence: number }> =
    JSON.parse(response.choices[0].message.content!).results ?? [];

  const map = new Map<string, ClassificationResult>();
  for (const r of results) {
    const product = products[r.index];
    if (product) {
      map.set(product.id, { categoryId: r.categoryId, confidence: r.confidence });
    }
  }

  return map;
}

10–20 товарів в одному запиті — розумний батч. Більше — промпт стає занадто довгим і якість падає.

Робітник з чергою

const categorizationWorker = new Worker(
  "categorization",
  async (job) => {
    const { productIds } = job.data;
    const products = await db.products.findMany({
      where: { id: { in: productIds } },
    });
    const categories = await db.categories.findAll({ active: true });

    const results = await classifyBatch(products, categories);

    for (const [productId, result] of results) {
      await db.products.update({
        where: { id: productId },
        data: {
          categoryId: result.confidence >= 0.7 ? result.categoryId : null,
          suggestedCategoryId: result.categoryId,
          categorizationConfidence: result.confidence,
          categorizationStatus:
            result.confidence >= 0.7 ? "auto_assigned" : "needs_review",
          categorizedAt: new Date(),
        },
      });
    }
  },
  { connection: redisConnection, concurrency: 3 }
);

Товари з confidence < 0.7 потрапляють у чергу ревю — категорію їм назначає менеджер, і це додатково навчає систему через few-shot приклади.

Few-shot навчання на прикладах з каталогу

Коли менеджер вручну виправляє категорію, це цінні дані. Накопичуємо їх та підставляємо у промпт:

async function getExamplesForCategory(categoryId: string, limit = 5): Promise<string> {
  const examples = await db.products.findMany({
    where: { categoryId, categorizationStatus: "manually_confirmed" },
    select: { name: true, brand: true },
    take: limit,
  });

  if (examples.length === 0) return "";

  return `\nПриклади товарів у цій категорії: ${examples.map((e) => `"${e.name}"`).join(", ")}`;
}

Через 2–3 тижні роботи системи з ревю точність автоматичної класифікації в конкретному каталозі виростає до 90%+ — модель бачить реальні приклади вашого каталогу.

Мониторинг якості

SELECT
  categorization_status,
  AVG(categorization_confidence) as avg_confidence,
  COUNT(*) as count
FROM products
WHERE categorized_at > NOW() - INTERVAL '7 дней'
GROUP BY categorization_status;

Якщо частка needs_review зростає — можливо, з'явилися нові типи товарів, які не покриваються поточним деревом категорій. Це сигнал до розширення каталогу.