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

Как проектировать 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 в тестовой среде и проверять корректность переключений. Пример сценария:

  1. Система в normal.
  2. Отключаем vector DB (например, блокируем порт).
  3. Проверяем, что через 3 неудачных health check система перешла в degraded 1.
  4. Если кэш пуст, через degraded 2.
  5. Восстанавливаем vector DB — возвращаемся в normal.
  6. Проверяем, что ответы снова содержат контекст из 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).

Инструменты

Шаги:

  1. Создать индекс FAISS с несколькими документами (например, 10 текстов).
  2. Написать HealthChecker, который пингует FAISS (пробует выполнить поиск dummy-вектора). Если ошибка — инкрементирует счётчик.
  3. Реализовать RetrievalRouter с тремя режимами: normal, cache, raw_llm.
  4. Кэш заполняется результатами успешных нормальных запросов.
  5. Добавить эндпоинт /health и /chat?query=....
  6. Написать тест: отключить FAISS (удалить файл индекса), проверить, что система отвечает через кэш или LLM с предупреждением в ответе.
  7. Запустить сервис локально и вручную проверить сценарий отказа.

Ожидаемый результат На локальной машине запускается сервис, который при рабочей FAISS даёт ответы с контекстом, а при её отсутствии — ответы без контекста с пометкой "Degraded mode: ответ основан только на знаниях модели". В логах видны алерты при переключении.

Связь с другими вопросами

ВопросТема
7Как уменьшать latency RAG-системы (кэширование)
9Как обновлять документы в существующей RAG-системе
5Как оценивать качество retrieval’а
10Что такое Self-RAG и когда его использовать
4Гибридный поиск (векторный + keyword)
832Как проектировать agentic RAG с fallback-действиями

Навигация