Автоматичне зіставлення товарів постачальників (Matching)

Сотні постачальників надсилають каталоги в різних форматах, і вручну зіставляти товари — це нескінченна рутина та помилки. Ми розробляємо систему автоматичного зіставлення товарів, яка знаходить відповідності від точних ідентифікаторів до ML-алгоритмів. Наша команда реалізує проєкт під ключ — від аудиту до впровадження, забезпечуючи стабільну роботу та підтримку.

Розробка та обслуговування будь-яких видів сайтів:

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Автоматичне зіставлення товарів постачальників (Matching)
Складний
~2-4 тижні
Часті запитання

Ви агрегатор із сотнею постачальників. Кожен надсилає каталог у своєму форматі. В одного — «Смартфон Samsung S24 256Gb», в іншого — «SAMSUNG Galaxy S24 (256 GB) Black SM-S921B». Це один товар. Це задача автоматичного зіставлення товарів постачальників, або product matching. Дедуплікація товарів — лише частина рішення. Без matching ви створите дублі, втратите залишки та ціни. Ми будуємо систему, яка автоматично знаходить відповідності — від жорстких правил до ML.

За 7–8 робочих днів ви отримуєте pipeline, що покриває 80–90% товарів без участі людини. При правильному налаштуванні автоматичне зіставлення покриває такий обсяг. Решта 10–20% йдуть у чергу ручної перевірки зіставлень. Підсумок — єдиний каталог з мінімальними витратами на модерацію.

В одному з проєктів з каталогом 200 000 позицій pipeline за перший місяць автоматично зіставив 85% товарів. Модератор витрачав по 1,5 години на день на решту 15%. Через три місяці, після донавчання на рішеннях модератора, автоматизація зросла до 93%. Ми маємо 7+ років досвіду в розробці matching-систем та успішно реалізували понад 10 проєктів, що підтверджує нашу експертизу та гарантує якість.

Matching вирішує проблему дублювання

Matching (зіставлення) принципово відрізняється від дедуплікації. Дедуплікація шукає явні дублі в одному каталозі. Matching працює з різними системами номенклатури. Ми використовуємо ланцюжок методів — від найнадійніших до ймовірнісних.

Точні ідентифікатори

  • GTIN/EAN — найнадійніший, покриває 40–60% товарів в електроніці.
  • MPN + бренд — дає ще 20–30%.
  • ISBN, ASIN для специфічних категорій.

Структуровані атрибути

Якщо немає штрихкоду, матчимо за брендом + моделлю + характеристиками (ємність, колір, розмір). Ефективно для стандартизованих категорій.

Нечіткий текст (fuzzy)

Алгоритми Jaro-Winkler та Levenshtein на нормалізованих назвах. TF-IDF + косинусна близькість на описах. Покриває нестандартизовані позиції — будматеріали, запчастини.

Векторний matching (ML)

Embeddings через sentence-transformers (локально) або OpenAI API. Працює навіть за відсутності спільних слів — вловлює сенс. Забезпечує ще 5–10% точності поверх fuzzy.

Методи matching: порівняння та вибір

Метод Точність Покриття Швидкість
GTIN/EAN 100% (за наявності) 40–60% ~1 мс
MPN+бренд 95%+ 20–30% ~2 мс
Нечіткий текст 85–90% 10–20% ~10 мс
Векторний (ML) 90–95% 5–10% ~50 мс (з GPU)

Fuzzy дешевший і швидший за ML, але ML точніший на 15–20% для складних описів. Ми комбінуємо їх у pipeline. Автоматичний matching у 10 разів швидший за ручний пошук — модератор витрачає 2 хвилини на товар, а pipeline обробляє 1000 позицій за секунду.

Як працює pipeline зіставлення?

Pipeline — це ланцюжок методів, де кожен наступний вмикається, якщо попередній не дав результату з достатньою впевненістю. Першими застосовуються точні методи (GTIN, MPN), потім — ймовірнісні (fuzzy, ML). Це дозволяє мінімізувати хибні спрацьовування та максимізувати покриття.

Переваги ML-матчингу

