Real-Time RAG: איך בונים מערכת שמשיבה על סמך נתונים של השנייה האחרונה
מאת צוות מדיה דיל · 03.08.2026 · Data Engineering · 10 דק׳
RAG רגיל מספיק כשמדובר במדיניות שמתעדכנת פעם בשבוע. אבל מה קורה כשצריך לענות על סמך מלאי, מחיר או סטטוס הזמנה שמשתנה כל שנייה? מדריך ל-Real-Time RAG.
דמיינו בוט תמיכה של חברת לוגיסטיקה שמקבל שאלה: "איפה החבילה שלי?". תשובה מבוססת RAG קלאסי, שמסתמכת על אינדקס וקטורי שמתעדכן פעם בשעה, פשוט לא תעבוד — הסטטוס של החבילה משתנה כל דקה, והמשתמש מצפה לתשובה שמשקפת את הרגע הנוכחי ולא תמונת מצב מלפני שעה. זהו בדיוק הפער בין RAG רגיל, שמצוין למידע יציב יחסית כמו מדיניות או תיעוד, לבין Real-Time RAG — ארכיטקטורה שמשלבת retrieval מבוסס embeddings עם קריאה חיה למקורות נתונים דינמיים, כדי לתת תשובה שנכונה לרגע השאילתה עצמה. ההבדל הזה נראה קטן על הנייר, אבל מבחינה ארכיטקטונית הוא משנה כמעט כל החלטה בשכבת ה-Retrieval, החל מאיך בונים את ה-context ועד איך מודדים latency ומטפלים בכשלים.
הפער בין RAG "רגיל" לצורך ב-Real-Time
ב-RAG הקלאסי, ה-pipeline עובד בשני שלבים נפרדים בזמן: קודם מזינים ומאנדקסים את הידע (offline), ורק אחר כך, בזמן שאילתה, מבצעים retrieval מהאינדקס הסטטי הזה. המודל הזה מניח שהמידע "כמעט לא זז" בין אינדוקס לשאילתה — הנחה סבירה למדיניות חברה, אבל שגויה לחלוטין למידע תפעולי כמו מלאי, מחירים דינמיים, סטטוס משלוח, או נתוני מדדים בזמן אמת. Real-Time RAG לא זונח את הרעיון של Retrieval מבוסס embeddings לגמרי — הוא מפריד בין שני סוגי מידע: מידע "יציב" (semantic knowledge) שממשיך לעבור דרך אינדקס וקטורי רגיל, ומידע "חי" (live facts) שנשלף ברגע השאילתה ישירות מהמקור.
מיפוי מקורות: אילו נתונים באמת חייבים להיות חיים
לפני כתיבת שורת קוד אחת, שווה לבצע מיפוי מפורש של כל סוגי המידע שהמערכת אמורה לענות עליהם, ולסווג כל אחד מהם לפי תדירות השינוי בפועל — לא לפי תחושת בטן. מסמכי מדיניות ותיעוד משתנים בקצב של ימים-שבועות ומתאימים ל-RAG רגיל. נתוני קטלוג מוצרים משתנים בקצב של שעות ומתאימים ל-Streaming Updates לאינדקס. נתוני מלאי, מחיר בזמן אמת וסטטוס הזמנה משתנים בקצב של שניות ומחייבים Live Lookup אמיתי. המיפוי הזה, שנעשה מוקדם, קובע לאילו סוגי שאלות בכלל צריך להשקיע במסלול ה-Real-Time היקר יותר, ומונע מצב שבו כל שאילתה עוברת דרך מסלול הקריאה החיה גם כשזה לא נדרש עסקית.
ארכיטקטורת Hybrid Retrieval: Vector Search + Live Lookup
התבנית הארכיטקטונית המרכזית היא Tool-Augmented RAG: ה-LLM, במקום להסתמך רק על chunks שהוחזרו מהאינדקס, מקבל גישה לכלים (function calling) שמאפשרים לו לבצע קריאה חיה למקור הרלוונטי — API של מערכת המלאי, שאילתת SQL למסד ההזמנות, קריאה ל-CRM. הזרימה בפועל: השאילתה של המשתמש מנותחת כדי לזהות אם היא דורשת מידע חי (Intent Classification), ואם כן — המודל בוחר את הכלי המתאים, מריץ אותו, ומשלב את התוצאה יחד עם ה-context הסמנטי שהוחזר מהאינדקס הוקטורי כדי לבנות תשובה מלאה. הגישה הזו מייצרת ארכיטקטורה היברידית: הידע ה"רך" (הסברים, מדיניות, הקשר) מגיע מ-RAG רגיל, והעובדות ה"קשות" (מספרים, סטטוסים, זמנים) מגיעות מקריאה חיה.
Trade-off: Latency מול טריות המידע
כל קריאה חיה למקור חיצוני מוסיפה latency — קריאת API יכולה לקחת מאות מילישניות עד שניות, ואם המודל צריך לבצע כמה קריאות ברצף (למשל לבדוק מלאי ואז לבדוק מחיר מבצע) זמן התגובה הכולל מצטבר במהירות. לעומת זאת, Vector Search על אינדקס מקומי הוא מהיר משמעותית — לרוב עשרות מילישניות. לכן ההחלטה האדריכלית המרכזית היא: אילו סוגי מידע חייבים להיות live לגמרי, ואילו אפשר "לקרב" באמצעות caching קצר טווח או near-real-time sync (עדכון כל כמה שניות במקום כל שנייה)? ברוב המקרים בפועל, "בזמן אמת" לא אומר מילישנייה-מילישנייה, אלא "עדכני מספיק ביחס לצורך העסקי" — הבדל שחוסך הרבה מורכבות הנדסית מיותרת.
Caching מדורג: השכבה שמאזנת בין המהירויות
הפתרון המעשי ביותר הוא שכבת caching עם TTL (Time To Live) שמותאם לאופי הנתון: נתוני מלאי אולי דורשים TTL של 10-30 שניות, בעוד סטטוס הזמנה בודדת עשוי להיות מוזמן ישירות בלי cache בכלל כי היא נדרשת פחות תדיר ודיוק בה קריטי. מבנה נפוץ הוא Read-Through Cache — כל בקשה בודקת קודם ב-cache (Redis, למשל), ורק אם אין hit (או שפג תוקף) פונה למקור המקורי ומעדכנת את ה-cache. זה מפחית עומס דרמטית על המערכות התפעוליות (שלרוב לא בנויות לעמוד בעומס שאילתות AI), תוך שמירה על טריות מקובלת.
async function getLiveFact(key, fetcher, ttlSeconds=15) {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const fresh = await fetcher();
await redis.set(key, JSON.stringify(fresh), 'EX', ttlSeconds);
return fresh;
}
עיצוב ה-Tool Layer: לא כל שאלה צריכה קריאה חיה
טעות נפוצה היא לתת למודל גישה לכלים חיים ולתת לו "להחליט" בכל שאלה אם להשתמש בהם — מה שמוביל לקריאות מיותרות שמאטות תשובות פשוטות ("מה שעות הפתיחה?" לא צריכה קריאה חיה למערכת מלאי). עיצוב נכון כולל שכבת סיווג מוקדמת (יכולה להיות מודל קטן וזול, לא ה-LLM הראשי) שמזהה אם השאלה דורשת מידע דינמי, ורק אז מפעילה את מסלול ה-Tool-Augmented. בנוסף, כדאי להגביל את מספר קריאות הכלים בסבב תשובה בודד (למשל מקסימום 3), כדי למנוע מצב שבו המודל "משתולל" בשרשרת קריאות ארוכה שמייצרת latency בלתי סביר וגם עלות API גבוהה.
אבטחה בקריאות חיות: הרשאות בזמן Tool Call
היבט שנוטים לפספס בהתלהבות מהיכולת הטכנית של Tool-Augmented Generation הוא שכל קריאה חיה למקור נתונים חייבת לעבור דרך אותה שכבת הרשאות כמו כל שאילתה רגילה במערכת — היא לא "מוגנת" רק בגלל שהיא יוצאת מתוך LLM ולא מממשק משתמש רגיל. אם ה-Tool שקורא למערכת ההזמנות פועל תחת חשבון שירות עם הרשאות רחבות (כדי לפשט את ההטמעה), כל משתמש שמצליח לנסח שאלה שגורמת למודל להפעיל את הכלי הזה יכול, תיאורטית, לקבל מידע שלא מיועד לו — כמו פרטי הזמנה של לקוח אחר. הפתרון הנכון הוא להעביר את זהות המשתמש (לא רק את הפרומפט) לכל Tool Call, כך שהקריאה בפועל מתבצעת בהקשר ההרשאות שלו, בדיוק כפי שמתואר במאמר על בקרת גישה ב-RAG. זה דורש שה-orchestration layer יזריק user context אוטומטית לכל קריאת כלי, ולא יסמוך על כך שהמודל "יזכור" לצרף מזהה משתמש כפרמטר — כי מודל שפה יכול להישכח, להטעות את עצמו, או אפילו להיות מתומרן על ידי משתמש זדוני שמנסח פרומפט שמנסה לגרום לו לבקש נתונים של מזהה אחר. בנוסף, רצוי להגביל כל Tool מבחינת scope מראש (למשל, פונקציית get_order_status יכולה לקבל רק order_id ששייך למשתמש המחובר, ולא כל מזהה שרירותי) — כך שגם אם המודל "יבקש" מידע לא מורשה, שכבת ה-Tool עצמה תחסום את הבקשה ברמת הקוד ולא תסתמך על שיקול הדעת של ה-LLM.
טעויות נפוצות בפרודקשן
הטעות הראשונה: לבנות מערכת Real-Time אמיתית כשבעצם לא נדרש — רוב "צורך בזמן אמת" שנשמע בישיבות תכנון מתברר בבדיקה שהוא בסדר גמור עם עדכון כל כמה דקות. השנייה: לא לטפל בכשל של המקור החי — אם ה-API של מערכת המלאי נופל, המערכת חייבת ליפול בחן (graceful degradation) ולהודיע שהמידע לא זמין כרגע, במקום להחזיר תשובה שגויה או "להמציא" (hallucinate) מספר. השלישית: להתעלם מ-rate limits של המקורות הפנימיים עצמם — מערכת ERP ישנה שלא תוכננה לעומס שאילתות AI עלולה לקרוס אם עשרות משתמשי בוט שולחים אליה בקשות בו-זמנית. הרביעית: לא להפריד בין latency budget לכל רכיב — בלי SLA פנימי ברור לכל קריאה (retrieval, tool call, generation), קשה לאתר את צוואר הבקבוק כשמשתמשים מתלוננים שהתשובות איטיות. טעות חמישית, עדינה יותר: לתת ל-LLM לנסח בעצמו את התשובה על סמך המספר החי בלי ולידציה — מודלי שפה נוטים "לעגל" או "לפרש" מספרים בניסוח חופשי, מה שעלול להפוך "יתרה של 1,204.50 ש״ח" ל"קצת יותר מאלף שקלים", ניסוח שעלול להטעות בהקשרים פיננסיים. לכן רצוי להעביר נתונים קריטיים כטקסט מוגן (guarded text) שה-LLM מצווה לצטט במדויק, ולא לנסח מחדש.
Streaming Updates כאלטרנטיבה זולה יותר ל-Live Lookup
קריאה חיה בזמן שאילתה היא לא הדרך היחידה להשיג טריות. אלטרנטיבה שלעיתים זולה משמעותית היא Streaming Updates לאינדקס עצמו: במקום לקרוא למקור בכל שאילתה, מחברים את המקור (למשל Change Data Capture ממסד ההזמנות) לזרם אירועים שמעדכן שדה ייעודי באינדקס הוקטורי בזמן כמעט-אמת, כפי שתואר במאמר על Incremental Indexing. כשה-latency הקריטי הוא בסדר גודל של שניות ולא מילישניות, הגישה הזו נותנת טריות טובה מספיק בלי לשלם את מחיר ה-latency של קריאה סינכרונית בכל שאילתה, ובלי להטיל עומס שאילתות ישיר על המערכות התפעוליות. הבחירה בין Live Lookup ל-Streaming Updates היא שאלה של דיוק נדרש: אם התשובה חייבת לשקף את הרגע הממש הנוכחי (יתרת חשבון, זמינות מלאי לפני רכישה) — Live Lookup הכרחי. אם מספיק דיוק ברמת שניות בודדות — Streaming Updates פשוט וזול יותר.
מתי כן ומתי לא
Real-Time RAG מוצדק כשהמידע הרלוונטי משתנה בקצב גבוה יותר ממה שאינדוקס מחזורי יכול לתפוס — מלאי, מחירים דינמיים, סטטוס תפעולי, מדדים חיים. הוא לא מוצדק כברירת מחדל למידע שמתעדכן בקצב איטי יחסית (מדיניות, תיעוד, FAQ) — שם RAG רגיל עם Incremental Indexing תכוף מספיק, ופשוט הרבה יותר לתחזוקה. הכלל המעשי: לפני שבונים Real-Time RAG, למדוד כמה זמן "פער עדכניות" סביר עבור המשתמשים בפועל — לעיתים קרובות מתגלה שכמה דקות מספיקות, וזה חוסך שכבת מורכבות שלמה. שקלול נוסף הוא עלות: כל קריאה חיה במהלך שיחה עם משתמש מוסיפה עלות תפעולית (קריאות API, עומס על מערכות פנימיות) שצריך להצדיק מול הערך העסקי של הטריות הנוספת — לא כל שאלה שווה את המחיר של קריאה חיה, גם אם היא טכנית אפשרית.
מדידת latency ברמת רכיב
כדי לתחזק מערכת Real-Time RAG בפרודקשן בלי לנחש למה תשובה מסוימת לקחה שלוש שניות, כדאי להטמיע tracing מפורט שמפריד בין שלבי הזמן: כמה זמן לקח ה-Intent Classification, כמה זמן ה-Vector Search, כמה זמן כל קריאת כלי חיצוני, וכמה זמן ה-Generation עצמו. בלי פירוק כזה, כל דיווח על "המערכת איטית" הופך לחקירה ידנית ארוכה. כלים כמו OpenTelemetry, עם span ייעודי לכל שלב, מאפשרים לבנות דשבורד שמראה בבירור אם צוואר הבקבוק הוא רשת (קריאת API חיצונית), חישוב (embedding), או המודל עצמו — ומכוון בדיוק היכן להשקיע מאמץ אופטימיזציה, במקום לנחש.
שיקול מעשי נוסף הוא parallelization: אם המודל צריך לבצע כמה קריאות כלים בלתי תלויות (למשל לבדוק גם מלאי וגם מחיר מבצע), אין סיבה להריץ אותן בטור. הרצה מקבילית של קריאות עצמאיות יכולה לקצץ latency כולל בחצי או יותר, אבל דורשת עיצוב מפורש של שכבת ה-orchestration כך שהיא מזהה קריאות בלתי תלויות ומריצה אותן בו-זמנית, ולא מסתמכת על ברירת המחדל הסדרתית של רוב מסגרות ה-agent הפשוטות.
סיכום
Real-Time RAG הוא הרחבה של ארכיטקטורת RAG הקלאסית שמשלבת retrieval סמנטי עם קריאה חיה למקורות דינמיים, דרך Tool-Augmented Generation ושכבת caching מדורגת. ההצלחה שלו תלויה בהפרדה נכונה בין מידע יציב לחי, בעיצוב זהיר של מתי לקרוא לכלים, ובטיפול הגון בכשלים. הוא כלי חזק אך יקר יחסית — כדאי לפרוס אותו נקודתית, רק היכן שהעדכניות באמת קריטית לתוצאה העסקית, ולמדוד latency ברמת רכיב כדי לשמור על חוויית משתמש טובה גם כשמערכות תפעוליות שונות הן חלק מהשרשרת.
תגיות: Real-Time RAG · Tool-Augmented Generation · Function Calling · Caching · Vector Search · Latency · LLM