中文翻译暂不可用,显示俄语原文。

Реализовать semantic cache для LLM

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать semantic cache для LLM

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

Реализовать семантический кэш для LLM‑запросов на основе Qdrant и косинусного порога (threshold similarity). Кэш позволяет повторно использовать ответы на семантически похожие запросы, снижая затраты на API и latency. Ключевой результат функционирующий semantic cache с cache hit rate 30–50% на репрезентативном наборе запросов.


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

Что нужноОткуда взять
Qdrant (векторное хранилище)Docker-образ qdrant/qdrant
Набор тестовых запросов (100–500 штук)Синтезировать на основе FAQ, датасетов (например, Natural Questions) или выгрузить из логов реального чата
Embedding модельsentence-transformers/all-MiniLM-L6-v2 (или аналог)
LLM API (OpenAI или любой совместимый)Аккаунт OpenAI / мок-сервер (например, FastAPI, возвращающий фиксированный ответ)
Python 3.10+Установленный интерпретатор
Библиотекиqdrant-client, sentence-transformers, openai, numpy, pandas, matplotlib

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

  1. Нет датасета запросов — сгенерируйте 200 вариаций для 10 сущностей (например, «Какой сегодня курс доллара?», «курс USD сейчас», «доллар к рублю сегодня»), чтобы естественно покрыть различные paraphrase.
  2. Нет LLM API — запустите мок-сервер на FastAPI, который принимает prompt и возвращает заранее заготовленную строку, эмулируя задержку 0.5–1 сек.
  3. Нет Qdrant — используйте Qdrant в Docker: docker run -p 6333:6333 qdrant/qdrant.

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

КомпонентИнструментыНазначение
Векторное хранилищеQdrant (Docker)Хранение эмбеддингов запросов и ответов, поиск по косинусной мере
Embeddingsentence-transformers/all-MiniLM-L6-v2Получение плотных векторных представлений запросов
LLM (для заполнения кэша)OpenAI API / мок-серверГенерация ответа при промахе кэша
ОркестрацияPython, asyncioКэш-клиент (проверка + вставка)
Профилированиеtime, matplotlib, pandasОценка hit rate, latency, экономии

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

Этап 1: Подготовка инфраструктуры и данных (1 час)

Действия

  1. Развернуть Qdrant в Docker:
    docker pull qdrant/qdrant
    docker run -d --name qdrant -p 6333:6333 qdrant/qdrant
    
  2. Установить Python-зависимости: qdrant-client, sentence-transformers, openai, numpy, pandas, matplotlib.
  3. Создать коллекцию llm_cache с параметрами:
    • vectors_config: размерность 384 (all-MiniLM-L6-v2), distance = Cosine
    • optimizers_config: default values
  4. Сгенерировать тестовый датасет запросов (CSV-файл с колонками original_query, variant_query, expected_answer). Варианты должны иметь косинусную схожесть > 0.7 с оригиналом.
  5. Написать функцию get_embedding(text) для превращения текста в вектор.

Ожидаемый результат этапа Работающий Qdrant, коллекция с векторами размерности 384, тестовый датасет в data/test_queries.csv.

Этап 2: Реализация ядра semantic cache (2–3 часа)

Действия

  1. Разработать класс SemanticCache:
    class SemanticCache:
        def __init__(self, host="localhost", port=6333, threshold=0.8, ttl_seconds=86400):
            # инициализация клиента Qdrant, эмбеддера
            # создание коллекции, если не существует
            pass
        def get_cached_response(self, query):
            # получить эмбеддинг запроса
            # search в Qdrant с limit=1, score_threshold=threshold
            # если результат найден и score >= threshold → возвращаем сохранённый ответ
            # иначе → None
            pass
        def store_cache(self, query, response):
            # получить эмбеддинг запроса
            # upsert: точка с payload={“query”: query, “response”: response, “timestamp”: time}
            pass
    
  2. Добавить поддержку TTL (удалять записи старше N секунд через background task или при вставке).
  3. Реализовать асинхронную версию методов для production-нагрузки (опционально — на async-клиенте Qdrant).

Ожидаемый результат этапа Модуль semantic_cache.py, который корректно возвращает None при промахе и ответ из кэша при попадании.

Этап 3: Интеграция с LLM и тест-драйв (1.5 часа)

Действия

  1. Написать функцию get_llm_response(prompt) — вызов OpenAI API (или мока).
  2. Реализовать функцию cached_llm(prompt, cache):
    def cached_llm(prompt, cache):
        cached = cache.get_cached_response(prompt)
        if cached:
            return cached, "cache_hit"
        else:
            response = get_llm_response(prompt)
            cache.store_cache(prompt, response)
            return response, "cache_miss"
    
  3. Провести прогон на 100 запросах (50 оригинальных + 50 paraphrase), замерить:
    • число cache hit / miss
    • время ответа (latency) для hit и miss
    • корректность: сравнить ответы из кэша с «эталонными» (для paraphrase – с ответом на оригинал, считается OK, если content схож >80% по BLEU или LLM-as-judge)

