⚡ 중급2026-05-207분
Tool Use 실전: AI가 도구 호출에 실패했을 때 무너지지 않는 에러 핸들링
Claude의 tool use 기능을 쓰다 보면 잘못된 인자, 타임아웃, 빈 결과 등 다양한 실패 상황을 만난다. 각 실패 유형별로 모델이 스스로 복구하도록 유도하는 패턴을 정리한다.
tool-useerror-handlingagentic-loop
Tool Use가 깨지는 3가지 순간
tool use를 처음 도입한 개발자들이 흔히 겪는 문제는 도구 호출 자체가 실패했을 때 어떻게 할지 설계하지 않은 것이다. 모델이 잘못된 파라미터를 생성하거나, 외부 API가 오류를 반환하거나, 결과가 비어 있을 때 파이프라인 전체가 멈춰버린다.
실패 유형과 대응 전략
| 실패 유형 | 원인 | 권장 대응 |
|---|---|---|
| 잘못된 인자 | 모델이 스키마 불일치 값 생성 | 에러 메시지를 tool_result로 되돌려 재시도 유도 |
| 외부 API 오류 | 네트워크·인증 실패 | 에러 내용을 자연어로 설명해 모델이 대안 행동 선택 |
| 빈 결과 | 검색 결과 없음 등 | "결과 없음" 명시 → 모델이 다른 쿼리로 재시도 |
| 무한 루프 | 모델이 같은 도구를 반복 호출 | 최대 반복 횟수(maxIterations) 하드 제한 |
에러를 모델에게 돌려주는 agentic 루프 구현
import anthropic
import json
from typing import Any
client = anthropic.Anthropic()
# 예시 도구: 날씨 조회 (실패 시뮬레이션 포함)
def get_weather(city: str) -> dict:
if city == "없는도시":
raise ValueError(f"'{city}'에 대한 날씨 데이터를 찾을 수 없습니다.")
return {"city": city, "temp": 22, "condition": "맑음"}
tools = [
{
"name": "get_weather",
"description": "도시 이름으로 현재 날씨를 조회합니다.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "조회할 도시 이름"}
},
"required": ["city"],
},
}
]
def run_agent(user_message: str, max_iterations: int = 5) -> str:
messages = [{"role": "user", "content": user_message}]
for iteration in range(max_iterations):
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages,
)
# 도구 호출 없이 최종 답변
if response.stop_reason == "end_turn":
text_blocks = [b.text for b in response.content if b.type == "text"]
return "\n".join(text_blocks)
# 도구 호출 처리
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type != "tool_use":
continue
# ★ 핵심: 에러를 잡아서 모델에게 되돌려 줌
try:
if block.name == "get_weather":
result = get_weather(**block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result, ensure_ascii=False),
})
except Exception as e:
# 에러를 is_error=True 로 반환 → 모델이 상황 인지 후 재시도
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": f"오류 발생: {str(e)}",
"is_error": True,
})
messages.append({"role": "user", "content": tool_results})
return "최대 반복 횟수를 초과했습니다. 작업을 완료할 수 없습니다."
# 테스트
print(run_agent("없는도시와 서울의 날씨를 비교해줘"))
에러 핸들링 설계 체크리스트
- [ ] 도구 실행 코드를
try/except로 감싸고is_error: true와 함께 에러 메시지를 반환하는가 - [ ]
max_iterations(또는max_turns)를 반드시 설정해 무한 루프를 차단했는가 - [ ] 빈 결과(
[],null)도 명시적인 문자열로 변환해 모델에 전달하는가 - [ ]
stop_reason이tool_use도end_turn도 아닌 경우(예:max_tokens)를 처리했는가 - [ ] 동일 도구가 연속 3회 이상 호출될 때 조기 탈출 로직이 있는가