Как тестировать fallback и graceful degradation?
Краткий тезис
Тестирование fallback (запасных сценариев) и graceful degradation (корректной деградации) — это процесс верификации того, что агентная RAG-система сохраняет работоспособность при отказах отдельных компонентов (LLM API, векторной базы данных, внешних инструментов) и при перегрузках. Основная стратегия — симулировать отказы, измерять метрики (например, долю успешных ответов, задержки, уровень эскалации) и сравнивать с эталонным поведением. Цель — гарантировать, что система не ломается целиком, а либо переключается на резервный компонент, либо выдает осмысленное сообщение об ошибке (graceful degradation), либо передает управление человеку (fallback).
1. Определения: Fallback и Graceful Degradation
- Fallback (запасной сценарий) — это автоматическое переключение на альтернативный компонент или последовательность действий при отказе основного. Например, если LLM API возвращает 5xx, агент пытается подключиться к другому провайдеру или переходит в режим «только поиск по ключевым словам».
- Graceful degradation (корректная деградация) — это сохранение части функциональности при снижении качества одного из компонентов. Система не падает целиком, а возвращает урезанный, но все еще полезный ответ (например, только извлеченные документы без генерации, если LLM перегружен).
- Resilience (устойчивость) — общая способность системы восстанавливаться после сбоев. Fallback и graceful degradation — две ключевые стратегии для достижения resilience.
2. Почему это важно для Agentic RAG
Агентная RAG-система состоит из нескольких звеньев: роутер запросов, LLM-агент, векторная база данных, инструменты (API, калькуляторы, базы знаний). Каждое звено может отказать:
- LLM API — задержки, таймауты, «галлюцинации» (garbage response).
- Векторная БД — недоступность индекса, медленный поиск, поврежденные чанки.
- Инструмент — внешний сервис не отвечает, возвращает ошибку.
- Rate Limits — превышение квот, перегрузка.
Без тестирования fallback и graceful degradation система рискует:
- терять пользователей из-за полного отказа (hard failure);
- давать неверные ответы при деградировавшем LLM;
- тратить ресурсы на бесконечные ретраи.
Тестирование помогает выявить неочевидные каскадные отказы (например, падение векторной БД приводит к бесконечному циклу ретраев без fallback).
3. Классификация сценариев отказов
| Компонент | Тип отказа | Ожидаемое graceful degradation | Ожидаемый fallback |
|---|---|---|---|
| LLM API | HTTP 500 / Timeout / Garbage response | Вернуть ответ «Извините, сервис временно недоступен»; или ответ без генерации (только поиск) | Переключиться на другой LLM-провайдер (если есть) или отправить запрос человеку (human-in-the-loop) |
| Векторная БД | Connection refused, corruption | Поиск по кэшу локальных эмбеддингов или fallback на keyword search (BM25) | Вернуть пустой контекст с пояснением «Поиск временно недоступен» |
| Внешний инструмент | API 503, невалидный ответ | Использовать кэшированные данные инструмента (если есть), или пропустить шаг, вернув частичный ответ | Вызвать другой аналогичный инструмент (например, погода от OpenWeatherMap вместо AccuWeather) |
| Rate limit / Overload | 429 / Retry-After | Увеличить задержку (backoff), ставить запрос в очередь с приоритетом | Понизить качество (меньше кандидатов, тримминг контекста); отправлять часть запросов в batch |
4. Метрики для оценки деградации
При тестировании фиксируем как общие метрики, так и метрики для каждого сценария.
| Метрика | Формула / описание | Хорошее значение при graceful degradation |
|---|---|---|
| Success Rate | (успешные ответы) / (все запросы) | > 95 % даже при отказе одного компонента |
| Error Rate | (ответы с ошибкой) / всего | < 5 % (ошибки должны быть только при множественных отказах) |
| Fallback Activation Rate | доля запросов, где пришлось использовать fallback | Зависит от частоты отказов; важно, чтобы fallback не срабатывал на каждый запрос (иначе плохая основная система) |
| Mean Time to Fallback | среднее время, через которое система переключается на запасной вариант | < 2 секунд (иначе пользователь уйдет) |
| Degradation Impact | на сколько ухудшилось качество ответа (оценивается по faithfulness, relevance) по сравнению с безотказным режимом | Не более 20 % ухудшения метрик качества |
5. Методика тестирования (пошагово)
5.1 Инвентаризация компонентов и точек отказа
Составьте карту всех сервисов, от которых зависит агент: LLM endpoint, векторный индекс, инструменты, кэш, база данных сессий. Для каждого определите типы отказов (сеть, таймаут, ошибочный ответ, перегрузка).
5.2 Создание тестовых сценариев
Для каждого типа отказа напишите:
- Название сценария (например, «LLM API возвращает 500 после 3 попыток»)
- Ожидаемое поведение: какой fallback должен активироваться, какой ответ увидит пользователь.
- Пороговые времена (timeout для первого запроса, интервал retry).
5.3 Симуляция отказов (Mocking и Chaos Engineering)
- Mocking: при unit-тестах заменяйте реальные вызовы на имитацию (mock) с нужным поведением (поднять исключение, вернуть garbage).
- Chaos Engineering: для интеграционных/end‑to‑end тестов используйте инструменты вроде Chaos Monkey или вручную блокируйте порты, внедряйте задержки через tc (traffic control), запускайте контейнеры с высокой нагрузкой.
- Stub-серверы: разверните локально stub для LLM (например, WireMock), который на определенные запросы отвечает 500 или мусором.
5.4 Написание тестов (py.test пример)
import pytest
import requests
from unittest.mock import patch
from my_agent import Agent
@pytest.fixture
def agent():
return Agent(primary_llm_url="http://mock-llm:8000", fallback_llm_url="http://fallback-llm:8001")
# Сценарий: LLM primary возвращает 500
def test_fallback_on_llm_500(agent):
with patch('my_agent.requests.post') as mock_post:
mock_post.side_effect = [
requests.exceptions.RequestException("500 error"), # primary
{"choices": [{"text": "fallback answer"}]} # fallback success
]
result = agent.process("user query")
assert result["status"] == "success"
assert "fallback answer" in result["response"]
assert mock_post.call_count >= 2
# Сценарий: векторная БД недоступна
def test_graceful_degradation_vector_db_down(agent):
with patch('my_agent.vector_store.search') as mock_search:
mock_search.side_effect = ConnectionError("DB unavailable")
result = agent.process("search query")
assert result["status"] == "partial"
assert "keyword search" in result["fallback_method"]
assert result["response"] is not None
5.5 Проверка метрик и логов
Убедитесь, что логи фиксируют:
- время начала и окончания каждого вызова;
- причину отказа (ошибка, таймаут, garbage response);
- активированный fallback;
- итоговый статус (full, degraded, error).
Метрики следует отправлять в Prometheus/Grafana и проверять в автоматическом тесте, что после отказа Success Rate не падает ниже порога.
6. Best Practices для тестирования
- Не переусердствуйте с fallback: частые переключения могут быть хуже, чем явное сообщение об ошибке (например, если fallback-LLM дает еще худший ответ). Устанавливайте четкие критерии активации.
- Тестируйте timeout'ы: часто fallback срабатывает только после таймаута. Используйте asyncio или ThreadPoolExecutor для установки максимального времени ожидания.
- Проверяйте идемпотентность: повторные вызовы при fallback не должны дублировать побочные эффекты (например, повторная отправка email).
- Симулируйте последовательные отказы: что произойдет, если оба LLM и оба инструмента упали? Должна быть эскалация человеку.
- Используйте FMEA (Failure Mode and Effects Analysis): для каждого компонента оценивайте вероятность отказов и тяжесть последствий, чтобы определить, какие fallback нужно тестировать в первую очередь.
7. Инструменты для симуляции отказов
| Инструмент | Назначение | Пример использования |
|---|---|---|
| WireMock / MockServer | Stub HTTP-серверы для имитации LLM/instrument API | Настройка ответа 500 на определенный эндпоинт |
| gremlin | Chaos engineering для Kubernetes | Внезапная остановка поды с векторной БД |
| toxiproxy | Прокси для внесения сетевых задержек и разрывов | Добавить задержки 5 секунд на соединение к инструменту |
| locust | Генерация нагрузки для проверки rate limiting | Имитация превышения лимитов вызовов LLM |
| unittest.mock / pytest-mock | Стандартные mock-библиотеки Python | Замена реальных вызовов на кастомные |
8. Пример комплексного теста graceful degradation
# Проверяем, что при отказе LLM и инструмента, но доступной векторной БД,
# система возвращает частичный ответ (только найденные документы)
def test_graceful_degradation_both_llm_and_tool_down(agent):
with patch('my_agent.llm_client.generate') as mock_llm, \
patch('my_agent.tools.calculate') as mock_tool, \
patch('my_agent.vector_store.search') as mock_search:
mock_llm.side_effect = ValueError("LLM unavailable")
mock_tool.side_effect = TimeoutError("tool timeout")
mock_search.return_value = [{"text": "document content", "score": 0.95}]
result = agent.process("what is 2+2?")
assert result["status"] == "partial"
assert result["response"] == "Я не смог вычислить ответ, но нашел документ: document content."
assert result["fallback_chain"] == ["primary_llm", "tool", "vector_only"]
9. Метрики, которые нужно мониторить в проде
- p50/p95/p99 latency при нормальной работе и при fallback.
- Fallback_rate (доля запросов с активацией fallback).
- Error_distribution (пропорция ошибок по компонентам).
- Human_escalation_rate (если агент не справился и передал человеку).
Эти метрики помогут не только проверить graceful degradation, но и выявить слабые места, которые нужно укрепить (например, слишком частые fallback на инструмент B).
Пет-проект для закрепления
Задача Разработать простого агента для ответа на вопросы о погоде, который использует:
- Primary LLM: OpenAI API.
- Vector DB: FAISS (локально).
- Инструмент: OpenWeatherMap API (с имитацией отказов). Требуется реализовать fallback: при отказе OpenAI переключиться на локальную модель (Hugging Face pipeline); при отказе инструмента — использовать кэш последнего ответа; при отказе FAISS — вернуть ответ «Поиск по документам недоступен». Написать тесты с помощью pytest и mock.
Инструменты Python, pytest, unittest.mock, requests-mock, FAISS, transformers.
Шаги:
- Определите интерфейс агента (process(query) -> dict).
- Реализуйте компоненты с возможностью установки режима (норма/отказ/деградация).
- Напишите 5 тестов:
- LLM primary отказ → fallback.
- VectorDB отказ → graceful degradation.
- Инструмент отказ → кэш.
- Все компоненты отказали → human escalation (вернуть сообщение).
- Превышение rate limit → backoff + очередь.
- Убедитесь, что логи содержат всю цепочку решений.
- Постройте локальный дашборд (Prometheus + Grafana) для визуализации метрик отказов.
Ожидаемый результат Набор скриптов и тестов, демонстрирующих, что агент не падает при отказах компонентов, а переключается на запасные сценарии, а метрики (Success Rate, Fallback Activation Rate) изменяются ожидаемым образом.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 790 | Как обеспечить отказоустойчивость (resilience) в Agentic RAG |
| 789 | Graceful degradation в мультимодальных RAG |
| 792 | Мониторинг и алертинг в Agentic RAG |
| 788 | Реализация fallback для LLM-вызовов |
| 787 | Human-in-the-loop при отказах |
| 794 | Интеграционное тестирование агентов |
Навигация
- Предыдущий: 790
- Следующий: 792
- Индекс: 00. Индекс разборов