Quando você faz uma solicitação para a API de Mensagens, a resposta do Claude inclui um campo stop_reason que indica por que o modelo parou de gerar sua resposta. Entender esses valores é crucial para construir aplicações robustas que tratam diferentes tipos de resposta apropriadamente.
Para detalhes sobre stop_reason na resposta da API, veja a referência da API de Mensagens.
O campo stop_reason faz parte de toda resposta bem-sucedida da API de Mensagens. Diferentemente de erros, que indicam falhas no processamento de 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": "Aqui está a resposta para sua pergunta..."
}
],
"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":
# Processa a resposta completa
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 geralmente acontece quando Claude interpreta que o turno do assistente está completo, particularmente após resultados de ferramentas.
Causas comuns:
Como prevenir respostas vazias:
# INCORRETO: Adicionar texto imediatamente após tool_result
messages = [
{"role": "user", "content": "Calcule a soma de 1234 e 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": "Aqui está o resultado" # Não adicione texto após tool_result
}
]}
]
# CORRETO: Enviar resultados de ferramentas diretamente sem texto adicional
messages = [
{"role": "user", "content": "Calcule a soma de 1234 e 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"
}
]} # Apenas o tool_result, sem texto adicional
]
# Se você ainda receber respostas vazias após corrigir o acima:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
# Verifica se a resposta está vazia
if (response.stop_reason == "end_turn" and
not response.content):
# INCORRETO: Não apenas tente novamente com a resposta vazia
# Isso não funcionará porque Claude já decidiu que terminou
# CORRETO: Adicione um prompt de continuação em uma NOVA mensagem do usuário
messages.append({"role": "user", "content": "Por favor, continue"})
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
return responseMelhores práticas:
Claude parou porque atingiu o limite max_tokens especificado em sua solicitação.
# Solicitação com tokens limitados
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=10,
messages=[{"role": "user", "content": "Explique física quântica"}]
)
if response.stop_reason == "max_tokens":
# A resposta foi truncada
print("A resposta foi cortada no limite de tokens")
# Considere fazer outra solicitação para continuarClaude encontrou uma de suas sequências de parada personalizadas.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
stop_sequences=["END", "STOP"],
messages=[{"role": "user", "content": "Gere texto até você dizer END"}]
)
if response.stop_reason == "stop_sequence":
print(f"Parou na sequência: {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 executor de ferramentas que trata automaticamente a execução de ferramentas, formatação de resultados e gerenciamento de conversas.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "Qual é o clima?"}]
)
if response.stop_reason == "tool_use":
# Extrai e executa a ferramenta
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# Retorna o resultado para Claude para a resposta finalUsado 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-sonnet-4-5",
max_tokens=1024,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
messages=[{"role": "user", "content": "Procure pelas últimas notícias de IA"}]
)
if response.stop_reason == "pause_turn":
# Continua a conversa
messages = [
{"role": "user", "content": original_query},
{"role": "assistant", "content": response.content}
]
continuation = client.messages.create(
model="claude-sonnet-4-5",
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}]
)Claude recusou gerar uma resposta por preocupações de segurança.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "[Solicitação insegura]"}]
)
if response.stop_reason == "refusal":
# Claude recusou responder
print("Claude não conseguiu processar esta solicitação")
# Considere reformular ou modificar a solicitaçãoSe 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 pelos filtros de segurança da API para Claude Sonnet 4.5, veja 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 conhecer o tamanho exato da entrada.
# Solicitação com tokens máximos para obter o máximo possível
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=64000, # Tokens de saída máximos do modelo
messages=[{"role": "user", "content": "Entrada grande que usa a maioria da janela de contexto..."}]
)
if response.stop_reason == "model_context_window_exceeded":
# A resposta atingiu o limite da janela de contexto antes de max_tokens
print("A resposta atingiu o limite da janela de contexto do modelo")
# A resposta ainda é válida mas foi limitada pela janela de contextoEsta razão de parada está disponível por padrão em Sonnet 4.5 e modelos mais novos. Para modelos anteriores, use o cabeçalho beta model-context-window-exceeded-2025-08-26 para ativar este 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:
# Trata end_turn e outros casos
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"]:
# Opção 1: Avise o usuário sobre o limite específico
if response.stop_reason == "max_tokens":
message = "[Resposta truncada devido ao limite de max_tokens]"
else:
message = "[Resposta truncada devido ao limite da janela de contexto]"
return f"{response.content[0].text}\n\n{message}"
# Opção 2: Continuar a geração
messages = [
{"role": "user", "content": original_prompt},
{"role": "assistant", "content": response.content[0].text}
]
continuation = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=messages + [{"role": "user", "content": "Por favor, 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-sonnet-4-5",
messages=messages,
tools=original_tools
)
return responseÉ importante distinguir entre valores de stop_reason e erros reais:
try:
response = client.messages.create(...)
# Trata resposta bem-sucedida com stop_reason
if response.stop_reason == "max_tokens":
print("A resposta foi truncada")
except anthropic.APIError as e:
# Trata erros reais
if e.status_code == 429:
print("Limite de taxa excedido")
elif e.status_code == 500:
print("Erro do servidor")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 terminou com: {stop_reason}")Mais simples com executor de ferramentas: O exemplo abaixo mostra tratamento manual de ferramentas. Para a maioria dos casos de uso, o executor de ferramentas trata automaticamente a 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-sonnet-4-5",
messages=messages,
tools=tools
)
if response.stop_reason == "tool_use":
# Executa ferramentas e continua
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Resposta final
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-sonnet-4-5",
messages=messages,
max_tokens=4096
)
full_response += response.content[0].text
if response.stop_reason != "max_tokens":
break
# Continua de onde parou
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Por favor, continue de onde parou."}
]
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):
"""
Obtém o máximo de tokens possível dentro da janela de contexto do modelo
sem precisar calcular a contagem de tokens de entrada
"""
response = client.messages.create(
model="claude-sonnet-4-5",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000 # Defina para os tokens de saída máximos do modelo
)
if response.stop_reason == "model_context_window_exceeded":
# Obteve o máximo de tokens possível dado o tamanho da entrada
print(f"Gerou {response.usage.output_tokens} tokens (limite de contexto atingido)")
elif response.stop_reason == "max_tokens":
# Obteve exatamente os tokens solicitados
print(f"Gerou {response.usage.output_tokens} tokens (max_tokens atingido)")
else:
# Conclusão natural
print(f"Gerou {response.usage.output_tokens} tokens (conclusão natural)")
return response.content[0].textAo tratar adequadamente os valores de stop_reason, você pode construir aplicações mais robustas que tratam graciosamente diferentes cenários de resposta e proporcionam melhores experiências ao usuário.