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 lidam adequadamente com diferentes tipos de resposta.
Para detalhes sobre stop_reason na resposta da API, consulte a referência da API Messages.
O que é stop_reason?
O campo stop_reason faz parte de toda resposta bem-sucedida da API Messages. Ao contrário dos erros, que indicam falhas no processamento da sua solicitação, stop_reason informa por que o 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
}
}
Valores de razão de parada
end_turn
A razão de parada mais comum. Indica que o Claude terminou sua resposta naturalmente.
if response.stop_reason == "end_turn":
# Processar a resposta completa
print(response.content[0].text)
Respostas vazias com end_turn
Às vezes o Claude retorna uma resposta vazia (exatamente 2-3 tokens sem conteúdo) com stop_reason: "end_turn". Isso normalmente acontece quando o Claude interpreta que o turno do assistente está completo, particularmente após resultados de ferramentas.
Causas comuns:
- Adicionar blocos de texto imediatamente após resultados de ferramentas (o Claude aprende a esperar que o usuário sempre insira texto após resultados de ferramentas, então ele termina seu turno para seguir o padrão)
- Enviar a resposta completa do Claude de volta sem adicionar nada (o Claude já decidiu que terminou, então permanecerá terminado)
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
)
# Verificar 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 o Claude já decidiu que terminou
# CORRETO: Adicionar 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 response
Melhores práticas:
- Nunca adicione blocos de texto imediatamente após resultados de ferramentas - Isso ensina o Claude a esperar entrada do usuário após cada uso de ferramenta
- Não tente novamente respostas vazias sem modificação - Simplesmente enviar a resposta vazia de volta não ajudará
- Use prompts de continuação como último recurso - Apenas se as correções acima não resolverem o problema
max_tokens
O 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":
# Resposta foi truncada
print("Resposta foi cortada no limite de tokens")
# Considere fazer outra solicitação para continuar
stop_sequence
O Claude encontrou uma de suas sequências de parada personalizadas.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
stop_sequences=["FIM", "PARAR"],
messages=[{"role": "user", "content": "Gere texto até você dizer FIM"}]
)
if response.stop_reason == "stop_sequence":
print(f"Parou na sequência: {response.stop_sequence}")
O Claude está chamando uma ferramenta e espera que você a execute.
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":
# Extrair e executar a ferramenta
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# Retornar resultado para o Claude para resposta final
pause_turn
Usado com ferramentas de servidor como busca na web quando o 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": "Busque as últimas notícias de IA"}]
)
if response.stop_reason == "pause_turn":
# Continuar 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"}]
)
refusal
O Claude se recusou a gerar uma resposta devido a 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 se recusou a responder
print("Claude não conseguiu processar esta solicitação")
# Considere reformular ou modificar a solicitação
Se 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 diferentes restrições de uso. Saiba mais sobre entendendo os filtros de segurança da API do Sonnet 4.5.
model_context_window_exceeded
O 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 maior parte da janela de contexto..."}]
)
if response.stop_reason == "model_context_window_exceeded":
# Resposta atingiu limite da janela de contexto antes de max_tokens
print("Resposta atingiu limite da janela de contexto do modelo")
# A resposta ainda é válida mas foi limitada pela janela de contexto
Esta razão de parada está disponível por padrão no Sonnet 4.5 e modelos mais recentes. Para modelos anteriores, use o cabeçalho beta model-context-window-exceeded-2025-08-26 para habilitar este comportamento.
1. Sempre verifique stop_reason
Torne 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:
# Lidar com end_turn e outros casos
return response.content[0].text
2. Lidar com respostas truncadas graciosamente
Quando uma resposta é truncada devido a limites de tokens ou janela de contexto:
def handle_truncated_response(response):
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
# Opção 1: Avisar o usuário sobre o limite específico
if response.stop_reason == "max_tokens":
message = "[Resposta truncada devido ao limite 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 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].text
3. Implementar lógica de retry para pause_turn
Para 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
Razões de parada vs. erros
É importante distinguir entre valores de stop_reason e erros reais:
Razões de parada (respostas bem-sucedidas)
- Parte do corpo da resposta
- Indicam por que a geração parou normalmente
- Resposta contém conteúdo válido
Erros (solicitações falhadas)
- Códigos de status HTTP 4xx ou 5xx
- Indicam falhas no processamento da solicitação
- Resposta contém detalhes do erro
try:
response = client.messages.create(...)
# Lidar com resposta bem-sucedida com stop_reason
if response.stop_reason == "max_tokens":
print("Resposta foi truncada")
except anthropic.APIError as e:
# Lidar com erros reais
if e.status_code == 429:
print("Limite de taxa excedido")
elif e.status_code == 500:
print("Erro do servidor")
Considerações de streaming
Ao usar streaming, stop_reason é:
null no evento inicial message_start
- Fornecido no evento
message_delta
- Não fornecido em nenhum outro evento
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 terminou com: {stop_reason}")
Padrões comuns
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":
# Executar ferramentas e continuar
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Resposta final
return response
Garantindo respostas completas
def 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
# Continuar de onde parou
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Por favor, continue de onde você parou."}
]
return full_response
Obtendo tokens máximos sem conhecer o tamanho da entrada
Com a razão de parada model_context_window_exceeded, você pode solicitar o máximo de tokens possível sem calcular o tamanho da entrada:
def get_max_possible_tokens(client, prompt):
"""
Obter 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 # Definir para 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].text
Ao lidar adequadamente com valores de stop_reason, você pode construir aplicações mais robustas que lidam graciosamente com diferentes cenários de resposta e fornecem melhores experiências do usuário.