k
korAI
고급 전체
🔥 고급2026-07-066~8분

멀티 에이전트 오케스트레이션 실패 모드: 루프 감지·컨텍스트 폭발·책임 추적

멀티 에이전트 시스템에서 발생하는 무한 루프, 컨텍스트 토큰 폭발, 책임 소재 불명확 문제를 수치 기반으로 진단하고 프로덕션 수준의 방어 설계를 제시한다.

multi-agentobservabilityreliability

왜 멀티 에이전트는 단일 에이전트보다 위험한가

단일 에이전트는 실패 경로가 선형이지만, 멀티 에이전트는 에이전트 간 메시지가 순환 참조를 만들 수 있다. 실제 프로덕션 사례에서 오케스트레이터가 서브 에이전트의 실패 응답을 재시도 신호로 해석해 평균 23회 루프 후 컨텍스트 한도(200k 토큰)를 소진한 케이스가 보고된다. 비용은 단일 요청 대비 최대 40배 폭증한다.

핵심 실패 모드 3가지:

  1. 루프(Loop): 에이전트 A → B → A 재호출 사이클
  2. 컨텍스트 폭발: 각 홉마다 전체 히스토리를 첨부해 토큰이 기하급수 증가
  3. 책임 공백: 서브 에이전트가 오류를 반환해도 오케스트레이터가 출처를 기록하지 않아 디버깅 불가

방어 설계: 루프 감지 + 슬림 컨텍스트 패싱

import anthropic
import hashlib
from collections import defaultdict

client = anthropic.Anthropic()

class AgentOrchestrator:
    def __init__(self, max_hops: int = 8):
        self.max_hops = max_hops
        self._call_fingerprints: dict[str, int] = defaultdict(int)
        self._audit_trail: list[dict] = []

    def _fingerprint(self, agent_id: str, task_summary: str) -> str:
        # 동일 에이전트에 동일 태스크 재진입 감지
        return hashlib.sha256(f"{agent_id}:{task_summary[:120]}".encode()).hexdigest()[:16]

    def call_agent(self, agent_id: str, system: str, task: str, hop: int = 0) -> str:
        if hop >= self.max_hops:
            raise RuntimeError(f"Max hops ({self.max_hops}) exceeded — possible loop")

        fp = self._fingerprint(agent_id, task)
        self._call_fingerprints[fp] += 1
        if self._call_fingerprints[fp] > 2:  # 동일 지문 3회 이상 = 루프
            raise RuntimeError(f"Loop detected: agent={agent_id}, fingerprint={fp}")

        response = client.messages.create(
            model="claude-opus-4-5",
            max_tokens=1024,
            system=system,
            messages=[{"role": "user", "content": task}],
        )
        result = response.content[0].text

        # 책임 추적: 에이전트 ID, 홉 번호, 입출력 요약 기록
        self._audit_trail.append({
            "hop": hop,
            "agent": agent_id,
            "input_tokens": response.usage.input_tokens,
            "output_tokens": response.usage.output_tokens,
            "result_preview": result[:80],
        })
        return result

    def get_audit(self) -> list[dict]:
        return self._audit_trail

슬림 컨텍스트 패싱 원칙: 서브 에이전트에 전체 히스토리를 넘기지 말고 요약 + 현재 태스크만 전달한다. 히스토리 전체를 넘길 경우 3홉 기준 평균 입력 토큰이 2.8배 증가한다.

운영 체크리스트

  • [ ] max_hops 값을 태스크 복잡도별로 분리 설정 (단순 Q&A: 4, 코드 생성: 8, 연구: 12)
  • [ ] 핑거프린트 기반 루프 감지를 모든 재귀 진입점에 적용
  • [ ] 오케스트레이터 레벨에서 audit_trail을 외부 저장소(예: Postgres)에 비동기 flush
  • [ ] 에이전트별 토큰 사용량을 집계해 이상치 알림 임계값 설정 (예: 단일 에이전트 >50k 토큰 시 PagerDuty)
  • [ ] 서브 에이전트 응답에 DONE / NEEDS_CLARIFICATION / ERROR 등 구조화된 상태 코드를 강제해 오케스트레이터의 재시도 판단 로직을 명확화
  • [ ] 프로덕션 배포 전 루프 시나리오 시뮬레이션 테스트를 CI에 포함