Workflow Automation

ساخت SaaS چند مستأجری مقیاس‌پذیر با Dify: انبارهای برداری ایزوله و محدودسازی پویای نرخ

پیاده‌سازی برنامه‌های مدل زبانی بزرگ (LLM) از طریق پلتفرم‌هایی مانند Dify، یکپارچه‌سازی هوش مصنوعی در جریان‌های کاری کسب‌وکار را ساده کرده است. با این حال، هنگام گذار از یک نمونه اولیه تک‌مستأجری به معماری SaaS چند مستأجری، دو چالش حیاتی پدیدار می‌شوند: ایزوله‌سازی داده و انصاف در تخصیص منابع. اگر داده‌های مستأجر A در نتایج بازیابی مستأجر B نشت کند، یا اگر یک مصرف‌کننده سنگین، تجربه سایر کاربران را خراب کند، پلتفرم شکست می‌خورد. این راهنما بررسی می‌کند که چگونه می‌توان با استفاده از Dify یک سیستم چند مستأجری مستحکم طراحی کرد، با تمرکز بر انبارهای برداری ایزوله و محدودسازی پیشرفته نرخ بر اساس مصرف.

ضرورت انبارهای برداری ایزوله

به طور پیش‌فرض، پایگاه‌های داده برداری (مانند Weaviate، Qdrant یا Milvus) که در خط لوله‌های RAG (تولید تقویت‌شده با بازیابی) استفاده می‌شوند، اغلب از یک فضای نام یا مجموعه مشترک بهره می‌برند. در یک محیط چند مستأجری، این یک کابوس امنیتی است. پرس‌وجوی "tenant_123" هرگز نباید مستنداتی متعلق به "tenant_456" را برگرداند. اگرچه Dify امکان فیلترسازی متادیتای سفارشی را فراهم می‌کند، اما اتکا صرف به فیلترسازی در سطح برنامه شکننده است. امن‌ترین رویکرد، ایزوله‌سازی معماری در لایه ذخیره‌سازی است.

برای مستأجران با امنیت بالا، ما توصیه می‌کنیم استراتژی "پایگاه داده برای هر مستأجر" یا "مجموعه برای هر مستأجر" را پیاده‌سازی کنید. این کار می‌تواند با تزریق یک فیلتر پویا در جریان کاری بازیابی Dify انجام شود. در زیر یک مثال از نحوه تغییر منطق بازیابی در یک پلاگین سفارشی Dify یا از طریق میانی‌افزار API برای اعمال ایزوله‌سازی سخت‌گیرانه فضای نام آورده شده است.


from dify import DifyClient
from contextlib import contextmanager

def execute_isolated_retrieval(tenant_id: str, query: str, client: DifyClient):
    """
    یک پرس‌وجوی بازیابی RAG را به طور سخت‌گیرانه در محدوده یک مستأجر خاص اجرا می‌کند.
    این اطمینان می‌دهد که جستجوی پایگاه داده برداری بر اساس متادیتای 'tenant_id' فیلتر شود.
    """
    # در Dify، می‌توانید متغیرها را به گره پایگاه دانش بفرستید.
    # اطمینان حاصل کنید که مجموعه داده شما طوری پیکربندی شده است که 'metadata.tenant_id' را به این متغیر نگاشت می‌کند.
    
    params = {
        "inputs": {
            "query": query,
            "tenant_id": tenant_id  # حیاتی: این به عنوان کلید فیلتر عمل می‌کند
        },
        "response_mode": "blocking"
    }
    
    try:
        response = client.chat_messages(params)
        return response["answer"], response.get("metadata", {}).get("sources")
    except Exception as e:
        raise SecurityError(f"عدم موفقیت در بازیابی داده ایزوله برای مستأجر {tenant_id}: {e}")

برای ایزوله‌سازی سخت‌گیرانه‌تر، می‌توانید از پشتیبانی Dify برای کانکتورهای مختلف پایگاه داده برداری در هر فضای کاری استفاده کنید، اگر چندین نمونه Dify را اجرا می‌کنید. با این حال، اگر یک نمونه واحد Dify را برای بسیاری از مستأجران اجرا می‌کنید، اطمینان حاصل کنید که درایور پایگاه داده برداری شما طوری پیکربندی شده است که یک بند `filter` اجباری بر اساس `tenant_id` ارسالی در ورودی‌های برنامه اعمال کند. این کار از حملات "تزریق پرامپت" که تلاش می‌کنند فیلترهای بازیابی را دور بزنند، جلوگیری می‌کند.

