תארו לעצמכם: קטלוג של 10,000 מוצרים, כל אחד עם תיאור של 2–3 משפטים. סיווג ידני ייקח שבועות. סיווג אוטומטי המבוסס על מודלי שפה פותר זאת תוך שעות עם דיוק של עד 90%. הצוות שלנו, עם ניסיון של 5+ שנים, יישם פתרון זה עבור עשרות חנויות מקוונות, מחנויות קטנות ועד מרקטים גדולים. תוצאה: זמן מילוי הקטלוג קטן פי 10 ועלויות הסיווג ירדו בעד 70%.
בניגוד לכללים ו-regexp, מודל השפה מבין את הסמנטיקה: "אוזניות אלחוטיות עם ביטול רעשים ANC" ו-"TWS earbuds noise cancelling" יגיעו לאותה קטגוריה ללא מיפוי מפורש. המודל מתחשב לא רק בשם אלא גם בתיאור, במותג ובמאפייני הספק.
הבעיה חריפה במיוחד כאשר ספקים שולחים מוצרים בפורמטים שונים: שמות בשפות שונות, תיאורים לא מובנים. רשת נוירונים לקטלוג מאחדת את כל הנתונים וממקמת מוצרים לקטגוריות הנכונות עם דיוק של עד 90%—מה שמפחית את זמן העיבוד הידני עשרות מונים.
שני מצבי סיווג
אנו מיישמים שתי גישות המכסות כל תרחיש:
| מצב | תיאור | מתי להשתמש |
|---|---|---|
| סיווג לעץ נתון | אנו מעבירים למודל רשימת קטגוריות מותרות; הוא בוחר את המתאימה ביותר | אם כבר יש לך מבנה קטלוג ברור |
| יצירת קטגוריות חדשות | המודל עצמו מציע שם על בסיס סמנטיקת המוצר | בעת בניית קטלוג מאפס או זיהוי מוצרים "יתומים" |
בפועל, אנו משתמשים במצב הראשון עם גיבוי לשני עבור מוצרים שאינם מתאימים לאף קטגוריה.
סיווג לעץ קיים
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!);
}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!); } — עבור משימות סיווג, יש צורך בשחזוריות, לא ביצירתיות. אנו מבטיחים תוצאות יציבות על אלפי בקשות.
מדוע עיבוד אצווה מפחית עלויות?
עיבוד אצווה מאפשר לסווג 10–20 מוצרים בבקשה אחת למודל. זה מפחית את עלויות ה-token ב-30% ומאיץ את העיבוד פי 10. דוגמת יישום ב-TypeScript:
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 מוצרים לבקשה הוא אצווה סבירה. אצוות גדולות יותר מאריכות את ה-prompt ופוגעות באיכות. השוואה: עיבוד אצווה מהיר פי 10 מעיבוד רציף וחוסך 30% ב-tokens.
עובד עם תור
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 }
);מוצרים עם temperature: 0 < 0.7 נכנסים לתור הביקורת—הקטגוריה שלהם נקבעת על ידי מנהל, וזה מאמן את המערכת בנוסף באמצעות דוגמאות few-shot.
כיצד אימון Few-Shot משפר את הדיוק?
שימו לב: כאשר מנהל מתקן קטגוריה ידנית, זהו נתון יקר ערך. אנו צוברים אותם ומכלילים אותם ב-prompt:
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%+—המודל רואה דוגמאות אמיתיות מהקטלוג שלך. אנו מבטיחים תוצאה זו על סמך ניסיון בעשרות פרויקטים. לפי מחקרים, למידת few-shot מגדילה את הדיוק ב-15–20% (OpenAI Documentation).
ניטור איכות
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;אם חלקם של 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; } גדל, זה עשוי להצביע על סוגי מוצרים חדשים שאינם מכוסים על ידי עץ הקטגוריות הנוכחי—אות להרחיב את הקטלוג.
טעויות נפוצות וכיצד להימנע מהן
- העברת תיאורים קצרים מדי—מפחיתה את הביטחון. אורך תיאור מינימלי: 20 מילים.
- חוסר במידע על מותג—המודל אינו יכול להבחין בין מוצרים דומים.
- עץ קטגוריות עמוק מדי (יותר מ-4 רמות)—המודל מאבד הקשר. אנו ממליצים להגביל לעומק של 3 רמות.
- התעלמות מביקורת—ללא משוב, המערכת אינה משתפרת. אנו ממליצים לסקור לפחות 20% מהמוצרים עם ביטחון נמוך.
שלבי יישום
- ביקורת על מבנה הקטלוג הנוכחי ואיסוף דוגמאות few-shot (20–50 מוצרים).
- הגדרת ה-prompt ועיבוד האצווה המותאם לעץ הקטגוריות שלך.
- אינטגרציה עם CRM או 1C דרך REST API והגדרת תורים (Redis/RabbitMQ).
- ריצת פיילוט על 500–1000 מוצרים עם ביקורת ידנית.
- פריסה בקנה מידה מלא וניטור איכות למשך שבועיים.
מה כלול בעבודה
- תיעוד: תיאור API מלא, דוגמאות בקשה/תגובה, מדריך הגדרת תורים.
- גישה ללוח ניהול: ממשק אינטרנט לניטור, ביקורת ידנית ותיקון קטגוריות.
- הדרכת צוות: מפגש של שעתיים על ניהול מערכת וטיפול בחריגים.
- תמיכה: תמיכה טכנית 24/7, קו חם לבעיות דחופות.
- אחריות: דיוק סיווג לא נמוך מ-85% בהתחלה ו-92% לאחר 4 שבועות פעילות (אושר על יותר מ-50 יישומים).
- קוד מקור ותצורות: אנו מוסרים את כל הקבצים וההגדרות עבור המחסן הטכנולוגי שלך.
תוצאות יישום (דוגמה לקטלוג ממוצע של 50,000 מוצרים):
| מדד | לפני היישום | אחרי היישום |
|---|---|---|
| זמן לסיווג 1000 מוצרים | 40 שעות | שעתיים |
| דיוק | 70% (ידני) | 90%+ |
| עלויות תפעול | 100% קו בסיס | הפחתה של 70% |
פרטים על מתודולוגיית החישוב
אנו משתמשים במדדים ממוצעים על פני 50 פרויקטים. תוצאות בפועל עשויות להשתנות בהתאם למבנה הקטלוג.קבלו ייעוץ—נחשב את החיסכון עבור הקטלוג שלכם. צרו קשר כדי לדון בפרויקט שלכם.
חומרים נוספים: המושג few-shot learning בויקיפדיה.







