Прискорення LLM: налаштування KV-cache та Semantic Cache
Уявіть: ваш LLM-сервіс обробляє запити, і 40% з них — однотипні питання. Щоразу модель запускає повний інференс: завантаження ваг, обчислення ключів та значень, генерація токенів. GPU-години горять, latency стрибає до 5 секунд. Ми вирішуємо це дворівневим кешуванням — Semantic Cache на рівні застосунку та KV-cache на рівні моделі. Комбінація дає економію до 70% витрат на інференс і знижує p99 latency у 5 разів порівняно з відсутністю кешування.
Кешування — не опція, а необхідність у продакшені. Без нього кожен запит обчислюється заново, навіть якщо відповідь вже була згенерована хвилину тому. Наші інженери мають 10+ років досвіду в ML та 15+ впроваджених рішень. Ми пропонуємо безкоштовний аудит вашого проєкту — оцінимо потенціал кешування. Пишіть нам.
Як працює дворівневе кешування?
Перший рівень — Semantic Cache. Він використовує векторні ембеддінги для пошуку семантично схожих запитів. Коли надходить новий запит, він кодується у вектор, і виконується пошук найближчих сусідів у векторній базі (Qdrant, pgvector). Якщо знайдено запис із схожістю вище порогу (зазвичай 0.85–0.95), кешована відповідь повертається без інференсу. Ось реалізація на Python з Sentence Transformers, Redis та Qdrant:
from sentence_transformers import SentenceTransformer import numpy as np import redis import json class SemanticCache: def __init__(self, similarity_threshold: float = 0.92): self.encoder = SentenceTransformer("paraphrase-multilingual-mpnet-base-v2") self.redis = redis.Redis(host="localhost", port=6379, db=1) self.threshold = similarity_threshold from qdrant_client import QdrantClient self.vector_db = QdrantClient("localhost", port=6333) def get(self, prompt: str, system_prompt: str = "") -> str | None: cache_key = self._make_key(prompt, system_prompt) exact = self.redis.get(cache_key) if exact: return json.loads(exact)["response"] embedding = self.encoder.encode(prompt) results = self.vector_db.search( collection_name="llm_cache", query_vector=embedding.tolist(), limit=1, score_threshold=self.threshold ) if results: cached_response = json.loads(results[0].payload["response"]) self.redis.expire(results[0].id, 3600) return cached_response return None def set(self, prompt: str, response: str, system_prompt: str = "", ttl: int = 3600): embedding = self.encoder.encode(prompt) cache_id = self._make_key(prompt, system_prompt) self.vector_db.upsert( collection_name="llm_cache", points=[{ "id": abs(hash(cache_id)) % (2**31), "vector": embedding.tolist(), "payload": {"prompt": prompt, "response": json.dumps(response), "system_prompt": system_prompt} }] ) self.redis.setex(cache_id, ttl, json.dumps({"response": response})) def _make_key(self, prompt: str, system_prompt: str) -> str: import hashlib return hashlib.sha256(f"{system_prompt}||{prompt}".encode()).hexdigest() Другий рівень — KV-cache на рівні моделі. vLLM автоматично кешує ключі та значення для спільних префіксів, наприклад system prompt. При увімкненому prefix caching hit rate досягає 60–80%, знижуючи latency у 2–5 разів.
# vLLM автоматично використовує prefix caching # system prompt повинен бути однаковим для різних запитів python -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-3-8b-instruct \ --enable-prefix-caching \ --max-model-len 8192 # Метрика: vllm:gpu_cache_usage_perc показує зайнятість кешу GPTCache — готове рішення
GPTCache — бібліотека, яка реалізує Semantic Cache і керує всією інфраструктурою: ембеддінги, векторний пошук, TTL. Інтеграція зводиться до заміни виклику openai на cached_openai:
from gptcache import cache from gptcache.adapter import openai as cached_openai from gptcache.embedding import Onnx from gptcache.manager import CacheBase, VectorBase, get_data_manager from gptcache.similarity_evaluation.distance import SearchDistanceEvaluation embedding_model = Onnx() data_manager = get_data_manager( CacheBase("sqlite"), VectorBase("qdrant", host="localhost", port=6333, dimension=512) ) cache.init( embedding_func=embedding_model.to_embeddings, data_manager=data_manager, similarity_evaluation=SearchDistanceEvaluation(max_distance=0.3), cache_enable_func=lambda *args, **kwargs: True ) response = cached_openai.ChatCompletion.create( model="gpt-4o", messages=[{"role": "user", "content": "What is Python?"}] ) Порівняння типів кешування
| Тип кешу | Рівень | Зниження latency | Економія GPU | Складність впровадження |
|---|---|---|---|---|
| Semantic Cache | Застосунок | 90-95% | до 80% | Середня (векторна БД) |
| KV-cache | Модель | 50-80% | до 40% | Вбудовано в vLLM |
| Prefix Cache | Модель | 30-60% | до 20% | Прапор --enable-prefix-caching |
Як вибрати поріг схожості для Semantic Cache?
Поріг схожості — ключовий параметр. Занадто низький (0.7) призводить до хибних спрацьовувань, високий (0.98) — до частих промахів. Оптимальне значення залежить від завдання. Для FAQ-ботів добре працює 0.85–0.90, для RAG з фіксованими документами — 0.90–0.95, для класифікації — 0.95+. Ми налаштовуємо поріг на основі аналізу ваших даних: беремо 1000 реальних запитів, розмічаємо семантично еквівалентні пари і підбираємо threshold за метрикою F1.
Коли кешування не дає виграшу?
Кешування безглузде для персоналізованих відповідей, запитів з поточним часом або датою, фінансових даних (курси, ціни), генерації коду та при temperature > 0.8. У таких випадках краще вимкнути кеш. Найбільший ефект дає кешування в FAQ-ботах, RAG з фіксованими документами та класифікаційних завданнях.
Чому кешування не панацея?
Кешування не виправляє якість моделі. Якщо базова модель галюцинує, кеш лише закріпить помилки. Обов'язково моніторити staleness rate — частку кешованих відповідей, що стали неактуальними. Ми впроваджуємо A/B-тести: періодично порівнюємо кешовану відповідь з новим інференсом. Якщо розбіжність перевищує поріг, кеш інвалідується.
Інтеграція з існуючою інфраструктурою
Кешування легко інтегрується з популярними MLOps-інструментами. vLLM і TGI підтримують prefix caching на рівні інференс-сервера. Для Semantic Cache ми використовуємо Redis як швидкий exact-matching кеш і Qdrant або pgvector для векторного пошуку. Всі компоненти розгортаються в Docker або Kubernetes, моніторяться через Prometheus і Grafana.
Приклад метрик для моніторингу
vLLM експортує метрику vllm:gpu_cache_usage_perc — відсоток зайнятості KV-cache. Для Semantic Cache налаштовуємо лічильник semantic_cache_hits_total і гістограму semantic_cache_lookup_duration_seconds. Alerting при cache hit rate нижче 20%.
Метрики кешування
| Метрика | Цільове значення | Як вимірюється |
|---|---|---|
| Cache hit rate | > 30% для FAQ, < 5% для creative | Logs / Prometheus |
| Latency reduction | p99 < 500 мс | APM (Datadog, Grafana) |
| Cost savings | % запитів, не відправлених на інференс | Billing API |
| Staleness rate | < 2% | Періодичний ре-інференс |
Що входить у роботу
Ми пропонуємо впровадження кешування під ключ. Етапи:
- Аудит архітектури вашого LLM-сервісу — 1 день.
- Вибір стратегії кешування (Semantic, KV, Prefix або комбінація).
- Налаштування vLLM з prefix caching.
- Інтеграція Semantic Cache (GPTCache або власна реалізація).
- Моніторинг та алертинг (Grafana, Prometheus).
- Документація та навчання команди. Вартість залежить від складності, середній чек — $5000-15000. Для проєкту з 100k запитів/день економія складає $5000/міс, що окупає впровадження за 1-3 місяці.
Наші інженери мають 10+ років досвіду в ML та 15+ впроваджених рішень. Ми гарантуємо якість — якщо cache hit rate не досягне 30%, повертаємо частину коштів. Зв'яжіться з нами для безкоштовного аудиту вашої архітектури — це займе не більше години. Обговоримо параметри вашого кешу.







