Claude tool_use가 안 잡히고 stop_reason이 end_turn으로 옵니다 (그냥 텍스트로만 답함)
Anthropic SDK(파이썬)로 에이전트를 만드는 중인데요. 툴 정의를 tools 파라미터로 넘기고 모델이 함수를 호출하길 기대하는데, 응답을 보면 stop_reason이 tool_use가 아니라 end_turn으로 오고 content에는 그냥 텍스트만 있습니다.
프롬프트에서 "이 도구를 써서 날씨를 조회해"라고 명시적으로 지시해도 절반은 그냥 텍스트로 "날씨를 조회하겠습니다"라고만 답하고 실제 tool_use 블록이 안 나옵니다.
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "서울 날씨 알려줘"}],
)
print(resp.stop_reason) # end_turn ...
description은 "도시 날씨를 조회한다" 한 줄로 넣었습니다. 뭐가 문제일까요?
답변 2개
- 채택된 답변에디터 검증
십중팔구 description이 빈약해서입니다. Claude는 그 도구를 "언제 써야 하는지"를 거의 전적으로 description으로 판단해요. "도시 날씨를 조회한다" 한 줄이면 모델이 "이 정도는 그냥 내가 답해도 되겠네" 하고 텍스트로 때우는 쪽을 고릅니다.
스키마부터 살을 붙이세요. 특히 언제 써라를 명령조로:
weather_tool = { "name": "get_weather", "description": "특정 도시의 현재 날씨를 조회한다. 사용자가 날씨/기온/비·눈 여부를 물으면 추측하지 말고 항상 이 도구로 실제 데이터를 가져올 것.", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "도시 이름. 예: 서울, 부산"} }, "required": ["city"], }, }그리고 파싱 쪽도 문제예요.
stop_reason만 보지 마세요. content는 텍스트 블록이랑 tool_use 블록이 섞여서 옵니다 — 모델이 "날씨를 조회하겠습니다" 텍스트 + tool_use를 같이 보내는 경우도 흔해서,stop_reason == end_turn만 보고 거르면 멀쩡한 tool_use를 놓칩니다. 블록을 순회해서 type으로 잡으세요:for block in resp.content: if block.type == "tool_use": print(block.name, block.input)그래도 무조건 매번 도구를 강제해야겠으면
tool_choice로요.{"type": "tool", "name": "get_weather"}면 그 도구를,{"type": "any"}면 "아무 도구든 반드시 하나"를 강제합니다. 디폴트가{"type": "auto"}라 호출 여부가 모델 재량인 거예요. 다만 강제는 부작용 있으니(아랫분 답 참고) 저는 description 손보는 걸 먼저 권합니다. 위 답이 정답이고,
tool_choice강제 쓸 때 주의점만 하나 보탤게요.any나 특정 tool로 강제하면 모델이 사용자한테 되묻거나 "정보가 부족합니다"라고 빠지는 경로가 막혀요. 그래서 필수 파라미터가 비었을 때 그냥 추측으로 채워 넣습니다. 날씨 예시면 도시 안 줬는데 멋대로 "서울"로 호출하는 식. 멀티턴 에이전트면 그 추측 인자로 엉뚱한 조회가 돌고 루프가 한 바퀴 헛돕니다.그래서 저는
auto두고 description 빡세게 쓰는 쪽 선호해요. 그리고 강제 모드는 루프 끝에서 자연어 마무리("조회 결과 서울은 맑음입니다")를 못 하게 막을 수도 있어서, 종료 턴에선auto로 풀어주는 식으로 단계별로 다르게 거는 것도 방법입니다.