Ожидаемый результат этапа Запуск на тестовых данных; видно, сколько запросов сэкономлено.

Этап 4: Оценка метрик и тюнинг threshold (1.5 часа)

Действия

  1. Прогнать весь тестовый датасет с разными значениями threshold: 0.7, 0.75, 0.8, 0.85, 0.9.
  2. Для каждого порога вычислить:
    • Cache hit rate = hits / total_requests * 100%
    • Accuracy = доля случаев, когда ответ кэша совпадает с правильным (на paraphrase — проверка семантики)
    • Average latency (ms) при hit vs miss
  3. Построить график зависимости hit rate от threshold.
  4. Выбрать порог, который даёт hit rate 30–50% при accuracy > 95%.

Ожидаемый результат этапа Таблица метрик, график, выбранный threshold.

Этап 5: Документация и отчёт (1 час)

Действия

  1. Написать краткую документацию к модулю (README или docstring) – как запустить, параметры, пример.
  2. Сформировать отчёт с метриками:
    Отчёт: Semantic Cache Evaluation
    - Threshold: 0.82
    - Cache hit rate: 38%
    - Miss latency: 1200 ms
    - Hit latency: 10 ms (включая поиск в Qdrant)
    - Estimated cost savings: 38% (при цене $0.01/запрос экономия ~$0.0038/test_run)
    
  3. Включить графики в отчёт (PNG или встроенные).

Ожидаемый результат этапа Пакет semantic_cache/ с кодом, требованиями, примером запуска и отчётом (Jupyter notebook или PDF).


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

  • Qdrant поднимается одной командой docker compose up (или docker run).
  • SemanticCache корректно сохраняет и находит запросы по косинусной близости.
  • При наличии семантически похожего запроса (≥ threshold) возвращается ответ из кэша.
  • Cache hit rate на синтезированном датасете составляет 30–50% при threshold, обеспечивающем accuracy ≥ 95%.
  • Latency при cache hit ≤ 50 мс (на локальной машине).
  • Реализован автоматический TTL (устаревшие записи не возвращаются).
  • Код покрыт докстрингами и содержит пример использования (examples/demo.py).
  • Отчёт содержит таблицу метрик для 3–5 значений threshold и график hit rate vs accuracy.

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

Основной артефакт Папка semantic-cache/ с:

  • semantic_cache.py — класс SemanticCache
  • requirements.txt
  • data/test_queries.csv
  • examples/demo.py — скрипт для тестового прогона
  • report.ipynb — Jupyter notebook с вычислением метрик и графиками

Дополнительно

  • docker-compose.yml (опционально, для единого подъёма Qdrant)
  • README.md с инструкцией по запуску и описание параметров (threshold, TTL)

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

СложностьРешение
Qdrant не отвечает на localhostПроверить, что контейнер запущен (docker ps); порт 6333 проброшен; использовать client.http=True
Embedding модель медленно загружаетсяЗагружать модель один раз при старте (Singleton); использовать GPU, если доступен
Слишком низкий hit rate — запросы не похожиУменьшить threshold (но не ниже 0.7); улучшить качество paraphrase в датасете
Слишком высокий hit rate — кэш выдаёт нерелевантные ответыУвеличить threshold до 0.85–0.9; добавить проверку на payload-фильтр (например, категорию запроса)
Нарушение TTLРеализовать recreate_index или фильтр при search по полю timestamp
Параллельные запросы: гонка данныхИспользовать опцию wait=True при upsert; рассмотреть блокировку на уровне in-memory, если нужна строгая консистентность

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

ЭтапВремя
1. Подготовка1 час
2. Реализация ядра2–3 часа
3. Интеграция и тест-драйв1.5 часа
4. Оценка метрик и тюнинг1.5 часа
5. Документация и отчёт1 час
Итого7–8 часов

Примечание для первого раза добавьте 2 часа на настройку окружения (Docker, зависимости) и отладку threshold.


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

ВопросТема
42Стратегии кэширования для LLM
67Векторные базы данных (Qdrant, Weaviate, Milvus)
89Sentence embeddings и cosine similarity
134Снижение latency в RAG-системах
201Cost optimization для OpenAI API
218TTL и eviction policy в кэшах
307Оценка качества paraphrase
411Интеграция Qdrant с Python
522Batch-обработка эмбеддингов
678A/B тестирование порогов similarity

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

  • Я развернул Qdrant и убедился, что клиент подключается (client.get_collections() не падает).
  • Я выбрал threshold и проверил на 10 ручных парах запросов, что кэш работает ожидаемо.
  • Я замерил hit rate на полном датасете — результат между 30% и 50%.
  • Я проверил, что при cache hit latency не превышает 50 мс.
  • Я добавил TTL и убедился, что старые записи не возвращаются (проверка через time.sleep после вставки).