למה self-query RAG נשבר על פילטרים מתוך השאילתה
שבר טיפוסי ב-self-query rag: המשתמש כותב "הצג רק חוזים פעילים מהרבעון האחרון", המערכת מריצה את המשפט דרך חיפוש וקטורי ומחזירה מסמכים קרובים סמנטית — אבל מעסקאות סגורות של שנים קודמות. התנאי היה בשאילתה, אבל החיפוש לא ראה אותו.
הסיבה היא ש"רק פעילים" ו"מהרבעון האחרון" הם לא משמעות, אלא הגבלות על מטא-נתונים. מודל האמבדינגים דוחס את כל המשפט לוקטור אחד, שבו הבהרות כאלה מתמוססות בין מילות התוכן וכמעט לא משפיעות על הקרבה. החיפוש מוצא דומה לפי נושא — ומתעלם מחתך הנתונים.
self-query RAG משנה את התרשים: מודל השפה מפרק את השאילתה לשני חלקים. החלק המשמעותי עובר לחיפוש הווקטורי, והתנאים הופכים לפילטרים מתוך השאילתה — סטטוס, תקופה, סוג מסמך, בעלים. הסינון מתבצע לפי כלל מאוחד, ולא רק לפי קרבת וקטורים.
זה נשבר בנקודות החיבור: ערכי המטא-נתונים באינדקס לא תואמים את מה שהמודל מחלץ — בשדה אחד מתקיימים זה לצד זה "פעיל", "בעבודה", "בתוקף". תקופות יחסיות יש לחשב מהתאריך הנוכחי, ולא להציב כמחרוזת.
אם הפילטר מוחל אחרי בחירת השכנים הקרובים, הוא מכווץ את התוצאות למסמך אחד-שניים: התנאי נשמר, אבל דיוק התוצאות ירד.
נפרק איך זה נסגר: חוזה בין השדות המחולצים לסכמת האינדקס, בדיקת הפילטרים לפני שליחה לחיפוש, ומטריקות שסופרות לא רק רלוונטיות אלא גם עמידה בתנאי השאילתה.
מה נותנת הבנייה האוטומטית של פילטרי מטא-נתונים
כשהשאילתה נשמעת כמו "תנאי אספקה ללקוחות קמעונאיים ברבעון הקודם", החיפוש הסמנטי יחזיר קטעים קרובים במשמעות, אבל לא יסנן אותם לפי סוג הלקוח והתקופה.
הבנייה האוטומטית של פילטרים סוגרת את הפער הזה: מהניסוח מחולצים ערכי מטא-נתונים — סוג מסמך, חטיבה, סטטוס, תאריך עדכניות — והם נכנסים לשאילתה כהגבלה קשיחה לפני הדירוג.
ההשפעה הראשונה — סינון אוטומטי מסיר רעש. ההגבלה מוצבת לפני ההשוואה לפי משמעות, ולכן לא נכנסים לסט התוצאות חטיבות זרות ומהדורות מיושנות, והחלק של שאילתות עם תוצאה ריקה יורד מאחוזים דו-ספרתיים לאחדים. "ריק" הגיע לרוב לא מהיעדר נתונים, אלא מכך שהקטע הנדרש לא עמד בסוג או בתקופה.
השנייה — תנאים מורכבים. שאילתות עם שתיים-שלוש הגבלות ("חשבוניות עבור קבלנים מהשנה הקודמת, רק חתומות") נותנות יותר תשובות שגויות: חלק מהתנאים הולך לאיבוד, והמודל עונה לפי קטע מקטגוריה אחרת.
פירוק של חיבורים ושלילות למבנה של תנאים מפחית את שיעור השגיאות האלה פי כמה — איכות התשובות עולה, כי התשובה או נשענת על החתך הנכון, או מדווחת בכנות שאין נתונים.
השלישית — מהירות: סינון מקדים מצמצם את סט התוצאות בסדר גודל, הדירוג מתבצע על מספר קטן יותר של קטעים, וזמן התגובה מתקצר בלי לאבד שלמות. הפילטרים נשענים על המנגנונים המקוריים של Qdrant — סינון מקדים לפני השוואה לפי וקטורים.
תנאים מעורפלים ("עדכניים", "עיקריים") אסור להפוך בשקט לפילטר קשיח: או שמחדדים אותם לטווח, או שמעבירים אותם לחיפוש הסמנטי — אחרת המערכת חותכת נתונים שהמשתמש חשב לזמינים.
מצבי self-query RAG: השוואה של גישות לסינון
self-query RAG שונה מחיפוש וקטורי רגיל בכך שלפני שליפת המסמכים המודל מפרק את השאילתה לחלק משמעותי ולמערכת תנאים על מטא-נתונים: תאריך, מקור, סוג מסמך, חטיבה.
עד כמה הוא חופשי לבנות את התנאים האלה נקבע על ידי מצב החיפוש — וממנו גם תלויים דיוק התוצאות וסוג הכשלים שמגיעים אחר כך לתמיכה. להלן שלוש גישות, שביניהן באמת בוחרים בהתחלה.
| מצב | מתי מתאים | מגבלות הסינון |
|---|---|---|
| את התנאים בונה המודל בזמן השאילתה | נתונים מגוונים, הרבה מאפיינים, הרכב השאילתות לא ידוע מראש | המודל עלול להתייחס לשדה שלא קיים או לערך לא חוקי; בלי בדיקה התוצאות מאבדות מסמכים בשקט |
| התנאים מוגדרים מראש על ידי הסכמה | סט מאפיינים צר ויציב, דרישות קשיחות לחיזוי מראש | שדה חדש מחייב תיקון הסכמה ובדיקות חוזרות; שאילתות כמו "הכול חוץ מהמקור הזה" יש לפרק ידנית |
| היברידי: המודל מציע, הסכמה בודקת | העומס גדל, מספר המאפיינים מתרבה, מחיר הטעות גבוה | נדרש שכבת בדיקה ויומן: צריך לראות אילו תנאים נזרקו ומדוע |
המצב ההיברידי הוא בדרך כלל גם זה שהופך לעבודה בפועל: סכמת הפילטרים מתארת את השדות, את הטיפוסים שלהם, את הערכים המותרים ואת טווחי התאריכים, והמאמת חותך תנאים על שדות שלא קיימים ומציב ערך ברירת מחדל בטוח.
פרט חשוב — פילטר שנזרק לא צריך להפוך בשקט את התשובה לתוצאה ריקה: מרחיבים את החיפוש ורושמים את הסיבה ביומן, אחרת המשתמש רואה "לא נמצא דבר" במקום שבו יש מסמכים.
הטמעת סינון אוטומטי: שלבי העבודה
סינון אוטומטי לא נדלק במתג: המודל מסיק תנאים מהניסוח, בהסתמך על תיאור השדות ועל האופן שבו משתמשים אמיתיים שואלים. לכן העבודה מתחילה בסקירת חיפוש ומסתיימת בתקנון שלפיו מילון השדות ממשיך להתקיים. להלן שלבי ההטמעה בסדר שבו אנחנו מובילים אותם.
- סקירת חיפוש. מייצאים את יומן השאילתות, מסירים רעש תפעולי ומקבצים לפי כוונות. בוחנים את שיעור השאילתות עם תוצאה ריקה — מיד רואים אילו פילטרים חסרים.
- טקסונומיה של שדות. מאחדים את המאפיינים לטיפוסים אחידים: קטגוריה, טווח, תאריך, קיום, אזור. מטיפוס השדה תלוי אילו תנאים בכלל אפשר לבנות.
- מדגם ייחוס. מתייגים ידנית כמה מאות שאילתות בזוגות "ניסוח → פילטר מצופה" — זהו הסרגל שמולו משווים וריאנטים של כיוונון.
- כיוונון הפילטרים. מתארים את סכמת השדות עבור המודל ואת הכללים למקרים דו-משמעיים: היכן התנאי לא מוסק והשאילתה נשארת חיפוש טקסטואלי רגיל.
- בדיקה על קונפליקטים. מריצים שלילות, תנאים סותרים וערכי גבול — ומחפשים ניסוחים שבהם הפילטר הבנוי מאפס את התוצאות.
- בדיקת התוצאות. משווים לפני ואחרי על מדגם סגור: שיעור התוצאות הרלוונטיות הראשונות, שיעור התוצאות הריקות, ההתנהגות על ניסוחים נדירים.
- מסירה. מוסרים תיעוד של השדות, כללי הפירוק, פאנל עם שאילתות לא טיפוסיות ותקנון לעדכון המילון בעת שינוי הקטלוג.
חפצי ההטמעה: סכמות, כללים, תקנונים
הטמעה של self-query RAG מסתיימת לא באב-טיפוס עובד, אלא בסט של חפצים שאיתם צוות הלקוח ממשיך לחיות בלעדינו. סכמת המטא-נתונים וכללי התנאים המותרים קובעים אילו פילטרים המערכת תבנה מהשאילתה, ואילו תדחה עוד לפני הפנייה לאינדקס. אם זה לא מעוגן בכתב, איכות המדגם נשענת על אדם שזוכר את הקונפיגורציה בראש.
- סכמת מטא-נתונים: שדות, טיפוסים, חובה וסטטוס, ורשימת אופרטורי ההשוואה המותרים לכל שדה.
- מילון ערכים: וריאנטים מנורמלים ומילים נרדפות — כדי שאופני כתיבה שונים של אותו ערך ייתנו את אותו הפילטר.
- כללי תנאים מותרים: אילו צירופי פילטרים מותרים, היכן חל איסור על טווחים בשדות טקסטואליים ותנאים ריקים.
- תקנון עדכון: מי בוחן מחדש את המילון עם הופעת ערכים חדשים, באיזה שלב התיקון נכנס לריליס ומי מאשר אותו.
- כללי גישה: הבחנה לפי תפקידים ברמת הפילטרים — הגבלת המדגם לא נעקפת על ידי ניסוח השאילתה.
- הוראות לאנליסטים: איך להוסיף שדה, איך לבדוק שהוא נכנס לפירוק השאילתה, איך להחזיר שינוי אחורה.
- תיעוד ויומן עריכות: גרסת הסכמה, תאריך וסיבת השינוי, סט שאילתות בדיקה לבדיקה אחרי כל עריכה.
הערכה מיועדת לכך שהסינון יורחב על ידי צוות הלקוח עצמו: שדה חדש מתווסף לפי ההוראות, נבדק על שאילתות ייחוס ויוצא לריליס. שיעור השאילתות שבהן תנאי הסינון נבנה נכון מפסיק להיות תלוי במי שנמצא במשמרת היום.
קייס: דיוק התשובות על שאילתות עם תנאים
זרם השאילתות היה אחיד: משתמשים שאלו עם תנאים מפורשים — "חוזים עם תשלום לאחר אספקה מהרבעון הקודם", "משרות במינסק עם רילוקיישן", "תעריפים בתוקף מתחילת השנה".
החיפוש הווקטורי מצא קטעים קרובים במשמעות, אבל התעלם לחלוטין מהתנאים הקשיחים: בתשובה נכנסו מסמכים עם סטטוס, עיר או תקופה אחרים. לא הצלחנו לסנן אותם ביציאה — המודל סיפר בביטחון רב מקור לא רלוונטי, וזה נראה משכנע יותר מ"לא מצאתי" כן.
מה הראו המדידות על מדגם ביקורת
| מטריקה | לפני | אחרי |
|---|---|---|
| שיעור התשובות הנכונות | 61% | 89% |
| שיעור התוצאות הריקות | 23% | 4% |
| תשובות מקטגוריה שגויה | 9% | 0.4% |
| שאילתות עם תנאים שעברו להבהרה | 31% | 6% |
מה בדיוק שינו
הוספנו שכבת פירוק של השאילתה לחלק משמעותי ולתנאים מובנים: שוויון, טווח תאריכים, שייכות לרשימה. המודל מחזיר פילטר לפי סכמת מטא-נתונים שתוארה מראש, השכבה בודקת את תקינותו לפני ביצוע החיפוש ורק אחר כך מעבירה אותו לאינדקס — כמו בגישה של Self-Query Retriever מ-LangChain. את סכמת המטא-נתונים הגדרנו דקלרטיבית, ולכן שדות חדשים לא מחייבים כתיבה מחדש של קוד החיפוש.
נעלם סוג שלם של כשלים: קודם התשובה לא הייתה ריקה פורמלית, אבל הגיעה מקטגוריה שגויה — עכשיו שאילתות כאלה או מקבלות מדגם תקין, או מקבלות שאלת הבהרה במקום ניחוש. בשאילתות בלי תנאים השיפור מזערי: פשוט אין מה להחיל שם פירוק פילטרים, והשכבה לא נותנת יתרון.
מה לעשות אם האינדקס כבר נבנה לפילטרים קשיחים?
אין צורך לבנות את האינדקס מחדש במלואו, אם המסמכים כבר שומרים מאפיינים בשדות נפרדים — תאריך, קטגוריה, אזור, סטטוס. הבעיה נפתרת בשכבת פירוק של השאילתה: המודל מתרגם את המשפט של המשתמש למערכת תנאים מובנית, והפילטרים הקשיחים הקיימים ממשיכים לעבוד כמו קודם. משתנה רק המקור של התנאים האלה — המערכת מציבה אותם, ולא האדם.
סכמת השדות הופכת לחוזה החדש היחיד במערכת. היא מתארת אילו מאפיינים אפשר לשלוף מהטקסט, באיזו צורה (טווח, התאמה מדויקת, רשימת ערכים) ומה לעשות אם המאפיין לא זוהה.
מימוש ייחוס לגישה כזו הוא Self-Query Retriever: מעל התנאים הקשיחים מונח חיפוש סמנטי, ושני הקריטריונים מיושמים בשאילתה אחת, ולא בשני מעברים עוקבים.
הגירת הפילטרים מתבצעת שדה אחר שדה, ולא במתג אחד. המסלול החדש נדלק בסימן נפרד על חלק מהתעבורה, התוצאות מושוות לקודמות לפי חפיפה של התוצאות העליונות, ואם הפער גבוה מהסף — התנאי עובר לפירוק ידני.
המטריקות פשוטות: שיעור השאילתות עם תנאים שהוסקו אוטומטית, שיעור השדות שזוהו, שיעור החזרות ללוגיקה הקודמת.
בנייה מחדש נדרשת רק במקום שבו המאפיין לא קיים במסמכים או שהטיפוס שלו השתנה. אז נדלקת כתיבה כפולה: מסמכים חדשים נכנסים לשתי המבנים, הקריאה עוברת בהדרגה לחדש, והקודם נשאר זמין לחזרה לאחור, עד שהמטריקות יתאמו. החיפוש עונה כל הזמן, בזמן שההחלפה מתבצעת.
ספקות טכניים של מהנדסים ותשובות להם
מהנדסים שואלים לא על הרעיון עצמו, אלא על מצבי הקצה: מה יקרה אם המודל יבנה תנאי על שדה שלא קיים במאגר, או יפרק לא נכון ניסוח דיבורי. להלן ארבעה ספקות שעולים בכל דיון.
מה יקרה אם המודל ימציא שדה שלא קיים בסכמה?
התנאי עובר ולידציה מול התיאור הדקלרטיבי של המטא-נתונים לפני הפנייה למאגר: שדה לא מוכר, טיפוס ערך לא תואם או אופרטור לא חוקי נזרקים, והשאילתה עוברת במסלול גיבוי — חיפוש סמנטי טהור. מחרוזת אקראית מהמודל לעולם לא נכנסת ישירות לשכבת האחסון. את שיעור המקרים האלה אנחנו מוציאים כמטריקה נפרדת — ממנה רואים אילו שדות חסרים בתיאור.
איך מטפלים בניסוחים דיבוריים?
המודל מנרמל מילים יומיומיות לערכים מתוך מפקדים סגורים: "זול לנסיעות" הופך לטווח לפי מחיר ולקטגוריה, ולא לטקסט חופשי. לכל שדה מוגדרים ערכים מותרים ודוגמאות תיוג — ככל שהם מדויקים יותר, כך פחות תנאים הולכים לאיבוד. חלקים שלא זוהו בשאילתה לא חוסמים את החיפוש: הם נשארים בענף הסמנטי ועובדים כבחירה וקטורית רגילה.
אנחנו מרחיבים את סכמת המטא-נתונים — מה יהיה צריך לעצב מחדש?
תיאור השדות מתקיים בנפרד מהלוגיקה של החיפוש, ולכן שדה חדש הוא תיקון קונפיגורציה, ולא עיבוד מחדש של הקוד ולא אימון מחדש של המודל. מוסיפים תיאור, מריצים את סט שאילתות הייחוס על השדה הזה ובוחנים את דיוק התנאים שנבנו. אם המדד ירד, מתקנים את ניסוחי התיאור ואת הדוגמאות, ולא את המנגנון עצמו.
איך בודקים שהתנאים נבנו נכון?
אוספים סט שאילתות ייחוס עם תנאים נכונים ידועים מראש ומשווים את התוצאה לפי כל שדה ואופרטור, ולא רק לפי התוצאה בכללותה. זה נותן שתי מטריקות נפרדות: דיוק התנאים שנבנו ומלאות — כמה רשומות רלוונטיות חתך פילטר מיותר. הסט מורץ בבדיקות רגרסיה לפני כל הפצה, כדי שעריכות בתיאור השדות לא ישברו תרחישים שכבר עובדים.
נדון במשימה שלכם לסינון שאילתות
כדי לדון במשימה של Self-Query RAG לא נדרש סט דרישות מוכן — מספיק האופן שבו החיפוש במסמכים בנוי היום. שלחו דוגמאות של שאילתות אמיתיות, רשימת מקורות ושדות שנדרש עבורם סינון — זה מספיק כדי לראות היכן הפילטרים הידניים מפסיקים לכסות תרחישים.
- דוגמאות של שאילתות שבהן החיפוש הנוכחי מחזיר מסמכים לא רלוונטיים;
- מקורות: בסיס ידע, תקנונים, כרטיסי מוצר, היסטוריית פניות;
- שדות שנדרש עבורם סינון: תאריך, אזור, קטגוריה, סטטוס, הרשאות גישה;
- תפקידי העובדים ואילו חתכי נתונים כל אחד מהם צריך לראות;
- מגבלות: קונטור סגור, מודלים משלכם, בסיס וקטורי שכבר עובד.
זה מספיק כדי לחזור עם הערכת עבודה ותוכנית הטמעה — בלי ניסוחים כלליים:
- סכמה, איך שאילתה הופכת לפילטרים ולתנאי חיפוש;
- רשימת המאפיינים שהמערכת תתחיל לזהות אוטומטית;
- שלבים: פירוק נתונים, תיוג, כיוונון, בדיקה על שאילתות חיות;
- הרכב הצוות, לוחות זמנים ונקודות שבהן נדרשת ההחלטה שלכם;
- קריטריוני קבלה: שיעור השאילתות שנותחו נכון ודיוק התוצאות.
תארו את המשימה בצורה חופשית — נחזור עם הערכה ותוכנית שמתפרקות לסעיפים.







