התאמת מוצרים אוטומטית מספקים

ספקים רבים שולחים קטלוגים בפורמטים שונים, והתאמת מוצרים ידנית היא שגרה אינסופית ומקור לשגיאות. אנו מפתחים מערכת אוטומטית להתאמת מוצרים, המוצאת התאמות ממזהים מדויקים ועד אלגוריתמי ML. הצוות שלנו מספק את הפרויקט במפתח מלא—מביקורת ועד הטמעה, תוך הבטחת פעילות יציבה ותמיכה שוטפת.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
התאמת מוצרים אוטומטית מספקים
מורכב
~2-4 שבועות

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

אתה אגרגטור עם מאה ספקים. כל אחד שולח קטלוג בפורמט משלו. אחד כותב "Smartphone Samsung S24 256Gb", אחר כותב "SAMSUNG Galaxy S24 (256 GB) Black SM-S921B". זה אותו מוצר. זו המשימה של התאמת מוצרים אוטומטית בין ספקים, או product matching. הסרת כפילויות היא רק חלק מהפתרון. ללא התאמה, אתה יוצר כפילויות, מאבד מלאי ומחירים. אנו בונים מערכת שמוצאת התאמות אוטומטית—מחוקים קשיחים ועד ML.

תוך 7–8 ימי עבודה, אתה מקבל pipeline שמכסה 80–90% מהמוצרים ללא התערבות אנושית. עם הגדרה נכונה, התאמה אוטומטית מכסה את הנפח הזה. ה-10–20% הנותרים עוברים לתור סקירה ידנית. התוצאה היא קטלוג אחיד עם עלויות מיתון מינימליות.

בפרויקט אחד עם קטלוג של 200,000 פריטים, ה-pipeline התאים אוטומטית 85% מהמוצרים בחודש הראשון. מפקח בילה 1.5 שעות ביום על 15% הנותרים. לאחר שלושה חודשים, עם אימון מחדש על החלטות המפקח, האוטומציה גדלה ל-93%.

התאמה פותרת את בעיית הכפילויות

התאמה שונה מהותית מהסרת כפילויות. הסרת כפילויות מחפשת כפילויות ברורות בקטלוג אחד. התאמה עובדת עם מערכות שמות שונות. אנו משתמשים בשרשרת שיטות—מהאמינות ביותר ועד הסתברותיות.

מזהים מדויקים

  • GTIN/EAN—האמין ביותר, מכסה 40–60% ממוצרי האלקטרוניקה.
  • MPN + מותג—מוסיף עוד 20–30%.
  • ISBN, ASIN לקטגוריות ספציפיות.

מאפיינים מובנים

אם אין ברקוד, אנו מתאימים לפי מותג + דגם + מאפיינים (קיבולת, צבע, גודל). יעיל לקטגוריות סטנדרטיות.

טקסט מטושטש

אלגוריתמים Jaro-Winkler ו-Levenshtein על שמות מנורמלים. TF-IDF + דמיון קוסינוס על תיאורים. מכסה פריטים לא סטנדרטיים—חומרי בניין, חלקי חילוף.

התאמה וקטורית (ML)

Embeddings דרך sentence-transformers (מקומי) או OpenAI API. עובד גם ללא מילים משותפות—תופס משמעות. מוסיף עוד 5–10% דיוק על גבי הטקסט המטושטש.

השוואת שיטות התאמה

שיטה דיוק כיסוי מהירות
GTIN/EAN 100% (אם קיים) 40–60% ~1 ms
MPN+מותג 95%+ 20–30% ~2 ms
טקסט מטושטש 85–90% 10–20% ~10 ms
וקטורי (ML) 90–95% 5–10% ~50 ms (עם GPU)

טקסט מטושטש זול ומהיר יותר מ-ML, אבל ML מדויק ב-15–20% יותר לתיאורים מורכבים. אנו משלבים אותם ב-pipeline.

איך עובד ה-Matching Pipeline

ה-pipeline הוא שרשרת שיטות שבה כל שיטה עוקבת מופעלת אם הקודמת לא הניבה תוצאה עם ביטחון מספק. ראשית, מופעלות שיטות מדויקות (GTIN, MPN), ולאחר מכן הסתברותיות (מטושטש, ML). זה ממזער תוצאות חיוביות שגויות וממקסם כיסוי.

יתרונות של התאמת ML

התאמת ML מוצדקת כאשר ספקים מתארים מוצרים במילים שונות. לדוגמה, אחד כותב "Smartphone Samsung Galaxy S24 256 GB", אחר כותב "Telefon Samsung S24 256 Gb". טקסט מטושטש מסורתי לא יבין שהם זהים, אבל מודל embedding תופס דמיון סמנטי. בפועל, התאמה וקטורית מוסיפה 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);

Matching Pipeline

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;
    }
}

Vector 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)),
        ]));
    }
}

ממשק סקירה ידנית ולולאת משוב

מוצרים עם סטטוס -- Таблица сопоставлений + таблица эмбеддингов 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); נכנסים לתור המפקח. הממשק מציג את מוצר הספק משמאל (שם, SKU, תמונה) ואת המועמד מהקטלוג מימין עם אחוז התאמה. כפתורים: אישור, דחייה, מצא אחר. מקשי קיצור (→ קבל, ← דחה). מפקח מנוסה מעבד 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+ פריטים, לא ניתן לבצע התאמה על ידי בדיקת כל הזוגות. אנו משתמשים ב:

  • Blocking—תחילה בחירת מועמדים לפי מותג/קטגוריה, ואז התאמה בתוך הבלוק.
  • Batch embeddings—בקשת וקטורים בקבוצות של 100.
  • אינדקס pgvector IVFFlat—חיפוש שכן קרוב משוער במילישניות.

עם קטלוג של 100,000 פריטים, ארכיטקטורה זו חוסכת עד €2,000 לחודש על מיתון. רוצה לבדוק התאמה על הנתונים שלך? צור קשר—נבחר את התצורה האופטימלית.

מה כלול בעבודה

  • פיתוח pipeline התאמה לסטאק שלך (Laravel, Django, Node.js)
  • הגדרת matchers מסוג GTIN, MPN, מטושטש ו-ML
  • עיצוב סכמת מסד נתונים (matches, embeddings, indexes)
  • ממשק סקירה ידנית עם מקשי קיצור
  • אינטגרציה עם ספקים (API או ייבוא)
  • תיעוד API וסכמת נתונים
  • הכשרת מפקחים (שעתיים)
  • חודש תמיכה לאחר ההשקה

לוח זמנים ליישום

שלב משך
GtinMatcher + MpnBrandMatcher + FuzzyMatcher יומיים
VectorMatcher + pgvector יומיים
Pipeline + תור סקירה ידנית + ממשק 2–3 ימים
לולאת משוב + מדדים יום אחד
סה"כ 7–8 ימי עבודה

למה להזמין התאמה מאיתנו?

יישמנו התאמה עבור חמישה אגרגטורים עם קטלוגים של 50,000 עד 500,000 פריטים. ה-pipeline שלנו מתאים אוטומטית 80–90% מהמוצרים. השאר הוא תור סקירה ידנית שלוקח לא יותר משעתיים ביום. אתה מקבל קטלוג אחיד ללא כפילויות או בלבול.

נעריך את המשימה שלך תוך יום אחד, נציע ארכיטקטורה ולוח זמנים מדויק. צור קשר כדי לדון בפרויקט. הזמן פיתוח התאמה לסטאק שלך.