Семантическое кэширование для AI-агента: как снизить затраты на токены без потери качества ответов

Разбираем, как работает семантическое кэширование в AI-агентах: какие библиотеки и подходы реально снижают расход токенов, где возникают ложные срабатывания и как настроить кэш без ущерба для точности ответов.

Схема семантического кэширования для AI-агента с Redis и эмбеддингами
Схема семантического кэширования для AI-агента с Redis и эмбеддингами
Amiroooo.jpg | by Siramirb | wikimedia_commons | CC BY-SA 4.0

Каждый вызов LLM в production — это не только задержка, но и прямые затраты. Для AI-агента, который делает десятки и сотни обращений к модели за сессию, счёт за токены может расти экспоненциально. Особенно когда агент многократно запрашивает одну и ту же или близкую по смыслу информацию: статус системы, документацию по API, результаты предыдущих вычислений.

Семантическое кэширование решает именно эту проблему. Вместо того чтобы отправлять каждый запрос в модель, агент проверяет, не был ли уже дан ответ на похожий вопрос. Если семантическая близость превышает настроенный порог, ответ возвращается из кэша. Никакого лишнего вызова API, никаких дополнительных токенов.

Но настройка такого кэша — не простая задача. Порог схожести, выбор модели эмбеддингов, инвалидация устаревших записей и обработка ложных срабатываний требуют понимания того, как именно агент взаимодействует с LLM. Разберём на примере GPTCache, Redis и реальных метрик.

Как работает семантическое кэширование: от хэша до эмбеддингов

Обычное кэширование сравнивает точные строки запросов. Если пользователь написал «какая погода в Москве», а потом «какая погода в Москве?» — это уже разные ключи. Семантическое кэширование переводит запрос в вектор эмбеддингов и сравнивает его с векторами ранее закэшированных запросов.

Процесс выглядит так:

Агент получает запрос.

Запрос преобразуется в эмбеддинг (обычно через модель типа text-embedding-3-small).
3. Эмбеддинг сравнивается с векторами в векторной базе данных (Redis, Milvus, FAISS).
4. Если косинусная близость превышает порог (например, 0.92), возвращается закэшированный ответ.
5. Если нет — запрос уходит в LLM, ответ сохраняется в кэш вместе с эмбеддингом запроса.

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

GPTCache: что умеет и где подводит

GPTCache — самая известная open-source библиотека для семантического кэширования LLM-запросов. Она поддерживает несколько бэкендов: Redis, FAISS, Milvus, и модели эмбеддингов от OpenAI, Hugging Face и Sentence-Transformers.

Базовый пример настройки:

python
from gptcache import cache
from gptcache.adapter import openai
from gptcache.embedding import OpenAIEmbedding
from gptcache.similarity_evaluation import CosineSimilarity

cache.init(
pre_embedding_func=openai.embedding,
embedding_func=OpenAIEmbedding(),
similarity_evaluation=CosineSimilarity(threshold=0.92),
data_manager=… # Redis или FAISS
)

response = openai.ChatCompletion.create(
model=»gpt-4″,
messages=[{«role»: «user», «content»: «Как настроить Redis для кэша?»}]
)

Второй запрос с текстом «Настройка Redis для кэширования» при пороге 0.92 вернёт тот же ответ без вызова API.

Проблемы GPTCache, которые стоит учитывать:

  • Задержка на вычисление эмбеддинга. Само преобразование запроса в вектор занимает 50–200 мс. Если кэш не срабатывает, это время добавляется к каждому вызову LLM.
  • Сложность с мультизапросными агентами. Если агент делает несколько последовательных вызовов с меняющимся контекстом (например, добавляет историю диалога), кэшировать весь промпт целиком неэффективно.
  • Инвалидация кэша. GPTCache не умеет автоматически определять, что ответ устарел. Если источник данных изменился, кэш продолжит возвращать старый ответ до ручной очистки.

Redis как векторная база: настройка порогов и метрики

Redis с модулем Redis Stack (поддержка векторного поиска) — популярный выбор для семантического кэша из-за низкой задержки и встроенного управления TTL.

Пример настройки индекса для кэша:

FT.CREATE idx:cache ON HASH PREFIX 1 «cache:» SCHEMA
embedding VECTOR KNN 10 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE
response TEXT
timestamp NUMERIC

Каждая запись кэша хранит эмбеддинг запроса, ответ и метку времени. Поиск ближайших соседей:

