Messages API에 요청을 보내면 Claude의 응답에는 모델이 응답 생성을 중단한 이유를 나타내는 stop_reason 필드가 포함됩니다. 이러한 값을 이해하는 것은 다양한 응답 유형을 적절하게 처리하는 견고한 애플리케이션을 구축하는 데 매우 중요합니다.
API 응답의 stop_reason에 대한 자세한 내용은 Messages API 레퍼런스를 참조하세요.
stop_reason 필드는 모든 성공적인 Messages API 응답의 일부입니다. 요청 처리 실패를 나타내는 오류와 달리, stop_reason은 Claude가 응답 생성을 성공적으로 완료한 이유를 알려줍니다.
{
"id": "msg_01234",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Here's the answer to your question..."
}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}가장 일반적인 중단 사유입니다. Claude가 자연스럽게 응답을 완료했음을 나타냅니다.
if response.stop_reason == "end_turn":
# 완료된 응답 처리
print(response.content[0].text)때때로 Claude는 stop_reason: "end_turn"과 함께 빈 응답(콘텐츠 없이 정확히 2-3개의 토큰)을 반환합니다. 이는 일반적으로 Claude가 어시스턴트 턴이 완료되었다고 해석할 때 발생하며, 특히 도구 결과 이후에 자주 나타납니다.
일반적인 원인:
빈 응답을 방지하는 방법:
# 잘못된 방법: tool_result 바로 뒤에 텍스트 추가
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 5678"},
{"role": "assistant", "content": [
{
"type": "tool_use",
"id": "toolu_123",
"name": "calculator",
"input": {"operation": "add", "a": 1234, "b": 5678}
}
]},
{"role": "user", "content": [
{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "6912"
},
{
"type": "text",
"text": "Here's the result" # tool_result 뒤에 텍스트를 추가하지 마세요
}
]}
]
# 올바른 방법: 추가 텍스트 없이 도구 결과만 직접 전송
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 5678"},
{"role": "assistant", "content": [
{
"type": "tool_use",
"id": "toolu_123",
"name": "calculator",
"input": {"operation": "add", "a": 1234, "b": 5678}
}
]},
{"role": "user", "content": [
{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "6912"
}
]} # tool_result만, 추가 텍스트 없음
]
# 위 방법을 적용한 후에도 빈 응답이 계속 발생하는 경우:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages
)
# 응답이 비어있는지 확인
if (response.stop_reason == "end_turn" and
not response.content:
# 잘못된 방법: 빈 응답을 그대로 재시도하지 마세요
# Claude가 이미 완료를 결정했으므로 작동하지 않습니다
# 올바른 방법: 새로운 사용자 메시지에 계속 진행 프롬프트 추가
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages
)
return response모범 사례:
Claude가 요청에서 지정한 max_tokens 제한에 도달하여 중단되었습니다.
# 토큰이 제한된 요청
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=10,
messages=[{"role": "user", "content": "Explain quantum physics"}]
)
if response.stop_reason == "max_tokens":
# 응답이 잘렸음
print("Response was cut off at token limit")
# 계속하기 위해 다른 요청을 보내는 것을 고려하세요Claude가 사용자 정의 중단 시퀀스 중 하나를 만났습니다.
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
stop_sequences=["END", "STOP"],
messages=[{"role": "user", "content": "Generate text until you say END"}]
)
if response.stop_reason == "stop_sequence":
print(f"Stopped at sequence: {response.stop_sequence}")Claude가 도구를 호출하고 있으며 실행을 기대합니다.
대부분의 도구 사용 구현에서는 도구 실행, 결과 포맷팅, 대화 관리를 자동으로 처리하는 도구 러너를 사용하는 것을 권장합니다.
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What's the weather?"}]
)
if response.stop_reason == "tool_use":
# 도구 추출 및 실행
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# 최종 응답을 위해 결과를 Claude에 반환웹 검색과 같은 서버 도구에서 Claude가 장시간 실행되는 작업을 일시 중지해야 할 때 사용됩니다.
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
messages=[{"role": "user", "content": "Search for latest AI news"}]
)
if response.stop_reason == "pause_turn":
# 대화 계속
messages = [
{"role": "user", "content": original_query},
{"role": "assistant", "content": response.content}
]
continuation = client.messages.create(
model="claude-opus-4-6",
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}]
)Claude가 안전 문제로 인해 응답 생성을 거부했습니다.
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}]
)
if response.stop_reason == "refusal":
# Claude가 응답을 거부함
print("Claude was unable to process this request")
# 요청을 다시 표현하거나 수정하는 것을 고려하세요Claude Sonnet 4.5 또는 Opus 4.1을 사용하면서 refusal 중단 사유를 자주 만나는 경우, API 호출을 Sonnet 4(claude-sonnet-4-20250514)로 업데이트해 보세요. 이 모델은 다른 사용 제한이 적용됩니다. Sonnet 4.5의 API 안전 필터 이해하기에서 자세히 알아보세요.
Claude Sonnet 4.5의 API 안전 필터에 의해 트리거되는 거부에 대해 자세히 알아보려면 Sonnet 4.5의 API 안전 필터 이해하기를 참조하세요.
Claude가 모델의 컨텍스트 윈도우 제한에 도달하여 중단되었습니다. 이를 통해 정확한 입력 크기를 알지 못해도 가능한 최대 토큰을 요청할 수 있습니다.
# 가능한 한 많은 토큰을 얻기 위해 최대 토큰으로 요청
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=64000, # 모델의 최대 출력 토큰
messages=[{"role": "user", "content": "Large input that uses most of context window..."}]
)
if response.stop_reason == "model_context_window_exceeded":
# max_tokens 이전에 컨텍스트 윈도우 제한에 도달한 응답
print("Response reached model's context window limit")
# 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한됨이 중단 사유는 Sonnet 4.5 및 최신 모델에서 기본적으로 사용할 수 있습니다. 이전 모델의 경우 베타 헤더 model-context-window-exceeded-2025-08-26을 사용하여 이 동작을 활성화하세요.
응답 처리 로직에서 stop_reason을 확인하는 것을 습관화하세요:
def handle_response(response):
if response.stop_reason == "tool_use":
return handle_tool_use(response)
elif response.stop_reason == "max_tokens":
return handle_truncation(response)
elif response.stop_reason == "model_context_window_exceeded":
return handle_context_limit(response)
elif response.stop_reason == "pause_turn":
return handle_pause(response)
elif response.stop_reason == "refusal":
return handle_refusal(response)
else:
# end_turn 및 기타 경우 처리
return response.content[0].text토큰 제한이나 컨텍스트 윈도우로 인해 응답이 잘린 경우:
def handle_truncated_response(response):
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
# 옵션 1: 특정 제한에 대해 사용자에게 경고
if response.stop_reason == "max_tokens":
message = "[Response truncated due to max_tokens limit]"
else:
message = "[Response truncated due to context window limit]"
return f"{response.content[0].text}\n\n{message}"
# 옵션 2: 생성 계속
messages = [
{"role": "user", "content": original_prompt},
{"role": "assistant", "content": response.content[0].text}
]
continuation = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages + [{"role": "user", "content": "Please continue"}]
)
return response.content[0].text + continuation.content[0].text일시 중지될 수 있는 서버 도구의 경우:
def handle_paused_conversation(initial_response, max_retries=3):
response = initial_response
messages = [{"role": "user", "content": original_query}]
for attempt in range(max_retries):
if response.stop_reason != "pause_turn":
break
messages.append({"role": "assistant", "content": response.content})
response = client.messages.create(
model="claude-opus-4-6",
messages=messages,
tools=original_tools
)
return responsestop_reason 값과 실제 오류를 구분하는 것이 중요합니다:
try:
response = client.messages.create(...)
# stop_reason으로 성공적인 응답 처리
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIError as e:
# 실제 오류 처리
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")스트리밍을 사용할 때 stop_reason은:
message_start 이벤트에서는 nullmessage_delta 이벤트에서 제공됨with client.messages.stream(...) as stream:
for event in stream:
if event.type == "message_delta":
stop_reason = event.delta.stop_reason
if stop_reason:
print(f"Stream ended with: {stop_reason}")도구 러너로 더 간단하게: 아래 예제는 수동 도구 처리를 보여줍니다. 대부분의 사용 사례에서 도구 러너가 훨씬 적은 코드로 도구 실행을 자동으로 처리합니다.
def complete_tool_workflow(client, user_query, tools):
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-4-6",
messages=messages,
tools=tools
)
if response.stop_reason == "tool_use":
# 도구 실행 및 계속
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# 최종 응답
return responsedef get_complete_response(client, prompt, max_attempts=3):
messages = [{"role": "user", "content": prompt}]
full_response = ""
for _ in range(max_attempts):
response = client.messages.create(
model="claude-opus-4-6",
messages=messages,
max_tokens=4096
)
full_response += response.content[0].text
if response.stop_reason != "max_tokens":
break
# 중단된 지점부터 계속
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."}
]
return full_responsemodel_context_window_exceeded 중단 사유를 사용하면 입력 크기를 계산하지 않고도 가능한 최대 토큰을 요청할 수 있습니다:
def get_max_possible_tokens(client, prompt):
"""
입력 토큰 수를 계산할 필요 없이
모델의 컨텍스트 윈도우 내에서 가능한 한 많은 토큰을 얻습니다
"""
response = client.messages.create(
model="claude-opus-4-6",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000 # 모델의 최대 출력 토큰으로 설정
)
if response.stop_reason == "model_context_window_exceeded":
# 입력 크기를 고려하여 가능한 최대 토큰을 얻음
print(f"Generated {response.usage.output_tokens} tokens (context limit reached)")
elif response.stop_reason == "max_tokens":
# 요청한 정확한 토큰 수를 얻음
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# 자연스러운 완료
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return response.content[0].textstop_reason 값을 적절히 처리함으로써 다양한 응답 시나리오를 우아하게 처리하고 더 나은 사용자 경험을 제공하는 더 견고한 애플리케이션을 구축할 수 있습니다.
Was this page helpful?