Как проектировать graceful degradation при отказе vector DB?
Краткий тезис
Graceful degradation (плавная деградация) — это проектирование системы таким образом, чтобы при отказе одного из компонентов (в данном случае векторной базы данных) система продолжала работать, пусть и с пониженным качеством, вместо полного падения. В контексте RAG и RAG|Agentic RAG это означает, что при недоступности vector DB система переключается на менее насыщенные режимы: сначала на поиск по кэшу, затем на ответы LLM без внешнего контекста с предупреждением пользователя, и, наконец, на запасной векторный индекс (реплику). Ключевые элементы — мониторинг (health checks), автоматическое переключение режимов, алерты и изоляция отказа.
1. Термин: Graceful degradation
Graceful degradation — свойство системы сохранять функциональность (пусть и урезанную) при выходе из строя части компонентов. Противоположность — полный отказ (crash). В контексте вопроса отказ vector DB (векторной базы данных) — базы данных, хранящей эмбеддинги документов и обеспечивающей поиск по сходству]]. При её отказе RAG-система не может выполнить retrieval, но всё же может сгенерировать ответ, полагаясь на внутренние знания LLM или на закешированные ранее контексты.
Почему это важно для Agentic RAG?
В Agentic RAG система может самостоятельно принимать решения (например, выбирать инструменты для поиска). Если vector DB падает, агент должен корректно обработать ошибку и перейти к альтернативному плану, а не завершиться с исключением.
2. Причины отказа vector DB
- Сетевые проблемы (таймауты, разрывы соединений).
- Перегрузка CPU/памяти на сервере vector DB (например, из-за пикового трафика).
- Ошибки конфигурации или обновления (некорректные индексы, изменения схемы).
- Отказ дисков или реплик в кластере.
- Достижение лимитов (rate limits) для облачных сервисов (Pinecone, Weaviate и др.).
Разработчик должен предусмотреть эти сценарии и заложить механизмы плавной деградации.
3. Режимы graceful degradation
Система проектируется с несколькими режимами, переключение между которыми происходит автоматически на основе health checks.
3.1 Normal (полноценный RAG)
- Условие vector DB доступна, health check успешен.
- Работа запрос → retrieval из vector DB → передача контекста LLM → генерация ответа.
- Производительность максимальное качество.
3.2 Degraded 1 (поиск по кэшу)
- Условие vector DB недоступна, но имеется локальный кэш (например, Redis, in-memory cache), в котором хранятся результаты поиска по самым частым запросам.
- Работа проверка кэша → если есть точное совпадение или ближайший контекст → передача в LLM.
- Качество снижается, так как кэш может быть неполным или устаревшим.
- Стратегия использовать LRU-кэш (Least Recently Used) и TTL (time-to-live) для устаревания.
3.3 Degraded 2 (только LLM без контекста)
- Условие vector DB недоступна и кэш пуст или не содержит подходящего контекста.
- Работа LLM генерирует ответ, полагаясь только на свои внутренние знания. К ответу добавляется предупреждение: «Информация может быть неполной, так как система временно не может получить доступ к внешним источникам».
- Качество низкое, возможно появление галлюцинаций.
3.4 Fallback (запасной векторный индекс)
- Условие основная vector DB недоступна, но есть реплика в другом регионе или другая vector DB (например, переключение с Pinecone на Qdrant).
- Работа автоматическое перенаправление запросов на запасной индекс с теми же данными (возможно, немного устаревшими).
- Качество близко к normal, если реплика синхронизирована асинхронно.
Можно также использовать гибридный fallback: переключиться на keyword search (BM25) как промежуточный этап перед чистым LLM.
4. Health checks и детекция отказа
Health check — регулярная проверка доступности сервиса. Для vector DB обычно используется TCP-проверка, проверка времени ответа, либо выполнение простого запроса (например, ping).
Пример конфигурации
- Интервал: каждые 5 секунд.
- Таймаут: 3 секунды.
- Количество неудачных попыток для перехода в degraded: 3 (т.е. если три раза подряд health check не прошёл, система переключается в Degraded 1).
Код на Python (асинхронный):
import asyncio
import logging
from vector_db_client import VectorDBClient
logger = logging.getLogger(__name__)
class HealthChecker:
def __init__(self, vdb: VectorDBClient, check_interval=5, failure_threshold=3):
self.vdb = vdb
self.interval = check_interval
self.threshold = failure_threshold
self.failures = 0
self.healthy = True
async def check(self):
while True:
try:
# Например, ping до vector DB
await self.vdb.ping()
self.failures = 0
if not self.healthy:
logger.info("Vector DB снова доступна")
self.healthy = True
except Exception as e:
self.failures += 1
logger.warning(f"Health check не удался ({self.failures}/{self.threshold}): {e}")
if self.failures >= self.threshold and self.healthy:
self.healthy = False
logger.error("Vector DB недоступна, переключаемся в degraded")
await asyncio.sleep(self.interval)
5. Проектирование переключения режимов
Нужен центральный Router или RetrievalManager, который на основе статуса health выбирает источник контекста.
Псевдокод Router
class RetrievalRouter:
def __init__(self, vdb, cache, fallback_vdb, llm):
self.vdb = vdb
self.cache = cache
self.fallback_vdb = fallback_vdb
self.llm = llm
self.mode = "normal"
def set_mode(self, mode):
self.mode = mode
# отправляем метрику/алерт
async def retrieve(self, query):
if self.mode == "normal":
try:
return await self.vdb.search(query)
except VDBUnavailable:
self.set_mode("cache")
# можно попробовать переключиться сразу на fallback
return await self.retrieve(query)
elif self.mode == "cache":
docs = await self.cache.get(query)
if docs:
return docs
else:
self.set_mode("raw_llm")
return None
elif self.mode == "fallback":
return await self.fallback_vdb.search(query)
else:
return None
async def answer(self, query):
context = await self.retrieve(query)
prompt = self._build_prompt(query, context, self.mode)
reply = await self.llm.generate(prompt)
return reply, self.mode
Управление возвратом в normal
При восстановлении vector DB система должна автоматически вернуться в normal. HealthChecker устанавливает healthy=True, а Router проверяет это при следующем запросе и переключается обратно.
6. Alerting (оповещение)
При переходе в degraded режим система должна отправлять алерт (логирование, метрики, уведомление в Slack/PagerDuty). Пример:
import prometheus_client
rag_mode_gauge = prometheus_client.Gauge('rag_mode', 'Current RAG degradation mode', ['mode'])
def on_mode_change(old_mode, new_mode):
logging.getLogger(__name__).warning(f"Mode changed: {old_mode} -> {new_mode}")
rag_mode_gauge.labels(mode=new_mode).set(1)
if new_mode != "normal":
# Уведомить дежурного
send_slack(f"⚠️ Vector DB недоступна, переключились в {new_mode}")
send_pagerduty(...)
7. Мониторинг и метрики
Для оценки работы graceful degradation отслеживайте:
- Время ответа в каждом режиме (latency).
- Количество запросов в каждом режиме.
- Процент попаданий в кэш (cache hit ratio).
- Частота переключений (flapping) — если режим постоянно меняется, нужно стабилизировать health check (например, добавить временной лаг перед переключением обратно).
- Количество алертов.
Эти метрики экспортируйте в Prometheus/Grafana.
8. Тестирование graceful degradation
Необходимо проводить Chaos Engineering — искусственно отключать vector DB в тестовой среде и проверять корректность переключений. Пример сценария:
- Система в normal.
- Отключаем vector DB (например, блокируем порт).
- Проверяем, что через 3 неудачных health check система перешла в degraded 1.
- Если кэш пуст, через degraded 2.
- Восстанавливаем vector DB — возвращаемся в normal.
- Проверяем, что ответы снова содержат контекст из retrieval.
Автоматизированные тесты можно писать с помощью pytest и mock'ов:
import pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_degraded_fallback():
vdb = AsyncMock()
vdb.search.side_effect = VDBUnavailable()
cache = AsyncMock()
cache.get.return_value = None
router = RetrievalRouter(vdb, cache, None, llm=AsyncMock())
router.set_mode("normal")
context = await router.retrieve("test")
assert context is None
assert router.mode == "raw_llm"
9. Trade-offs и компромиссы
| Подход | Плюсы | Минусы |
|---|---|---|
| Кэш (Redis) | Быстро, дешево | Только для частых запросов, устаревание данных |
| Только LLM | Просто, не требует дополнительной инфраструктуры | Высокий риск галлюцинаций, низкое качество |
| Запасная vector DB | Почти полное качество | Дорого (нужна реплика), сложность синхронизации |
| Keyword search (BM25) | Не требует эмбеддингов, устойчив к отказам | Ниже качество, чем векторный поиск для семантических запросов |
10. Архитектура на схеме
[User] → [RAG Gateway]
|
[Health Checker] — мониторит vector DB
|
[Router] выбирает:
|
+--------+--------+--------+
| | | |
[normal] [cache] [raw_llm] [fallback]
| | | |
[vectorDB] [Redis] [LLM only] [Replica VDB]
Пет-проект для закрепления
Задача Реализовать простой RAG-сервис на FastAPI, который поддерживает graceful degradation при отказе векторной БД (использовать FAISS как векторный индекс, Redis как кэш, OpenAI API как LLM).
Инструменты
Шаги:
- Создать индекс FAISS с несколькими документами (например, 10 текстов).
- Написать
HealthChecker, который пингует FAISS (пробует выполнить поиск dummy-вектора). Если ошибка — инкрементирует счётчик. - Реализовать
RetrievalRouterс тремя режимами: normal, cache, raw_llm. - Кэш заполняется результатами успешных нормальных запросов.
- Добавить эндпоинт
/healthи/chat?query=.... - Написать тест: отключить FAISS (удалить файл индекса), проверить, что система отвечает через кэш или LLM с предупреждением в ответе.
- Запустить сервис локально и вручную проверить сценарий отказа.
Ожидаемый результат На локальной машине запускается сервис, который при рабочей FAISS даёт ответы с контекстом, а при её отсутствии — ответы без контекста с пометкой "Degraded mode: ответ основан только на знаниях модели". В логах видны алерты при переключении.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 7 | Как уменьшать latency RAG-системы (кэширование) |
| 9 | Как обновлять документы в существующей RAG-системе |
| 5 | Как оценивать качество retrieval’а |
| 10 | Что такое Self-RAG и когда его использовать |
| 4 | Гибридный поиск (векторный + keyword) |
| 832 | Как проектировать agentic RAG с fallback-действиями |
Навигация
- Предыдущий: 830
- Следующий: 832
- Индекс: 00. Индекс разборов