Реализация AI-кэширования ответов (Semantic Cache) в мобильном приложении
Обычный кэш работает по точному совпадению ключа. «Как добавить транзакцию?» и «Как мне добавить новую транзакцию?» — разные строки, разные запросы, два вызова API. Семантический кэш работает по смыслу: оба вопроса получают один и тот же кэшированный ответ, потому что их embeddings близки в векторном пространстве.
Архитектура семантического кэша
Схема работы: запрос пользователя → генерация embedding → поиск ближайшего в векторном хранилище → если cosine similarity > threshold, вернуть кэшированный ответ → иначе запрос к LLM → сохранить embedding + ответ в кэш.
# Серверная часть (FastAPI) — semantic cache middleware
import numpy as np
from openai import AsyncOpenAI
client = AsyncOpenAI()
cache: list[dict] = [] # В проде — Redis + pgvector или Pinecone
async def get_embedding(text: str) -> list[float]:
response = await client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
def cosine_similarity(a: list[float], b: list[float]) -> float:
a_arr, b_arr = np.array(a), np.array(b)
return float(np.dot(a_arr, b_arr) / (np.linalg.norm(a_arr) * np.linalg.norm(b_arr)))
async def semantic_cache_lookup(query: str, threshold: float = 0.92) -> str | None:
query_emb = await get_embedding(query)
for entry in cache:
similarity = cosine_similarity(query_emb, entry["embedding"])
if similarity >= threshold:
return entry["response"]
return None
Threshold — критичный параметр. При 0.85 кэш слишком агрессивен: разные по смыслу вопросы получают один ответ. При 0.97 — почти не работает. Оптимальный диапазон для большинства доменов: 0.90–0.95, подбирается на реальных запросах.
Где хранить кэш
Redis + RediSearch — для небольших объёмов. Vector similarity search встроен начиная с Redis Stack. Latency поиска — 1–5мс для десятков тысяч записей.
pgvector — если PostgreSQL уже в стеке. Расширение добавляет тип vector и поддержку HNSW/IVFFlat индексов. Запрос к кэшу:
SELECT response, 1 - (embedding <=> $1::vector) AS similarity
FROM ai_cache
WHERE 1 - (embedding <=> $1::vector) > 0.92
ORDER BY embedding <=> $1::vector
LIMIT 1;
Pinecone / Weaviate — managed-решения, не требуют администрирования. Оправданы при миллионах записей или мультитенантной архитектуре.
Инвалидация и TTL
Семантический кэш нужно инвалидировать при обновлении системного промпта или базовой модели — старые ответы могут не соответствовать новому поведению. Минимальный TTL — 7–30 дней для стабильных FAQ-подобных вопросов. Для вопросов с временной привязкой («какой у меня баланс?») кэширование неприменимо — выявляем через классификатор или по ключевым словам.
Ориентиры по срокам
Базовый семантический кэш на Redis + OpenAI Embeddings — 2–3 дня. С подбором threshold на реальных данных и мониторингом hit rate — 3–5 дней.







