أدى نشر تطبيقات نماذج اللغة الكبيرة (LLM) عبر منصات مثل Dify إلى تبسيط دمج الذكاء الاصطناعي في سير العمل التجاري. ومع ذلك، عند الانتقال من نموذج أولي أحادي المستأجر إلى بنية SaaS متعددة المستأجرين، تظهر تحديات حرجة: عزل البيانات والإنصاف في الموارد. إذا تسربت بيانات المستأجر أ إلى نتائج استرجاع المستأجر ب، أو إذا أدى مستهلك واحد كثيف إلى تدهور تجربة الجميع، فإن المنصة تفشل. يستكشف هذا الدليل كيفية تصميم نظام متعدد المستأجرين قوي باستخدام 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، غالبًا ما تقدم خططًا متدرجة (مثل: مجاني، احترافي، مؤسسات). قد يكون للخطط المجانية استخدام محدود للرموز (Tokens)، بينما تتمتع خطط المؤسسات بمعدل تدفق عالٍ. غالبًا ما يكون تحديد معدل 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
دمج هذا الفحص في غلاف API الخاص بـ Dify قبل إرسال الطلب إلى نموذج اللغة الكبيرة. من خلال تقدير استخدام الرموز قبل الطلب (بناءً على طول الأمر والإخراج المتوقع)، يمكنك منع المستأجرين الذين يستهلكون أكثر من اللازم بشكل استباقي دون إهدار موارد الحوسبة الخلفية.
الخاتمة
بناء SaaS متعدد المستأجرين على Dify يتطلب الانتقال إلى ما هو أبعد من هندسة الأوامر البسيطة نحو تصميم بنية تحتية قوي. من خلال فرض عزل صارم لمخازن المتجهات عبر تصفية البيانات الوصفية وتنفيذ تحديد معدل ديناميكي متدرج، تضمن الأمان والإنصاف والقابلية للتوسع. ابدأ بمراجعة خط أنابيب استرجاع البيانات الخاص بك بحثًا عن تسرب محتمل، ثم أضف قيود الموارد بناءً على نموذج عملك. مع نمو قاعدة مستخدميك، ستحدد هذه القرارات المعمارية ما إذا كانت منصتك ستبقى عالية الأداء أم تنهار تحت ثقل نجاحها الخاص.