Home / Strands 멀티에이전트 운영 / Strands 에러 처리
Note

Strands 에러 처리

실패 모드, 패턴별 에러 처리, 복구 전략

⏱ 35분 116 / 189

멀티에이전트 에러 처리와 복원력

실패 모드, 패턴별 에러 처리, 복구 전략

멀티에이전트를 프로덕션에서 운영한다

에이전트 간 통신·에러 복원·안전 보장 — 운영의 3축

에러 처리와 복원력

5가지 실패 모드 · 내장 안전장치 · 복구 전략 · 노드 훅

PART 3 · 에러 처리와 복원력

에러 처리와 복원력

분산 에이전트 고유의 실패 모드와 단계적 복구 — 장애 격리의 원칙

에러 처리와 복원력

단일 에이전트 장애가 전체 시스템으로 전파되기 전에 — 감지하고, 차단하고, 대체하고, 에스컬레이션합니다

  • 5가지 실패 모드 — 타임아웃·환각·도구 실패·핸드오프 유실·무한 루프
  • SDK 내장 안전장치 — 타임아웃·핸드오프 상한·핑퐁 감지
  • 복구 4단계 — Retry · Circuit Breaker · Fallback · Supervisor Escalation
  • Strands Hooks — 도구 훅(BeforeToolCallEvent) + 노드 훅(BeforeNodeCallEvent)
  • 알림 연동 — EventBridge → SNS
takeaway

실패를 없앨 수는 없습니다 — 실패가 전파되지 않게 격리하고 자동 복구하는 구조가 복원력입니다.

PART 3 · 에러 처리와 복원력

멀티에이전트 5가지 실패 모드

타임아웃 연쇄·환각 전파·도구 실패·핸드오프 유실·무한 루프 — 격리가 필요한 이유

타임아웃 연쇄

에이전트 A가 B를 기다리고, B가 C를 기다리는 체인
하나의 지연이 전체 파이프라인을 블로킹

환각 전파

에이전트 A의 환각 출력을 B가 사실로 받아들여 증폭
멀티에이전트에서 환각이 기하급수적으로 확대

도구 실패

외부 API 장애·스로틀링으로 도구 호출 실패
에이전트가 대체 전략 없이 무한 재시도

핸드오프 유실

에이전트 간 작업 인계 시 컨텍스트 손실
수신 에이전트가 불완전한 정보로 잘못된 판단

무한 루프

에이전트가 동일 도구를 반복 호출하거나 A↔B 핑퐁
max_iterations 없으면 비용 폭발

takeaway

다섯 모드의 공통점은 번진다는 것입니다 — 타임아웃은 체인으로, 환각은 증폭으로, 루프는 비용으로. 그래서 대응의 핵심은 격리입니다.

PART 3 · 에러 처리와 복원력

SDK 내장 안전장치 파라미터

Swarm 생성자·Graph 빌더에 이미 들어 있는 실행 상한 — 직접 구현 전의 1차 방어선

대상파라미터기본값막는 실패 모드
Swarmexecution_timeout900.0초전체 실행 폭주 — 타임아웃 연쇄를 상위에서 차단
Swarmnode_timeout300.0초단일 노드 응답 지연 — 노드만 실패 처리하고 격리
Swarmmax_handoffs · max_iterations각 20무한 루프 — 핸드오프·반복 횟수 상한
Swarmrepetitive_handoff_detection_window · repetitive_handoff_min_unique_agents0 (비활성)A↔B 핑퐁 — 최근 N회 핸드오프 창의 고유 에이전트 수 검사
GraphBuilderset_execution_timeout() · set_node_timeout()미설정Graph 전체·노드별 시간 상한
GraphBuilderset_max_node_executions()미설정Graph 노드 실행 횟수 폭주
takeaway

직접 구현 전에 내장부터 — Circuit Breaker를 짜기 전에 생성자 파라미터 한 줄로 막히는 실패인지 먼저 확인합니다.

PART 3 · 에러 처리와 복원력

복구 전략 4단계

실패 감지부터 Supervisor Escalation까지 단계적 복구 흐름

  1. 1
    Retry (재시도)

    Exponential backoff로 일시적 장애 극복. 모델 호출 스로틀링 재시도는 SDK 기본 내장 — Agent(retry_strategy=ModelRetryStrategy(...)), 기본 max_attempts=6·초기 지연 4초·최대 지연 128초(Python 기준)이며 None을 주면 꺼집니다. is_retryable() 오버라이드로 재시도 대상 예외를 확장할 수 있습니다. 도구 재시도는 AfterToolCallEvent 훅의 retry 플래그로 트리거합니다.

  2. 2
    Circuit Breaker (차단)

    연속 N회 실패 시 회로 차단 → 빠른 실패 반환. half-open 상태에서 주기적으로 복구 확인. 장애가 다른 에이전트로 전파되는 것을 방지합니다.

  3. 3
    Fallback (대체)

    주 경로 실패 시 대체 에이전트 또는 간소화된 응답 반환. 예: GPT 실패 → Claude로 전환, 검색 실패 → 캐시된 결과 사용.

  4. 4
    Supervisor Escalation

    모든 자동 복구 실패 시 Supervisor 에이전트가 개입. 작업을 재분배하거나 사람에게 에스컬레이션. EventBridge → SNS 알림 연동.

