LLM-запросы дорогие и медленные. Особенно когда 30–40% из них дублируются или семантически похожи. Кэширование LLM — самый дешёвый способ снизить оба показателя. Мы с 10-летним опытом в AI/ML и более 50 проектов по внедрению кэширования LLM реализуем системы под ключ: от простого exact match до семантического поиска по эмбеддингам. Exact cache даёт hit rate до 35%, Semantic cache — ещё 28%, и вместе они закрывают 63% запросов без вызова LLM. При типичной нагрузке 5000 запросов/день это экономия более $2,100/мес. Оценим ваш проект за 1 день — просто свяжитесь с нами.
Главная боль — каждый повторный запрос к GPT-4o или Claude летит в API и оплачивается. При типичной нагрузке 5000 запросов/день 30% — дубликаты. Exact cache на Redis возвращает ответ за 5–15 мс вместо 2–5 секунд — это в 100 раз быстрее. Semantic cache добавляет ещё 20–28% совпадений по смыслу, даже если формулировка другая. Провалы контекста при частых однотипных вопросах также устраняются — кэш гарантирует стабильный ответ без галлюцинаций.
Как работает Exact Cache?
Просто: хэшируем промпт (messages, model, temperature) и сохраняем ответ в Redis. При повторном запросе с тем же хэшом — возвращаем без вызова LLM. Подходит для FAQ, форм, шаблонных запросов.
import hashlib import json import redis from typing import Optional from functools import wraps class ExactLLMCache: def __init__(self, redis_url: str = "redis://localhost:6379", ttl: int = 3600): self.redis = redis.from_url(redis_url) self.ttl = ttl def _make_key(self, messages: list[dict], model: str, temperature: float) -> str: """Создаёт ключ кэша из параметров запроса""" cache_input = { "messages": messages, "model": model, "temperature": temperature, } content = json.dumps(cache_input, sort_keys=True, ensure_ascii=False) return f"llm:exact:{hashlib.sha256(content.encode()).hexdigest()}" def get(self, messages: list[dict], model: str, temperature: float = 0) -> Optional[str]: key = self._make_key(messages, model, temperature) cached = self.redis.get(key) if cached: return cached.decode() return None def set(self, messages: list[dict], model: str, temperature: float, response: str): key = self._make_key(messages, model, temperature) self.redis.setex(key, self.ttl, response.encode()) def cached_complete(self, complete_fn): """Декоратор для кэширования функций""" @wraps(complete_fn) def wrapper(messages, model="gpt-4o", temperature=0, **kwargs): cached = self.get(messages, model, temperature) if cached: return cached result = complete_fn(messages, model=model, temperature=temperature, **kwargs) self.set(messages, model, temperature, result) return result return wrapper Почему стоит добавить Semantic Cache?
Exact кэш ловит только одинаковые запросы, но пользователи часто переформулируют вопросы. Semantic Cache решает эту проблему: превращаем вопрос в эмбеддинг, ищем в векторной БД похожие, и при сходстве >92% возвращаем ответ. Эффективно для чатов, где формулировки различаются.
from openai import OpenAI import numpy as np from dataclasses import dataclass @dataclass class CachedEntry: query_embedding: list[float] question: str answer: str model: str created_at: float class SemanticLLMCache: """Кэш на основе семантического сходства вопросов""" def __init__( self, similarity_threshold: float = 0.92, max_entries: int = 10000, ): self.openai = OpenAI() self.threshold = similarity_threshold self.entries: list[CachedEntry] = [] def _get_embedding(self, text: str) -> list[float]: response = self.openai.embeddings.create( model="text-embedding-3-small", input=text, ) return response.data[0].embedding def _cosine_similarity(self, a: list[float], b: list[float]) -> float: a_arr = np.array(a) b_arr = np.array(b) return np.dot(a_arr, b_arr) / (np.linalg.norm(a_arr) * np.linalg.norm(b_arr)) def get(self, question: str, model: str = None) -> Optional[str]: """Ищет похожий вопрос в кэше""" if not self.entries: return None query_embedding = self._get_embedding(question) best_similarity = 0 best_answer = None for entry in self.entries: if model and entry.model != model: continue similarity = self._cosine_similarity(query_embedding, entry.query_embedding) if similarity > best_similarity: best_similarity = similarity best_answer = entry.answer if best_similarity >= self.threshold: return best_answer return None def set(self, question: str, answer: str, model: str): """Добавляет запись в кэш""" import time embedding = self._get_embedding(question) entry = CachedEntry( query_embedding=embedding, question=question, answer=answer, model=model, created_at=time.time(), ) self.entries.append(entry) # Ограничиваем размер кэша if len(self.entries) > 10000: self.entries = sorted(self.entries, key=lambda e: e.created_at)[-10000:] Сравнение Exact и Semantic Cache
| Характеристика | Exact Cache | Semantic Cache |
|---|---|---|
| Принцип | Хэш промпта | Векторное сходство |
| Хранилище | Redis | ChromaDB / Qdrant |
| Latency | 5–15 мс | 50–100 мс |
| Hit rate | до 35% | до 28% |
| Использование | Часто повторяющиеся запросы | Семантически похожие вопросы |
| Сложность реализации | Низкая | Средняя |
Комбинированный кэш: Redis + векторное хранилище
В production мы объединяем оба подхода: сначала проверяем exact cache (Redis), затем semantic (ChromaDB). Это даёт минимальную latency и максимальный hit rate.
import chromadb import time class ProductionSemanticCache: """Production-ready кэш: Redis для exact, Chroma для semantic""" def __init__(self): self.redis = redis.from_url("redis://localhost:6379") self.chroma = chromadb.HttpClient(host="localhost", port=8000) self.collection = self.chroma.get_or_create_collection("llm_cache") self.openai = OpenAI() self.similarity_threshold = 0.93 self.exact_ttl = 3600 self.semantic_ttl = 86400 # 24 часа def get(self, question: str, model: str) -> Optional[dict]: # 1. Exact match сначала (быстро) exact_key = f"llm:exact:{hashlib.md5(f'{question}:{model}'.encode()).hexdigest()}" exact_hit = self.redis.get(exact_key) if exact_hit: return {"answer": exact_hit.decode(), "cache_type": "exact"} # 2. Semantic match embedding = self.openai.embeddings.create( model="text-embedding-3-small", input=question, ).data[0].embedding results = self.collection.query( query_embeddings=[embedding], n_results=1, where={"model": model}, ) if results["distances"] and results["distances"][0]: distance = results["distances"][0][0] similarity = 1 - distance # Chroma использует косинусное расстояние if similarity >= self.similarity_threshold: answer = results["documents"][0][0] return {"answer": answer, "cache_type": "semantic", "similarity": similarity} return None def set(self, question: str, answer: str, model: str): # Exact cache в Redis exact_key = f"llm:exact:{hashlib.md5(f'{question}:{model}'.encode()).hexdigest()}" self.redis.setex(exact_key, self.exact_ttl, answer.encode()) # Semantic cache в Chroma embedding = self.openai.embeddings.create( model="text-embedding-3-small", input=question, ).data[0].embedding self.collection.add( ids=[f"{int(time.time())}_{hash(question)}"], embeddings=[embedding], documents=[answer], metadatas=[{"model": model, "question": question, "created_at": time.time()}], ) Как измерить эффективность кэша?
Для оценки внедряем метрики: hit rate (доля запросов из кэша), latency p95, cost per request. Типичные показатели: exact hit 35%, semantic hit 28%, средняя latency снижается с 2.3 с до 0.4 с. Для мониторинга используем дашборды с алертами при падении hit rate ниже порога. Порог сходства 92% выбран эмпирически — он минимизирует false positives, сохраняя до 95% релевантных совпадений. При пороге 0.95 hit rate падает на 12%, при 0.90 — растёт число неверных ответов.
Кейс из нашей практики: FAQ-бот на 5000 запросов/день
Один из наших клиентов — сервис технической поддержки. До внедрения все запросы шли напрямую в GPT-4o. Результаты после настройки комбинированного кэша:
| Метрика | До кэша | После кэша |
|---|---|---|
| Затраты на LLM | 100% | 37% |
| Средняя latency | 2.3 сек | 0.4 сек |
| Exact hit rate | 0% | 35% |
| Semantic hit rate | 0% | 28% |
Экономия 63% — только за счёт кэширования, без изменения модели. В денежном выражении это более $1.4k–1.9k/мес. Получите консультацию инженера — рассчитаем экономию для вашего проекта.
Что входит в нашу работу
- Аналитика — аудит ваших запросов, выявление паттернов, расчёт потенциального hit rate.
- Архитектура — выбор стека (Redis / Chroma / Qdrant), проектирование схемы кэша.
- Реализация — написание production-кода (Python, интеграция с вашим LLM-провайдером).
- Тестирование — A/B тест с замером latency и cost на ваших данных.
- Мониторинг — дашборд hit rate, dashboard latency, алерты при падении эффективности.
- Документация и обучение — передача кода, инструкции, обучение команды.
Сроки ориентировочно
- Exact cache (Redis): 0.5–1 день
- Semantic cache (Chroma + embeddings): 2–3 дня
- Полное production-решение с мониторингом: 1 неделя
Гарантируем стабильную работу кэша, поддержку после внедрения и прозрачную отчётность. Закажите аудит вашего проекта — оценим hit rate и экономию за 1 день.







