כדי שמודל AI יהיה שימושי בהקשרים ספציפיים, הוא לרוב זקוק לגישה לידע רקע. לדוגמה, צ'אטבוטים של שירות לקוחות דורשים ידע מקיף על העסק הספציפי שעבורו הם פועלים, וסוכני AI לניתוח משפטי זקוקים להיכרות עם מגוון עצום של תיקים קודמים.
מפתחים נוהגים לשפר את הידע של מודל AI באמצעות RAG (Retrieval-Augmented Generation). RAG היא שיטה השולפת מידע רלוונטי ממאגר ידע ומצרפת אותו לפרומפט של המשתמש, ובכך משפרת משמעותית את תגובת המודל. הבעיה היא שפתרונות RAG מסורתיים נוטים להסיר הקשר בעת קידוד המידע, מה שלעיתים קרובות גורם למערכת להיכשל בשליפת המידע הרלוונטי ממאגר הידע.
בפוסט זה, אנו מציגים שיטה המשפרת באופן דרמטי את שלב השליפה ב-RAG. השיטה נקראת "שליפה הקשרית" (Contextual Retrieval) ומשתמשת בשתי תת-טכניקות: Contextual Embeddings ו-Contextual BM25. שיטה זו יכולה להפחית את מספר הכשלים בשליפה ב-49%, ובשילוב עם reranking, ב-67%. אלו הם שיפורים משמעותיים בדיוק השליפה, המתורגמים ישירות לביצועים טובים יותר במשימות עוקבות.
באפשרותכם לפרוס בקלות פתרון "שליפה הקשרית" משלכם עם Claude, באמצעות ה-cookbook שלנו.
הערה על שימוש בפרומפט ארוך יותר
לעתים, הפתרון הפשוט ביותר הוא הטוב ביותר. אם מאגר הידע שלכם קטן מ-200,000 טוקנים (כ-500 עמודי חומר), תוכלו לכלול את מאגר הידע כולו בפרומפט שאתם מעבירים למודל, ללא צורך ב-RAG או בשיטות דומות.
לפני מספר שבועות, השקנו את prompt caching עבור Claude, מה שהופך גישה זו למהירה ויעילה משמעותית מבחינת עלויות. כעת, מפתחים יכולים לשמור פרומפטים בשימוש תכוף בין קריאות API, ובכך להפחית את זמן השהיה ביותר מפי 2 ואת העלויות בעד 90% (תוכלו לראות כיצד זה עובד ב-prompt caching cookbook שלנו).
עם זאת, ככל שמאגר הידע שלכם גדל, תזדקקו לפתרון סקיילינג יעיל יותר. כאן נכנסת לתמונה "שליפה הקשרית".
היכרות עם RAG: סקיילינג למאגרי ידע גדולים יותר
עבור מאגרי ידע גדולים שאינם נכנסים לחלון הקשר, RAG הוא הפתרון המקובל. RAG פועל על ידי עיבוד מקדים של מאגר ידע באמצעות השלבים הבאים:
- פירוק מאגר הידע (ה"קורפוס" של המסמכים) לחלקי טקסט קטנים יותר, בדרך כלל לא יותר מכמה מאות טוקנים;
- שימוש במודל embedding כדי להמיר חלקיקים אלו ל-embedding וקטוריים המקודדים משמעות;
- אחסון ה-embeddings הללו במסד נתונים וקטורי המאפשר חיפוש לפי דמיון סמנטי.
בזמן ריצה, כאשר משתמש מזין שאילתה למודל, מסד הנתונים הווקטורי משמש למציאת החלקיקים הרלוונטיים ביותר בהתבסס על דמיון סמנטי לשאילתה. לאחר מכן, החלקיקים הרלוונטיים ביותר מתווספים לפרומפט הנשלח למודל הגנרטיבי.
בעוד שמודלי embedding מצטיינים בלכידת קשרים סמנטיים, הם עלולים לפספס התאמות מדויקות קריטיות. למרבה המזל, קיימת טכניקה ותיקה יותר שיכולה לסייע במצבים אלו. BM25 (Best Matching 25) היא פונקציית דירוג המשתמשת בהתאמה לקסיקלית כדי למצוא התאמות מדויקות של מילים או ביטויים. היא יעילה במיוחד עבור שאילתות הכוללות מזהים ייחודיים או מונחים טכניים.
BM25 פועל על בסיס הרעיון של TF-IDF (Term Frequency-Inverse Document Frequency). TF-IDF מודד את חשיבותה של מילה למסמך באוסף. BM25 משכלל זאת על ידי התחשבות באורך המסמך ויישום פונקציית רוויה לתדירות המונחים, מה שמסייע למנוע ממילים נפוצות להשתלט על התוצאות.
הנה כיצד BM25 יכול להצליח היכן שמודלי embedding סמנטיים נכשלים: נניח שמשתמש שואל "קוד שגיאה TS-999" במסד נתונים של תמיכה טכנית. מודל embedding עשוי למצוא תוכן על קודי שגיאה באופן כללי, אך עלול לפספס את ההתאמה המדויקת ל-"TS-999". BM25 מחפש את מחרוזת הטקסט הספציפית הזו כדי לזהות את התיעוד הרלוונטי.
פתרונות RAG יכולים לשלוף בצורה מדויקת יותר את החלקיקים הרלוונטיים ביותר על ידי שילוב טכניקות ה-embeddings ו-BM25 באמצעות השלבים הבאים:
- פירוק מאגר הידע (ה"קורפוס" של המסמכים) לחלקי טקסט קטנים יותר, בדרך כלל לא יותר מכמה מאות טוקנים;
- יצירת קידודי TF-IDF ו-embeddings סמנטיים עבור חלקיקים אלו;
- שימוש ב-BM25 כדי למצוא את החלקיקים המובילים על בסיס התאמות מדויקות;
- שימוש ב-embeddings כדי למצוא את החלקיקים המובילים על בסיס דמיון סמנטי;
- שילוב והסרת כפילויות של התוצאות מ-(3) ו-(4) באמצעות טכניקות איחוד דירוגים (rank fusion);
- הוספת K-החלקיקים המובילים לפרומפט כדי לייצר את התגובה.
על ידי מינוף BM25 ומודלי embedding כאחד, מערכות RAG מסורתיות יכולות לספק תוצאות מקיפות ומדויקות יותר, תוך איזון בין התאמת מונחים מדויקת להבנה סמנטית רחבה יותר.

גישה זו מאפשרת סקיילינג חסכוני למאגרי ידע עצומים, הרבה מעבר למה שיכול להיכנס לפרומפט יחיד. אך למערכות RAG מסורתיות אלו יש מגבלה משמעותית: לעיתים קרובות הן הורסות הקשר.
חידת ההקשר ב-RAG מסורתי
ב-RAG מסורתי, מסמכים מחולקים בדרך כלל לחלקיקים קטנים יותר לשם שליפה יעילה. בעוד שגישה זו פועלת היטב עבור יישומים רבים, היא עלולה להוביל לבעיות כאשר חלקיקים בודדים חסרים הקשר מספק.
לדוגמה, דמיינו שיש לכם אוסף של מידע פיננסי (למשל, דוחות SEC אמריקאיים) מוטמע במאגר הידע שלכם, וקיבלתם את השאלה הבאה: "מה הייתה צמיחת ההכנסות של ACME Corp ברבעון השני של 2023?"
חלקיק רלוונטי עשוי להכיל את הטקסט: "הכנסות החברה צמחו ב-3% לעומת הרבעון הקודם." עם זאת, חלקיק זה לבדו אינו מציין לאיזו חברה הוא מתייחס או מהי תקופת הזמן הרלוונטית, מה שמקשה על שליפת המידע הנכון או שימוש יעיל במידע.
הכירו את "שליפה הקשרית"
"שליפה הקשרית" פותרת בעיה זו על ידי הוספת הקשר הסברני ספציפי לחלקיק לכל חלקיק, לפני ה-embedding ("Contextual Embeddings") ויצירת אינדקס BM25 ("Contextual BM25").
נחזור לדוגמת אוסף דוחות ה-SEC שלנו. הנה דוגמה לאופן שבו חלקיק עשוי לעבור טרנספורמציה:
original_chunk = "The company's revenue grew by 3% over the previous quarter."
contextualized_chunk = "This chunk is from an SEC filing on ACME corp's performance in Q2 2023; the previous quarter's revenue was $314 million. The company's revenue grew by 3% over the previous quarter."ראוי לציין כי גישות אחרות לשימוש בהקשר לשיפור השליפה הוצעו בעבר. הצעות אחרות כוללות: הוספת סיכומי מסמכים כלליים לחלקיקים (ניסינו וראינו שיפורים מוגבלים מאוד), embedding של מסמכים היפותטיים, ו-אינדוקס מבוסס סיכומים (הערכנו וראינו ביצועים נמוכים). שיטות אלו שונות ממה שמוצע בפוסט זה.
יישום "שליפה הקשרית"
כמובן, יהיה זה עבודה רבה מדי לסמן ידנית אלפי או אפילו מיליוני חלקיקים במאגר ידע. כדי ליישם "שליפה הקשרית", אנו פונים לקלוד. כתבנו פרומפט המורה למודל לספק הקשר תמציתי וספציפי לכל חלקיק, המסביר את החלקיק תוך שימוש בהקשר המסמך הכולל. השתמשנו בפרומפט הבא של Claude 3 Haiku כדי לייצר הקשר לכל חלקיק:
<document>
{{WHOLE_DOCUMENT}}
</document>
Here is the chunk we want to situate within the whole document
<chunk>
{{CHUNK_CONTENT}}
</chunk>
Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else.הטקסט ההקשרי המתקבל, בדרך כלל 50-100 טוקנים, מתווסף בתחילת החלקיק לפני ה-embedding שלו ולפני יצירת אינדקס BM25.

