דלג לתוכן הראשי
רמה: מתקדם עודכן: אוגוסט 2026

Embeddings — וקטורים סמנטיים

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

מה זה בעצם

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

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

מה שהופך את זה לשימושי הוא ששאלה ותשובה שאין ביניהן אף מילה משותפת יכולות להיות קרובות. "איך אני מבטל?" ו"להפסקת המנוי, פנו לשירות הלקוחות" לא חולקות מילה אחת, ובמרחב הווקטורי הן שכנות. זה מה שחיפוש מילולי לא יכול לעשות, וזו הסיבה שכל מערכת RAG נשענת על זה.

התמונה שמספיקה

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

דמיון הוא לא רלוונטיות

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

דוגמה שמבהירה מיד. המשתמש שואל "האם אפשר לבטל אחרי שבוע?" ובמאגר יש שני קטעים: אחד שמסביר את מדיניות הביטול תוך 14 יום, ואחד שמסביר איך לבטל הזמנה שטרם נשלחה. שניהם עוסקים בביטול, שניהם דומים לשאלה, ורק אחד עונה עליה. לחיפוש וקטורי אין מושג מה ההבדל — הוא מודד קרבה, לא מענה.

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

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

איך מודדים את המרחק

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

import numpy as np

def cosine(a, b):
    a, b = np.array(a), np.array(b)
    return float(a @ b / (np.linalg.norm(a) *
                          np.linalg.norm(b)))

q = embed("איך מבטלים מנוי?")
docs = [embed(t) for t in texts]

scores = [cosine(q, d) for d in docs]
top = sorted(zip(scores, texts), reverse=True)[:5]

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

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

איך בוחרים מודל

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

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

שאלה וקטע אינם אותו סוג טקסט

יש אי-התאמה מובנית שקל לפספס: השאלה קצרה והקטע ארוך, והם כתובים אחרת. "איך מבטלים?" הוא שלוש מילים בגוף ראשון; הקטע שעונה עליו הוא פסקה רשמית בגוף שלישי. הם אמורים להיות קרובים במרחב, ולעתים קרובות הם פחות קרובים ממה שנדרש.

שלוש דרכים מקובלות לצמצם את הפער, לפי סדר העלות:

עברית: מה באמת שונה

זו הנקודה שבה רוב התיעוד לא עוזר, כי הוא נכתב על אנגלית. ארבעה דברים משתנים.

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

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

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

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

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

החלוקה קובעת יותר מהמודל

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

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

מה שעובד:

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

מה נכנס לקטע חוץ מהטקסט

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

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

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

איפה שומרים את הווקטורים

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

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

מאגר שמתעדכן

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

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

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

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

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

למה שילוב עם חיפוש מילולי כמעט תמיד עדיף

חיפוש וקטורי טוב במשמעות וחלש בדיוק מילולי. יש סוג שאילתות שהוא נכשל בהן באופן עקבי: מזהים, מק״טים, שמות מוצר, מספרי גרסה, שמות אנשים. מי שמחפש "SKU-4471" רוצה התאמה מדויקת, והמרחב הווקטורי יחזיר לו גם את SKU-4472 באותה מידה.

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

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

כמה תוצאות להביא

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

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

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

לא רק חיפוש

Embeddings מזוהים עם RAG, ויש להם שימושים פשוטים יותר שמחזירים ערך מיידי:

מה קורה כשמחליפים מודל

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

המשמעות המעשית: החלפת מודל היא הטמעה מחדש של כל המאגר. לא עדכון, לא הדרגתי — הכל. ובמאגר גדול זו עבודה שעולה זמן וכסף.

שלושה דברים שהופכים את זה לנסבל:

מה עולה להטמיע מאגר

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

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

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

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

איך יודעים שהחיפוש טוב

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

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

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

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

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

כשתוצאה נראית שגויה, יש סדר בדיקה שחוסך שעות במקום לנחש:

  1. האם הקטע הנכון קיים בכלל במאגר. חפשו אותו בטקסט חופשי, לא דרך הווקטורים. במקרים רבים מתברר שהוא מעולם לא נכנס — קובץ שנכשל בעיבוד, מסמך שלא הועלה.
  2. האם הוא נחתך. אם הקטע ארוך מהמותר במודל, סופו נעלם בשקט. בדקו את האורך בטוקנים ולא בתווים.
  3. איך הוא נראה אחרי החלוקה. הדפיסו את הקטע כפי שנשמר. זה השלב שבו מתגלים חיתוכים באמצע משפט וטבלאות שהפכו לרצף מספרים חסר משמעות.
  4. באיזה מקום הוא דורג. אם הוא במקום השמיני — יש בעיית דירוג, לא בעיית הטמעה, והתיקון שונה לגמרי.
  5. מה הסינון עשה. תנאי הרשאה או תאריך שמורידים תוצאות טובות הם הסבר נפוץ ל"החיפוש הפסיק לעבוד" אחרי שינוי שנראה לא קשור.

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

מתי לא צריך את זה

טעויות שחוזרות