مع انتقال نماذج اللغات الكبيرة (LLMs) من النماذج التجريبية إلى أدوات الإنتاج الرئيسية، أصبحت اقتصاديات الاستدعاء مصدر قلق بالغ. وعلى الرغم من شعبية التخزين المؤقت الدلالي—وهو تخزين النتائج بناءً على تشابه المتجهات—إلا أنه ليس حلاً سحرياً. يؤدي المطابقة الدلالية إلى زيادة الحمل الحسابي وقد تفوت أحياناً التطابقات الدقيقة عندما تكون الدقة أمراً حاسماً. بالنسبة للعديد من حالات الاستخدام المؤسسية، تقدم الاستراتيجيات الحتمية مثل التخزين المؤقت "الأقل استخداماً مؤخراً" (LRU) و"الوقت المتبقي للعمر الافتراضي" (TTL) موثوقية وكفاءة في التكلفة متفوقة. تستكشف هذه المقالة كيفية تنفيذ هذه الاستراتيجيات لخفض تكاليف الاستدعاء بشكل كبير دون المساس بجودة الخدمة.
قيود التخزين المؤقت الدلالي البحت
يعتمد التخزين المؤقت الدلالي على نماذج التضمين (Embedding) لتحديد ما إذا كانت الاستجابة السابقة "قريبة بما يكفي" من استعلام جديد. وعلى الرغم من فعاليته في المهام الاستكشافية، إلا أن له عيبين رئيسيين للتطبيقات عالية المخاطر: زمن الوصول والتكلفة. يضيف حساب التضمينات لكل طلب وارد خطوة معالجة قد تلغي الوفورات الناتجة عن تخطي استدعاء نموذج اللغة الكبيرة. علاوة على ذلك، في السيناريوهات التي تتطلب استرجاع بيانات دقيق (مثل البحث في تذاكر دعم العملاء أو التقارير المالية)، لا يمكن قبول التطابقات التقريبية. هنا، تكون عمليات البحث بالمفتاح الدقيق عبر LRU أو الإبطال المحدد زمنياً عبر TTL أكثر ملاءمة بكثير.
تنفيذ التخزين المؤقت LRU (الأقل استخداماً مؤخراً)
يعد التخزين المؤقت LRU مثالياً لأنماط الاستعلام المتكررة. إذا كان تطبيقك يطرح الأسئلة نفسها بشكل متكرر، فإن تخزين المخرجات الدقيقة المرتبطة بالاستعلام يسمح للطلبات اللاحقة بالعودة فوراً من الذاكرة أو من مخزن سريع في الذاكرة مثل Redis. المبدأ الأساسي بسيط: عندما تصل الذاكرة المؤقتة إلى سعتها، يتم إخراج العنصر الذي تم الوصول إليه قبل فترة أطول.
في بايثون، يمكنك تنفيذ ذاكرة مؤقتة LRU قوية باستخدام مكتبة `functools` أو التكامل مع Redis للأنظمة الموزعة. فيما يلي مثال عملي باستخدام نهج الزخرفة (Decorator) للبسطة، والذي يُستخدم غالباً في الخدمات المصغرة المحلية.
import time
from functools import lru_cache
# تحديد maxsize بـ 128 يعني أن العنصر الجديد رقم 129 سيؤدي إلى إخراج الأقدم استخداماً
@lru_cache(maxsize=128)
def get_llm_response(query: str, model: str = "gpt-4") -> str:
"""
يحاكي استدعاء نموذج لغة كبير. في الإنتاج، سيقوم هذا باستدعاء واجهة برمجة التطبيقات.
تتعامل الزخرفة مع التخزين المؤقت تلقائياً.
"""
print(f"تم إجراء استدعاء API للطلب: {query[:20]}...")
# محاكاة تأخير الشبكة
time.sleep(1)
return f"رد الذكاء الاصطناعي لـ: {query}"
# الاستدعاء الأول - يضرب واجهة برمجة التطبيقات
print(get_llm_response("ما هي عاصمة فرنسا؟"))
# الاستدعاء الثاني - يخدم من الذاكرة المؤقتة
print(get_llm_response("ما هي عاصمة فرنسا؟"))
# استعلام مختلف - يضرب واجهة برمجة التطبيقات مرة أخرى
print(get_llm_response("ما هو 2+2؟"))
الاستخدام الاستراتيجي لـ TTL (الوقت المتبقي للعمر الافتراضي)
غالباً ما تكون مخرجات نماذج اللغات الكبيرة معتمدة على السياق أو حساسة للوقت. قد تكون الحقيقة صحيحة بالأمس ولكنها خاطئة اليوم. يعالج التخزين المؤقت TTL هذه المشكلة عن طريق ربط كل إدخال في الذاكرة المؤقتة بمؤشر زمني للانتهاء. هذه الاستراتيجية مفيدة بشكل خاص لملخصات الأخبار، وتحليل السوق في الوقت الفعلي، أو الأسئلة الشائعة الديناميكية.
عند تنفيذ TTL، خاصة في بيئة موزعة باستخدام Redis، يجب عليك ضبط وقت الانتهاء بناءً على تقلب البيانات. بالنسبة لاستعلامات التوثيق الثابتة، قد يكون TTL مدته 24 ساعة كافياً. أما لتحليل الأسهم في الوقت الفعلي، فقد يكون TTL مدته 60 ثانية ضرورياً.
import redis
import json
# الاتصال بـ Redis
r = redis.Redis(host='localhost', port=6379, db=0)
def get_cached_llm_response(query: str, ttl_seconds=3600):
cache_key = f"llm:{query}"
# محاولة الحصول على البيانات من الذاكرة المؤقتة
cached_response = r.get(cache_key)
if cached_response:
print("تم العثور على البيانات في الذاكرة المؤقتة (Cache Hit)")
return json.loads(cached_response)
# محاكاة استدعاء نموذج اللغة الكبيرة
print("لم يتم العثور على البيانات في الذاكرة المؤقتة (Cache Miss) - جاري استدعاء نموذج اللغة الكبيرة")
# response = call_llm_api(query)
response = f"النتيجة لـ: {query}"
# تخزين البيانات في الذاكرة المؤقتة مع TTL
r.setex(cache_key, ttl_seconds, json.dumps(response))
return response
# سينتهي هذا الصلاحية بعد ساعة واحدة
get_cached_llm_response("الطقس الحالي في لندن")
نهج هجين لـ LLMOps
غالباً ما تجمع الاستراتيجية الأكثر فعالية من حيث التكلفة بين هذه التقنيات. يمكنك استخدام التخزين المؤقت LRU الدقيق للاستعلامات الحتمية، والتخزين المؤقت الدلالي للمهام الإبداعية أو مفتوحة النهاية. بالإضافة إلى ذلك، يضمن وضع TTL فوق LRU حتى لا تقدم التطابقات الدقيقة بيانات قديمة إلى ما لا نهاية. من خلال ضبط أحجام الذاكرة المؤقتة وسياسات الانتهاء بعناية، يمكن لفرق الهندسة تقليل تكاليف واجهة برمجة تطبيقات نماذج اللغات الكبيرة بنسبة تصل إلى 40-60% مع الحفاظ على تجربة مستخدم سريعة الاستجابة.
الخاتمة
يُعد التخزين المؤقت الدلالي أداة قوية، لكنه ليس الحل الوحيد لتحسين استدعاء نماذج اللغات الكبيرة. يوفر تنفيذ استراتيجيات قوية لـ LRU و TTL بديلاً حتمياً، منخفض زمن الوصول، وعالي الكفاءة في التكلفة للعديد من أحمال عمل الإنتاج. ومع نضج مجال LLMOps، ستتميز القدرة على اختيار آلية التخزين المؤقت المناسبة لحالة الاستخدام المناسبة التطبيقات عالية الأداء عن تلك التي تكافح مع فواتير واجهات برمجة التطبيقات المتضخمة.