אם אתם מעוניינים להשתמש ב"שליפה הקשרית", תוכלו להתחיל עם ה-cookbook שלנו.
שימוש ב-Prompt Caching להפחתת עלויות "שליפה הקשרית"
"שליפה הקשרית" מתאפשרת באופן ייחודי בעלות נמוכה עם Claude, הודות לתכונת ה-prompt caching המיוחדת שהזכרנו לעיל. עם prompt caching, אין צורך להעביר את מסמך הייחוס עבור כל חלקיק. פשוט טוענים את המסמך למטמון פעם אחת ולאחר מכן מתייחסים לתוכן שנשמר במטמון. בהנחה של חלקיקים בני 800 טוקנים, מסמכים בני 8k טוקנים, הוראות הקשר של 50 טוקנים, ו-100 טוקנים של הקשר לכל חלקיק, העלות החד-פעמית ליצירת חלקיקים בעלי הקשר היא 1.02 דולר למיליון טוקנים של מסמך.
מתודולוגיה
ערכנו ניסויים על פני מגוון תחומי ידע (בסיסי קוד, ספרות בדיונית, מאמרים מ-ArXiv, מאמרי מדע), מודלי embedding, אסטרטגיות שליפה ומדדי הערכה. כללנו כמה דוגמאות לשאלות ולתשובות שבהן השתמשנו עבור כל תחום ב-נספח II.
הגרפים שלהלן מציגים את הביצועים הממוצעים על פני כל תחומי הידע עם תצורת ה-embedding בעלת הביצועים הטובים ביותר (Gemini Text 004) ושליפת 20 החלקיקים המובילים. אנו משתמשים ב-1 פחות recall@20 כמדד ההערכה שלנו, אשר מודד את אחוז המסמכים הרלוונטיים שלא נשלפים בקרב 20 החלקיקים המובילים. ניתן לראות את התוצאות המלאות בנספח – הוספת הקשר משפרת את הביצועים בכל שילוב של embedding ומקור שבדקנו.
שיפורי ביצועים
- Contextual Embeddings הפחיתו את שיעור הכשלים בשליפת 20 החלקיקים המובילים ב-35% (5.7% ← 3.7%).
- שילוב Contextual Embeddings ו-Contextual BM25 הפחית את שיעור הכשלים בשליפת 20 החלקיקים המובילים ב-49% (5.7% ← 2.9%).

