Multi-Tenant RAG چیست؟ طراحی ACL برای جلوگیری از نشت اطلاعات بین کاربران
در RAG سازمانی، relevance بدون authorization خطرناک است. این مقاله طراحی tenant isolation، visibility، users/groups و ACL-aware retrieval را با یک تجربه واقعی بررسی میکن
- Multi Tenant RAG
- RAG Access Control
- RAG ACL
- Enterprise RAG
- Tenant Isolation
- Vector Database ACL

فرض کنید یک موتور RAG برای چند تیم شرکت دارید: مالی، منابع انسانی، فنی و فروش. همه از یک Search API استفاده میکنند، اما نباید همه اسناد برای همه کاربران قابل مشاهده باشند.
در چنین سیستمی بزرگترین خطر لزوماً پاسخ اشتباه نیست؛ ممکن است سیستم پاسخ درست را از سندی بدهد که کاربر اصلاً مجاز به دیدن آن نیست.
این مسئله باعث میشود ACL و tenant isolation جزو core retrieval باشند، نه یک قابلیت جانبی UI.
Multi-Tenancy در RAG یعنی چه؟
Tenant میتواند نماینده این موارد باشد:
- یک شرکت در SaaS
- یک سازمان
- یک business unit
- یک customer workspace
هدف این است که داده tenant A به tenant B نرسد.
در کنار tenant، داخل هر tenant نیز access policy داریم.
مثلاً:
{
"tenant_id": "acme",
"visibility": "private",
"owner": "user-17",
"allowed_groups": ["finance"],
"allowed_users": ["user-42"]
}
چرا Filtering بعد از Search کافی نیست؟
یک طراحی ساده این است:
Search all documents
-> get top 100
-> remove unauthorized docs
-> return top 10
این روش چند مشکل دارد.
اول، ممکن است ۹۰ نتیجه اول unauthorized باشند و بعد از حذف، recall مجاز ضعیف شود.
دوم، document غیرمجاز وارد retrieval/reranking pipeline شده است.
سوم، ممکن است trace یا cache اطلاعات آن را نگه دارد.
بهتر است policy تا حد امکان هنگام candidate generation اعمال شود.
Push-down Filter
اگر backend مثل Qdrant payload filter دارد، tenant و ACL conditions باید به همان query backend اضافه شوند.
بهطور مفهومی:
vector nearest neighbors
WHERE tenant_id = current_tenant
AND ACL allows current principal
این کار candidate space را از ابتدا محدود میکند.
Tenant Isolation و Global Documents
گاهی بعضی اسناد global هستند و همه tenantها اجازه مشاهده دارند.
Policy باید این حالت را صریح تعریف کند.
مثلاً:
document.tenant_id == user.tenant_id
OR document.visibility == global
در ماژول default tenant mode ایزوله طراحی شده تا tenant-scoped documentها بدون مجوز وارد tenant دیگر نشوند.
Policy دقیق باید در application تعریف شود.
باگ خطرناک: متن ثابت، ACL جدید
یکی از مهمترین bugهایی که در hardening پیدا کردیم مربوط به re-ingest بود.
سناریو:
- سند public ingest میشود.
- بعداً administrator همان سند را private میکند.
- متن سند هیچ تغییری نکرده است.
- سیستم با content hash تشخیص میدهد source unchanged است.
- اگر metadata update نادیده گرفته شود، ACL قدیمی public باقی میماند.
این bug نشان میدهد idempotency نباید فقط text-aware باشد.
قانون درست:
تغییر policy حتی با محتوای ثابت، یک تغییر معتبر source state است.
در v4 این مورد با regression test پوشش داده شد.
Users و Groups
ACL سازمانی فقط user list نیست. group-based access معمولاً مقیاسپذیرتر است.
مثلاً:
allowed_groups = ["finance", "audit"]
هنگام query، principal context باید شامل groupهای کاربر باشد:
user_id=user-42
groups=[finance]
tenant_id=acme
retrieval backend سپس documentهای مجاز را انتخاب میکند.
Owner و Visibility
یک policy متداول:
- public: همه کاربران tenant
- private: فقط owner یا explicitly allowed
- group: گروههای مشخص
- global: در صورت policy، همه tenantها
مهم است semantics این values در کل سیستم ثابت باشد.
اگر ingestion و backend دو برداشت متفاوت از public داشته باشند، security bug ایجاد میشود.
ACL در چند Retrieval Channel
Hybrid Search معمولاً چند path دارد:
Dense
BM25F
Exact
Sparse
Graph
Visual
اگر فقط Dense path ACL-aware باشد ولی Exact Index نباشد، مهاجم میتواند با error code یا document ID به نتیجه دسترسی پیدا کند.
تمام candidate providerها باید policy contract یکسان داشته باشند.
این موضوع مخصوص vector database نیست؛ یک مسئله معماری retrieval است.
Cache و Multi-Tenancy
Query cache میتواند ناخواسته منبع leakage شود.
اگر cache key فقط query باشد:
"salary policy"
و نتیجه کاربر Finance برای کاربر Engineering reuse شود، ACL bypass رخ میدهد.
Cache key باید context امنیتی لازم را لحاظ کند، مثل:
query + tenant + principal/group scope + retrieval version
یا cache فقط نتایج pre-ACL امن داشته باشد.
Parent/Neighbor Expansion هم باید ACL-aware باشد
فرض کنید hit مجاز است و context builder parent آن را اضافه میکند.
اگر parent یا neighbor policy متفاوتی داشته باشد و assembler آن را بدون check اضافه کند، leakage رخ میدهد.
بنابراین ACL فقط برای hit اولیه نیست؛ هر documentی که وارد final context میشود باید policy مناسب داشته باشد.
در طراحی production بهتر است hierarchy داخل یک source policy-consistent باشد یا expansion مجدداً authorization را enforce کند.
Delete و Revocation
فرض کنید دسترسی یک سند revoke شود یا source حذف شود.
سیستم باید مطمئن شود:
- vector entries حذف/بهروز شدهاند
- lexical index stale نیست
- exact IDs stale نیستند
- cache invalidate شده
- persistence state sync است
در v4 source deletion و replacement با persistence و rollback سختتر شدند تا state نیمهکاره کمتر ایجاد شود.
Authorization و Source Trust دو مفهوم متفاوتاند
ممکن است سندی کاملاً trusted باشد ولی user اجازه دیدن آن را نداشته باشد.
یا user اجازه دیدن یک upload کماعتماد را داشته باشد.
پس:
ACL -> permission
Trust -> ranking/risk signal
نباید این دو را مخلوط کرد.
Auditability
در محیط سازمانی بهتر است بتوانید جواب دهید:
- این result چرا دیده شد؟
- کدام ACL rule اجازه داد؟
- source متعلق به کدام tenant بود؟
- چه ranking reasonهایی داشت؟
- چه زمانی index شد؟
Trace و metadata مناسب debugging security incident را بسیار سادهتر میکنند.
تستهایی که باید داشته باشید
حداقل این caseها را تست کنید:
Cross-tenant
کاربر tenant A نباید private document tenant B را ببیند.
ACL update بدون text change
public -> private باید فوراً اعمال شود.
Group access
عضو Finance نتیجه را ببیند؛ کاربر خارج گروه نبیند.
Exact query
حتی با document ID دقیق، سند غیرمجاز برنگردد.
Context expansion
parent/neighbor غیرمجاز وارد context نشود.
Restart
ACL بعد از restart persistence همان رفتار را داشته باشد.
Separate Index per Tenant یا Shared Index؟
دو معماری رایج وجود دارد.
Index جداگانه
مزایا:
- isolation سادهتر
- blast radius کمتر
معایب:
- operational overhead
- tenantهای زیاد مدیریت را سخت میکنند
Shared Index + Payload Filter
مزایا:
- مدیریت سادهتر در scale متوسط
- resource sharing
معایب:
- correctness ACL بسیار حساس است
- filter bug میتواند شدید باشد
انتخاب به scale، compliance و معماری backend بستگی دارد.
برای سازمان کوچک با تعداد tenant محدود، هر دو قابل استفادهاند؛ ولی shared index فقط با test جدی ACL.
معماری پیشنهادی
Authenticated Request
↓
Principal Context
(user, tenant, groups)
↓
Query Router
↓
ACL-aware Dense / Lexical / Exact retrieval
↓
Fusion / Reranking
↓
Policy + Trust enforcement
↓
ACL-safe Context Expansion
↓
LLM
Authorization context باید از ابتدای request تا آخر pipeline همراه query باشد.
جمعبندی
در RAG سازمانی، relevance بدون permission خطرناک است.
یکی از مهمترین تجربههای ما این بود که ACL را نمیتوان بعداً روی Search «اضافه» کرد؛ باید از ingestion state و candidate generation تا cache، context expansion و deletion جزو معماری باشد.
اگر RAG قرار است روی اسناد واقعی سازمان کار کند، Multi-Tenancy و ACL correctness به اندازه embedding quality مهماند—و در بسیاری از سناریوها مهمترند.
بهروزرسانی:
همهٔ نوشتهها