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

Что такое «circuit breaker» на уровне меж-агентских вызовов?

Краткий тезис

Circuit breaker (автоматический выключатель) — это паттерн отказоустойчивости, предотвращающий каскадные отказы в распределённых системах. На уровне меж-агентских вызовов он отслеживает ошибки при обращении одного агента к другому и при превышении порога временно блокирует вызовы, давая целевому агенту время на восстановление. Без такого механизма один упавший агент может вызвать лавину таймаутов и исчерпание ресурсов во всей цепочке.


1. Термин: Circuit breaker (автоматический выключатель)

Circuit breaker — это паттерн, заимствованный из электротехники. В программных системах он оборачивает вызов удалённого сервиса (или агента) и переключается между тремя состояниями:

  • Closed (замкнут) — обычный режим: вызовы проходят, ошибки учитываются.
  • Open (разомкнут) — вызовы немедленно завершаются с исключением (или fallback-ответом), не доходя до целевого сервиса.
  • Half-Open (полуоткрыт) — после заданной паузы пропускается ограниченное число пробных запросов, чтобы проверить, восстановился ли сервис.

Цель — не тратить ресурсы на заведомо неудачные вызовы и дать системе стабилизироваться.

Контекст RAG|agentic RAG: агенты вызывают друг друга (например, агент-планировщик вызывает агента-исполнителя). Если агент-исполнитель перегружен или упал, breaker|circuit breaker не даёт планировщику бесконечно ждать и позволяет сразу переключиться на другой агент или вернуть кэшированный ответ.


2. Состояния circuit breaker и их поведение

СостояниеЧто происходитПереход
ClosedВсе вызовы выполняются. Счётчик неудач (или доля ошибок) сбрасывается, если вызов успешен.При превышении порога ошибок → Open
OpenВызовы блокируются (ошибка «circuit breaker open»). Через sleep window (время сна) переходит в Half-Open.По истечении sleep window → Half-Open
Half-OpenПропускается ограниченное число пробных запросов. Если все успешны → Closed, иначе → Open.Успех → Closed; неудача → Open

Пример с настройками:

  • Порог ошибок: 5 неудач за 10 секунд.
  • Sleep window: 30 секунд.
  • Пробных запросов в Half-Open: 3.

3. Триггеры, пороги и параметры

breaker|Circuit breaker реагирует на наблюдаемые сбои. Основные триггеры:

  • Процент ошибок — например, >50% запросов завершились с ошибкой за последнюю минуту.
  • Абсолютное число ошибок — более N ошибок подряд.
  • Таймауты — если запрос не укладывается в заданное время, он засчитывается как ошибка.
  • Исключения — только определённые типы (например, ServiceUnavailable), а не все ошибки (логические ошибки не должны открывать breaker).

Параметры конфигурации:

  • failureThreshold — количество или процент ошибок для открытия.
  • successThreshold — количество успешных вызовов в Half-Open для закрытия.
  • timeout — максимальное время ожидания ответа (превышение = ошибка).
  • sleepWindow — время, в течение которого breaker остаётся открытым перед переходом в Half-Open.

4. Реализации circuit breaker

4.1 Готовые библиотеки

ИнструментЯзык/ПлатформаОсобенности
Resilience4jJavaЛегковесная, модульная, поддержка реактивного программирования.
Hystrix (Netflix, устарел)JavaБыл стандартом, но в maintenance mode.
pybreakerPythonПростая реализация для Python-приложений.
Polly.NETБогатая функциональность, поддержка политик retry, timeout, circuit breaker.
Istio (Envoy)Service Meshcircuit breaker на уровне сети, без изменения кода.

4.2 Пример на Python с pybreaker

import pybreaker
import requests

breaker = pybreaker.CircuitBreaker(
    fail_max=3,          # 3 ошибки подряд -> Open
    reset_timeout=30,    # 30 секунд в Open -> Half-Open
    exclude=[requests.exceptions.Timeout]  # не учитывать таймауты
)

@breaker
def call_agent_b(payload):
    resp = requests.post("http://agent-b:8080/process", json=payload, timeout=5)
    resp.raise_for_status()
    return resp.json()

# Использование
try:
    result = call_agent_b({"query": "как настроить VPN?"})
except pybreaker.CircuitBreakerError:
    # fallback — вернуть кэшированный ответ или обратиться к другому агенту
    result = {"answer": "Агент B временно недоступен. Попробуйте позже."}

4.3 Custom implementation (прототип)

В микросервисной архитектуре можно реализовать на основе redis или in-memory:

import time

