LLMOps

بهینه‌سازی کش هوش مصنوعی در LLMOps: راهنمای عملی کاهش هزینه و تأخیر

مدل‌های زبانی بزرگ (LLM) نحوه ساخت نرم‌افزار را متحول کرده‌اند، اما با چالش‌های ذاتی همراه هستند: هزینه‌های استنتاج بالا، تأخیر متغیر و خروجی‌های غیرقطعی. برای کاربردهای سطح تولید، تکیه صرف بر فراخوانی‌های API بلادرنگ اغلب پایدار نیست. اینجاست که کش هوش مصنوعی به یک مؤلفه حیاتی در استک LLMOps شما تبدیل می‌شود. برخلاف کش سنتی که با جفت‌های کلید-ارزش ایستا سروکار دارد، کش هوش مصنوعی نیازمند مدیریت شباهت معنایی، تغییرات زمینه و ویژگی‌های منحصربه‌فرد خروجی‌های مولد است.

در این مقاله، انواع مختلف کش هوش مصنوعی، زمان‌های استفاده از آن‌ها و نحوه پیاده‌سازی استراتژی‌های کش محکم را که تعادلی بین کارایی هزینه و کیفیت خروجی برقرار می‌کنند، بررسی خواهیم کرد.

چرا کش هوش مصنوعی متفاوت است؟

کش وب سنتی بر اساس تطبیق دقیق کار می‌کند. اگر کاربر درخواست /api/products/123 را بدهد، پاسخ JSON را کش می‌کنید. اگر درخواست /api/products/123?sort=date را بدهد، کلید کش متفاوتی است. با این حال، مدل‌های هوش مصنوعی بر زبان طبیعی عمل می‌کنند. کاربری که می‌پرسد "پایتان فرانسه کجاست؟" و کاربری که می‌پرسد "پاریس در چه کشوری قرار دارد؟" از نظر معنایی یکسان هستند، اما از نظر دستوری متفاوت‌اند.

بنابراین، کش مؤثر هوش مصنوعی باید دو مسئله متمایز را حل کند:

  1. کش پرامپت: جلوگیری از فراخوانی‌های API مکرر برای پرامپت‌های مشابه از نظر معنایی.
  2. کش پاسخ: ذخیره و بازیابی خروجی‌های تولیدشده برای کاهش تأخیر در پرسش‌های تکراری.

انواع استراتژی‌های کش هوش مصنوعی

۱. کش تطبیق دقیق

ساده‌ترین شکل کش شامل هش کردن رشته پرامپت است. اگر دقیقاً همان پرامپت دوباره ارسال شود، پاسخ کش‌شده را برگردانید. این روش کم‌هزینه و آسان برای پیاده‌سازی است، اما در زمینه‌های گفتگویی پویا نرخ برخورد (hit rate) پایینی دارد.

۲. کش معنایی (بکتوری)

این رویکرد از جاسازی‌ها (embeddings) برای یافتن "تطبیق‌های نزدیک" به پرسش‌های قبلی استفاده می‌کند. اگر پرامپت جدید از نظر معنایی شبیه به یکی از کش‌شده‌ها باشد (بر اساس شباهت کسینوس)، پاسخ کش‌شده را برگردانید. این کار به‌طور قابل‌توجهی نرخ برخورد را افزایش می‌دهد، اما ریسک ارائه پاسخ‌های کمی منقضی‌شده یا نادرست از نظر زمینه را وارد می‌کند.

۳. کش ترکیبی

بیشتر سیستم‌های تولید از یک رویکرد ترکیبی استفاده می‌کنند: ابتدا به دنبال تطبیق دقیق بگردید (اعتماد بالا)، سپس به تطبیق معنایی روی بیاورید (هزینه بالاتر، اعتماد کمتر).

نمونه پیاده‌سازی: کش معنایی با پایتون

در زیر یک نمونه ساده‌شده از نحوه پیاده‌سازی یک کش معنایی با استفاده از numpy برای شباهت کسینوس و یک انبار بکتوری مانند ChromaDB یا Pinecone آورده شده است. برای خلاصه‌سازی، از یک رویکرد حافظه‌ای محلی با جاسازی‌های تولیدشده توسط یک API سازگار با OpenAI استفاده خواهیم کرد.


import hashlib
import numpy as np
from typing import Dict, Tuple
import time

