Reranking — RAG מדויק יותר
השדרוג הכי משתלם ל-RAG: שכבה שנייה שמדרגת מחדש את התוצאות ומעלה את הרלוונטי באמת לראש. שיפור דיוק דרמטי במאמץ קטן.
למה אחזור וקטורי לבד לא מספיק
ב-RAG בסיסי, מחפשים ב-Vector DB את הקטעים עם ה-embedding הקרוב לשאילתה. זה מהיר ומצוין ל"סינון גס", אבל לא תמיד מדויק: ה-embedding לוכד משמעות כללית, ולעיתים מדרג גבוה קטע ש"נשמע דומה" אך לא באמת עונה על השאלה. התוצאה — קטעים לא רלוונטיים בהקשר, שגורמים לתשובות פחות טובות ואף להזיות.
Reranking הוא לרוב השיפור הכי משתלם שאפשר לעשות ל-RAG — עלייה ניכרת בדיוק בכמה שורות קוד.
אחזור דו-שלבי (Two-stage Retrieval)
הרעיון: לשלב שני שלבים משלימים —
- שליפה רחבה (Retrieve): ה-Vector DB מחזיר הרבה מועמדים (למשל 50) — מהיר, ממקד לתת-קבוצה רלוונטית.
- דירוג מדויק (Rerank): מודל reranker בוחן כל מועמד מול השאילתה ומחזיר את ה-N הטובים באמת (למשל 5) — איטי יותר, אבל מדויק בהרבה.
מזינים ל-LLM רק את ה-5 המובילים אחרי rerank — הקשר תמציתי ומדויק (ראו גם Context Engineering).
איך reranker עובד — Cross-encoder מול Bi-encoder
ההבדל הטכני שמסביר את הדיוק:
- Bi-encoder (embeddings): מקודד את השאילתה ואת הקטע בנפרד לווקטורים, ומשווה. מהיר מאוד (אפשר לחשב מראש), אך פחות מדויק כי אין אינטראקציה בין השאילתה לקטע.
- Cross-encoder (reranker): מזין את השאילתה והקטע יחד למודל, שמחזיר ציון רלוונטיות. מדויק הרבה יותר כי הוא "קורא" את שניהם ביחד — אבל איטי, ולכן מפעילים אותו רק על מעט מועמדים.
מכאן ההיגיון של הדו-שלבי: bi-encoder מהיר לסינון גס, cross-encoder מדויק לליטוש.
Hybrid Search — בונוס
שיפור משלים: לשלב חיפוש סמנטי (embeddings) עם חיפוש מילולי (keyword / BM25). לכל אחד יתרון — הסמנטי תופס משמעות, המילולי תופס מונחים מדויקים, שמות וקודים. משלבים את שתי רשימות המועמדים, ואז מריצים reranker על האיחוד. זה נותן את הטוב משני העולמות ומצוין לעברית ולמונחים טכניים.
דוגמת קוד — reranker בשירות
שירותי reranking (כמו Cohere Rerank) מקבלים שאילתה ורשימת מסמכים ומחזירים אותם מדורגים. גם קיימים מודלים פתוחים (BGE-reranker) דרך Hugging Face.
# שלב 1: שליפה רחבה מה-Vector DB
candidates = vector_search(query, top_k=50) # 50 מועמדים
# שלב 2: דירוג מחדש (דוגמה עם ספריית reranker)
scored = reranker.rank(query=query,
documents=[c.text for c in candidates])
top = sorted(scored, key=lambda x: x.score, reverse=True)[:5]
# שלב 3: מזינים ל-LLM רק את ה-5 המובילים
context = "\n\n".join(t.text for t in top)
answer = llm_answer(query, context)
טיפים
- שלוף רחב, החזר צר. נסה top_k=30-50 בשליפה, והחזר 3-8 אחרי rerank.
- מדוד עם evals. בדוק שיפור בדיוק על eval set אמיתי — לא "בעין".
- latency: reranker מוסיף זמן. אזן בין דיוק למהירות לפי הצורך.
- עברית: ודא שה-reranker תומך רב-לשונית; hybrid עוזר במיוחד למונחים ושמות.
- עלות: reranker זול ביחס ל-LLM, וחוסך טוקנים כי מזינים פחות קטעים.