Настроить TTL для semantic cache

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить TTL для semantic cache

1. Цель задачи

Реализовать семантический кэш ответов LLM, в котором время жизни (TTL) кэш-записи зависит от “горячности” запроса: для часто встречающихся (hot topics) TTL = 1 минута, для редких — 1 час. Настроить механизм определения популярности, хранить кэш с разными TTL и добиться hit rate ≥ 40% на тестовом наборе запросов.

Ключевой результат Рабочий прототип semantic cache с динамическим TTL, достигающий hit rate 40% на заданном датасете.


2. Исходные данные

Что нужноОткуда взять
Тестовый набор вопросов (~500–1000) с искусственной популярностьюСгенерировать скриптом: 20% вопросов повторяются 10+ раз (hot), 80% — 1–2 раза (редкие)
Embedding-модель для семантической близостиsentence-transformers/all-MiniLM-L6-v2 (HuggingFace)
Redis (или in-memory симулятор) для хранения кэшаDocker-образ redis:7-alpine или Python-словарь с expiry
Python 3.10+Локальная среда / Google Colab
Базовый RAG-пайплайн (опционально)Пет-проект из задачи 221

Если нет реального инструмента — симулируем:

  1. Вместо Redis используем Python-словарь с флагами expires_at для каждого ключа (семантический ключ — нормализованный эмбеддинг запроса).
  2. Для генерации запросов пишем скрипт, который создаёт список из 1000 запросов: 200 уникальных “горячих” (каждый повторён 10 раз) и 800 уникальных “редких” (каждый встречается 1–2 раза).
  3. Вычисление семантической близости: cosine similarity через numpy.

3. Технологический стек

КомпонентИнструментыНазначение
ЯзыкPython 3.10+Реализация логики кэша
Embedding modelsentence-transformers/all-MiniLM-L6-v2Преобразование запроса в вектор близости
Хранилище кэшаRedis (docker) или dict + heapqХранение пар (эмбеддинг, ответ) с TTL
Частотный мониторcollections.Counter + скользящее окноОпределение hot vs rare запросов
Тестированиеpytest + numpyЗамер hit rate, unit-тесты
Логированиеlogging / tqdmОтслеживание состояния кэша

4. Этапы выполнения

Этап 1: Проектирование архитектуры semantic cache с динамическим TTL (30 мин)

Действия

  1. Определить структуру кэш-записи: {key: (embedding_vector, answer, expires_at, is_hot)}, где ключ — строка запроса.
  2. Разработать алгоритм определения “горячности” на основе частоты обращений за последние N запросов (скользящее окно размером 200).
  3. Спроектировать функцию get(query) → answer_or_none: вычисляется эмбеддинг запроса → ищется ближайший по cosine similarity (>0.9) среди кэшированных → если найден и не истёк TTL → возвращается ответ; иначе None.
  4. Спроектировать функцию put(query, answer): вычисляется эмбеддинг, определяется is_hot по частоте, назначается TTL (1 мин для hot, 1 час для rare), сохраняется.

Ожидаемый результат этапа Документированная схема (псевдокод + диаграмма) функций get и put.


Этап 2: Реализация базового кэша (2 часа)

Действия

  1. Загрузить embedding-модель: model = SentenceTransformer('all-MiniLM-L6-v2').
  2. Реализовать класс SemanticCache:
    • __init__: инициализировать пустой словарь self.store, счётчик self.hit_ratio.
    • _embed(text) → np.ndarray: нормализованный эмбеддинг (L2-normalize).
    • _nearest(embedding, threshold=0.9) → (key, similarity): проход по self.store, поиск по cosine similarity.
    • _compute_hot_flag(query): хранить self.query_history (deque длины 200) последних уникальных запросов; если частота текущего query в истории > порога (например, >3), считать hot.
    • get(query): проверить кэш, если попадание → увеличить hit_count, вернуть answer, иначе None.
    • put(query, answer): сохранить с соответствующим TTL.
  3. Написать вспомогательный скрипт generate_dataset.py для создания 1000 запросов с распределением 20% hot / 80% rare.

Ожидаемый результат этапа Работающий класс SemanticCache с методами get и put, протестированный на 10–20 запросах.


Этап 3: Настройка TTL и мониторинг hit rate (1 час)

Действия

  1. Интегрировать таймер: в put записывать expires_at = time.time() + (60 if is_hot else 3600).
  2. В get проверять time.time() < expires_at; иначе удалять запись.
  3. Реализовать цикл эмуляции:
    • Прогнать все 1000 запросов последовательно.
    • На каждом запросе: get, если промах → put (имитируем получение ответа от LLM).
    • Собирать статистику: количество hit, miss, текущий hit rate каждые 100 запросов.
  4. Вывести итоговый hit rate.

Ожидаемый результат этапа Hit rate вычислен; если <40% — переходим к этапу 4.


Этап 4: Оптимизация для достижения hit rate ≥ 40% (1–2 часа)

