vLLM ومحركات الاستدلال مفتوحة المصدر
التخزين المؤقت للبادئة والتحسين المتقدم
3 دقيقة للقراءة
التخزين المؤقت للبادئة يقلل بشكل كبير من زمن الاستجابة للتطبيقات ذات المحثات النظامية أو السياقات المتكررة. دعنا نستكشف هذا التحسين الحرج.
لماذا التخزين المؤقت للبادئة مهم
العديد من تطبيقات LLM تشترك في بادئات مشتركة:
الطلب 1: [محث النظام] + "ما هو Python؟"
الطلب 2: [محث النظام] + "اشرح JavaScript"
الطلب 3: [محث النظام] + "كيف يعمل Go؟"
بدون التخزين المؤقت للبادئة:
- كل طلب: ملء مسبق كامل لـ [محث النظام] + السؤال
- 1000 رمز محث نظام × 3 طلبات = 3000 رمز ملء مسبق
مع التخزين المؤقت للبادئة:
- الطلب 1: ملء مسبق لـ [محث النظام]، تخزينه
- الطلب 2-3: إعادة استخدام KV المخزن، ملء مسبق للأسئلة فقط
- المجموع: 1000 + 50 + 50 = 1100 رمز ملء مسبق
التسريع: ~3 أضعاف لـ TTFT
التخزين المؤقت للبادئة في vLLM (V1)
vLLM V1 قدم تخزين مؤقت للبادئة بدون تكلفة:
from vllm import LLM, SamplingParams
# تمكين التخزين المؤقت للبادئة
llm = LLM(
model="meta-llama/Llama-3.3-70B-Instruct",
enable_prefix_caching=True, # بدون تكلفة في V1
)
# محث نظام مشترك
system_prompt = """أنت مساعد ذكاء اصطناعي متخصص في البرمجة.
اتبع أفضل الممارسات. قدم شروحات واضحة مع أمثلة كود.
دائماً ضع في اعتبارك الحالات الحدية ومعالجة الأخطاء."""
# طلبات متعددة بنفس البادئة
questions = [
"كيف أرتب قائمة في Python؟",
"ما الفرق بين async و sync؟",
"اشرح المُزخرفات في Python",
]
sampling = SamplingParams(max_tokens=500, temperature=0.7)
for q in questions:
prompt = f"{system_prompt}\n\nالمستخدم: {q}\nالمساعد:"
output = llm.generate([prompt], sampling)
# الطلب الثاني+ يعيد استخدام البادئة المخزنة
الكشف التلقائي عن البادئة
vLLM يكتشف ويخزن البادئات المشتركة تلقائياً:
┌─────────────────────────────────────────────────────────┐
│ آلية التخزين المؤقت للبادئة │
├─────────────────────────────────────────────────────────┤
│ │
│ مطابقة البادئة القائمة على التجزئة: │
│ │
│ 1. حساب تجزئة كتل بادئة المحث │
│ 2. التحقق من وجود التجزئة في الذاكرة المؤقتة │
│ 3. إذا وجدت: إعادة استخدام كتل KV cache │
│ 4. إذا لم توجد: الحساب والتخزين للمستقبل │
│ │
│ دقة على مستوى الكتلة (مثلاً 16 رمز/كتلة): │
│ │
│ المحث: "النظام: أنت مفيد. المستخدم: مرحبا" │
│ [كتلة 0: hash_a] [كتلة 1: hash_b] │
│ │
│ محث جديد: "النظام: أنت مفيد. المستخدم: أهلاً" │
│ [كتلة 0: hash_a] [كتلة 1: hash_c] │
│ └── إصابة! └── فقدان │
│ │
│ الكتلة 1 فقط تحتاج حساب │
│ │
└─────────────────────────────────────────────────────────┘
نمط تطبيق الدردشة
للمحادثات متعددة الأدوار:
from vllm import LLM, SamplingParams
from vllm.utils import random_uuid
llm = LLM(
model="meta-llama/Llama-3.3-70B-Instruct",
enable_prefix_caching=True,
)
class ChatSession:
def __init__(self, system_prompt: str):
self.system_prompt = system_prompt
self.history = []
def build_prompt(self, user_message: str) -> str:
messages = [{"role": "system", "content": self.system_prompt}]
messages.extend(self.history)
messages.append({"role": "user", "content": user_message})
# التحويل إلى قالب الدردشة
# كل دور يضاف للبادئة، يعيد استخدام الأدوار السابقة المخزنة
return format_chat(messages)
def chat(self, user_message: str) -> str:
prompt = self.build_prompt(user_message)
output = llm.generate([prompt], SamplingParams(max_tokens=500))
response = output[0].outputs[0].text
self.history.append({"role": "user", "content": user_message})
self.history.append({"role": "assistant", "content": response})
return response
# محادثة متعددة الأدوار
session = ChatSession("أنت خبير Python.")
session.chat("ما هي المُزخرفات؟") # ملء مسبق كامل
session.chat("أرني مثالاً") # يعيد استخدام ذاكرة الدور 1
session.chat("ماذا عن مُزخرفات الفئات؟") # يعيد استخدام ذاكرة الدور 1-2
متقدم: استراتيجيات تخزين المحث
الاستراتيجية 1: التخزين القائم على القوالب
# تسخين الذاكرة المؤقتة بالقوالب الشائعة
templates = {
"code_review": "أنت مهندس كبير تراجع الكود...",
"translation": "أنت مترجم محترف...",
"summarization": "أنت خبير في تلخيص المستندات...",
}
# تسخين الذاكرة المؤقتة عند البدء
def warm_cache(llm, templates):
for name, template in templates.items():
# توليد إخراج أدنى لملء الذاكرة المؤقتة
llm.generate([template + "\nاختبار"], SamplingParams(max_tokens=1))
print(f"تم تسخين {len(templates)} ذاكرة مؤقتة للقوالب")
warm_cache(llm, templates)
الاستراتيجية 2: تخزين سياق RAG
# لتطبيقات RAG مع قطع مستندات متكررة
class RAGCache:
def __init__(self, llm):
self.llm = llm
self.chunk_cache = {}
def query_with_context(self, query: str, chunks: list[str]) -> str:
# بناء السياق من القطع
context = "\n\n".join(chunks)
# السياق يُخزن تلقائياً عبر تخزين البادئة
prompt = f"""السياق:
{context}
السؤال: {query}
أجب بناءً على السياق أعلاه:"""
output = self.llm.generate([prompt], SamplingParams(max_tokens=500))
return output[0].outputs[0].text
# نفس القطع في استعلامات مختلفة تعيد استخدام الذاكرة
rag = RAGCache(llm)
rag.query_with_context("ما هو X؟", [chunk1, chunk2]) # فقدان الذاكرة
rag.query_with_context("كيف يعمل X؟", [chunk1, chunk2]) # إصابة الذاكرة!
ضبط الأداء
# تحسين التخزين المؤقت للبادئة لحمل عملك
llm = LLM(
model="meta-llama/Llama-3.3-70B-Instruct",
# التخزين المؤقت للبادئة
enable_prefix_caching=True,
# حجم الكتلة يؤثر على دقة الذاكرة
# أصغر = مطابقة أفضل، تكلفة أكثر
block_size=16, # افتراضي، جيد لمعظم الحالات
# الذاكرة للتخزين المؤقت للبادئة
gpu_memory_utilization=0.9,
# الجدولة تؤثر على كفاءة الذاكرة
scheduling_policy="fcfs", # يساعد في تجميع البادئات المتشابهة
)
# مراقبة كفاءة الذاكرة المؤقتة
# vLLM يكشف المقاييس:
# - vllm_cache_hit_rate
# - vllm_prefix_cache_hit_rate
# - vllm_num_preemption_events
مراقبة معدل إصابة الذاكرة المؤقتة
import httpx
async def monitor_cache_metrics(base_url: str):
async with httpx.AsyncClient() as client:
response = await client.get(f"{base_url}/metrics")
metrics = response.text
# تحليل المقاييس ذات الصلة
cache_hit_rate = parse_metric(metrics, "vllm_cache_hit_rate")
prefix_hit_rate = parse_metric(metrics, "vllm_prefix_cache_hit_rate")
return {
"cache_hit_rate": cache_hit_rate,
"prefix_hit_rate": prefix_hit_rate,
}
# الهدف: >70% معدل إصابة ذاكرة البادئة لتطبيقات الدردشة
التخزين المؤقت للبادئة تلقائي في vLLM الحديث—المفتاح هو هيكلة المحثات لتعظيم إعادة استخدام الذاكرة.
الوحدة التالية: TensorRT-LLM لأقصى تحسين GPU. :::