takeaway

순서가 곧 전략입니다 — 재시도·차단·대체까지는 자동으로 버티고, 전부 실패했을 때만 사람에게 에스컬레이션합니다.

PART 3 · 에러 처리와 복원력

Retry + Circuit Breaker 구현

Strands Hooks 기반의 자동 복구 패턴 코드

핵심 포인트

HookProvider
훅 클래스의 베이스 — register_hooks(registry)로 이벤트별 콜백 등록 (필수)
BeforeToolCallEvent
도구 호출 직전 — 회로가 열려 있으면 event.cancel_tool 설정으로 도구만 취소 (예외 전파 없이 루프 지속)
AfterToolCallEvent
도구 호출 직후 — event.exception으로 실패 판별, failures에 횟수·시각 집계 (코드에선 등록만 표시)
hooks=[...]
Agent에 훅 주입 — 이 한 줄로 자동 차단 활성화

코드

python
import time
from strands import Agent
from strands.hooks import BeforeToolCallEvent, AfterToolCallEvent, HookProvider

class CircuitBreakerHook(HookProvider):
    def __init__(self, threshold=3, reset_timeout=60):
        self.failures, self.th, self.reset = {}, threshold, reset_timeout

    def register_hooks(self, registry, **kwargs):
        registry.add_callback(BeforeToolCallEvent, self.on_before)
        registry.add_callback(AfterToolCallEvent, self.on_after)  # 실패 집계

    def on_before(self, event):  # 임계 초과면 차단 (reset 경과 후 half-open)
        s = self.failures.get(event.tool_use["name"], {})
        if s.get("count", 0) >= self.th and time.time() - s["last"] < self.reset:
            event.cancel_tool = "circuit open"  # 도구만 취소 — 루프는 지속

agent = Agent(hooks=[CircuitBreakerHook()])
PART 3 · 에러 처리와 복원력

노드 레벨 훅 — 멀티에이전트 이벤트 5종

단일 에이전트 훅 8종에 더해 노드 실행을 가로채는 전용 이벤트 — 노드 단위 승인·차단·계측

핵심 포인트

MultiAgentInitializedEvent
Swarm·Graph 초기화 완료 시 1회 — 오케스트레이션 시작점 계측
NodeCallEvent 쌍
Before/AfterNodeCallEvent — 노드(에이전트) 실행 전후, event.node_id로 어느 노드인지 식별하는 위임 추적의 정본 지점
MultiAgentInvocationEvent
Before/After 쌍 — 멀티에이전트 호출 전체의 전후, 실행 단위 비용·시간 집계
cancel_node
BeforeNodeCallEvent에서 설정 — 해당 노드만 취소. event.interrupt()로 실행 전 사람 승인 대기도 가능
hooks=[...]
Swarm·Graph 생성자에 그대로 주입 — 단일 에이전트 훅과 같은 등록 방식

코드

python
from strands import Agent
from strands.multiagent import Swarm
from strands.hooks import HookProvider, BeforeNodeCallEvent

class NodeApprovalHook(HookProvider):
    def register_hooks(self, registry, **kwargs):
        registry.add_callback(BeforeNodeCallEvent, self.on_node)

    def on_node(self, event):
        # 배포 노드만 실행 전 검사 — 거부되면 그 노드만 취소
        if event.node_id == "deploy" and not approved(event.node_id):
            event.cancel_node = "승인 거부 — deploy 노드 차단"

# 단일 에이전트와 같은 hooks= — Swarm/Graph 생성자 지원
swarm = Swarm(nodes=[research, write, deploy],
              hooks=[NodeApprovalHook()])
PART 3 · 에러 처리와 복원력

운영 장면 — 장애에서 복구까지

리서치 Swarm의 요약 노드 타임아웃부터 session_manager 재개까지 — 안전장치가 개입하는 순서








research-swarm — 실행 로그 (예시 장면)
execution_timeout 900s · node_timeout 300s



14:02:11
WARN
summarizer 노드 응답 지연
대형 문서 요약에서 반복 무응답 — Swarm 전체가 이 노드를 기다리며 블로킹


14:07:11
AUTO
node_timeout=300.0 발동
SDK가 노드 실행을 강제 종료하고 실패로 기록 — 직접 짠 감시 코드 없이 개입


14:07:38
STOP
핑퐁 감지 — 안전 종료
researcher ↔ summarizer 왕복이 감지 창(detection_window) 안에서 고유 에이전트 2개뿐 — 재시도 루프 차단


14:09:40
RESUME
session_manager로 중단 지점 재개
serialize_state가 영속화한 상태·실행 이력을 deserialize_state로 복원 — 완료된 리서치 결과는 다시 돌리지 않음, shared context도 직렬화/역직렬화 시 보존




상한은 생성자 파라미터가 지키고, 재개는 session_manager가 맡습니다 — 장애 대응 코드보다 상태 영속화가 먼저입니다


takeaway

복구의 핵심은 처음부터 다시가 아니라 중단 지점부터입니다 — 상태가 영속화되어 있어야 상한 파라미터가 안심하고 실행을 끊을 수 있습니다.

멀티에이전트는 만드는 것보다 운영이 어렵습니다.

통신 · 에러 · 안전장치 — 세 축을 잡아야 프로덕션에서 살아남습니다