Семантическое кэширование с использованием Redis для векторного поиска
Сократите расходы на API для встраивания данных с помощью семантического кэширования в Redis: двухуровневый кэш, который повторно использует результаты векторного поиска, когда запросы означают одно и то же, в Node.js.
Источники: Semantic Caching with Redis: Slashing API Costs in Vector Search
При разработке продукта на основе ИИ команды чаще удивляются не качеству модели, а счетам за ее использование. Чат-бот, который кажется дешевым при нескольких сотнях пользователей, может внезапно потребовать значительных затрат каждый раз, когда поиск, рекомендация или вопрос запускают новый вызов модели генерации векторов.
Многие из этих вызовов являются избыточными. Семантическое кэширование позволяет повторно использовать ранее полученные результаты, если новый запрос означает практически то же самое. С использованием Redis такой подход может снизить задержки и расходы на инфраструктуру без необходимости переписывания бизнес-логики приложения.
Скрытые затраты на векторный поиск
Типичная схема работы метода RAG выглядит следующим образом:
User Query
↓
Embedding API
↓
Vector Database
↓
Retrieved Documents
↓
LLM
Каждый новый запрос обычно подразумевает:
- вызов модели генерации векторов
- оплату задержек в сети
- проведение векторного поиска
- возврат найденных результатов
Вызовы модели генерации векторов стоят денег, а их многократное выполнение тратит время.
Рассмотрим два поиска, сделанных с интервалом в две минуты:
- “лучший игровой ноутбук бюджетного класса”
- “дешевые игровые ноутбуки”
Формулировки отличаются; намерения практически не меняются. Тем не менее многие системы пересчитывают оба запроса с нуля. Учитывая тысячи пользователей, платформа неоднократно оплачивает почти одинаковую работу.
Что такое семантическое кэширование?
Классические кэши хранят ключи в виде точных строк. Если сохранить кэш с фразой “красные туфли”, будет найдена только идентичная фраза; выражение “туфли красного цвета” не будет найдено.
Семантическое кэширование сравнивает значение. Каждый запрос преобразуется в векторное представление. Когда два вектора становятся достаточно близкими, можно повторно использовать сохраненный ответ.
Например:
Query A
"best beginner Python book"
↓
Embedding A
Позже:
Query B
"good Python books for beginners"
Если косинусное сходство превышает установленный порог — скажем, 0.95 — система возвращает кэшированный результат. Не требуется новый запрос для генерации вектора, нет необходимости в поиске с использованием новых векторов, и не появляется дополнительная статья расходов.
Создание двухуровневой кэш-системы
В производственных решениях часто используется несколько кэшей.
Уровень 1: кэш встроенных представлений
Самый быстрый уровень. Один и тот же строковый запрос возвращает уже сохраненное представление вместо повторного вызова модели.
Query
↓
Redis
↓
Embedding
Уже это позволяет избежать одного из самых распространенных платных вызовов в приложениях искусственного интеллекта.
Уровень 2: кэш семантических результатов
На втором уровне хранятся окончательные ответы на поисковые запросы. Сопоставление осуществляется по соседству векторов, а не по тексту.
Embedding
↓
Redis Vector Search
↓
Cached Search Results
Близкое соседство означает, что векторная база данных игнорируется, и пользователь получает почти мгновенный ответ из кэша.
Почему Redis?
Redis — это быстрая памятная база данных. Благодаря Redis Stack и RediSearch он также может выполнять поиск по сходству векторов.
Это сочетание позволяет Redis:
- хранить представления векторов
- сравнивать векторы
- возвращать ближайшие соседние векторы
Кэш перестаёт быть простым хранилищем в формате «ключ-значение» и превращается в слой семантического поиска.
Выбор подходящего порога схожести
Выбор порога влияет на качество результатов. При значении 0.99 совпадают только почти идентичные данные — высокая точность, меньше результатов. При значении 0.85 увеличивается процент совпадений, но могут появляться несвязанные запросы (например, кроссовки для бега и баскетбольные кроссовки).
Уровень толерантности зависит от области применения. В электронной коммерции часто допускаются более свободные пороги; в медицинских поисках это обычно невозможно. Некоторые системы даже устанавливают разные пороги для отдельных коллекций.
Обновление кэша
Записи в кэше полезны только до тех пор, пока они актуальны. Информация вроде «актуальные цены на iPhone» может устареть за ночь без плана обновления. Два распространённых подхода покрывают большинство случаев.
Истечение срока действия по времени
Укажите TTL, чтобы Redis автоматически удалял записи — это полезно для каталогов, которые часто меняются.
Аннулирование на основе событий
Когда приложение узнает о произошедших изменениях — обновлении запасов, корректировке цен, появлении новых документов или их удалении — оно немедленно удаляет соответствующие ключи, вместо того чтобы ждать истечения TTL.
Как Redis ищет эмбеддинги
Современные версии Redis хранят эмбеддинги в виде векторов с плавающей точкой и индексируют их с помощью таких структур, как:
- FLAT
- HNSW
Новый запрос не проверяет все векторы методом полного перебора; индексы быстро находят близкие кандидаты, поэтому семантический поиск часто завершается за несколько миллисекунд.
Сводка всего процесса
Путь запроса от начала до конца выглядит так:
User Query
↓
Embedding Cache
↓
Hit?
↓
Yes → Use Cached Embedding
No
↓
Embedding API
↓
Redis Semantic Cache
↓
Hit?
↓
Yes → Return Cached Results
No
↓
Vector Database
↓
Store Results in Redis
↓
Return Results
Затратный процесс поиска с использованием эмбеддингов выполняется только в случае неудачного поиска. Все остальные операции остаются быстрыми.
Какого улучшения можно ожидать?
Цифры варьируются в зависимости от продукта, но многие отчеты о производительности выглядят следующим образом:
- общий семантический поиск обычно дает показатель точности в пределах от одной пятой до одной трети
- автодополнение и повторные запросы могут составлять примерно половину всего трафика
- запросы к кэшу обычно отвечают за несколько десятков миллисекунд
- запросы, требующие полного анализа содержимого и поиска от начала до конца, могут занять несколько сотен миллисекунд
Каждый успешный запрос сокращает количество вызовов API для генерации эмбеддингов. При тысячах запросов это приводит к реальной экономии средств и снижению задержек.
Распространенные ошибки
Отравление кэша
Некорректный ответ, попавший в кэш, может повлиять на похожие будущие запросы. Мониторинг в сочетании с разумным временем истечения срока хранения снижает масштабы такого воздействия.
Рост объема памяти
Эмбеддинги имеют большие размеры: по сотням или тысячам чисел с плавающей точкой каждый. Коллекции быстро растут. Перед запуском необходимо установить лимиты памяти и правила удаления данных.
Начало работы с чистого состояния
При новой развертке кэш начинает работу пустым, поэтому первые запросы обходятся дорого. Со временем популярные запросы заполняют кэш; некоторые команды заранее загружают часто используемые запросы для сокращения времени запуска.
Когда семантический кэширование — не лучший выбор
Этот подход эффективен, когда множество пользователей задают похожие вопросы. Он становится менее эффективным, когда ответы сильно персонализированы или содержат крайне свежую информацию:
- «Рестораны поблизости»
- Персонализированные рекомендации
- Панели управления, адаптированные под пользователя
- Актуальные запасы товаров
- Быстро меняющиеся цены
В таких случаях использование кэшированных семантических ответов может оказаться просто неверным решением.
Заключение
По мере роста объемов трафика, связанного с ИИ, затраты на инфраструктуру часто конкурируют с качеством моделей за внимание разработчиков. Семантическое кэширование — это практичное решение: выявление общей цели позволяет избежать повторной обработки данных и поиска. Redis делает это возможным, сочетая скорость работы в оперативной памяти с встроенным векторным поиском — что обеспечивает более быстрые ответы, меньшее количество платных вызовов для обработки данных и более эффективное масштабирование. Иногда лучшей оптимизацией является не улучшение самой модели, а отказ от операций, которые вообще не требовались к выполнению.