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. :::

اختبار

الوحدة 2: vLLM ومحركات الاستدلال مفتوحة المصدر

خذ الاختبار