DanLevy.net

חיפוש וקטורי סמנטי ונושאים נוספים לזכייה בלבבות ובאהבתם של אחרים

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

חיפוש אינו דבר אחד, וחיפוש סמנטי אינו תחליף לכל השאר.

מפת האפשרויות לחיפוש וקטורי: השוואה בין 16 אפשרויות

השוו בין מודל הפריסה, הרישוי, יכולות החיפוש והתאמה לעומסי עבודה. הסברים על העמודות ודוגמאות SQL מופיעים בהמשך.

איך לקרוא את ההשוואה הזו

מסד נתוניםפריסהרישיוןחיפוש היברידיוקטורים דליליםממשק שאילתותEmbedding מולטימודלי מובנהאינדקס בדיסקמגבלות ממד וקטורמתאים במיוחד ל-
pgvectorאירוח עצמי / מנוהל (Supabase, Neon, RDS)OSS (PostgreSQL)ידני (‏RRF באמצעות SQL)❌✅ SQL מלא❌✅ HNSW בדיסק16,000 באחסון; 2,000 באינדקס vectorכבר עובדים עם Postgres; כמויות וקטורים בינוניות
Qdrantאירוח עצמי / ענןApache 2.0✅ BM25 מובנה✅ תמיכה בשלה❌ (REST/gRPC)❌✅65,535שאילתות מסוננות בקנה מידה גדול; מטא-דטה מורכב
Weaviateאירוח עצמי / ענןBSD 3✅ BM25 + RRF מובנים✅❌ (GraphQL / gRPC)✅ באמצעות מודולים✅65,535דפוסי גישה של GraphQL; וקטוריזציה מובנית
Pineconeענן בלבדקנייני✅ (נוסף ב-2024)✅❌❌✅ (serverless)20,000פשטות מנוהלת; ללא צוות תפעול
Milvus / Zillizאירוח עצמי / ענן (Zilliz)Apache 2.0✅ מובנה✅✅ דמוי SQL (Milvus Query Language)✅✅ DiskANN32,768קנה מידה של מיליארדים; on-prem ארגוני
ChromaEmbedded / אירוח עצמיApache 2.0❌❌❌❌❌65,535פיתוח מקומי ואבות-טיפוס בלבד
LanceDBEmbedded / ענןApache 2.0✅❌✅ SQL באמצעות DataFusion✅ מובנה✅ (פורמט Lance)ללא הגבלהEdge / serverless; lakehouse מולטימודלי
OramaEmbedded / ענןApache 2.0✅ טקסט מלא + וקטור❌❌❌❌משתנהאפליקציות JS/edge; חיפוש קל לאתרים ולאפליקציות
Turbopufferענן בלבד (serverless)קנייני✅ BM25 + וקטור❌❌❌✅ (אחסון אובייקטים)16,000SaaS רב-דיירים; מיליוני namespaces
Elasticsearchאירוח עצמי / Elastic CloudSSPL / AGPLv3✅ RRF + ELSER דליל✅ (ELSER)✅ Query DSL❌✅ DiskBBQ4,096כבר משתמשים ב-Elastic stack; חיפוש היברידי ארגוני
OpenSearchאירוח עצמי / מנוהל ב-AWSApache 2.0✅ RRF + Neural Search✅✅ Query DSL❌✅ FAISS + HNSW16,000AWS-native; חלופת Elastic בקוד פתוח
Vespaאירוח עצמי / ענןApache 2.0✅ מובנה✅ טנסורים / דירוג לקסיקלי✅ YQL✅ טנסורים✅למעשה ללא הגבלהמערכות חיפוש + דירוג + המלצות
ClickHouseאירוח עצמי / ענןApache 2.0ידני❌✅ SQL מלא❌✅ עמודות + HNSWמשתנהאנליטיקה ולוגים עם חיפוש וקטורי לצד OLAP
MongoDB Atlasענן / אירוח עצמיSSPL✅ מובנה❌✅ MQL + aggregation❌✅ HNSW8,192כבר משתמשים ב-MongoDB; מסמכים + וקטורים במקום אחד
Redis (VSS)אירוח עצמי / Redis CloudRSALv2 / SSPL✅ (RediSearch)✅❌❌❌ RAM בלבד32,768השהיה נמוכה במיוחד; חיפוש וקטורי בשכבת המטמון
Marqoענן / אירוח עצמיApache 2.0✅❌❌✅ דגש מובנה✅משתנהמולטימודלי מקצה לקצה: תמונה + טקסט + וידאו