Действия

  1. Проанализировать причины низкого hit rate:
    • Слишком высокий порог similarity? (уменьшить до 0.85)
    • Слишком узкое окно для определения hot? (расширить до 500)
    • Hot-запросы получают короткий TTL и быстро вытесняются, хотя должны оставаться? (увеличить TTL для hot до 2 минут)
  2. Внести изменения:
    • Поэкспериментировать с порогом similarity (0.80–0.95).
    • Изменить размер окна (100, 200, 500).
    • Добавить механизм “renewal”: при попадании по hot-запросу обновлять TTL ещё на 1 минуту.
  3. Повторно прогнать эмуляцию и замерить hit rate.
  4. Зафиксировать оптимальные параметры в конфигурационном файле (YAML).

Ожидаемый результат этапа Hit rate ≥ 40% на тестовом наборе.


Этап 5: Тестирование и документирование (30 мин)

Действия

  1. Написать pytest-тесты:
    • Проверка, что hot-запрос имеет TTL 1 мин, редкий — 1 час.
    • Проверка, что кэш возвращает None после истечения TTL.
    • Проверка, что hit rate на сгенерированном наборе ≥ 40% (с заданными параметрами).
    • Проверка, что при одинаковых запросах возвращается один и тот же ответ.
  2. Написать README с описанием алгоритма, параметров и результатов.
  3. Оформить код в виде модуля semantic_cache.py с точкой входа main.py для запуска эмуляции.

Ожидаемый результат этапа Набор тестов (зелёные) и README.


5. Критерии приемки (Definition of Done)

  • Реализован класс SemanticCache с методами get и put, поддерживающий разные TTL для hot и rare запросов.
  • TTL для hot-запросов равен 1 минуте, для rare — 1 час (допустимо ±5%).
  • Определение “горячности” основано на частоте запросов за скользящее окно (размер конфигурируемый).
  • Запуск эмуляции на тестовом наборе (1000 запросов) даёт hit rate ≥ 40%.
  • Все unit-тесты проходят успешно (минимум 4 теста).
  • Код покрыт комментариями и имеет README с инструкцией по запуску.
  • Зависимости перечислены в requirements.txt (или pyproject.toml).
  • Решение не использует внешние API (кроме загрузки модели) — полностью воспроизводимо офлайн.
  • В коде обработаны краевые случаи: пустой запрос, нулевой TTL, переполнение кэша (можно ограничить размер).

6. Ожидаемый результат

  • Главный артефакт Папка semantic_cache_ttl/ с файлами:
    • semantic_cache.py — реализация кэша.
    • generate_dataset.py — генератор тестового набора.
    • main.py — эмуляция и замер hit rate.
    • test_cache.py — unit-тесты.
    • requirements.txt — зависимости.
    • README.md — описание, параметры, результаты.
  • Дополнительно Датасет queries.json (1000 запросов с метками hot/rare) и файл конфигурации config.yaml с оптимальными параметрами.

7. Возможные сложности и их решение

СложностьРешение
Определение similarity: сравнение каждого запроса со всеми записями O(n*m) медленно при большом кэше.Использовать approximate nearest neighbor (FAISS) или ограничить размер кэша (например, last 1000 записей). Для задачи с 1000 запросов достаточно линейного поиска.
Скользящее окно: частота hot-запросов может меняться со временем.Использовать экспоненциально-затухающий счётчик вместо окна (реализовать в 4 этапе как альтернативу).
Cache stampede: если hot-запрос истекает одновременно у многих пользователей.Добавить случайный разброс TTL (jitter) ±10% от базового значения.
Размер эмбеддинговой модели: загрузка 80 МБ может быть ресурсоёмкой.Использовать более лёгкую модель (all-MiniLM-L6-v2 — 80 МБ).
Порог similarity 0.9 может пропускать семантически близкие, но не идентичные запросы.Снизить порог до 0.85, проверить hit rate — если слишком много false positives, вернуть 0.9.

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Проектирование30 мин
Этап 2: Реализация базового кэша2 ч
Этап 3: Настройка TTL и мониторинг1 ч
Этап 4: Оптимизация до hit rate 40%1.5 ч
Этап 5: Тестирование и документирование30 мин
Итого5.5 ч

Примечание: Для первого выполнения может потребоваться до 7 часов из-за отладки.


9. Связанные вопросы из базы знаний

ВопросТема
128Настройка Redis для RAG-кэша
130Экспоненциальное затухание частоты запросов
145Hit rate, miss rate, latency в кэшах
260Настроить TTL для semantic cache (текущая задача)
310Стратегии инвалидации кэша (write-through, lazy)
420Использование sentence-transformers для семантического поиска
515Cache stampede и методы борьбы
620Параметры similarity threshold в semantic cache
730Оценка качества кэша: hit rate / cost trade-off
845Скользящее окно vs экспоненциальное сглаживание для определения hot keys

10. Чек-лист самопроверки

  • Я реализовал класс SemanticCache с методами get и put, которые корректно обрабатывают TTL.
  • В моём решении TTL для hot-запросов составляет 1 минуту, для rare — 1 час (проверено юнит-тестом).
  • Алгоритм определения hot-запросов использует скользящее окно (или экспоненциальный счётчик) и даёт разумные результаты на сгенерированных данных.
  • Я запустил эмуляцию на 1000 запросов и получил hit rate ≥ 40%.
  • Все unit-тесты проходят, код покрыт комментариями на русском/английском, README содержит инструкцию по запуску.