패스노트
도메인 2. 툴 설계와 MCP 통합

2.2구조화된 오류 응답

예상 학습 시간 30

툴 실행이 실패했을 때 모델에게 무엇을 돌려주느냐가 에이전트의 복구 능력을 결정합니다. 이 수업에서는 is_error 플래그의 올바른 사용법, 모델이 스스로 복구할 수 있게 만드는 오류 메시지 설계, 그리고 오류 유형별 대응 전략을 다룹니다.

tool_result와 is_error 플래그

Claude API에서 툴 실행 결과는 tool_result 콘텐츠 블록으로 반환합니다. 실행이 실패했다면 is_error: true를 설정합니다.

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A2B3C4",
      "is_error": true,
      "content": "ValidationError: 'created_after'는 ISO 8601 형식이어야 합니다. 받은 값: '어제'. 예시: '2026-08-10T00:00:00Z'"
    }
  ]
}

is_error: true는 모델에게 "이전 호출이 실패했으니 결과를 사실로 취급하지 말고 대응하라"는 신호입니다. 이 플래그 없이 오류 문자열만 반환하면 모델이 오류 텍스트를 정상 데이터로 오해할 수 있습니다. 반대로 비어 있는 결과는 오류가 아닙니다. 검색 결과 0건은 is_error 없이 "결과 없음"을 정상 데이터로 반환해야 합니다. 이를 오류로 처리하면 모델이 불필요한 재시도를 반복합니다.

예외(exception)를 프로세스 밖으로 던져 에이전트 루프를 중단시키는 것은 최악의 선택입니다. 툴 실행부는 모든 예외를 잡아 tool_result로 변환해 모델에게 돌려주고, 복구 시도의 기회를 줘야 합니다.

모델이 복구할 수 있는 오류 메시지

좋은 오류 메시지의 기준은 사람용 로그가 아니라 모델이 다음 행동을 결정할 수 있는가입니다. Anthropic의 툴 작성 가이드는 "스택 트레이스가 아니라 행동 가능한(actionable) 안내를 반환하라"고 명시합니다.

좋은 오류 메시지는 세 가지를 담습니다.

  1. 무엇이 잘못됐는가 — 어떤 파라미터/조건이 문제인지 특정
  2. 왜 잘못됐는가 — 위반한 규칙이나 제약
  3. 어떻게 고치는가 — 올바른 형식의 예시, 대안 툴, 다음 단계
# 나쁨: 모델이 할 수 있는 일이 없다
return ToolResult(is_error=True, content="Error 500")

# 나쁨: 내부 구현 노출, 힌트 없음
return ToolResult(is_error=True,
    content="Traceback (most recent call last): File 'db.py', line 42 ...")

# 좋음: 원인 + 수정 방법 + 예시
return ToolResult(is_error=True, content=(
    "InvalidTicker: 'apple'은 유효한 종목 코드가 아닙니다. "
    "대문자 심볼을 사용하세요 (예: 'AAPL'). "
    "심볼을 모르면 search_ticker 툴로 먼저 검색하세요."
))

특히 마지막 예시처럼 대안 경로를 알려주는 것이 강력합니다. 모델은 안내받은 대로 search_ticker를 호출한 뒤 올바른 심볼로 재시도하는 2단계 복구를 스스로 수행합니다. 오류 메시지는 곧 모델을 위한 미니 프롬프트라고 생각하면 됩니다.

오류 분류 체계와 대응 전략

모든 오류를 같은 방식으로 처리하면 안 됩니다. 오류를 성격에 따라 분류하고 각각 다른 신호를 모델에게 보내야 합니다.

유형모델에게 줄 신호
입력 오류형식 위반, 없는 ID수정 방법과 예시 → 즉시 재시도 유도
일시적 오류타임아웃, 429, 네트워크"일시적 오류, 재시도 가능" 명시
권한 오류인증 실패, 접근 금지"재시도 무의미, 사용자에게 보고" 유도
정상적 빈 결과검색 0건is_error 없이 정상 데이터로 반환

일시적 오류는 툴 실행 계층에서 지수 백오프(exponential backoff)로 모델 모르게 재시도한 뒤, 최종 실패만 모델에게 알리는 편이 토큰 효율이 좋습니다. 모델 턴 하나하나가 비용이기 때문입니다.

def run_tool(fn, args, retries=3):
    for attempt in range(retries):
        try:
            return ToolResult(content=fn(**args))
        except TransientError as e:
            if attempt == retries - 1:
                return ToolResult(is_error=True, content=(
                    f"일시적 오류가 {retries}회 지속됐습니다: {e}. "
                    "지금은 이 작업을 완료할 수 없다고 사용자에게 알리세요."
                ))
            time.sleep(2 ** attempt)
        except ValidationError as e:
            return ToolResult(is_error=True, content=e.model_guidance())