“מצא משתמש עם כתובת האימייל dan@example.com” ו“מצא לי מאמרים על דיבוג כמהנדס מתחיל” מתוארים שניהם כחיפוש, אבל כמעט שאין ביניהם דבר משותף מבחינת הבעיה ההנדסית. לראשון יש תשובה נכונה ואיתור באינדקס ב־O(log n). לשני אין תשובה נכונה — יש רק רלוונטיות — והוא דורש הבנה של שפה, כוונה ומשמעות.

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

המאמר הזה עוסק בשכבה הסמנטית: מה חיפוש וקטורי עושה בפועל, מתי הוא מנצח, ומתי עדיף שלא יפריע. הגרסה השימושית אינה “להטמיע הכול”. היא לדעת מתי הווקטורים צריכים לפעול לצד חיפוש לקסיקלי, fuzzy וחיפוש בהתאמה מדויקת בתוך ארכיטקטורה היברידית.

החצי הלקסיקלי וה־fuzzy של התמונה — tsvector, pg_trgm, pg_search — מופיע ב־מדריך לחיפוש טקסט ב-Postgres 2026.


מונחים המשמשים במדריך הזה

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

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

חיפוש סמנטי — חיפוש המבוסס על משמעות ולא על טוקנים. שאילתה כמו “איך מטפלים ב-timeouts” יכולה למצוא מסמך שכותרתו “הגדרת מדיניות retry” בלי אף מילה משותפת, מפני שה־embeddings שלהם קרובים גאומטרית.

וקטור — רשימת מספרים. בהקשר של חיפוש, זהו הפלט של מודל embedding. “חיפוש וקטורי” מוצא את הווקטורים הקרובים ביותר לווקטור השאילתה לפי מרחק גאומטרי.

FTS (Full-Text Search) — החיפוש הלקסיקלי המובנה של Postgres, המופעל באמצעות tsvector / tsquery. הוא מפרק טקסט לטוקנים, מבצע stemming ומאנדקס אותו עבור שאילתות מילות מפתח. חזק עבור פרוזה ואיתור מונחים מדויקים; עיוור למשמעות.

BM25 — אלגוריתם דירוג לחיפוש לקסיקלי (בשימוש Elasticsearch, ‏Qdrant ואחרים). הוא מדרג תוצאות לפי תדירות המונח, כשהיא משוקללת לפי מידת הנדירות של המונח בכלל הקורפוס. טוב יותר מהתאמת מילות מפתח גולמית; עדיין לקסיקלי.

HNSW (Hierarchical Navigable Small World) — אינדקס חיפוש השכנים הקרובים בקירוב, שהוא הסטנדרט לחיפוש וקטורי. הוא בונה גרף קירבה רב־שכבתי עבור שאילתות דמיון מהירות ובעלות recall גבוה. pgvector, ‏Qdrant, ‏Weaviate ורוב האחרים משתמשים בו.

RRF (Reciprocal Rank Fusion) — אלגוריתם למיזוג רשימות תוצאות מדורגות ממספר מערכות אחזור. הוא משתמש רק במיקום בדירוג — אין צורך בנרמול ציונים. תוצאה שמדורגת גבוה גם ברשימת ה־FTS וגם ברשימת הווקטורים תקבל ציון משולב חזק יותר מתוצאה ששולטת רק באחת מהן.


כיצד Embeddings מוצאים תוכן קשור

Embeddings וקטוריים ממירים טקסט (או תמונות, אודיו וכדומה) לרשימת מספרים — נקודה במרחב רב־ממדי. מודל embedding מאומן כך שטקסטים בעלי קשר סמנטי ימוקמו קרוב זה לזה במרחב הזה. "Dog" ו־"canine" יסתיימו קרוב זה לזה. "Running a marathon" ו־"running a Python script" יסתיימו רחוק זה מזה, אף על פי שהם חולקים מילה.

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

המשמעות היא ש:

חיפוש לקסיקלי (tsvector, pg_trgm) לא יכול לעשות דבר מזה. הוא פועל על מילים ותווים, לא על משמעות. הכלים אינם בני־החלפה — הם פותרים בעיות שונות.


מתי pgvector מנצח