שיקולי יישום
- גבולות חלקיקים (Chunk boundaries): שקלו כיצד אתם מפצלים את המסמכים שלכם לחלקיקים. הבחירה בגודל החלקיק, גבול החלקיק וחפיפת חלקיקים יכולה להשפיע על ביצועי השליפה.
- מודל embedding: בעוד ש"שליפה הקשרית" משפרת ביצועים בכל מודלי ה-embedding שבדקנו, ייתכן שחלק מהמודלים יפיקו תועלת רבה יותר מאחרים. מצאנו כי embeddings של Gemini ושל Voyage היו יעילים במיוחד.
- פרומפטים מותאמים אישית לייצור הקשר (Custom contextualizer prompts): בעוד שהפרומפט הגנרי שסיפקנו פועל היטב, ייתכן שתוכלו להשיג תוצאות טובות אף יותר עם פרומפטים המותאמים לתחום הספציפי שלכם או למקרה השימוש (לדוגמה, הכללת מילון מונחים של מונחי מפתח שעשויים להיות מוגדרים רק במסמכים אחרים במאגר הידע).
- מספר החלקיקים: הוספת חלקיקים נוספים לחלון הקשר מגדילה את הסיכויים שתכללו את המידע הרלוונטי. עם זאת, מידע רב מדי עלול להסיח את דעתם של המודלים, ולכן יש לכך גבול. ניסינו להעביר 5, 10 ו-20 חלקיקים, ומצאנו ששימוש ב-20 היה בעל הביצועים הטובים ביותר מבין אפשרויות אלו (ראו נספח להשוואות) אך כדאי להתנסות במקרה השימוש הספציפי שלכם.
בצעו הערכות באופן קבוע: יצירת תגובות עשויה להשתפר על ידי העברת החלקיק הממוסגר בהקשר והבחנה בין מהו הקשר ומהו החלקיק עצמו.
שיפור נוסף בביצועים באמצעות Reranking
בשלב אחרון, אנו יכולים לשלב את "שליפה הקשרית" עם טכניקה נוספת כדי להשיג שיפורים נוספים בביצועים. ב-RAG מסורתי, מערכת ה-AI מחפשת במאגר הידע שלה כדי למצוא את חלקיקי המידע שעשויים להיות רלוונטיים. עם מאגרי ידע גדולים, שליפה ראשונית זו מחזירה לעיתים קרובות חלקיקים רבים – לפעמים מאות – בעלי רלוונטיות וחשיבות משתנות.
Reranking היא טכניקת סינון נפוצה שמטרתה לוודא שרק החלקיקים הרלוונטיים ביותר יועברו למודל. Reranking מספק תגובות טובות יותר ומפחית עלויות וזמן שהיה, מכיוון שהמודל מעבד פחות מידע. שלבי המפתח הם:
- ביצוע שליפה ראשונית כדי לקבל את החלקיקים בעלי הרלוונטיות הפוטנציאלית הגבוהה ביותר (אנו השתמשנו ב-150 המובילים);
- העברת N-החלקיקים המובילים, יחד עם שאילתת המשתמש, דרך מודל ה-reranking;
- באמצעות מודל reranking, מתן ציון לכל חלקיק בהתבסס על רלוונטיותו וחשיבותו לפרומפט, ולאחר מכן בחירת K-החלקיקים המובילים (אנו השתמשנו ב-20 המובילים);
- העברת K-החלקיקים המובילים למודל כהקשר ליצירת התוצאה הסופית.