class SemanticCache:
    def __init__(self, similarity_threshold: float = 0.95):
        self.cache: Dict[str, Dict] = {}  # hash: {prompt, response, embedding, timestamp}
        self.threshold = similarity_threshold

    def _get_embedding(self, text: str) -> np.ndarray:
        # Placeholder: In production, use your LLM's embedding API
        # e.g., openai.embedding.create(input=text)
        # Here we simulate a deterministic embedding for demonstration
        # In real code, this calls an embedding service
        dummy_hash = hashlib.md5(text.encode()).hexdigest()
        # Simulate a 1024-dimensional vector
        np.random.seed(int(dummy_hash, 16))
        return np.random.rand(1024)

    def _cosine_similarity(self, vec1: np.ndarray, vec2: np.ndarray) -> float:
        dot_product = np.dot(vec1, vec2)
        norms = np.linalg.norm(vec1) * np.linalg.norm(vec2)
        if norms == 0:
            return 0
        return float(dot_product / norms)

    def get(self, prompt: str) -> Tuple[bool, str]:
        prompt_hash = hashlib.md5(prompt.encode()).hexdigest()
        
        # 1. Check Exact Match
        if prompt_hash in self.cache:
            return True, self.cache[prompt_hash]['response']

        # 2. Check Semantic Match
        current_embedding = self._get_embedding(prompt)
        best_match_score = 0
        best_match_response = None

        for hash_key, data in self.cache.items():
            cached_embedding = data['embedding']
            similarity = self._cosine_similarity(current_embedding, cached_embedding)
            
            if similarity > best_match_score:
                best_match_score = similarity
                best_match_response = data['response']

        if best_match_score >= self.threshold:
            return True, best_match_response

        return False, ""

    def put(self, prompt: str, response: str):
        prompt_hash = hashlib.md5(prompt.encode()).hexdigest()
        embedding = self._get_embedding(prompt)
        self.cache[prompt_hash] = {
            'prompt': prompt,
            'response': response,
            'embedding': embedding,
            'timestamp': time.time()
        }

# Usage Example
if __name__ == "__main__":
    cache = SemanticCache(similarity_threshold=0.90)
    
    # First call: Cache miss
    hit, resp = cache.get("What is the capital of France?")
    print(f"Call 1: Hit={hit}")  # Expected: False
    
    # Simulate LLM response
    llm_response = "The capital of France is Paris."
    cache.put("What is the capital of France?", llm_response)
    
    # Second call: Exact match
    hit, resp = cache.get("What is the capital of France?")
    print(f"Call 2: Hit={hit}, Response={resp}")  # Expected: True, Paris
    
    # Third call: Semantic match
    hit, resp = cache.get("Where is Paris located in terms of country?")
    # Note: With real embeddings, this would likely be a hit if similarity > 0.90
    print(f"Call 3: Hit={hit}, Response={resp}")

بهترین روش‌ها و تله‌ها

  • باطل‌سازی کش: دانش LLM می‌تواند منقضی شود. سیاست‌های TTL (زمان ماندگاری) یا محرک‌های باطل‌سازی دستی را هنگام به‌روزرسانی پایگاه دانش خود پیاده‌سازی کنید.
  • حساسیت به زمینه: با کش معنایی در گفتگوهای چندگانه با احتیاط رفتار کنید. یک پرسش مشابه ممکن است بسته به زمینه قبلی، پاسخ متفاوتی داشته باشد. در نظر بگیرید تاریخچه گفتگو را در کلید کش گنجانده یا کش معنایی را برای چت‌های دارای حالت (stateful) غیرفعال کنید.
  • تنظیم آستانه: آستانه شباهت یک هایپرپارامتر حیاتی است. اگر خیلی پایین باشد، ریسک ارائه پاسخ‌های نادرست یا دارای سوگیری وجود دارد. اگر خیلی بالا باشد، نرخ برخورد کش شما کاهش می‌یابد. با ۰.۹۵+ شروع کنید و بازخورد کاربران را پایش کنید.
  • لاگ‌گذاری و پایش: برخوردها/عدم برخوردهای کش، امتیازات شباهت و خطاهای گزارش‌شده توسط کاربران روی پاسخ‌های کش‌شده را ردیابی کنید. این داده‌ها برای تنظیم استراتژی شما ضروری هستند.

نتیجه‌گیری

کش هوش مصنوعی صرفاً یک بهینه‌سازی عملکردی نیست؛ بلکه یک اهرم استراتژیک برای کنترل هزینه‌ها و بهبود تجربه کاربری در کاربردهای LLM است. با ترکیب کش تطبیق دقیق و کش معنایی همراه با پایش محکم، می‌توانید تأخیر و هزینه API را به‌طور قابل‌توجهی کاهش دهید، بدون اینکه از کیفیت خروجی‌های هوش مصنوعی خود بکاهید. با تطبیق دقیق شروع کنید، سپس کش معنایی را با تنظیم دقیق آستانه معرفی کنید. با رشد بلوغ LLMOps شما، این تکنیک‌ها به بنیادی برای ساخت سیستم‌های هوش مصنوعی مقیاس‌پذیر و مقرون‌به‌صرفه تبدیل خواهند شد.

Share: