Ускорение LLM: настройка KV-cache и Semantic Cache

Ускорение LLM: настройка KV-cache и Semantic Cache

Направления AI-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1440
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    997
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1264
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1002

Ускорение LLM: настройка KV-cache и Semantic Cache

Представьте: ваш LLM-сервис обрабатывает запросы, и 40% из них — однотипные вопросы. Каждый раз модель запускает полный инференс: загрузка весов, вычисление ключей и значений, генерация токенов. GPU-часы горят, latency прыгает до 5 секунд. Мы решаем это двухуровневым кэшированием — Semantic Cache на уровне приложения и KV-cache на уровне модели. Комбинация даёт экономию до 70% затрат на инференс и снижает p99 latency в 5 раз.

Кэширование — не опция, а необходимость в production. Без него каждый запрос считается заново, даже если ответ уже был сгенерирован минуту назад. Получите консультацию по настройке кэширования для ваших моделей.

Как работает двухуровневое кэширование?

Первый уровень — 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% Периодический ре-инференс

Наши инженеры имеют опыт внедрения кэширования в 15+ проектах. Средняя экономия — 60% затрат на инференс. Свяжитесь с нами для бесплатного аудита вашей архитектуры — это займёт не более часа. Обсудим параметры вашего кэша.