בניית RAG. Retrieval-Augmented Generation מאחזר את מקטעי המסמכים שמשמעותם קרובה ביותר לשאלת המשתמש, ואז מעביר אותם למודל שפה כהקשר. שלב האחזור הזה הוא פעולה וקטורית. FTS יפספס ניסוחים מחדש, מילים נרדפות והתאמות רעיוניות שמקטע רלוונטי עשוי לבטא בצורה אחרת. היתרון של pgvector על פני מאגר וקטורים עצמאי: הוא רץ בתוך מופע ה־Postgres הקיים — בלי שירות נפרד לפריסה, לתפעול או לסנכרון נתונים אליו.

המשתמשים מתארים את מה שהם רוצים, לא את מה שצריך לחפש. ל־"Articles about building confidence as a new manager" אין מילות מפתח שסביר שיופיעו באופן עקבי בפוסטים הרלוונטיים. ״A lightweight framework for handling side effects״ עשוי שלא להשתמש באותן מילים בדיוק בתיעוד. חיפוש וקטורי מתאים לפי הכוונה, לא לפי האיות.

מציאת פריטים דומים. מוצרים קשורים, פניות תמיכה דומות, דיווחי באגים כפולים, מאמרים שאולי יעניינו אתכם גם. "Find issues similar to this one" הוא חיפוש שכנים קרובים — מטמיעים את הפריט ומוצאים את שכניו הגאומטריים. הסתייגות חשובה: חיפוש וקטורי תמיד מחזיר תוצאות, גם כששום דבר אינו באמת דומה. במקרי שימוש של מניעת כפילויות והמלצות, סננו לפי סף דמיון מינימלי (למשל, דמיון קוסינוס ≥ 0.80), כדי להימנע מהצגת התאמות ברמת ביטחון נמוכה כאילו הן משמעותיות.

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

חיפוש רב־לשוני. מודלי embedding רב־לשוניים ממפים תוכן בעל משמעות שקולה בשפות שונות לווקטורים סמוכים. שאילתה בספרדית עבור "perder peso" יכולה להתאים למאמר באנגלית על "sustainable weight loss habits" — ללא טוקנים משותפים, אך עם אותה משמעות בסיסית. FTS דורש הגדרת מילון נפרדת לכל שפה ומתמודד poorly עם שאילתות חוצות־שפות. pg_trgm אינו תלוי בשפה, אך הוא אורטוגרפי, לא סמנטי.

הגדרת pgvector

מהתקנת ההרחבה ועד לשאילתת דמיון, ההגדרה מסתכמת בכמה פקודות SQL:

CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE documents ADD COLUMN embedding vector(1536);
-- HNSW is usually the first index to try for moderate-size datasets
CREATE INDEX documents_embedding_idx
ON documents USING hnsw (embedding vector_cosine_ops);
-- Semantic search query
SELECT id, title, 1 - (embedding <=> $1::vector) AS similarity
FROM documents
ORDER BY embedding <=> $1::vector
LIMIT 10;

<=> הוא מרחק קוסינוס. 1 - cosine_distance נותן דמיון קוסינוס (‏1.0 = זהה, 0.0 = אורתוגונלי). עבור ivfflat (החלופה הוותיקה ומהירה יותר לבנייה), השתמשו ב־lists = sqrt(row_count) כנקודת התחלה.

היכן חיפוש וקטורי מחזיר את התשובה הלא נכונה


שלבו מילות מפתח ווקטורים עבור שאילתות מעורבות

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

משתמשים שמחפשים “how to configure timeouts” זקוקים להתאמה מושגית: מאמר שכותרתו “Setting retry policies and connection limits” אינו חולק מילות מפתח עם השאילתה, אבל הוא בדיוק מה שהם צריכים.

אותם משתמשים מחפשים גם את withRetry(), את ECONNRESET ואת ERR_SOCKET_TIMEOUT. המחרוזות המדויקות האלה חייבות להופיע — התאמה סמנטית עלולה שלא למצוא אותן באופן אמין, ותוצאה חיובית שגויה (דומה מבחינה מושגית, אבל לא ה־API הנכון) מטעה בפועל.

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

הפתרון הוא חיפוש היברידי: מריצים את שניהם וממזגים את התוצאות.

מיזוג תוצאות מדורגות באמצעות RRF

Reciprocal Rank Fusion (RRF) הוא האלגוריתם המקובל לשילוב רשימות מדורגות ממערכות אחזור שונות. הוא אינו דורש לנרמל ציונים בין המערכות — הוא משתמש רק במיקומים בדירוג. תוצאה שמופיעה גבוה בשתי הרשימות מקבלת ציון משולב חזק יותר מתוצאה ששולטת רק באחת מהן.

WITH fts_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, query) DESC) AS rank
FROM documents, to_tsquery('english', $1) query
WHERE search_vector @@ query
LIMIT 50
),
vector_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank
FROM documents
ORDER BY embedding <=> $2::vector
LIMIT 50
),
rrf AS (
SELECT
COALESCE(f.id, v.id) AS id,
COALESCE(1.0 / (60 + f.rank), 0) +
COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score
FROM fts_results f
FULL OUTER JOIN vector_results v ON f.id = v.id
)
SELECT d.id, d.title, rrf.rrf_score
FROM rrf
JOIN documents d ON d.id = rrf.id
ORDER BY rrf_score DESC
LIMIT 10;

ה־60 במכנה הוא קבוע ה־RRF. ערכים גבוהים יותר ממתנים את ההבדלים בין מיקומי הדירוג; ערכים נמוכים יותר מעצימים אותם. ברירת המחדל, 60, עובדת היטב עבור רוב סוגי התוכן.

RRF עוקף את הבעיה הקשה יותר של נרמול ts_rank (ציון המבוסס על תדירות לוגריתמית) מול מרחק קוסינוס (מדד גאומטרי). אלה מדדים שאי אפשר להשוות ישירות. RRF שואל רק: “כמה גבוה הופיעה התוצאה הזו בכל אחת מהרשימות?”

הוסיפו טריגרמות לשגיאות כתיב ולשמות

בחיפוש למשתמשים על תוכן מעורב — שבו משתמשים עשויים לחפש שם של אדם, מושג או מונח מדויק באותו סשן — מיזוג תלת־כיווני מטפל בכולם:

WITH trgm_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY similarity(title, $1) DESC) AS rank
FROM documents
WHERE title % $1
LIMIT 50
),
fts_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, to_tsquery('english', $1)) DESC) AS rank
FROM documents
WHERE search_vector @@ to_tsquery('english', $1)
LIMIT 50
),
vector_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank
FROM documents
ORDER BY embedding <=> $2::vector
LIMIT 50
),
rrf AS (
SELECT
COALESCE(t.id, f.id, v.id) AS id,
COALESCE(1.0 / (60 + t.rank), 0) +
COALESCE(1.0 / (60 + f.rank), 0) +
COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score
FROM trgm_results t
FULL OUTER JOIN fts_results f ON t.id = f.id
FULL OUTER JOIN vector_results v ON COALESCE(t.id, f.id) = v.id
)
SELECT d.id, d.title, rrf.rrf_score
FROM rrf
JOIN documents d ON d.id = rrf.id
ORDER BY rrf_score DESC
LIMIT 10;

כך מתקבל טיפול בהתאמות שמות גמישות (טריגרמות), בהתאמות מדויקות של מילות מפתח (FTS) ובשאילתות מושגיות (וקטור). תיבת חיפוש יחידה יכולה לשרת את שלושת סוגי הכוונה האלה של המשתמשים.


התאימו כל ממשק חיפוש לסוגי השאילתות שלו

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

ממשקמה המשתמשים מחפשיםשכבות מומלצות
חיפוש בבלוג / בתיעודמילות מפתח + מושגיםFTS + pgvector (RRF)
חיפוש שמות משתמשים/לקוחותשמות עם שגיאות כתיבpg_trgm
חיפוש מוצריםשמות, תיאורים, “דומה ל־“pg_trgm + FTS + pgvector
מניעת כפילויות בכרטיסי תמיכה”בעיות הדומות לזו”pgvector בלבד
חיפוש פנימי לפי SKU/הזמנהמזהים מדויקיםאינדקס B-tree
RAG על בסיס ידע גדולשאלות בשפה טבעיתpgvector (מסמכים מחולקים למקטעים)
“אולי תאהבו גם” במסחר אלקטרונידמיון התנהגותי + סמנטיpgvector
השלמה אוטומטיתקידומת, עמידות לשגיאות כתיבpg_trgm

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

הוסיפו שכבת חיפוש כשסוג שאילתה נכשל

הוסיפו שכבה כאשר מופיע מצב כשל שהשכבה הנוכחית אינה יכולה לתקן:


מתי לעבור מעבר ל־pgvector

pgvector מטפל בחלק גדול מצורכי החיפוש של יישומים, עוד לפני שיש צורך במסד נתונים נוסף. נקודת החיתוך המשוערת תלויה במספר הווקטורים, בהגדרות האינדקס, בקצב הכתיבות, במסננים, בחומרה ובמקביליות. לכן יש להתייחס לכלל כמו “פחות מ־10 מיליון וקטורים” כהנחת פתיחה שצריך למדוד באמצעות בנצ’מרק, לא כמגבלת מוצר. כאשר באמת גדלתם מעבר ליכולותיו — מקביליות גבוהה מאוד, דרישות p99 נמוכות מאוד, מיליארדי וקטורים או צורך רציני בבידוד בין דיירים — השוק של מסדי נתונים וקטוריים ייעודיים רחב, וכדאי להבין אותו.

איך לקרוא את ההשוואה

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

וקטורים דלילים הולכים רחוק יותר מ־BM25. לווקטור SPLADE יש כ־30,000 ממדים (אחד לכל מונח באוצר המילים), וכ־98% מהם הם אפסים. המיקומים שאינם אפס מציינים אילו מונחים חשובים ובאיזו מידה. שאילתה עבור “dogs” תשקלל גם את “canine” ואת “pet” — דיוק ברמה של BM25, בתוספת הרחבת מונחים בתוך אינדקס וקטורי. אם העמודה הזו היא false, תצטרכו שכבת FTS נפרדת עבור שאילתות של מונחים מדויקים.

# SPLADE: ~30,000 dims, ~60 non-zero — only relevant vocabulary positions fire
def encode_splade(text: str) -> dict:
tokens = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
output = model(**tokens)
vec = torch.log1p(torch.relu(output.logits)).max(dim=1).values.squeeze()
return {"indices": vec.nonzero().squeeze().tolist(), "values": vec[vec != 0].tolist()}

SQL / דמוי־SQL עוסק למעשה בסינון. חיפוש וקטורי ללא סינון הוא דמו. עדיין צריך תחום של דייר, טווחי תאריכים, הרשאות ומסנני קטגוריות. SQL מלא (pgvector, LanceDB) מאפשר לבטא זאת לצד ה־joins הקיימים שלכם. מסדי נתונים ייעודיים משתמשים באובייקטי מסנני JSON (Qdrant, Pinecone), ב־DSL לשאילתות (Elasticsearch, Milvus) או ב־GraphQL (Weaviate). הם עובדים; SQL נעשה אטרקטיבי יותר ככל שלוגיקת הסינון מורכבת יותר.

-- pgvector: vector similarity is just another expression
SELECT id, title, 1 - (embedding <=> $1) AS score
FROM documents
WHERE tenant_id = $2
AND category = ANY($3::text[])
AND created_at > NOW() - INTERVAL '90 days'
ORDER BY embedding <=> $1
LIMIT 10;
# Qdrant: equivalent filter as a Python object — same result, more ceremony
results = client.query_points(
collection_name="documents", query=query_embedding,
query_filter=models.Filter(must=[
models.FieldCondition(key="tenant_id", match=models.MatchValue(value=tenant_id)),
models.FieldCondition(key="category", match=models.MatchAny(any=categories)),
models.FieldCondition(key="created_at", range=models.DatetimeRange(gte=cutoff)),
]),
limit=10,
)

תמיכה מולטימודלית מובנית פירושה שמסד הנתונים מגיע עם מודלי embedding עבור תוכן שאינו טקסט. אתם מוסרים לו URL גולמי של תמונה; הוא מטפל בוקטוריזציה. רוב מסדי הנתונים אינם תלויים בשיטת embedding — אתם אחראים לצינור ה־embedding. Marqo ו־Weaviate (באמצעות מודולי CLIP/ImageBind) סוגרים את הלולאה הזו.

# Marqo: POST raw images, query with text — no external embedding step
mq.index("products").add_documents(
[{"id": "shoe-001", "image": "https://cdn.example.com/shoes/001.jpg"}],
tensor_fields=["image"]
)
results = mq.index("products").search(q="lightweight shoes for summer")
# Returns shoe-001 despite zero keyword overlap — CLIP handles the cross-modal match

