Автоматическое сопоставление товаров поставщиков (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%.

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.

Как работает 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€ в месяц на модерации.

Хотите протестировать 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 под ваш стек.

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

Последние работы

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