프로덕션 에이전트는 반드시 실패한다
LLM API 타임아웃, 툴 오류, 무한 루프 — 잘 설계된 에이전트도 프로덕션에서 예상 밖의 방식으로 실패합니다. 이때 시스템 전체가 다운되지 않도록 복구 아키텍처가 필요합니다.
LLM 에이전트 장애 유형
| 유형 | 예시 | 빈도 | 영향도 |
|---|---|---|---|
| API 타임아웃 | OpenAI 30s 초과 | 중간 | 높음 |
| 모델 거부 | 콘텐츠 정책 위반 | 낮음 | 중간 |
| 툴 오류 | DB 연결 실패 | 높음 | 높음 |
| 무한 루프 | 같은 툴 반복 호출 | 낮음 | 치명 |
| 컨텍스트 초과 | 토큰 한도 초과 | 중간 | 중간 |
Pattern 1: Circuit Breaker
반복 실패를 감지하면 해당 경로를 자동 차단해 전체 시스템을 보호합니다.
Python
from enum import Enum
from datetime import datetime, timedelta
class CircuitState(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class AgentCircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.state = CircuitState.CLOSED
self.failure_count = 0
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.last_failure_time = None
async def call(self, agent_fn, *args, **kwargs):
if self.state == CircuitState.OPEN:
if self._should_recover():
self.state = CircuitState.HALF_OPEN
else:
raise Exception("Circuit breaker OPEN")
try:
result = await agent_fn(*args, **kwargs)
self.failure_count = 0
self.state = CircuitState.CLOSED
return result
except Exception:
self.failure_count += 1
self.last_failure_time = datetime.now()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
print(f"서킷 OPEN: {self.failure_count}회 연속 실패")
raise
def _should_recover(self):
elapsed = datetime.now() - self.last_failure_time
return elapsed > timedelta(seconds=self.recovery_timeout)Pattern 2: Fallback 체인
메인 경로 실패 시 대안을 순차적으로 시도합니다.
Python
async def query_with_fallback(query: str) -> str:
chain = [
lambda q: claude_opus(q), # 1순위: 최고 품질
lambda q: claude_sonnet(q), # 2순위: 빠른 모델
lambda q: cached_response(q), # 3순위: 캐시
lambda q: rule_based(q), # 4순위: 규칙 기반
]
for i, fn in enumerate(chain):
try:
return await asyncio.wait_for(fn(query), timeout=10.0)
except Exception as e:
if i > 0:
print(f"Fallback level {i} 사용")
return "잠시 후 다시 시도해주세요."Pattern 3: Graceful Degradation
전체를 끄는 대신 핵심 기능만 유지합니다.
Python
class DegradedService:
def __init__(self):
self.features = {
"tool_use": True,
"memory": True,
"personalization": True
}
async def handle(self, request):
if not self.features["tool_use"]:
return await self.knowledge_only(request)
return await self.full_agent(request)
def disable(self, feature: str):
self.features[feature] = False
print(f"기능 비활성화: {feature}")무한 루프 방지
Python
MAX_STEPS = 15
async def safe_run(agent, query):
steps = 0
async for step in agent.stream(query):
steps += 1
if steps >= MAX_STEPS:
return {
"result": step.partial_result,
"warning": f"최대 스텝({MAX_STEPS}) 도달 — 중간 결과 반환"
}
return step.final_result핵심 운영 지표
- MTTR: 장애 후 복구까지 평균 시간 → 목표 < 30초
- Circuit Open Rate: 전체 요청 중 차단 비율 → 경고 > 1%
- Fallback Rate: 폴백 실행 비율 → 모델별 추적
이 3편 시리즈로 에이전트 신뢰성의 3층 방어(비결정성 제어→테스트→장애 복구)가 완성됩니다.
SLA 예시
| 지표 | 목표 예 |
|---|---|
| 작업 성공률 | 99% / 30일 |
| p95 지연 | < 8s (툴 포함) |
| 예산 | 일 토큰 $X |
TEXT
trace: request_id → llm_calls → tool_calls → outcome알람 우선순위
- 성공률 급락 / 툴 전면 실패
- 비용 스파이크
- 정책 위반 급증
SLA 위반이 났을 때 원인을 분리하는 절차
에이전트 SLA 위반은 우리 코드, LLM 공급사, 외부 도구 API 중 어디서 생겼는지 빠르게 나눠야 대응이 가능합니다.
원인 분리 순서
- 오류 유형별 분포: 공급사 응답 코드(429 한도 초과, 5xx 서버 오류)와 우리 쪽 예외(타임아웃, 파싱 실패)를 나눠 봅니다.
- 구간별 지연: 트레이스에서 LLM 호출, 도구 호출, 우리 서비스 처리 구간 중 어디가 늘었는지 확인합니다.
- 외부 상태: 공급사 상태 페이지의 장애 공지 시각과 우리 지표 악화 시각을 비교합니다.
SQL
-- 최근 1시간 오류 유형별 건수
SELECT coalesce(provider_status::text, error_type) AS kind, count(*)
FROM agent_steps
WHERE created_at > now() - interval '1 hour' AND NOT success
GROUP BY 1 ORDER BY 2 DESC;OpenTelemetry로 단계마다 span을 남겨 두면(예: llm.call, tool.search, agent.plan) 지연이 늘어난 구간을 트레이스 화면에서 바로 찾을 수 있습니다.
대응
- 429가 대부분이면 동시 요청 수 제한과 백오프를 조정하고, 공급사 한도 상향이나 보조 공급사로의 폴백을 검토합니다.
- 5xx·지연 급증이 공급사 장애와 겹치면 서킷 브레이커를 열고 축소 모드(캐시 응답, 단순 답변)로 전환합니다.
- 우리 쪽 파싱 실패가 늘었다면 최근 프롬프트·모델 변경을 먼저 의심합니다.
재발 방지
- SLA 보고에 원인 분류(내부/공급사/도구)를 함께 기록해, 어떤 의존성이 에러 버짓을 가장 많이 소모하는지 분기마다 검토합니다.
참고 자료
검증 환경·편집자 주
- 2026-09. SLA는 비즈니스 합의 후에 숫자화하세요.
자주 묻는 질문 (FAQ)
Q. LLM 지연을 SLA에 넣는 게 맞나요?
사용자 체감 경로만. 배치/비동기 경로는 별도 목표를 둡니다.
✦ ✦ ✦
출처·검증 안내
글마다 출처와 확인 범위가 다릅니다. 아래에 확인일·확인 범위·출처가 기록된 글만 그 범위에서 확인된 것입니다. 기록이 없는 글은 적용 전 사용 중인 버전의 공식 문서로 직접 확인하고, 오류는 이메일로 알려주세요. 편집 정책 · 이메일로 제보
이 글에는 사람이 확인한 날짜·범위 기록이 아직 없습니다.
이 글이 도움이 되었나요?
댓글
첫 번째 댓글을 남겨보세요.