Quando você faz uma solicitação para a API Messages, a resposta do Claude inclui um campo stop_reason que indica por que o modelo parou de gerar sua resposta. Compreender esses valores é crucial para construir aplicações robustas que lidem com diferentes tipos de resposta apropriadamente.
Para detalhes sobre stop_reason na resposta da API, consulte a referência da API Messages.
O campo stop_reason faz parte de cada resposta bem-sucedida da API Messages. Diferentemente dos erros, que indicam falhas no processamento da sua solicitação, stop_reason informa por que Claude completou com sucesso a geração de sua resposta.
{
"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
}
}A razão de parada mais comum. Indica que Claude terminou sua resposta naturalmente.
if response.stop_reason == "end_turn":
# Process the complete response
print(response.content[0].text)Às vezes Claude retorna uma resposta vazia (exatamente 2-3 tokens sem conteúdo) com stop_reason: "end_turn". Isso normalmente acontece quando Claude interpreta que o turno do assistente está completo, particularmente após resultados de ferramentas.
Causas comuns:
Como prevenir respostas vazias:
# INCORRECT: Adding text immediately after 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" # Don't add text after tool_result
}
]}
]
# CORRECT: Send tool results directly without additional text
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"
}
]} # Just the tool_result, no additional text
]
# If you still get empty responses after fixing the above:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages
)
# Check if response is empty
if (response.stop_reason == "end_turn" and
not response.content:
# INCORRECT: Don't just retry with the empty response
# This won't work because Claude already decided it's done
# CORRECT: Add a continuation prompt in a NEW user message
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages
)
return responseMelhores práticas:
Claude parou porque atingiu o limite max_tokens especificado em sua solicitação.
# Request with limited 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":
# Response was truncated
print("Response was cut off at token limit")
# Consider making another request to continueClaude encontrou uma de suas sequências de parada personalizadas.
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 está chamando uma ferramenta e espera que você a execute.
Para a maioria das implementações de uso de ferramentas, recomendamos usar o tool runner que trata automaticamente da execução de ferramentas, formatação de resultados e gerenciamento de conversas.
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":
# Extract and execute the tool
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# Return result to Claude for final responseUsado com ferramentas de servidor como busca na web quando Claude precisa pausar uma operação de longa duração.
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":
# Continue the conversation
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 recusou gerar uma resposta devido a preocupações de segurança.
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}]
)
if response.stop_reason == "refusal":
# Claude declined to respond
print("Claude was unable to process this request")
# Consider rephrasing or modifying the requestSe você encontrar razões de parada refusal frequentemente ao usar Claude Sonnet 4.5 ou Opus 4.1, você pode tentar atualizar suas chamadas de API para usar Sonnet 4 (claude-sonnet-4-20250514), que tem restrições de uso diferentes. Saiba mais sobre entender os filtros de segurança da API do Sonnet 4.5.
Para saber mais sobre recusas acionadas por filtros de segurança da API para Claude Sonnet 4.5, consulte Entender os Filtros de Segurança da API do Sonnet 4.5.
Claude parou porque atingiu o limite da janela de contexto do modelo. Isso permite que você solicite o máximo de tokens possível sem saber o tamanho exato da entrada.
# Request with maximum tokens to get as much as possible
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=64000, # Model's maximum output tokens
messages=[{"role": "user", "content": "Large input that uses most of context window..."}]
)
if response.stop_reason == "model_context_window_exceeded":
# Response hit context window limit before max_tokens
print("Response reached model's context window limit")
# The response is still valid but was limited by context windowEsta razão de parada está disponível por padrão no Sonnet 4.5 e modelos mais novos. Para modelos anteriores, use o cabeçalho beta model-context-window-exceeded-2025-08-26 para ativar esse comportamento.
Faça um hábito verificar o stop_reason em sua lógica de tratamento de resposta:
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:
# Handle end_turn and other cases
return response.content[0].textQuando uma resposta é truncada devido a limites de token ou janela de contexto:
def handle_truncated_response(response):
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
# Option 1: Warn the user about the specific limit
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}"
# Option 2: Continue generation
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].textPara ferramentas de servidor que podem pausar:
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 responseÉ importante distinguir entre valores de stop_reason e erros reais:
try:
response = client.messages.create(...)
# Handle successful response with stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIError as e:
# Handle actual errors
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")Ao usar streaming, stop_reason é:
null no evento inicial message_startmessage_deltawith 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}")Mais simples com tool runner: O exemplo abaixo mostra tratamento manual de ferramentas. Para a maioria dos casos de uso, o tool runner trata automaticamente da execução de ferramentas com muito menos código.
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":
# Execute tools and continue
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Final response
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
# Continue from where it left off
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."}
]
return full_responseCom a razão de parada model_context_window_exceeded, você pode solicitar o máximo de tokens possível sem calcular a contagem de tokens de entrada:
def get_max_possible_tokens(client, prompt):
"""
Get as many tokens as possible within the model's context window
without needing to calculate input token count
"""
response = client.messages.create(
model="claude-opus-4-6",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000 # Set to model's maximum output tokens
)
if response.stop_reason == "model_context_window_exceeded":
# Got the maximum possible tokens given input size
print(f"Generated {response.usage.output_tokens} tokens (context limit reached)")
elif response.stop_reason == "max_tokens":
# Got exactly the requested tokens
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Natural completion
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return response.content[0].textAo lidar adequadamente com valores de stop_reason, você pode construir aplicações mais robustas que lidam graciosamente com diferentes cenários de resposta e proporcionam melhores experiências ao usuário.
Was this page helpful?