חיפוש רגיל באתר מחזיר מאמרים רק אם הם מכילים את מילות השאילתה המדויקות. משתמש מחפש "איך לשלם" אבל לא מוצא את המאמר "שיטות תשלום". חיפוש סמנטי פותר את זה: הוא מבין משמעות, לא מחרוזות. אנו מיישמים מערכות כאלה עבור חנויות מקוונות, תיעוד ופורטלים. הניסיון שלנו: מעל 10 פרויקטים עם חיפוש AI, פתרונות מוסמכים המבוססים על PostgreSQL ו-Qdrant. צרו קשר כדי לדון בתרחיש שלכם. לקוח אחד השיג ROI של 300% תוך 6 חודשים על ידי הפחתת פניות לתמיכה וירידה באחוזי הנטישה.
למה חיפוש סמנטי עדיף על חיפוש טקסט מלא?
חיפוש טקסט מלא (PostgreSQL tsvector, Elasticsearch) מתאים מילים. חיפוש סמנטי מתאים משמעות. הוא ממיר טקסט לווקטור—מערך מספרי של 768–3072 מספרים. טקסטים עם וקטורים קרובים דומים סמנטית. במבחנים שלנו, חיפוש היברידי מספק כיסוי טוב פי 3 מחיפוש טקסט מלא ודיוק טוב פי 2 מוקטור בלבד. זה משפר את דיוק התוצאות הרלוונטיות, במיוחד עבור שאילתות ארוכות ושיחתיות.
איך אנחנו עושים את זה: מחסנית ומקרה בוחן
עבור חנות מקוונת עם 50,000 מוצרים, יישמנו חיפוש היברידי באמצעות OpenAI embeddings ו-pgvector. תוצאה: זמן תגובה ממוצע של 0.3 שניות, דיוק של 92%.
בחירת מודל. אנו משתמשים ב-text-embedding-3-small (1536 ממדים)—האיזון האופטימלי בין מהירות ואיכות. עבור רוסית הוא מספק תוצאות מצוינות.
מסד נתונים וקטורי. PostgreSQL עם תוסף pgvector ואינדקס HNSW:
CREATE EXTENSION vector;
CREATE TABLE content_chunks (
id BIGSERIAL PRIMARY KEY,
content_id BIGINT REFERENCES content(id),
chunk_text TEXT NOT NULL,
chunk_index INT,
embedding vector(1536),
metadata JSONB
);
CREATE INDEX ON content_chunks USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);אינדוקס. אנו מפצלים טקסט לנתחים של 400 טוקנים עם חפיפה של 50 מילים, מקבלים embeddings דרך OpenAI API, ושומרים לטבלה:
import OpenAI from 'openai';
const openai = new OpenAI();
async function indexContent(contentItem) {
const chunks = chunkText(contentItem.body, { maxTokens: 400, overlap: 50 });
const { data: embeddings } = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: chunks,
});
// Сохраняем в pgvector батчами по 100
for (let i = 0; i < chunks.length; i += 100) {
const batchChunks = chunks.slice(i, i + 100);
const batchEmbeds = embeddings.slice(i, i + 100);
await db.query(`
INSERT INTO content_chunks (content_id, chunk_text, chunk_index, embedding, metadata)
VALUES ($1, $2, $3, $4::vector, $5)
`, [contentItem.id, batchChunks, /* ... */]);
}
}חיפוש. אנו משלבים חיפוש וקטורי וטקסט מלא באמצעות RRF (Reciprocal Rank Fusion):
async function semanticSearch(query, { limit = 10, threshold = 0.7 } = {}) {
const { data: [{ embedding }] } = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: query,
});
const results = await db.query(`
WITH semantic AS (
SELECT content_id, chunk_text, 1 - (embedding <=> $1::vector) AS score,
ROW_NUMBER() OVER (ORDER BY embedding <=> $1::vector) AS rank
FROM content_chunks
ORDER BY embedding <=> $1::vector
LIMIT 20
),
fulltext AS (
SELECT id AS content_id, body AS chunk_text,
ts_rank(to_tsvector('russian', body), plainto_tsquery('russian', $2)) AS score,
ROW_NUMBER() OVER (ORDER BY ts_rank(...) DESC) AS rank
FROM content
WHERE to_tsvector('russian', body) @@ plainto_tsquery('russian', $2)
LIMIT 20
)
SELECT COALESCE(s.content_id, f.content_id) AS id,
COALESCE(s.chunk_text, f.chunk_text) AS text,
(COALESCE(1.0 / (60 + s.rank), 0) + COALESCE(1.0 / (60 + f.rank), 0)) AS rrf_score
FROM semantic s
FULL OUTER JOIN fulltext f ON s.content_id = f.content_id
ORDER BY rrf_score DESC
LIMIT $3
`, [`[${embedding.join(',')}]`, query, limit]);
return results.rows;
} מה זה חיפוש היברידי ולמה אתם צריכים אותו?
חיפוש היברידי משלב תוצאות וקטוריות וטקסט מלא באמצעות RRF. זה מפצה על החולשות של כל שיטה: חיפוש וקטורי מוצא לפי משמעות אבל עלול לפספס מונחים מדויקים; טקסט מלא עושה את ההפך. יחד הם מבטיחים רלוונטיות גבוהה גם לשאילתות מורכבות. אנו משתמשים בגישה זו בכל הפרויקטים. חשוב: יישום איכותי דורש ניסיון—המהנדסים שלנו מבטיחים תוצאות.
איך לבחור מודל Embedding עבור רוסית?
בחירת המודל היא קריטית. מודלים רב-לשוניים (למשל, Cohere) לרוב מתפקדים פחות טוב בהשוואה למודלים ייעודיים לרוסית. בדקנו מספר אפשרויות ואנו ממליצים:
| מודל | ממדים | איכות ברוסית | מהירות | עלות |
|---|---|---|---|---|
| OpenAI text-embedding-3-small | 1536 | מצוינת | גבוהה | נמוכה |
| OpenAI text-embedding-3-large | 3072 | יוצאת דופן | בינונית | בינונית |
| Cohere embed-multilingual-v3 | 1024 | טובה | גבוהה | בינונית |
| BGE-M3 (באירוח עצמי) | 1024 | טובה | תלוי GPU | חינם |
מקור: תיעוד OpenAI Embeddings
תהליך העבודה
- ביקורת תוכן — זיהוי סוגי טקסט, גודל, תדירות עדכון.
- בחירת מודל ומסד נתונים וקטורי — קביעת האיזון בין איכות לתקציב.
- אינדוקס תוכן וחלוקה לנתחים — הגדרה לאחזור אופטימלי.
- פיתוח API לחיפוש — נקודת קצה עם פרמטרים: שאילתה, פילטרים, פגינציה.
- יצירת ממשק משתמש — שורת חיפוש, קטעים עם הדגשה, טעינה פרוגרסיבית.
- בדיקות — מבחן A/B מול החיפוש הנוכחי, ניטור מדדים.
- פריסה וניטור — התראות על זמן אחזור, שאילתות ללא תוצאות.
דוגמה לאינדוקס מחדש מצטבר: כדי להימנע מאינדוקס מחדש של כל המסמכים בכל שינוי, אנו משתמשים בטריגרים על טבלת התוכן ותור משימות (Bull/PGBoss). בעת הוספה או עדכון, נוצרת משימת אינדוקס מחדש רק עבור אותו מסמך. עובד רקע מרים את המשימה, מקבל embeddings, ומעדכן את הנתח המתאים. זה שומר על נתונים עדכניים ללא אינדוקס מלא גם עם אלפי שינויים יומיים.
לוחות זמנים משוערים
| שלב | משך (ימים) |
|---|---|
| חיפוש סמנטי עבור 10K מסמכים (pgvector) | 4–5 |
| חיפוש היברידי (וקטורי + טקסט מלא) | +1–2 |
| Reranking באמצעות Cohere Rerank | +1 |
| ממשק משתמש עם הדגשה ואנליטיקה | +2–3 |
| אינדוקס מחדש מצטבר | +1–2 |
עלות יישום: החל מ-$3,000 בהתאם לנפח הנתונים.
מה כלול
- תיעוד ארכיטקטורה מלא (סכמת DB, מפרט API, מדריך פריסה).
- קוד מקור מוכן עם CI/CD.
- גישה לריפוזיטורי, גיבוי נתונים, לוח מחוונים לניטור.
- הדרכת צוות (2–3 שעות).
- 3 חודשי תמיכה טכנית.
טעויות נפוצות: חלוקה לא נכונה לנתחים (אופטימלי 300-500 טוקנים עם חפיפה של 50-100), בחירת מודל ללא התחשבות בשפה (מודלים רב-לשוניים לרוב מתפקדים פחות טוב ברוסית), וחוסר ב-reranking (rerank עם cross-encoder פותר את זה).
רוצים ליישם חיפוש מבוסס משמעות? צרו קשר כדי לדון בפרויקט שלכם. קבלו ייעוץ למקרה השימוש שלכם.