پیاده‌سازی محدودسازی نرخ بر اساس مصرف

ایزوله‌سازی تنها نیمی از نبرد است؛ نیمه دیگر مدیریت منابع است. در مدل SaaS، اغلب برنامه‌های سلسله‌مراتبی (مثلاً رایگان، حرفه‌ای، سازمانی) ارائه می‌دهید. سطح رایگان ممکن است مصرف توکن محدودی داشته باشد، در حالی که سطح سازمانی پهنای باند بالایی دارد. محدودسازی نرخ بومی Dify اغلب ایستا است، بنابراین ما نیاز داریم محدودسازی نرخ پویا و با پنجره لغزان را در دروازه API یا در یک لایه میانی‌افزار سفارشی پیاده‌سازی کنیم.

ما توصیه می‌کنیم از Redis برای این کار استفاده کنید، زیرا عملیات اتمی مورد نیاز برای محدودسازی نرخ با هم‌زمانی بالا را فراهم می‌کند. منطق باید سطح اشتراک کاربر را بررسی کند، سهمیه او را تعیین کند و شمارنده را کاهش دهد. اگر سهمیه فراتر رود، درخواست با وضعیت `429 Too Many Requests` رد می‌شود.


import redis
import time

r = redis.Redis(host='localhost', port=6379, db=0)

# پیکربندی سطوح: [حداکثر درخواست در دقیقه، حداکثر توکن در روز]
TIERS = {
    "free": [10, 1000],
    "pro": [100, 10000],
    "enterprise": [1000, 100000]
}

def check_rate_limit(user_id: str, tier: str, estimated_tokens: int = 100) -> bool:
    """
    بررسی می‌کند که آیا کاربر در محدوده نرخ خود قرار دارد یا خیر، با استفاده از پنجره لغزان برای درخواست‌ها
    و شمارنده روزانه برای توکن‌ها.
    """
    now = time.time()
    one_minute_ago = now - 60
    today_key = f"token_usage:{user_id}:{int(now // 86400)}"
    request_key = f"req_window:{user_id}:{int(now)}"

    max_req, max_tok = TIERS.get(tier, TIERS["free"])

    # 1. بررسی سهمیه توکن (روزانه)
    current_tokens = r.get(today_key) or 0
    if int(current_tokens) + estimated_tokens > max_tok:
        return False
    
    # 2. بررسی نرخ درخواست (پنجره لغزان ۱ دقیقه)
    # استفاده از ZSET (مجموعه مرتب‌شده) برای پنجره لغزان دقیق بهتر است،
    # اما برای سادگی، اینجا از یک شمارنده پنجره ثابت استفاده می‌کنیم.
    pipe = r.pipeline()
    pipe.incr(request_key)
    pipe.expire(request_key, 60)
    current_requests = pipe.execute()[0]
    
    if current_requests > max_req:
        return False

    # اگر در محدوده باشد، تعداد توکن را به‌روزرسانی کنید
    r.incrby(today_key, estimated_tokens)
    # کلید توکن را در پایان روز منقضی کنید
    r.expireat(today_key, int((now // 86400 + 1) * 86400))
    
    return True

این بررسی را قبل از ارسال درخواست به LLM در پوشش API خود Dify یکپارچه کنید. با تخمین مصرف توکن قبل از درخواست (بر اساس طول پرامپت و خروجی مورد انتظار)، می‌توانید به طور پیش‌دستانه مستأجران مصرف‌کننده بیش از حد را مسدود کنید بدون اینکه منابع محاسباتی بک‌اند را هدر دهید.

نتیجه‌گیری

ساخت SaaS چند مستأجری روی Dify نیازمند گذار فراتر از مهندسی پرامپت ساده به سمت طراحی زیرساخت مستحکم است. با اعمال ایزوله‌سازی سخت‌گیرانه انبار برداری از طریق فیلترسازی متادیتا و پیاده‌سازی محدودسازی نرخ پویا و مبتنی بر سطح، شما امنیت، انصاف و مقیاس‌پذیری را تضمین می‌کنید. با ممیزی خط لوله بازیابی داده خود برای نشتی‌های احتمالی شروع کنید، سپس محدودیت‌های منابع را بر اساس مدل کسب‌وکار خود لایه‌بندی کنید. با رشد پایگاه کاربری شما، این تصمیمات معماری تعیین‌کننده خواهند بود که آیا پلتفرم شما عملکردی باقی می‌ماند یا زیر بار موفقیت خود فرو می‌پاشد.

Share: