כדי שמודל 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 שלנו).

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

הקדמה ל-RAG: סקיילינג למאגרי ידע גדולים יותר

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

  1. פירוק מאגר הידע ('קורפוס' המסמכים) לחלקי טקסט קטנים יותר, בדרך כלל לא יותר מכמה מאות טוקנים;
  2. שימוש במודל embedding כדי להמיר את החלקים הללו ל-embedding וקטורי המקודד משמעות;
  3. אחסון ה-embeddings הללו בבסיס נתונים וקטורי המאפשר חיפוש לפי דמיון סמנטי.

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

בעוד שמודלי embedding מצטיינים בלכידת קשרים סמנטיים, הם עלולים לפספס התאמות מדויקות קריטיות. למרבה המזל, קיימת טכניקה ותיקה יותר שיכולה לסייע במצבים אלה. BM25 (Best Matching 25) היא פונקציית דירוג המשתמשת בהתאמה לקסיקלית כדי למצוא התאמות מדויקות של מילים או ביטויים. היא יעילה במיוחד עבור שאילתות הכוללות מזהים ייחודיים או מונחים טכניים.

BM25 פועל על בסיס הרעיון של TF-IDF (Term Frequency-Inverse Document Frequency). TF-IDF מודד את חשיבותה של מילה למסמך באוסף. BM25 משכלל זאת על ידי התחשבות באורך המסמך ויישום פונקציית רוויה לתדירות המונח, מה שעוזר למנוע ממילים נפוצות לשלוט בתוצאות.

הנה כיצד BM25 יכול להצליח היכן ש-embeddings סמנטיים נכשלים: נניח שמשתמש שואל "Error code TS-999" במאגר נתונים של תמיכה טכנית. מודל embedding עשוי למצוא תוכן על קודי שגיאה באופן כללי, אך עלול לפספס את ההתאמה המדויקת ל-"TS-999". BM25 מחפש את מחרוזת הטקסט הספציפית הזו כדי לזהות את התיעוד הרלוונטי.

פתרונות RAG יכולים לשלוף באופן מדויק יותר את חלקי המידע הרלוונטיים ביותר על ידי שילוב של טכניקות embeddings ו-BM25 בצעדים הבאים:

  1. פירוק מאגר הידע ('קורפוס' המסמכים) לחלקי טקסט קטנים יותר, בדרך כלל לא יותר מכמה מאות טוקנים;
  2. יצירת קידודי TF-IDF ו-embeddings סמנטיים עבור חלקי מידע אלה;
  3. שימוש ב-BM25 למציאת החלקים המובילים על בסיס התאמות מדויקות;
  4. שימוש ב-embeddings למציאת החלקים המובילים על בסיס דמיון סמנטי;
  5. שילוב והסרת כפילויות של תוצאות מ-(3) ו-(4) באמצעות טכניקות rank fusion;
  6. הוספת ה-K-החלקים המובילים לפרומפט כדי ליצור את התגובה.

על ידי מינוף מודלי BM25 ו-embedding, מערכות RAG מסורתיות יכולות לספק תוצאות מקיפות ומדויקות יותר, תוך איזון בין התאמת מונחים מדויקת להבנה סמנטית רחבה יותר.

תרשים המתאר את תהליך השליפה ב-RAG מסורתי, המשלב BM25 ו-Embeddings.
תרשים זרימה של תהליך RAG מסורתי, המשלב שליפה מבוססת BM25 ו-embeddings סמנטיים.

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

חידת ההקשר ב-RAG מסורתי

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

