Реализация автоматической категоризации товаров (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: предложение новых категорий. Модель сама предлагает название категории на основе семантики товара. Используется при первичном построении каталога или выявлении «осиротевших» товаров.

На практике нужен первый режим с фолбэком на второй для товаров, не попавших ни в одну категорию.

Классификация в существующее дерево

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 = `
Classify this product into the most appropriate category.

Product:
- Name: ${product.name}
- Description: ${product.description?.slice(0, 300) ?? "—"}
- Brand: ${product.brand ?? "—"}
- Supplier category: ${product.supplierCategory ?? "—"}
- Attributes: ${JSON.stringify(product.attributes ?? {}).slice(0, 200)}

Available categories (id: path):
${categoryList}

Return JSON:
{
  "categoryId": "the id from the list above",
  "confidence": 0.0-1.0,
  "reasoning": "one sentence why"
}

If no category fits well, use the closest parent category and set confidence below 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 ? ` by ${p.brand}` : "") +
        (p.supplierCategory ? ` (supplier: ${p.supplierCategory})` : "")
    )
    .join("\n");

  const prompt = `
Classify each product into one of the categories. Return JSON array.

Categories:
${categoryList}

Products:
${productList}

Return: [{"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 `\nExamples of products in this category: ${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 days'
GROUP BY categorization_status;

Если доля needs_review растёт — возможно, появились новые типы товаров, которые не покрываются текущим деревом категорий. Это сигнал к расширению каталога.