אינדקס מבוסס־דיסק הוא מנוף עלויות. אינדקסי HNSW השוכנים ב־RAM עשויים לדרוש כמה גיגה־בייטים של RAM לכל מיליון וקטורים בעלי 1536 ממדים, לאחר שסופרים את הווקטורים הגולמיים, תקורת הגרף והמטא־נתונים. חלופות שהן native לדיסק (Milvus DiskANN, Elasticsearch DiskBBQ, פורמט Lance של LanceDB ושכבת אחסון האובייקטים של Turbopuffer) מוותרות לעיתים על חלק מזמן האחזור בתמורה לעלות תשתית נמוכה יותר. בעומסי RAG, שבהם זמן האחזור של המודל כבר שולט בתמונה, הפשרה הזו ראויה לעיתים קרובות לבנצ’מרק.

מספר הממדים המרבי הוא הגירה שמסתתרת בתוך הארכיטקטורה שלכם. text-embedding-3-large משתמש ב־3072 ממדים, ‏Jina v3 יכול להפיק embeddings גדולים יותר, ומודלי מחקר ממשיכים לדחוף לממדים גבוהים יותר. חלק מהשירותים המנוהלים מפרסמים תקרות קשיחות למספר הממדים; אחרים מתעדים תקרות גבוהות או שאין להם תקרה מעשית עבור מודלי embedding טיפוסיים. בדקו את התיעוד העדכני לפני התחייבות. בחרו משהו עם מרווח נשימה; הגירה של אינדקס וקטורי לאחר שנתקלים בתקרת ממדים היא ספרינט כואב.

פשרות תפעוליות שהטבלה אינה יכולה להציג

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

המצב המשובץ של LanceDB הוא הדבר הקרוב ביותר ל־“SQLite לחיפוש וקטורי”. הוא רץ בתוך התהליך, אינו דורש שרת, ועובד ב־Lambda, ב־Cloudflare Workers ובסביבות edge. הפורמט העמודתי Lance הופך הפעלה משובצת למעשית גם בקנה מידה אמיתי.

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

Vespa היא מה שבוחרים כששליפה היא רק חצי מהמוצר. היא משלבת שליפה לקסיקלית, חיפוש nearest-neighbor, טנסורים, ביטויי דירוג, קיבוץ והגשה מקוונת. העוצמה הזו אמיתית, וכך גם המורכבות התפעולית ומורכבות המידול. היא מתאימה יותר לצוותי חיפוש/המלצות מאשר ל־“להוסיף חיפוש סמנטי לאפליקציית ה־CRUD שלי”.

ClickHouse שייכת לשיחה כשחיפוש מחובר לאנליטיקה. אם מקור האמת שלכם הוא אירועים, לוגים, traces או מדדים, ClickHouse משאירה מרחק וקטורי, סינון, אגרגציה ואינדוקס full-text רציני בתוך מנוע SQL אחד. היא אינה מסד נתונים וקטורי ייעודי, אבל לעיתים קרובות זו התשובה המשעממת-אך-נכונה לשליפה אנליטית.

וקטורים דלילים הם הדרך לקבל התאמת מילות מפתח באיכות של BM25 בתוך אינדקס וקטורי — בלי להפעיל מנוע full-text נפרד. ל־Qdrant ול־Elasticsearch יש כאן מימושים בשלים במיוחד. אם חיפוש היברידי הוא קריטי וארכיטקטורה של שתי מערכות אינה באה בחשבון, תמיכה בווקטורים דלילים היא מה שצריך לחפש.

בחרו מאגר לפי עומס העבודה


אל תבצעו Embedding למזהים ותצפו להתאמות מדויקות

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

“מצאו לי את המשתמש עם כתובת האימייל dan@example.com” אינו בעיית חיפוש וקטורי. “מצאו את ההזמנה עם המזהה ORD-12345” גם לא. ביצוע embedding ל־ORD-12345 וחיפוש לפי דמיון קוסינוס יחזיר משהו — אבל הוא עלול להיות שגוי. למזהה יש תשובה נכונה. התאמה מקורבת של מזהה היא באג.

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

אותו דבר עובד גם בכיוון ההפוך: אל תשתמשו ב־FTS עבור שאילתות שבהן המשתמש מתאר מושג. “מאמרים על קבלת החלטות קשות בתנאי אי־ודאות” אינו מכיל מילות מפתח אמינות. FTS יחזיר רעש או לא יחזיר דבר. השתמשו בכלי שמתאים למבנה השאילתה.


בנו את החיפוש סביב מבנה השאילתה

רוב מערכות החיפוש בייצור זקוקות ליותר משכבה אחת:

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

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