에이전트 실패의 근본 원인 분석
모델 오류, 도구 오류, 데이터 오류, 논리 오류로 실패를 체계적으로 분류합니다.
에이전트 실패의 근본 원인 분석은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
에이전트 실패 유형 분류
에이전트 실패는 네 가지 범주로 나뉩니다.
- 모델 오류: LLM이 잘못된 도구를 호출하거나 잘못된 출력을 생성하는 경우
- 도구 오류: 외부 API가 실패하거나 예상치 못한 데이터를 반환하는 경우
- 데이터 오류: 잘못된 입력(형식 오류, 필드 누락, 예상치 못한 유형)이 제공되는 경우
- 논리 오류: 단계는 올바르지만 순서가 잘못되었거나 잘못된 가정을 사용하는 경우
모델 오류: 잘못된 도구 호출
모델 오류는 LLM이 잘못된 도구를 선택하거나, 잘못된 인수를 전달하거나, 형식이 잘못된 JSON을 생성할 때 발생합니다. 이러한 오류는 불명확한 도구 설명이나 모호한 프롬프트 때문에 발생하는 경우가 많습니다.
import openai
import json
client = openai.OpenAI(api_key='sk-...')
def detect_model_errors(response) -> list:
errors = []
message = response.choices[0].message
if message.tool_calls:
for tc in message.tool_calls:
tool_name = tc.function.name
try:
args = json.loads(tc.function.arguments)
except json.JSONDecodeError as e:
errors.append({
'type': 'model_error',
'subtype': 'malformed_tool_args',
'tool': tool_name,
'raw_args': tc.function.arguments,
'parse_error': str(e)
})
continue
# Validate required arguments
expected_tools = {
'search_web': ['query'],
'send_email': ['to', 'subject', 'body'],
'create_task': ['title']
}
required = expected_tools.get(tool_name, [])
missing = [r for r in required if r not in args]
if missing:
errors.append({
'type': 'model_error',
'subtype': 'missing_required_args',
'tool': tool_name,
'missing': missing
})
return errors
print('Model error detection function defined')도구 오류: API 실패
도구 오류는 외부 API가 오류(4xx, 5xx)를 반환하거나, 시간 초과가 발생하거나, 예상치 못한 형식의 데이터를 반환할 때 발생합니다. 진단을 위해 정확한 오류, 도구 인수 및 응답을 기록하십시오.
import httpx
from dataclasses import dataclass
from typing import Optional
@dataclass
class ToolError:
tool_name: str
error_type: str
status_code: Optional[int]
message: str
args_used: dict
retry_possible: bool
def classify_tool_error(tool_name: str, args: dict, exception: Exception) -> ToolError:
if isinstance(exception, httpx.TimeoutException):
return ToolError(
tool_name=tool_name,
error_type='timeout',
status_code=None,
message=str(exception),
args_used=args,
retry_possible=True # Retries are appropriate for timeouts
)
elif isinstance(exception, httpx.HTTPStatusError):
status = exception.response.status_code
retry = status >= 500 or status == 429 # Server errors and rate limits are retryable
return ToolError(
tool_name=tool_name,
error_type='http_error',
status_code=status,
message=exception.response.text[:200],
args_used=args,
retry_possible=retry
)
else:
return ToolError(
tool_name=tool_name,
error_type='unexpected_error',
status_code=None,
message=str(exception),
args_used=args,
retry_possible=False
)
print('Tool error classification defined')데이터 오류: 입력 검증
데이터 오류는 에이전트에 잘못된 입력이 제공될 때 발생합니다. 필수 필드가 누락되거나, 데이터 형식이 잘못되거나, 숫자가 예상되는 곳에 문자열이 들어오는 경우가 이에 해당합니다. 에이전트 진입점에서 입력을 검증하여 이러한 오류를 조기에 발견하십시오.
from pydantic import BaseModel, validator, ValidationError
from typing import Optional
class EmailAgentInput(BaseModel):
email_id: str
action: str
user_id: int
priority: Optional[str] = 'normal'
@validator('action')
def action_must_be_valid(cls, v):
valid_actions = ['reply', 'forward', 'archive', 'summarize']
if v not in valid_actions:
raise ValueError(f'action must be one of {valid_actions}, got: {v}')
return v
@validator('email_id')
def email_id_not_empty(cls, v):
if not v.strip():
raise ValueError('email_id cannot be empty')
return v
def validate_agent_input(raw_input: dict) -> tuple:
try:
validated = EmailAgentInput(**raw_input)
return validated, None
except ValidationError as e:
return None, [
{'field': err['loc'][0], 'message': err['msg']}
for err in e.errors()
]
# Test with bad input
valid, errors = validate_agent_input({'email_id': '', 'action': 'delete', 'user_id': 'abc'})
if errors:
print('Data errors found:')
for err in errors:
print(f' {err["field"]}: {err["message"]}')논리 오류: 잘못된 단계 순서
논리 오류는 디버깅하기가 가장 어렵습니다. 에이전트가 올바른 도구를 올바른 인수와 함께 호출하지만 순서가 잘못되거나, 필수 단계를 건너뛰거나, 이전 단계의 출력에 대해 잘못된 가정을 합니다.
import logging
logger = logging.getLogger('agent.logic')
class AgentStepGuard:
'''
Enforces that steps execute in the required sequence.
'''
def __init__(self):
self.completed_steps = set()
self.STEP_DEPENDENCIES = {
'extract_action_items': ['read_email'],
'create_trello_card': ['extract_action_items'],
'send_slack_notification': ['create_trello_card']
}
def mark_complete(self, step_name: str):
self.completed_steps.add(step_name)
def can_run(self, step_name: str) -> tuple:
required = self.STEP_DEPENDENCIES.get(step_name, [])
missing = [r for r in required if r not in self.completed_steps]
if missing:
return False, f'Logic error: {step_name} requires {missing} to complete first'
return True, None
def assert_can_run(self, step_name: str):
ok, error = self.can_run(step_name)
if not ok:
logger.error(error)
raise RuntimeError(error)
guard = AgentStepGuard()
# Simulate trying to skip a step
try:
guard.assert_can_run('create_trello_card')
except RuntimeError as e:
print('Caught logic error:', e)
# Correct sequence
guard.mark_complete('read_email')
guard.mark_complete('extract_action_items')
guard.assert_can_run('create_trello_card') # Now allowed
print('Step sequence valid')구조화된 오류 기록
사후 분석에 필요한 충분한 컨텍스트를 포함하여 모든 오류를 기록하십시오. 오류 유형, 전체 스택 추적, 단계 입력 및 실패 당시의 관련 에이전트 상태를 기록해야 합니다.
import logging
import traceback
import json
from datetime import datetime
logger = logging.getLogger('agent.errors')
def log_agent_error(error_type: str, step: str, inputs: dict, exception: Exception, agent_state: dict = None):
error_record = {
'timestamp': datetime.utcnow().isoformat(),
'error_type': error_type,
'step': step,
'exception_type': type(exception).__name__,
'exception_message': str(exception),
'traceback': traceback.format_exc(),
'inputs': inputs,
'agent_state': agent_state or {}
}
logger.error(json.dumps(error_record))
return error_record
# Example usage
try:
raise ValueError('Email ID not found in database')
except Exception as e:
record = log_agent_error(
error_type='data_error',
step='read_email',
inputs={'email_id': 'missing-id-123'},
exception=e,
agent_state={'user_id': 42, 'session_id': 'sess-abc'}
)
print('Error logged:', record['error_type'], '-', record['exception_message'])오류율 모니터링
시스템 전반의 문제를 식별할 수 있도록 단계별 및 오류 유형별 오류율을 추적하십시오. 모델 오류가 갑자기 증가하면 프롬프트 변경으로 도구 호출이 중단되었을 수 있고, 도구 오류가 증가하면 API 성능이 저하되었을 수 있습니다.
from collections import defaultdict
from datetime import datetime
class ErrorTracker:
def __init__(self):
self.errors = defaultdict(list)
def record(self, error_type: str, step: str):
key = f'{error_type}:{step}'
self.errors[key].append(datetime.utcnow())
def get_rates(self, window_minutes: int = 60) -> dict:
from datetime import timedelta
cutoff = datetime.utcnow() - timedelta(minutes=window_minutes)
rates = {}
for key, timestamps in self.errors.items():
recent = [ts for ts in timestamps if ts >= cutoff]
rates[key] = len(recent)
return dict(sorted(rates.items(), key=lambda x: x[1], reverse=True))
def has_spike(self, error_type: str, step: str, threshold: int = 5, window_minutes: int = 10) -> bool:
key = f'{error_type}:{step}'
rates = self.get_rates(window_minutes)
return rates.get(key, 0) >= threshold
tracker = ErrorTracker()
for _ in range(8):
tracker.record('tool_error', 'web_search')
tracker.record('model_error', 'intent_detection')
print('Error rates:', tracker.get_rates())
print('Spike detected:', tracker.has_spike('tool_error', 'web_search'))재현을 통한 모델 오류 디버깅
모델 오류가 발생하면 동일한 입력으로 정확히 같은 LLM 호출을 재현하십시오. 출력을 예상 결과와 비교하십시오. 시스템 프롬프트에 더 구체적인 지침을 추가하거나 도구 설명을 개선하여 문제를 해결하십시오.
import openai
import json
client = openai.OpenAI(api_key='sk-...')
def save_failed_call(step_name: str, messages: list, tools: list, actual_response: str, expected_tool: str, filepath: str):
record = {
'step': step_name,
'messages': messages,
'tools': tools,
'actual_response': actual_response,
'expected_tool': expected_tool
}
with open(filepath, 'w') as f:
json.dump(record, f, indent=2)
print(f'Failed call saved to: {filepath}')
def replay_failed_call(filepath: str, improved_system_prompt: str = None) -> str:
with open(filepath) as f:
record = json.load(f)
messages = record['messages']
if improved_system_prompt:
# Replace system prompt
messages = [m if m['role'] != 'system' else {'role': 'system', 'content': improved_system_prompt}
for m in messages]
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
tools=record['tools']
)
return response.choices[0].message
print('Replay debugging functions defined')근본 원인 분석 체크리스트
에이전트가 실패하면 다음 체크리스트를 체계적으로 확인하십시오.
- 입력이 유효했습니까? (데이터 오류)
- 외부 API에서 오류를 반환했습니까? (도구 오류)
- LLM이 올바른 도구를 호출했습니까? 인수가 올바른가요? (모델 오류)
- 단계가 올바른 순서로 실행되었습니까? (논리 오류)
- 프롬프트에 충분한 컨텍스트가 있었습니까? (모델 오류 - 컨텍스트)
def diagnose_failure(error_log: dict) -> dict:
diagnosis = {
'error_type': error_log.get('error_type'),
'root_cause': None,
'immediate_fix': None,
'long_term_fix': None
}
if error_log.get('error_type') == 'data_error':
diagnosis['root_cause'] = 'Invalid or missing input data'
diagnosis['immediate_fix'] = 'Return clear error to caller with field-level validation feedback'
diagnosis['long_term_fix'] = 'Add Pydantic validation at agent entry point'
elif error_log.get('error_type') == 'tool_error':
status = error_log.get('status_code')
if status == 429:
diagnosis['root_cause'] = 'Rate limit hit'
diagnosis['immediate_fix'] = 'Retry with exponential backoff'
diagnosis['long_term_fix'] = 'Add rate limiter to tool calls'
elif status and status >= 500:
diagnosis['root_cause'] = 'Upstream service degradation'
diagnosis['immediate_fix'] = 'Retry up to 3 times, then graceful degradation'
diagnosis['long_term_fix'] = 'Add circuit breaker pattern'
elif error_log.get('error_type') == 'model_error':
diagnosis['root_cause'] = 'LLM tool selection failure'
diagnosis['immediate_fix'] = 'Add explicit tool selection validation'
diagnosis['long_term_fix'] = 'Improve tool descriptions; add few-shot examples'
return diagnosis
result = diagnose_failure({'error_type': 'tool_error', 'status_code': 429})
print('Diagnosis:', result)중요한 실패에 대한 경보
모든 오류에 즉각적인 조치가 필요한 것은 아닙니다. 오류를 심각도별로 분류하고 그에 맞게 경보를 전달하십시오. 중요하지 않은 흐름에서 발생한 조용한 데이터 오류는 기록만 해도 되지만, 핵심 흐름이 중단되면 즉시 경보를 보내야 합니다.
ERROR_SEVERITY = {
'data_error': 'low', # Bad input: log and return error to caller
'model_error': 'medium', # LLM misbehavior: investigate, may need prompt fix
'tool_error': 'medium', # API failure: may self-recover with retry
'logic_error': 'high' # Sequencing bug: needs code fix immediately
}
def route_alert(error_type: str, step: str, message: str, is_core_flow: bool = False):
severity = ERROR_SEVERITY.get(error_type, 'medium')
if is_core_flow:
severity = 'high'
if severity == 'high':
print(f'[PAGERDUTY] CRITICAL: {error_type} in {step}: {message}')
# Call PagerDuty API here
elif severity == 'medium':
print(f'[SLACK] WARNING: {error_type} in {step}: {message}')
# Call Slack API here
else:
print(f'[LOG] INFO: {error_type} in {step}: {message}')
route_alert('logic_error', 'create_trello_card', 'Prerequisites not met', is_core_flow=True)
route_alert('data_error', 'parse_input', 'Missing optional field', is_core_flow=False)사후 분석 템플릿
중대한 에이전트 실패가 발생하면 사후 분석을 작성하십시오. 좋은 사후 분석에는 시간 흐름, 근본 원인, 영향, 잘된 점, 잘못된 점 및 재발 방지를 위한 후속 조치가 포함됩니다.
def generate_post_mortem(failure_data: dict) -> str:
return f'''
## Post-Mortem: {failure_data.get("title", "Agent Failure")}
**Date**: {failure_data.get("date")}
**Duration**: {failure_data.get("duration_minutes")} minutes
**Impact**: {failure_data.get("impact")}
### Timeline
{chr(10).join(failure_data.get("timeline", []))}
### Root Cause
{failure_data.get("root_cause")}
### Contributing Factors
{chr(10).join(failure_data.get("contributing_factors", []))}
### Action Items
{chr(10).join([f"- [ ] {item}" for item in failure_data.get("action_items", [])])}
'''.strip()
post_mortem_data = {
'title': 'Email Pipeline Failure - Wrong Trello List',
'date': '2025-01-15',
'duration_minutes': 45,
'impact': '200 emails processed but Trello cards created in wrong list',
'timeline': ['09:00 - Pipeline started', '09:15 - First error logged', '09:45 - Fixed and redeployed'],
'root_cause': 'Logic error: TRELLO_LIST_ID env var defaulted to staging value in production',
'contributing_factors': ['- Missing config validation at startup', '- No alert on wrong list ID'],
'action_items': ['Add config validation on startup', 'Alert if list_id changes unexpectedly']
}
print(generate_post_mortem(post_mortem_data))지식 확인: 근본 원인 분석
에이전트 실패의 근본 원인 분석에 대한 이해도를 확인해 보십시오.
디버깅 요약
에이전트 실패를 체계적으로 근본 원인 분석하려면 네 가지 범주 체계를 사용합니다. 즉, 모델 오류(LLM의 의사 결정 문제), 도구 오류(응용 프로그래밍 인터페이스 실패), 데이터 오류(잘못된 입력), 논리 오류(순서 지정 버그)입니다. 여기에 구조화된 로그 기록, 오류율 감시, 모델 오류에 대한 재생 디버깅, 중대한 실패에 대한 사후 분석을 함께 적용하십시오.
자주 묻는 질문
“에이전트 실패의 근본 원인 분석” 강의는 무료인가요?
네 — “에이전트 실패의 근본 원인 분석” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
“에이전트 실패의 근본 원인 분석”에서 뭘 배우나요?
모델 오류, 도구 오류, 데이터 오류, 논리 오류로 실패를 체계적으로 분류합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Agents을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“에이전트 실패의 근본 원인 분석” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- LangSmith와 Langfuse를 활용한 추적 분석
- 단계별 토큰 및 비용 프로파일링
- 느리고 비용이 많이 드는 단계 식별
- 에이전트 실패의 근본 원인 분석