ML-матчинг виправданий, коли постачальники описують товари різними словами. Наприклад, один пише «Смартфон Samsung Galaxy S24 256 ГБ», інший — «Telefon Samsung S24 256 Gb». Традиційний fuzzy не зрозуміє, що це одне й те саме, а embedding модель вловить семантичну близькість. На практиці векторний matching додає 5–10% точності, що критично для каталогів із 30% неструктурованих позицій.

Схема даних

-- Таблиця зіставлень + таблиця ембендингів
CREATE TABLE product_matches (
    id BIGSERIAL PRIMARY KEY,
    master_id BIGINT NOT NULL REFERENCES products(id),
    supplier_id INT NOT NULL REFERENCES suppliers(id),
    supplier_sku VARCHAR(255) NOT NULL,
    match_method VARCHAR(30) NOT NULL, -- 'gtin', 'mpn_brand', 'fuzzy', 'ml', 'manual'
    confidence FLOAT, -- 0.0–1.0
    status VARCHAR(20) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(supplier_id, supplier_sku)
);

CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE product_embeddings (
    product_id BIGINT PRIMARY KEY REFERENCES products(id),
    embedding vector(1536),
    updated_at TIMESTAMP
);

CREATE INDEX idx_matches_master ON product_matches(master_id);
CREATE INDEX idx_matches_confidence ON product_matches(confidence) WHERE status = 'pending_review';
CREATE INDEX idx_embeddings_cosine ON product_embeddings USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

Pipeline matching

class ProductMatchingPipeline {
    private array $matchers = [];

    public function __construct(
        private GtinMatcher $gtinMatcher,
        private MpnBrandMatcher $mpnBrandMatcher,
        private FuzzyMatcher $fuzzyMatcher,
        private VectorMatcher $vectorMatcher,
    ) {
        $this->matchers = [
            ['matcher' => $gtinMatcher, 'threshold' => 1.0, 'auto_accept' => true],
            ['matcher' => $mpnBrandMatcher, 'threshold' => 1.0, 'auto_accept' => true],
            ['matcher' => $fuzzyMatcher, 'threshold' => 0.90, 'auto_accept' => true],
            ['matcher' => $vectorMatcher, 'threshold' => 0.85, 'auto_accept' => false],
        ];
    }

    public function match(SupplierProductDTO $dto): MatchResult {
        foreach ($this->matchers as $config) {
            $result = $config['matcher']->find($dto);
            if (!$result) continue;

            if ($result->confidence >= $config['threshold'] && $config['auto_accept']) {
                return new MatchResult(
                    masterProductId: $result->productId,
                    confidence: $result->confidence,
                    method: $result->method,
                    status: 'active',
                );
            }

            if ($result->confidence >= 0.70) {
                return new MatchResult(
                    masterProductId: $result->productId,
                    confidence: $result->confidence,
                    method: $result->method,
                    status: 'pending_review',
                );
            }
        }

        return new MatchResult(masterProductId: null, confidence: 0, method: 'none', status: 'new');
    }
}

GTIN matcher

class GtinMatcher {
    public function find(SupplierProductDTO $dto): ?MatchCandidate {
        if (!$dto->barcode) return null;
        $normalized = $this->normalizeGtin($dto->barcode);
        $fingerprint = ProductFingerprint::where('type', 'gtin')
            ->where('value', $normalized)
            ->first();
        if (!$fingerprint) return null;
        return new MatchCandidate(
            productId: $fingerprint->product_id,
            confidence: 1.0,
            method: 'gtin',
        );
    }

    private function normalizeGtin(string $raw): string {
        $digits = preg_replace('/\D/', '', $raw);
        if (strlen($digits) === 8) {
            $digits = str_pad($digits, 13, '0', STR_PAD_LEFT);
        }
        return $digits;
    }
}

Векторний matcher через OpenAI Embeddings

class VectorMatcher {
    public function find(SupplierProductDTO $dto): ?MatchCandidate {
        $text = $this->buildText($dto);
        $vector = $this->openai->embeddings()->create([
            'model' => 'text-embedding-3-small',
            'input' => $text,
        ])->embeddings[0]->embedding;

        $result = DB::selectOne(
            " SELECT product_id, 1 - (embedding <=> :vec) AS similarity
              FROM product_embeddings
              WHERE 1 - (embedding <=> :vec) > 0.80
              ORDER BY embedding <=> :vec
              LIMIT 1 ",
            ['vec' => '[' . implode(',', $vector) . ']']
        );

        if (!$result) {
            return null;
        }

        return new MatchCandidate(
            productId: $result->product_id,
            confidence: (float) $result->similarity,
            method: 'vector',
        );
    }

