Настроить 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 |
Если нет реального инструмента — симулируем:
- Вместо Redis используем Python-словарь с флагами
expires_atдля каждого ключа (семантический ключ — нормализованный эмбеддинг запроса). - Для генерации запросов пишем скрипт, который создаёт список из 1000 запросов: 200 уникальных “горячих” (каждый повторён 10 раз) и 800 уникальных “редких” (каждый встречается 1–2 раза).
- Вычисление семантической близости: cosine similarity через numpy.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык | Python 3.10+ | Реализация логики кэша |
| Embedding model | sentence-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 мин)
Действия
- Определить структуру кэш-записи:
{key: (embedding_vector, answer, expires_at, is_hot)}, где ключ — строка запроса. - Разработать алгоритм определения “горячности” на основе частоты обращений за последние N запросов (скользящее окно размером 200).
- Спроектировать функцию
get(query) → answer_or_none: вычисляется эмбеддинг запроса → ищется ближайший по cosine similarity (>0.9) среди кэшированных → если найден и не истёк TTL → возвращается ответ; иначе None. - Спроектировать функцию
put(query, answer): вычисляется эмбеддинг, определяется is_hot по частоте, назначается TTL (1 мин для hot, 1 час для rare), сохраняется.
Ожидаемый результат этапа Документированная схема (псевдокод + диаграмма) функций get и put.
Этап 2: Реализация базового кэша (2 часа)
Действия
- Загрузить embedding-модель:
model = SentenceTransformer('all-MiniLM-L6-v2'). - Реализовать класс
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.
- Написать вспомогательный скрипт
generate_dataset.pyдля создания 1000 запросов с распределением 20% hot / 80% rare.
Ожидаемый результат этапа Работающий класс SemanticCache с методами get и put, протестированный на 10–20 запросах.
Этап 3: Настройка TTL и мониторинг hit rate (1 час)
Действия
- Интегрировать таймер: в
putзаписыватьexpires_at = time.time() + (60 if is_hot else 3600). - В
getпроверятьtime.time() < expires_at; иначе удалять запись. - Реализовать цикл эмуляции:
- Прогнать все 1000 запросов последовательно.
- На каждом запросе:
get, если промах →put(имитируем получение ответа от LLM). - Собирать статистику: количество hit, miss, текущий hit rate каждые 100 запросов.
- Вывести итоговый hit rate.
Ожидаемый результат этапа Hit rate вычислен; если <40% — переходим к этапу 4.
Этап 4: Оптимизация для достижения hit rate ≥ 40% (1–2 часа)
Действия
- Проанализировать причины низкого hit rate:
- Слишком высокий порог similarity? (уменьшить до 0.85)
- Слишком узкое окно для определения hot? (расширить до 500)
- Hot-запросы получают короткий TTL и быстро вытесняются, хотя должны оставаться? (увеличить TTL для hot до 2 минут)
- Внести изменения:
- Поэкспериментировать с порогом similarity (0.80–0.95).
- Изменить размер окна (100, 200, 500).
- Добавить механизм “renewal”: при попадании по hot-запросу обновлять TTL ещё на 1 минуту.
- Повторно прогнать эмуляцию и замерить hit rate.
- Зафиксировать оптимальные параметры в конфигурационном файле (YAML).
Ожидаемый результат этапа Hit rate ≥ 40% на тестовом наборе.
Этап 5: Тестирование и документирование (30 мин)
Действия
- Написать
pytest-тесты:- Проверка, что hot-запрос имеет TTL 1 мин, редкий — 1 час.
- Проверка, что кэш возвращает None после истечения TTL.
- Проверка, что hit rate на сгенерированном наборе ≥ 40% (с заданными параметрами).
- Проверка, что при одинаковых запросах возвращается один и тот же ответ.
- Написать README с описанием алгоритма, параметров и результатов.
- Оформить код в виде модуля
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 | Экспоненциальное затухание частоты запросов |
| 145 | Hit rate, miss rate, latency в кэшах |
| 260 | Настроить TTL для semantic cache (текущая задача) |
| 310 | Стратегии инвалидации кэша (write-through, lazy) |
| 420 | Использование sentence-transformers для семантического поиска |
| 515 | Cache 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 содержит инструкцию по запуску.