שיפורי ביצועים
קיימים מספר מודלי reranking בשוק. אנו ערכנו את הבדיקות שלנו עם ה-reranker של Cohere. Voyage מציעה גם reranker, אם כי לא היה לנו זמן לבדוק אותו. הניסויים שלנו הראו כי, על פני תחומי ידע שונים, הוספת שלב reranking מייעלת עוד יותר את השליפה.
באופן ספציפי, מצאנו כי Contextual Embedding ו-Contextual BM25 יחד עם reranking הפחיתו את שיעור הכשלים בשליפת 20 החלקיקים המובילים ב-67% (5.7% ← 1.9%).

שיקולי עלות וזמן שהיה
שיקול חשוב אחד ב-reranking הוא ההשפעה על זמן השהיה ועלויות, במיוחד כאשר מבצעים reranking על מספר רב של חלקיקים. מכיוון ש-reranking מוסיף שלב נוסף בזמן ריצה, הוא בהכרח מוסיף כמות קטנה של זמן שהיה, למרות שה-reranker מדרג את כל החלקיקים במקביל. ישנו Trade-off מובנה בין reranking של יותר חלקיקים לביצועים טובים יותר, לבין reranking של פחות חלקיקים עבור זמן שהיה ועלות נמוכים יותר. אנו ממליצים להתנסות בהגדרות שונות במקרה השימוש הספציפי שלכם כדי למצוא את האיזון הנכון.
מסקנה
ערכנו מספר רב של בדיקות, תוך השוואת שילובים שונים של כל הטכניקות שתוארו לעיל (מודל embedding, שימוש ב-BM25, שימוש בשליפה הקשרית, שימוש ב-reranker, והמספר הכולל של תוצאות ה-K-המובילות שנשלפו), על פני מגוון רחב של סוגי מערכי נתונים שונים. הנה סיכום הממצאים שלנו:
- embeddings + BM25 עדיף על embeddings לבדם;
- ל-Voyage ול-Gemini יש את ה-embeddings הטובים ביותר מבין אלה שבדקנו;
- העברת 20 החalקיקים המובילים למודל יעילה יותר מאשר רק 10 או 5 המובילים;
- הוספת הקשר לחלקיקים משפרת מאוד את דיוק השליפה;
- Reranking עדיף על היעדר reranking;
- כל היתרונות הללו נערמים זה על זה: כדי למקסם את שיפורי הביצועים, אנו יכולים לשלב Contextual Embeddings (מ-Voyage או Gemini) עם Contextual BM25, בתוספת שלב reranking, והוספת 20 החלקיקים לפרומפט.
אנו מעודדים את כל המפתחים העובדים עם מאגרי ידע להשתמש ב-ה-cookbook שלנו כדי להתנסות בגישות אלו ולפתוח רמות חדשות של ביצועים.
נספח I
להלן פירוט תוצאות על פני מערכי נתונים, ספקי embedding, שימוש ב-BM25 בנוסף ל-embeddings, שימוש בשליפה הקשרית, ושימוש ב-reranking עבור שליפות ב-20 (Retrievals @ 20).
ראו נספח II לפירוט עבור שליפות ב-10 וב-5 (Retrievals @ 10 ו-@ 5) וכן דוגמאות לשאלות ותשובות עבור כל מערך נתונים.

תודות
מחקר וכתיבה מאת דניאל פורד (Daniel Ford). תודות לאורווה סיקדר (Orowa Sikder), גאוטם מיטאל (Gautam Mittal) וקנת' ליין (Kenneth Lien) על המשוב הקריטי, לסמואל פלמיני (Samuel Flamini) על יישום ה-cookbooks, ללורן פולנסקי (Lauren Polansky) על תיאום הפרויקט, ולאלכס אלברט (Alex Albert), סוזן פיין (Susan Payne), סטיוארט ריצ'י (Stuart Ritchie) ובראד אברמס (Brad Abrams) על עיצוב פוסט זה.