לדוגמה, דמיינו שיש לכם אוסף מידע פיננסי (למשל, דוחות SEC של ארה"ב) המשולב במאגר הידע שלכם, וקיבלתם את השאלה הבאה: "מה הייתה צמיחת ההכנסות של ACME Corp ברבעון השני של 2023?"

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

היכרות עם 'שליפה קונטקסטואלית'

'שליפה קונטקסטואלית' פותרת בעיה זו על ידי הוספת הקשר הסברני ספציפי לחלק מידע לכל חלק מידע, לפני הטמעתו (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. כתבנו פרומפט המורה למודל לספק הקשר תמציתי וספציפי לחלק המידע, שמסביר את חלק המידע באמצעות ההקשר של המסמך כולו. השתמשנו בפרומפט 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 טוקנים, מתווסף בתחילת חלק המידע לפני הטמעתו ולפני יצירת אינדקס ה-BM25.

כך נראה תהליך העיבוד המקדים בפועל:

תרשים זרימה של תהליך Preprocessing עבור Contextual Retrieval, המשלב יצירת הקשר עם Claude.
תרשים זרימה המציג את תהליך ה-preprocessing עבור 'שליפה קונטקסטואלית', כולל יצירת הקשר על ידי Claude.

אם אתם מעוניינים להשתמש ב'שליפה קונטקסטואלית', תוכלו להתחיל עם המדריך שלנו (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%).
גרף השוואת ביצועים בין RAG מסורתי ל-Contextual Retrieval (Embeddings ו-BM25), מראה ירידה בשיעור כשלי השליפה.
השוואת ביצועי שליפה: RAG מסורתי מול Contextual Embeddings ו-Contextual BM25, המציג ירידה משמעותית בכשלי שליפה.

שיקולי יישום

בעת יישום 'שליפה קונטקסטואלית', יש כמה שיקולים שכדאי לזכור:

  1. גבולות חלקי המידע: שקלו כיצד אתם מפצלים את המסמכים שלכם לחלקי מידע. הבחירה בגודל חלקי המידע, גבול חלקי המידע וחפיפת חלקי המידע יכולה להשפיע על ביצועי השליפה.
  2. מודל Embedding: בעוד ש'שליפה קונטקסטואלית' משפרת ביצועים בכל מודלי ה-embedding שבדקנו, ייתכן שחלק מהמודלים יפיקו תועלת רבה יותר מאחרים. מצאנו כי embeddings של Gemini ושל Voyage יעילים במיוחד.
  3. פרומפטים מותאמים אישית ליצירת הקשר: בעוד שהפרומפט הגנרי שסיפקנו עובד היטב, ייתכן שתוכלו להשיג תוצאות טובות אף יותר עם פרומפטים המותאמים לתחום הספציפי או למקרה השימוש שלכם (לדוגמה, הכללת מילון מונחים של מונחי מפתח שייתכן שמוגדרים רק במסמכים אחרים במאגר הידע).
  4. מספר חלקי המידע: הוספת חלקי מידע נוספים לחלון הקשר מגבירה את הסיכוי שתכללו את המידע הרלוונטי. עם זאת, יותר מידע יכול להסיט את תשומת הלב של המודלים, ולכן יש לכך גבול. ניסינו להעביר 5, 10 ו-20 חלקי מידע, ומצאנו כי שימוש ב-20 הוא בעל הביצועים הטובים ביותר מבין האפשרויות הללו (ראו נספח להשוואות) אך כדאי להתנסות במקרה השימוש הספציפי שלכם.

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

הגברת ביצועים נוספת באמצעות Reranking

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

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

  1. ביצוע שליפה ראשונית כדי לקבל את חלקי המידע העשויים להיות רלוונטיים ביותר (השתמשנו ב-150 המובילים);
  2. העברת ה-N-חלקי מידע המובילים, יחד עם שאילתת המשתמש, דרך מודל ה-reranking;
  3. באמצעות מודל reranking, מתן ציון לכל חלק מידע על בסיס הרלוונטיות והחשיבות שלו לפרומפט, ולאחר מכן בחירת ה-K-חלקי מידע המובילים (השתמשנו ב-20 המובילים);
  4. העברת ה-K-חלקי מידע המובילים למודל כהקשר ליצירת התוצאה הסופית.
תרשים זרימה של תהליך RAG המשלב reranking כדי לשפר את הרלוונטיות של חלקי המידע לפני העברתם למודל.
תרשים זרימה של תהליך RAG המשלב את שלב ה-reranking לאחר השליפה הראשונית.

שיפורי ביצועים

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

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

גרף המדגים את שיפור הביצועים הנוסף עם שילוב Contextual Retrieval ו-Reranking, מראה ירידה משמעותית ביותר בשיעור כשלי השליפה.
השפעת שילוב reranking עם Contextual Embeddings ו-Contextual BM25 על שיעור כשלי השליפה.

שיקולי עלות וזמן השהיה

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

סיכום

ערכנו מספר רב של בדיקות, השוואת שילובים שונים של כל הטכניקות שתוארו לעיל (מודל embedding, שימוש ב-BM25, שימוש ב'שליפה קונטקסטואלית', שימוש ב-reranker, ומספר כולל של תוצאות K-מובילות שנשלפו), וזאת על פני מגוון רחב של סוגי מערכי נתונים שונים. הנה סיכום הממצאים שלנו:

  1. Embeddings+BM25 טוב יותר מ-embeddings בפני עצמם;
  2. ל-Voyage ול-Gemini יש את ה-embeddings הטובים ביותר מבין אלה שבדקנו;
  3. העברת 20 חלקי המידע המובילים למודל יעילה יותר מאשר רק 10 או 5 המובילים;
  4. הוספת הקשר לחלקי מידע משפרת מאוד את דיוק השליפה;
  5. Reranking טוב יותר מאי-שימוש ב-reranking;
  6. כל היתרונות הללו נערמים: כדי למקסם את שיפורי הביצועים, נוכל לשלב contextual embeddings (מ-Voyage או Gemini) עם Contextual BM25, בתוספת שלב reranking, ולהוסיף את 20 חלקי המידע לפרומפט.

אנו מעודדים את כל המפתחים העובדים עם מאגרי ידע להשתמש במדריך שלנו (cookbook) כדי להתנסות בגישות אלה ולפתוח רמות חדשות של ביצועים.

נספח I

להלן פירוט התוצאות על פני מערכי נתונים, ספקי embedding, שימוש ב-BM25 בנוסף ל-embeddings, שימוש ב'שליפה קונטקסטואלית' ושימוש ב-reranking עבור שליפות @ 20.

ראו נספח II לפירוט עבור שליפות @ 10 ו-@ 5, כמו גם דוגמאות לשאלות ותשובות עבור כל מערך נתונים.

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

תודות

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