Multi-Tenant RAG: איך מבודדים נתוני לקוחות באינדקס וקטורי משותף
מאת צוות מדיה דיל · 06.08.2026 · Data Engineering · 10 דק׳
כשבונים מוצר SaaS עם RAG לכל לקוח, שאלת הבידוד בין Tenants באינדקס הוקטורי היא לא פרט טכני — היא ההבדל בין מוצר בטוח למוצר עם דליפת מידע.
סטארט-אפ שבונה פיצ'ר "שאל את המסמכים שלך" עבור לקוחות B2B מגלה מהר שהאתגר האמיתי הוא לא ה-RAG עצמו אלא הבידוד: איך מוודאים שלקוח A לעולם לא יקבל, אפילו בטעות, פיסת מידע מהמסמכים של לקוח B? באפליקציה רגילה עם מסד נתונים יחסי, זו שאלה מוכרת עם פתרונות מבוססים — Row-Level Security, schema-per-tenant. אבל באינדקס וקטורי, שבו החיפוש מבוסס דמיון סמנטי ולא שאילתת SQL מדויקת, בידוד בין Tenants דורש מחשבה ארכיטקטונית נפרדת. Multi-Tenant RAG הוא בדיוק התשתית הזו: איך מריצים מערכת RAG אחת שמשרתת עשרות או אלפי לקוחות, כשכל אחד מהם רואה אך ורק את הנתונים שלו — ובלי לשלם את המחיר הכלכלי של תשתית נפרדת לגמרי לכל לקוח.
שלוש אסטרטגיות בידוד: Namespace, Metadata, Index נפרד
האסטרטגיה הראשונה, Index-per-Tenant, מקצה אינדקס וקטורי נפרד לכל לקוח — בידוד מוחלט ברמת התשתית, בסגנון "silo". זו הגישה הבטוחה ביותר אבל היקרה ביותר תפעולית: אלפי לקוחות משמעם אלפי אינדקסים לתחזק, לנטר ולעדכן, וברוב מסדי הנתונים הוקטוריים יש מגבלת ביצועים או עלות לכל אינדקס פעיל. האסטרטגיה השנייה, Namespace-per-Tenant, משתמשת ביכולת מובנית שקיימת ברוב הפלטפורמות המודרניות (Pinecone Namespaces, Weaviate Multi-Tenancy, Qdrant Collections) שמאפשרת בידוד לוגי בתוך אותה תשתית פיזית — יעיל יותר, ועדיין נותן ערבויות בידוד חזקות ברמת ה-API. האסטרטגיה השלישית, Metadata Filtering משותף, שמה את כל הלקוחות באותו אינדקס פיזי ולוגי, עם שדה tenant_id שמסונן בכל שאילתה — הזולה והפשוטה ביותר, אך גם המסוכנת ביותר אם יש באג בשכבת האכיפה.
מה קורה כשלקוחות חולקים גם תוכן משותף (Shared Knowledge)
לא כל תוכן ב-RAG רב-דיירי הוא פרטי ללקוח בודד. לעיתים יש שכבת ידע משותפת — תיעוד מוצר כללי, מאמרי עזרה, FAQ — שרלוונטית לכל הלקוחות, לצד תוכן פרטי ייחודי לכל אחד (המסמכים שהם עצמם העלו). עיצוב נאיבי מתייחס לכל תוכן כפרטי-לקוח, מה שיוצר כפילות מיותרת (אותו תיעוד מוצר מאונדקס בנפרד לכל לקוח) ומבזבז תקציב אחסון וחישוב. עיצוב נכון יותר מפריד בין namespace משותף לתוכן כללי לבין namespace פרטי לכל tenant, ובזמן שאילתה מבצע שני חיפושים מקבילים — אחד בתוכן המשותף ואחד בתוכן הפרטי — וממזג את התוצאות. זה מוסיף מורכבות קטנה בשכבת ה-Retrieval Service אך חוסך משמעותית בעלות ומאפשר לעדכן תוכן משותף פעם אחת במקום פעם לכל לקוח.
למה Metadata Filtering לבדו לא מספיק כערובת בידוד
הבעיה המרכזית ב-Metadata Filtering כערובת יחידה היא שהיא מבוססת על משמעת קוד: כל מקום בקוד שמבצע שאילתה חייב לזכור להוסיף את הפילטר tenant_id, ואם מפתח אחד שוכח — מתקבלת דליפת מידע חמורה בין לקוחות, שלרוב מתגלה רק כשלקוח מדווח שהוא רואה מידע שלא שלו, ולא כתוצאה מבדיקה יזומה שתפסה את הבעיה מוקדם יותר. זו בדיוק אותה בעיה שקיימת ב-Row-Level Security ברמת אפליקציה בלי אכיפה ברמת DB — כל עוד הבידוד תלוי בזיכרון המפתח ולא באכיפה מבנית, זה עניין של זמן עד לכשל. הפתרון הנכון הוא לעולם לא לסמוך רק על השכבה האפליקטיבית: אם הפלטפורמה תומכת ב-Namespace-per-Tenant, זה עדיף משמעותית כי הבידוד נאכף ברמת ה-database engine עצמו, לא ברמת קוד שאפשר לטעות בו.
עלות ה-Embedding בעולם Multi-Tenant
שיקול כלכלי ייחודי ל-Multi-Tenant RAG הוא עלות ה-Embedding כתלות במודל המחירים של המוצר עצמו. אם התמחור ללקוח מבוסס על נפח מסמכים, עלות ה-embedding מתומחרת ישירות מול הכנסה. אבל אם התמחור הוא flat-rate (מחיר קבוע לחודש ללא תלות בנפח), לקוח שמעלה כמות עצומה של מסמכים הופך פתאום ליקר משמעותית לתפעול מבלי שההכנסה ממנו גדלה בהתאם — בעיה כלכלית שצריכה להיפתר או בשכבת התמחור (quota למספר מסמכים או chunks) או בשכבה הטכנית (דחיסת embeddings, אסטרטגיית caching אגרסיבית יותר ללקוחות עם תוכן חוזר). שילוב בין הצוות המסחרי לצוות ההנדסה בשלב תכנון ה-tiering חיוני כאן, כי החלטה טכנית טהורה בלי הקשר עסקי עלולה ליצור מודל עסקי לא בר-קיימא. שיקול נוסף שכדאי לבדוק מראש הוא עלות ה-storage לאורך זמן: אינדקס וקטורי שגדל בלי מדיניות ניקוי מפורשת (למשל מחיקת מסמכים ישנים שהלקוח כבר לא צריך, או דחיסת embeddings ישנים לרזולוציה נמוכה יותר) הופך בהדרגה ליקר יותר לתפעול מהצפוי המקורי, במיוחד עבור לקוחות ותיקים שצברו נפח גדול לאורך שנות שימוש. מודל תמחור בשל כולל בדרך כלל מדיניות retention ברורה שמתואמת מראש עם הלקוח, ולא רק עם הצוות הטכני הפנימי.
Trade-off: עלות תפעולית מול חוזק ערבות הבידוד
ההחלטה בין שלוש הגישות היא בעצם ספקטרום בין עלות לבטיחות. Index-per-Tenant נותן את הערובה החזקה ביותר אך לא scale-able כלכלית מעבר למאות לקוחות. Namespace-per-Tenant הוא לרוב sweet spot: בידוד חזק, עלות תפעולית סבירה, ותמיכה טובה ברוב הפלטפורמות המובילות כיום. Metadata Filtering משותף מתאים בעיקר כשמדובר בכמות עצומה של Tenants קטנים מאוד (למשל אלפי לקוחות פרטיים עם כמות מסמכים זעירה כל אחד) שבהם אינדקס נפרד לכל אחד פשוט לא הגיוני כלכלית — ואז חובה להוסיף שכבות בקרה נוספות שמפצות על הסיכון.
עיצוב שכבת ה-Query: אכיפה מרכזית ולא מבוזרת
ההמלצה הארכיטקטונית המרכזית: לבנות שכבת גישה יחידה (Retrieval Service) שדרכה עוברת כל שאילתה ל-Vector DB, ושבה tenant_id מוזרק אוטומטית מתוך ה-session המאומת של המשתמש — לא מתקבל כפרמטר מהקליינט. כך אף מפתח לא יכול "לשכוח" את הפילטר, כי הוא לא חלק מהקוד שהם כותבים בכל endpoint — הוא אכוף מרכזית בשכבה אחת שעברה code review קפדני ובדיקות ייעודיות. זהו אותו עיקרון שמנחה גם ארכיטקטורות SaaS כלליות, כפי שמפורט במאמר על בידוד Tenants ב-SaaS.
class RetrievalService {
async search(sessionUser, query) {
const tenantId = sessionUser.tenantId; // לא מגיע מהקליינט
return vectorDb.query({
namespace: tenantId,
vector: await embed(query),
topK: 8
});
}
}
בידוד גם ב-Embedding וב-Ingestion, לא רק ב-Query
בידוד Multi-Tenant לא מסתיים בשכבת ה-Query. גם ה-pipeline של ingestion חייב לשמור tenant_id לכל אורך התהליך, כולל ב-caching של embeddings — content hash זהה משני לקוחות שונים (למשל תבנית מסמך משותפת) לא אמור לשתף embedding מ-cache, כי זה עלול ליצור תלות בין הלקוחות ברמת התשתית, גם אם לא ברמת התוצאה הנראית למשתמש. בנוסף, לוגים ו-observability (מטריקות, error tracking) חייבים גם הם לתייג tenant_id, כי בלי זה קשה מאוד לאבחן בעיה שספציפית ללקוח אחד מתוך אלפים.
טעויות נפוצות בפרודקשן
הטעות הראשונה: לבדוק בידוד רק ב-happy path ולא לכתוב טסטים ייעודיים ל-cross-tenant leakage — טסט שמוודא במפורש שלקוח A לעולם לא מקבל תוצאה עם tenant_id של B, גם תחת תנאי קצה. השנייה: לבנות multi-tenancy רק בשכבת ה-Vector DB ולשכוח את שאר המערכת — cache משותף, לוגים משותפים, rate limiting משותף שיכול לחשוף מידע עקיף (למשל, זמן תגובה שונה חושף אם tenant מסוים קיים במערכת). השלישית: לא לתכנן מראש למעבר בין רמות בידוד — לקוח Enterprise גדול שדורש Index-per-Tenant משלו, כשהמערכת נבנתה מלכתחילה רק סביב Namespace משותף. כדאי לתכנן את שכבת ה-Retrieval Service כך שהיא אגנוסטית לאסטרטגיית הבידוד בפועל, ומאפשרת להעביר tenant בודד לרמת בידוד גבוהה יותר בלי שינוי קוד נרחב.
Noisy Neighbor: כשלקוח אחד משפיע על ביצועי כולם
בעיה שלרוב לא מתגלה עד לפרודקשן היא Noisy Neighbor — לקוח בודד עם קורפוס מסמכים ענק או קצב שאילתות גבוה במיוחד, שמשפיע על זמני התגובה של כל שאר הלקוחות באותה תשתית משותפת. זה קורה כי גם עם בידוד לוגי מלא (Namespace נפרד), רוב הפלטפורמות הוקטוריות עדיין חולקות משאבי חישוב פיזיים — CPU, זיכרון, רוחב פס — בין Namespaces. הפתרון כולל שילוב של rate limiting לפי tenant (לא רק גלובלי), ניטור שמזהה לקוחות שחורגים משמעותית מהפרופיל הממוצע, ולעיתים גם מדרג עדיפות (tiering) שמעביר לקוחות Enterprise גדולים לתשתית ייעודית משלהם לפני שהם הופכים ל-Noisy Neighbor עבור לקוחות קטנים יותר. תכנון מוקדם של מדרג כזה — איזה tenant profile מצדיק מעבר לבידוד פיזי מלא — חוסך הרבה כאב ראש כשמגיע לקוח Enterprise ראשון עם דרישות היקף שהמערכת לא תוכננה אליהן. כדי לזהות Noisy Neighbor לפני שהוא הופך לתקלה מורגשת, כדאי לנטר לא רק נפח שאילתות אלא גם עומק chunks בשאילתה בודדת (top_k גבוה חריג) וגודל ממוצע של context שנשלף — לקוח שמבצע שאילתות מורכבות עם top_k של מאות תוצאות מכביד על התשתית הרבה יותר מלקוח עם אותו נפח שאילתות אך top_k סטנדרטי, ולכן מדד גולמי של "מספר בקשות בשנייה" לבדו לא מספיק לזיהוי מוקדם.
מיגרציה בין רמות בידוד בלי downtime
כשלקוח גדל מ"עוד tenant בתוך namespace משותף" ל"לקוח שדורש אינדקס נפרד לגמרי" — לרוב בעקבות דרישת אבטחה חוזית או SLA — צריך מנגנון מיגרציה שלא דורש downtime עבור אותו לקוח. הגישה המעשית: להעלות אינדקס חדש ריק ללקוח, להריץ Backfill מלא של הנתונים הקיימים שלו מהאינדקס המשותף לאינדקס החדש, לוודא equivalence (השוואת מדגם שאילתות ומוודאים שהתוצאות זהות), ורק אז להחליף את ניתוב השאילתות שלו לאינדקס החדש באופן אטומי ברמת ה-Retrieval Service. לבסוף מוחקים את הנתונים שלו מהאינדקס המשותף הישן. תהליך כזה, אם תוכנן מראש כחלק מארכיטקטורת ה-Retrieval Service, הוא עניין של שעות; אם לא תוכנן מראש, הוא הופך לפרויקט הנדסי מורכב שדורש שינוי בכל מקום בקוד שמניח שיש namespace יחיד ידוע מראש.
מתי כל אסטרטגיה מתאימה
למוצר B2B עם עשרות עד מאות לקוחות, במיוחד כאלה עם דרישות אבטחה גבוהות (פיננסים, בריאות, ממשל) — Namespace-per-Tenant הוא ברירת המחדל הנכונה, עם אפשרות ל-Index-per-Tenant ללקוחות Enterprise ספציפיים שדורשים זאת חוזית. למוצר עם אלפי משתמשים פרטיים בעלי כמות מסמכים קטנה, Metadata Filtering עם שכבת אכיפה מרכזית קפדנית הוא הפתרון הריאלי היחיד מבחינה כלכלית, ובלבד שמלווה בבדיקות בידוד קפדניות. השאלה שכדאי לשאול מוקדם היא לא רק "כמה לקוחות יש לנו היום" אלא "איך נראה חוזה האבטחה שהלקוח הכי גדול שלנו יחתום עליו בעוד שנתיים" — כי המעבר בין רמות בידוד תמיד קל יותר כשהוא תוכנן מראש כמסלול אפשרי, ולא כתוספת דחופה בלחץ מו"מ.
סיכום
Multi-Tenant RAG דורש להתייחס לבידוד בין לקוחות כדרישה ארכיטקטונית מהיום הראשון, לא כפיצ'ר שמתווסף בדיעבד. הבחירה בין Index-per-Tenant, Namespace-per-Tenant ו-Metadata Filtering היא איזון בין עלות לחוזק ערבות, אבל בכל מקרה — אכיפה מרכזית בשכבת Retrieval Service אחת, ובדיקות ייעודיות לדליפת מידע, הן לא אופציונליות.
תגיות: Multi-Tenant RAG · Tenant Isolation · Vector Database · Namespace · SaaS · Data Leakage · Multi-Tenancy