Реализовать graceful degradation при отказе vector DB
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать graceful degradation при отказе vector DB
1. Цель задачи
Разработать механизм graceful degradation для RAG-агента, который при недоступности векторной базы данных (vector DB) продолжает отвечать пользователю, последовательно используя fallback: сначала — кэш ранее найденных документов, затем — вызов LLM без контекста (zero-shot). Это обеспечивает отказоустойчивость: система не падает с ошибкой, а деградирует с контролируемой потерей качества.
Ключевой результат Рабочий код fallback-цепочек (кэш → без контекста) с тестами, демонстрирующими, что система отвечает при отказе vector DB, а качество ответов падает не более чем на 30% по метрике ответа (например, по оценке LLM-as-judge).
2. Исходные данные
Перед началом необходимо иметь:
| Что нужно | Откуда взять |
|---|---|
| Рабочая RAG-система (минимальный прототип) | Пет-проект или пример из документации LangChain/LlamaIndex |
| Vector DB (Qdrant / FAISS / Pinecone) | Docker-образ Qdrant (qdrant/qdrant), либо FAISS локально |
| Кэш (Redis) | Docker-образ redis:7-alpine |
| LLM API (OpenAI / Anthropic / local через Ollama) | API-ключ или модель через Ollama (llama3) |
| Python 3.11+ и менеджер пакетов | Установлены |
Если нет реального инструмента — симулируем:
- Эмуляция отказа vector DB
- Эмуляция кэша
- Synthetic тестовый набор
- 10–20 вопросов с известными ответами (ground truth) для оценки деградации.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.11+ | Реализация логики |
| Vector DB | Qdrant (Docker) / FAISS | Хранение и поиск эмбеддингов |
| Кэш | Redis 7 (Docker) / dict | Хранение результатов предыдущих retrieval |
| LLM | OpenAI API / Ollama (llama3) | Генерация ответа (с контекстом и без) |
| Фреймворк | FastAPI / LangChain | Построение endpoint и цепочек |
| Контейнеризация | Docker, docker-compose | Поднятие vector DB и Redis |
| Тестирование | pytest + pytest-asyncio | Юнит- и интеграционные тесты |
| Мониторинг (опционально) | Prometheus + Grafana | Метрики отказов (fallback_count) |
4. Этапы выполнения
Этап 1: Подготовка окружения и базовая RAG-система (1–1.5 часа)
Действия
- Создать проект с структурой папок:
graceful-degradation-lab/
├── docker-compose.yml
├── src/
│ ├── __init__.py
│ ├── vector_store.py
│ ├── cache.py
│ ├── llm_client.py
│ ├── rag_pipeline.py
│ └── fallback_manager.py
├── tests/
│ ├── __init__.py
│ ├── test_fallback.py
│ └── conftest.py
├── data/
│ └── sample_docs.txt
├── requirements.txt
└── .env
- Запустить vector DB и Redis через docker-compose:
version: '3.8'
services:
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
-
Реализовать минимальный класс VectorStore (на примере Qdrant):
-
Реализовать класс LLMClient:
-
Реализовать базовую RAG pipeline:
embed_query→ vector_store.search →format_context→ llm.generate.
Ожидаемый результат этапа Докер-контейнеры запущены, базовая RAG даёт ответы на тестовые вопросы. Есть простой endpoint /ask (FastAPI) или CLI-команда.
Этап 2: Реализация кэша на Redis (45–60 минут)
Действия
-
Реализовать класс Cache:
-
Интегрировать кэш в RAG pipeline:
- Перед вызовом
vector_store.searchпроверить кэш по вопросу (или его embedding). - Если найден – сразу вернуть документ (увеличить
cache_hit). - Если нет – после успешного поиска в vector DB сохранить результат в кэш (для будущих запросов).
- Перед вызовом
-
Добавить тайм-ауты и обработку ошибок Redis:
- Если Redis недоступен, пропустить кэш (логировать warning).
Ожидаемый результат этапа При повторном вопросе ответ берётся из кэша (значительное снижение latency). Кэш работает при отказе vector DB (если документ уже был закэширован ранее).
Этап 3: Реализация graceful degradation (fallback chain) (1–1.5 часа)
Действия
- Создать класс FallbackManager:
class FallbackManager:
def __init__(self, vector_store, cache, llm):
self.vector_store = vector_store
self.cache = cache
self.llm = llm
async def answer(self, query: str) -> dict:
# Этап 1: попробовать векторную БД
try:
docs = await self.vector_store.search(query)
if docs:
context = self._format_context(docs)
answer = await self.llm.generate(context + query)
return {"source": "vector_db", "answer": answer, "quality_score": 0.9}
except VectorDBUnavailable:
pass
# Этап 2: fallback на кэш
try:
cached_docs = await self.cache.get(query)
if cached_docs:
context = self._format_context(cached_docs)
answer = await self.llm.generate(context + query)
return {"source": "cache", "answer": answer, "quality_score": 0.7}
except CacheUnavailable:
pass
# Этап 3: fallback на LLM без контекста
answer = await self.llm.generate(query) # zero-shot
return {"source": "zero_shot_llm", "answer": answer, "quality_score": 0.5}
-
Логировать каждый fallback-шаг (через
loggingили метрики Prometheus):- Счётчик
fallback_vector_db_to_cache. - Счётчик
fallback_cache_to_zero_shot.
- Счётчик
-
Настроить тайм-ауты для каждого шага:
- Например, ждать векторную БД не более 5 секунд (настраиваемо), иначе считать её недоступной.
-
Обработка ошибок на каждом уровне:
- Если все fallback упали – вернуть сообщение об ошибке (но это крайний случай, задача этого избежать).
Ожидаемый результат этапа При симуляции отказа vector DB система сначала пробует кэш, при провале кэша – LLM без контекста. Ответ всегда есть.
Этап 4: Тестирование и симуляция отказа (1–1.5 часа)
Действия
-
Написать интеграционные тесты (pytest):
- Тест 1: Нормальная работа vector DB (базовый сценарий) – ответ из vector DB.
- Тест 2: Отказ vector DB, кэш пуст – ответ из LLM без контекста.
- Тест 3: Отказ vector DB, кэш содержит документ – ответ из кэша.
- Тест 4: Отказ vector DB, отказ Redis (кэша) – ответ из LLM без контекста.
-
Реализовать фикстуры для симуляции отказов:
@pytest.fixture
def vector_db_failure(monkeypatch):
monkeypatch.setenv("SIMULATE_VECTOR_DB_FAILURE", "true")
yield
monkeypatch.delenv("SIMULATE_VECTOR_DB_FAILURE", raising=False)
-
Измерить качество ответов:
- Подготовить 10 вопросов с golden answer.
- Для каждого уровня fallback оценить ответ с помощью LLM-as-judge (например, попросить GPT-4 оценить по шкале 1-5).
- Убедиться, что средний score:
- vector_db > 4.0
- cache > 3.5
- zero_shot > 3.0
-
Проверить метрики:
- Убедиться, что при отказе vector DB не возникает исключений, возвращающих 500.
Ожидаемый результат этапа Все тесты проходят. Система отказоустойчива.
Этап 5: Документация и финальная проверка (30 минут)
Действия
-
Написать README.md:
- Описание архитектуры fallback chain.
- Инструкция по запуску, симуляции отказов.
- Примеры вывода каждого уровня.
-
Проверить, что код соответствует PEP8 (прогнать flake8 / black).
-
Запустить полный цикл
docker-compose up,SIMULATE_VECTOR_DB_FAILURE=true python main.py "вопрос"- Убедиться в работе.
Ожидаемый результат этапа Готовый репозиторий с чистой документацией.
5. Критерии приемки (Definition of Done)
- Реализован класс
FallbackManagerс цепочкой: vector DB → cache → zero-shot LLM. - При отказе vector DB (симуляция) ответ всегда возвращается (источник: cache или zero-shot).
- При отказе vector DB и пустом кэше ответ приходит от LLM без контекста (zero-shot).
- Кэш на Redis корректно сохраняет и возвращает документы (с TTL).
- Написаны минимум 4 интеграционных теста (pytest), покрывающих основные сценарии отказов.
- Все тесты проходят без ошибок.
- Добавлены метрики/логи, позволяющие отслеживать количество fallback-срабатываний.
- README содержит описание архитектуры и инструкцию по симуляции отказов.
- Код соответствует PEP8, проект структурирован.
6. Ожидаемый результат
Основной артефакт Репозиторий на GitHub (или zip-архив) с кодом, тестами и документацией.
Содержание
src/rag_pipeline.py— основная RAG логика.src/fallback_manager.py— механизм graceful degradation.tests/test_fallback.py— интеграционные тесты.docker-compose.yml— окружение (Qdrant, Redis).README.md— документация.
Опционально
- Дашборд Grafana с метриками fallback (через Prometheus).
- Скрипт для оценки качества (LLM-as-judge) и сравнения трёх уровней.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Трудно симулировать отказ vector DB в тестах | Использовать unittest.mock или env-флаг SIMULATE_VECTOR_DB_FAILURE; проверять в рантайме. |
| Redis может быть недоступен в CI | В тестах использовать redis-mock или in-memory dict, если Redis нет. |
| Оценка качества ответов субъективна | Использовать LLM-as-judge с чёткой шкалой (1-5) и одним и тем же промптом для всех вариантов. |
| Время ожидания vector DB (timeout) может привести к ложно-положительному fallback | Настроить разумный тайм-аут (например, 3-5 секунд) и добавить retry (1-2 попытки) перед fallback. |
| Кэш может содержать устаревшие документы | Использовать TTL, инвалидацию по версии данных. |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Подготовка окружения и базовая RAG | 1–1.5 часа |
| Этап 2: Реализация кэша на Redis | 45–60 минут |
| Этап 3: Реализация graceful degradation | 1–1.5 часа |
| Этап 4: Тестирование и симуляция отказа | 1–1.5 часа |
| Этап 5: Документация и финальная проверка | 30 минут |
| Итого | 4–5 часов |
Примечание: Для первого раза возможно увеличение до 6 часов (из-за отладки docker-compose и Redis).
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 23 | Кэширование результатов инференса LLM |
| 47 | Обработка ошибок в RAG pipeline |
| 82 | Graceful degradation микросервисов |
| 134 | Интерфейсы vector store и их альтернативы |
| 211 | Метрики качества ответов LLM (LLM-as-judge) |
| 278 | Circuit breaker pattern в AI-агентах |
| 315 | Использование Redis для кэша в AI-системах |
| 451 | Timeout и retry стратегии при работе с LLM API |
| 567 | Симуляция отказов внешних сервисов в тестах |
| 689 | Zero-shot и few-shot промпты: плюсы и минусы |
10. Чек-лист самопроверки
- Я последовательно реализовал fallback chain, начиная от vector DB, затем кэш, затем zero-shot.
- Я проверил, что при симуляции отказа vector DB (установка env-переменной) система не падает с 500, а возвращает ответ из fallback источника.
- Я написал минимум один тест, который проверяет сценарий, когда vector DB недоступна, кэш пуст – должен использоваться zero-shot LLM.
- Я убедился, что время отклика при fallback на zero-shot не превышает reasonable limit (например, 10 секунд с учётом LLM).
- Я зафиксировал метрики (через логи или Prometheus), чтобы в production было видно, как часто срабатывают fallback.