반면 권한 오류처럼 재시도가 무의미한 경우에는 "재시도하지 말라"는 신호를 명시해야 합니다. 그렇지 않으면 모델이 같은 호출을 반복하며 턴을 낭비하는 루프에 빠질 수 있습니다.

재시도 한도와 루프 탈출

오류 응답 설계의 마지막 조각은 무한 루프 방지입니다. 모델이 같은 툴을 같은 방식으로 계속 실패 호출하는 상황은 실무에서 흔히 발생하며, 시험에서도 단골 시나리오입니다.

방어선은 두 겹으로 둡니다.

1) 애플리케이션 계층의 하드 리밋. 에이전트 루프에서 동일 툴의 연속 실패 횟수를 세고, 한도(예: 3회)를 넘으면 루프를 중단하거나 사람에게 에스컬레이션합니다. 모델의 판단에만 맡기지 않는 결정적(deterministic) 안전장치입니다.

2) 오류 메시지에 남은 기회 명시. 실패가 반복될 때 오류 메시지에 맥락을 누적해 모델의 전략 전환을 유도합니다.

"3회 연속 같은 오류입니다 (InvalidDateFormat).
같은 방식의 재시도는 중단하세요. 대신:
1. list_valid_formats 툴로 지원 형식을 확인하거나
2. 사용자에게 날짜 형식을 질문하세요."

이렇게 하면 모델이 '같은 시도 반복'에서 '다른 전략 탐색'으로 전환할 근거가 생깁니다.

마지막으로, 오류 이력은 관측성(observability)의 핵심 데이터입니다. 어떤 툴이 어떤 오류를 자주 내는지 로깅하면 스키마 개선 지점(2.1 수업)이 그대로 드러납니다. 오류율이 높은 툴은 대부분 description이 모호하거나 스키마 제약이 느슨한 툴입니다.

시험 함정

  • 툴 실패는 예외를 던져 루프를 중단하는 게 아니라 is_error: true인 tool_result로 모델에게 돌려줘야 한다.
  • 검색 결과 0건 같은 '정상적 빈 결과'를 is_error로 처리하면 오답이다. 오류와 빈 결과를 구분하라.
  • 좋은 오류 메시지는 스택 트레이스가 아니라 원인 + 수정 방법 + 예시(또는 대안 툴)를 담은 행동 가능한 안내다.
  • 일시적 오류(429, 타임아웃)는 툴 계층에서 백오프 재시도 후 최종 실패만 모델에게 알리는 것이 효율적이다.
  • 권한 오류에는 '재시도 무의미' 신호를 명시해야 모델의 재시도 루프를 막을 수 있다.
  • 동일 실패 반복에 대한 하드 리밋은 모델 판단이 아니라 애플리케이션 코드에 두는 것이 정답 선택지다.

실습 시나리오

실제 시험과 같은 형식의 시나리오 문제입니다.

에이전트가 외부 API 툴을 호출했는데 429(Too Many Requests)가 반환됐습니다. 로그를 보니 모델이 같은 호출을 즉시 5번 반복하며 모두 실패했습니다. 가장 좋은 개선책은 무엇입니까?

빌드 연습 · 복구 가능한 오류 계층 만들기

45
  1. 1.오류 유형 정의

    입력 오류·일시적 오류·권한 오류·빈 결과 4가지를 구분하는 오류 클래스를 설계한다

    기대 결과 · 각 유형이 서로 다른 tool_result 메시지 템플릿을 갖는다

  2. 2.행동 가능한 메시지 작성

    각 오류 유형에 원인·수정법·예시(또는 대안 툴)를 담은 메시지 생성 함수를 구현한다

    기대 결과 · 어떤 오류든 모델이 다음 행동을 결정할 수 있는 문장이 반환된다

  3. 3.백오프 재시도 래퍼

    일시적 오류를 지수 백오프로 3회 재시도하는 실행 래퍼를 만든다

    기대 결과 · 일시적 장애가 모델에게 노출되지 않고 툴 계층에서 흡수된다

  4. 4.연속 실패 하드 리밋

    동일 툴 3연속 실패 시 전략 전환을 유도하는 메시지를 반환하고, 5회에 루프를 중단한다

    기대 결과 · 의도적으로 실패를 주입해도 에이전트가 무한 루프에 빠지지 않는다

  5. 5.복구 시나리오 테스트

    잘못된 종목 코드를 주고 모델이 검색 툴 경유로 스스로 복구하는지 관찰한다

    기대 결과 · 오류 안내에 따라 모델이 2단계 복구(검색 후 재시도)를 수행한다

출처 및 더 읽기