    private function buildText(SupplierProductDTO $dto): string {
        return implode(' ', array_filter([
            $dto->brand,
            $dto->name,
            $dto->sku,
            implode(' ', array_values($dto->attributes)),
        ]));
    }
}

Інтерфейс ручної перевірки та зворотний зв'язок

Товари зі статусом pending_review потрапляють до черги модератора. Інтерфейс показує ліворуч товар постачальника (назва, артикул, фото), праворуч — кандидата з каталогу з відсотком збігу. Кнопки: Підтвердити, Відхилити, Знайти інший. Гарячі клавіші (→ прийняти, ← відхилити). Досвідчений модератор обробляє 100–150 пар на годину.

Кожне рішення модератора стає навчальним прикладом для pipeline:

class MatchFeedbackService {
    public function recordDecision(int $matchId, string $decision, int $userId): void {
        $match = ProductMatch::findOrFail($matchId);
        $match->update([
            'status' => $decision === 'accept' ? 'active' : 'rejected',
            'reviewed_by' => $userId,
        ]);
        MatchTrainingExample::create([
            'supplier_product_data' => $match->supplierProduct->toArray(),
            'master_product_id' => $match->master_id,
            'label' => $decision === 'accept' ? 1 : 0,
            'confidence_was' => $match->confidence,
        ]);
        if ($decision === 'reject') {
            $this->createNewMaster($match->supplierProduct);
        }
    }
}

Продуктивність та оптимізація

При каталозі 100 000+ позицій matching не можна запускати перебором усіх пар. Використовуємо:

  • Blocking — спочатку відбираємо кандидатів за брендом/категорією, потім матчимо всередині блоку.
  • Batch embeddings — запитуємо вектори пачками по 100 штук.
  • pgvector IVFFlat index — approximate nearest neighbor за мілісекунди.

При каталозі 100 000 позицій така архітектура економить до 2000€ на місяць на модерації. Це в 5 разів вигідніше, ніж найм додаткового модератора.

Хочете протестувати matching на своїх даних? Зв'яжіться з нами — ми підберемо оптимальну конфігурацію.

Що входить в роботу

  • Розробка pipeline matching під ваш стек (Laravel, Django, Node.js)
  • Налаштування GTIN-, MPN-, fuzzy- та ML-матчерів
  • Проектування схеми БД (матчинги, ембендинги, індекси)
  • Інтерфейс ручної перевірки з гарячими клавішами
  • Інтеграція з постачальниками (API або імпорт)
  • Документація API та схеми даних
  • Навчання модераторів (2 години)
  • Підтримка 1 місяць після запуску

Строки реалізації

Етап Тривалість
GtinMatcher + MpnBrandMatcher + FuzzyMatcher 2 дні
VectorMatcher + pgvector 2 дні
Pipeline + черга ручної перевірки + інтерфейс 2–3 дні
Feedback loop + метрики 1 день
Разом 7–8 робочих днів

Чому варто замовити matching у нас?

Ми реалізували matching для п'яти агрегаторів з каталогами від 50 000 до 500 000 позицій. Наш pipeline автоматично зіставляє 80–90% товарів. Залишок — черга ручної перевірки, яка не займає більше 2 годин на день. Ви отримуєте єдиний каталог без дублів та плутанини.

Оцінимо вашу задачу за 1 день, запропонуємо архітектуру та точні строки. Зв'яжіться з нами, щоб обговорити проєкт. Замовте розробку matching під ваш стек.

Кроки для впровадження matching 1. Підготуйте дані: зберіть каталоги постачальників та визначте структуру. 2. Налаштуйте базові матчери: GTIN, MPN, fuzzy. 3. Інтегруйте ML-матчинг, якщо потрібна вища точність. 4. Запустіть pipeline та налаштуйте чергу ручної перевірки. 5. Аналізуйте метрики та донавчайте модель на рішеннях модератора.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1543
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1374
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060