class CircuitBreaker:
    def __init__(self, threshold=5, timeout=30):
        self.threshold = threshold
        self.timeout = timeout
        self.failures = 0
        self.last_failure_time = 0
        self.state = "closed"

    def call(self, func, *args, **kwargs):
        if self.state == "open":
            if time.time() - self.last_failure_time >= self.timeout:
                self.state = "half-open"
            else:
                raise CircuitBreakerOpenError

        try:
            result = func(*args, **kwargs)
            if self.state == "half-open":
                self.state = "closed"
                self.failures = 0
            return result
        except Exception:
            self.failures += 1
            self.last_failure_time = time.time()
            if self.failures >= self.threshold:
                self.state = "open"
            raise

5. Применение на уровне меж-агентских вызовов

В агентной системе (Agentic RAG) несколько агентов обмениваются сообщениями. Например:

  • Роутер → направляет запрос к нужному агенту.
  • Агент-планировщик → разбивает задачу на шаги.
  • Агент-исполнитель → вызывает LLM, ищет в базе знаний.
  • Агент-валидатор → проверяет ответ.

Если один из агентов (допустим, поисковый) перегружен, circuit breaker на вызове agent_search.call() мгновенно переключит планировщик на fallback — либо использовать кэш, либо другого поискового агента (если есть реплика), либо вернуть сообщение о недоступности.

Особенности для агентов:

  • Каждый вызов — это, как правило, HTTP/gRPC запрос или вызов очереди сообщений.
  • Idempotency (идемпотентность) важна: повторная отправка запроса после открытия Half-Open не должна приводить к дублированию действия.
  • Circuit breaker должен учитывать не только ошибки сети, но и логические ошибки (например, агент вернул пустой ответ) — по желанию.

6. Fallback и комбинирование с другими паттернами

Circuit breaker редко работает один. Стандартный стек отказоустойчивости включает:

ПаттернНазначениеСочетание
TimeoutОграничить время ожиданияБез таймаута circuit breaker может не сработать (зависшие запросы не учитываются)
RetryПовторить вызов при временной ошибкеRetry ставится перед circuit breaker; после нескольких retry breaker открывается
BulkheadОграничить число одновременных вызововПредотвращает истощение пулов потоков при каскаде
FallbackВернуть запасной ответ при открытом breakerНапример, кэш, статические данные, ответ от другого агента

Пример цепочки:

  1. Вызвать агента с таймаутом 5 с.
  2. Если таймаут или ошибка — retry до 2 раз.
  3. Если retry не помогли — circuit breaker считает ошибку.
  4. При открытом breaker → fallback: вернуть ответ из кэша, помеченного как «возможно устаревший».

7. Преимущества и риски

ПлюсыМинусы
Быстрый отказ (fail-fast)Дополнительная сложность конфигурации
Защита от каскадных отказовВозможно ложное открытие при кратковременных скачках
Уменьшение нагрузки на упавший сервисТребует мониторинга состояний
Возможность graceful degradationНе решает проблему идемпотентности повторных вызовов

8. Тонкости настройки для агентов

  • Порог ошибок должен быть адаптивным: для критичных агентов (например, оплата) можно делать более чувствительным.
  • Sleep window — не слишком короткий (иначе breaker будет часто срабатывать), не слишком длинный (агент может быть готов раньше).
  • Пробные запросы в Half-Open должны быть идемпотентными и лёгкими (можно отправлять heartbeats).
  • Мониторинг: состояния circuit breaker нужно логировать и показывать в дашбордах (например, Prometheus + Grafana).

Пет-проект для закрепления

Задача: Реализовать простую имитацию агентной системы с circuit breaker на Python, где два агента общаются через HTTP.

Инструменты:

  • Python с библиотеками requests, pybreaker, flask (для имитации второго агента).

Шаги:

  1. Создать Flask-сервер agent_b.py, который иногда отвечает с ошибкой 500 (например, 50% запросов).
  2. Создать agent_a.py, который вызывает agent_b с circuit breaker (pybreaker).
  3. Настроить: fail_max=2, reset_timeout=10, exclude=[].
  4. Написать fallback-функцию, возвращающую сообщение "Agent B busy, using cached data".
  5. Запустить оба процесса, послать 20 запросов подряд, наблюдать логи.

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

  • После первых двух ошибок breaker переходит в Open.
  • Следующие запросы (пока не прошло 10 с) сразу получают fallback.
  • Через 10 с breaker переходит в Half-Open, отправляется пробный запрос.
  • Если сервер снова отвечает ошибкой -> breaker снова Open, если успехом -> Closed.

Расширение: добавить ещё одного агента-дублёра и реализовать переключение при открытом breaker.


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

ВопросТема
818Timeout и retry для агентских вызовов
820Backpressure в распределённых AI-системах
822Idempotency в меж-агентской коммуникации
817Bulkhead для изоляции ресурсов агентов
825Graceful degradation и fallback-стратегии
832Мониторинг отказоустойчивости агентной системы

Навигация