FT.SEARCH idx:cache «*=>[KNN 1 @embedding $vec AS score]» RETURN 2 response score SORTBY score

Метрики, которые стоит отслеживать:

  • Cache hit rate — доля запросов, которые вернулись из кэша. Для типичного агента, работающего с повторяющимися вопросами, hit rate 30–50% считается хорошим.
  • False positive rate — доля ответов из кэша, которые не соответствуют запросу. При пороге 0.85 false positive rate может достигать 15–20%.
  • Average latency — время проверки кэша. Redis с локальным размещением даёт 1–5 мс на поиск, что значительно быстрее вызова LLM (500–3000 мс).

TTL для записей кэша стоит устанавливать в зависимости от типа данных. Для ответов на вопросы о документации — 24 часа. Для статусов системы — 1–5 минут. Для персональных данных пользователя кэширование лучше отключить полностью.

Когда семантическое кэширование ломает логику агента

Семантическое кэширование — это компромисс между скоростью и точностью. Вот типичные сценарии, где кэш может навредить:

  • Разные вопросы с похожим смыслом. «Как установить Python» и «Как установить Python 3.12» — для человека разные запросы, но эмбеддинги могут оказаться близкими. При низком пороге агент начнёт выдавать инструкции для Python 3.10 вместо 3.12.
  • Контекстно-зависимые запросы. Агент спрашивает «какой бюджет проекта?» в начале разговора и «какой бюджет проекта?» после того, как пользователь уточнил параметры. Ответ из кэша будет содержать старые данные.
  • Зависимость от времени. «Последний коммит в репозиторий» должен возвращать актуальные данные, а не результат часовой давности.

Решение для этих случаев — гибридная стратегия: кэшировать только те запросы, где ответ гарантированно не меняется со временем, а для динамических данных всегда вызывать LLM.

Альтернативный подход: кэширование на уровне tool calling

Вместо кэширования полных ответов LLM можно кэшировать результаты вызова инструментов. Например, если агент запрашивает статус развёртывания через API CI/CD, результат этого вызова кэшируется на 30 секунд. При повторном запросе агент получает данные из кэша, не дожидаясь ответа от внешнего сервиса.

Этот подход сложнее в реализации, но даёт более предсказуемые результаты, поскольку кэшируются не текстовые ответы, а структурированные данные. Для этого потребуется:

Нормализовать вызовы инструментов до стандартного формата (функция + аргументы).

Использовать точное совпадение аргументов (без семантики) — это снижает риск ложных срабатываний.
3. Настроить TTL для каждого типа инструмента отдельно.

Как оценить экономию: пример с реальными цифрами

Возьмём агента, который обрабатывает 10 000 запросов в день. Средняя длина промпта — 500 токенов, средняя длина ответа — 300 токенов. Модель — GPT-4o mini ($0.15 / 1M input, $0.60 / 1M output).

Без кэша затраты в день: (10 000 × 500 × 0.15 / 1 000 000) + (10 000 × 300 × 0.60 / 1 000 000) = $0.75 + $1.80 = $2.55.

С кэшем при hit rate 40%: 6 000 запросов до LLM и 4 000 из кэша. Затраты: (6 000 × 500 × 0.15 / 1 000 000) + (6 000 × 300 × 0.60 / 1 000 000) = $0.45 + $1.08 = $1.53. Экономия — $1.02 в день, или $30 в месяц.

Для GPT-4o (цены выше в 10–15 раз) экономия будет пропорционально больше. При 10 000 запросов в день на GPT-4o затраты без кэша — около $50–80 в день, с кэшем — $30–48.

Что нужно проверить перед внедрением

Семантическое кэширование — не серебряная пуля. Перед тем как добавлять его в production, стоит:

Проанализировать типы запросов агента. Если 80% запросов уникальны, кэш не даст значимой экономии.
2. Выбрать модель эмбеддингов с учётом языковой специфики. Для русского языка text-embedding-3-small работает хуже, чем для английского — стоит протестировать на своих данных.
3. Настроить мониторинг false positive rate. Если ответы из кэша начинают расходиться с ожиданиями пользователей — снижать порог схожести.
4. Убедиться, что задержка на проверку кэша не превышает выгоду от его использования. При hit rate ниже 20% семантическое кэширование может увеличить среднее время ответа.

GPTCache и Redis — зрелые инструменты, но их настройка требует понимания того, как именно ваш агент формирует запросы. Лучший способ проверить гипотезу — запустить A/B-тест на части трафика и сравнить метрики качества ответов и затрат.

Источники