Was this page helpful?
Quando effettui una richiesta alla Messages API, la risposta di Claude include un campo stop_reason che indica perché il modello ha smesso di generare la sua risposta. Comprendere questi valori è fondamentale per creare applicazioni robuste che gestiscano in modo appropriato i diversi tipi di risposta.
Per i dettagli su stop_reason nella risposta dell'API, consulta il riferimento della Messages API.
Il campo stop_reason fa parte di ogni risposta riuscita della Messages API. A differenza degli errori, che indicano problemi nell'elaborazione della tua richiesta, stop_reason ti dice perché Claude ha completato la generazione della sua risposta.
{
"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
}
}Il motivo di interruzione più comune. Indica che Claude ha terminato la sua risposta in modo naturale.
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
# Elabora la risposta completa
print(response.content[0].text)A volte Claude restituisce una risposta vuota (esattamente 2-3 token senza contenuto) con stop_reason: "end_turn". Questo accade tipicamente quando Claude interpreta che il turno dell'assistente è completo, in particolare dopo i risultati degli strumenti.
Cause comuni:
Come prevenire le risposte vuote:
# ERRATO: Aggiungere testo subito dopo 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
},
],
},
]
# CORRETTO: Invia i risultati degli strumenti direttamente senza testo aggiuntivo
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
]
# Se ottieni ancora risposte vuote dopo aver corretto la struttura dei messaggi:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-4-8", max_tokens=1024, messages=messages
)
# Verifica se la risposta è vuota
if response.stop_reason == "end_turn" and not response.content:
# ERRATO: Non limitarti a riprovare con la risposta vuota
# Questo non funzionerà perché Claude ha già deciso di aver finito
# CORRETTO: Aggiungi un prompt di continuazione in un NUOVO messaggio utente
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-opus-4-8", max_tokens=1024, messages=messages
)
return responseBest practice:
Claude si è fermato perché ha raggiunto il limite max_tokens specificato nella tua richiesta.
# Richiesta con token limitati
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=10,
messages=[{"role": "user", "content": "Explain quantum physics"}],
)
if response.stop_reason == "max_tokens":
# La risposta è stata troncata
print("Response was cut off at token limit")
# Considera di effettuare un'altra richiesta per continuareSe la risposta di Claude viene troncata a causa del raggiungimento del limite max_tokens, e la risposta troncata contiene un blocco di uso degli strumenti incompleto, dovrai riprovare la richiesta con un valore max_tokens più alto per ottenere l'uso degli strumenti completo.
Claude ha incontrato una delle tue sequenze di interruzione personalizzate.
response = client.messages.create(
model="claude-opus-4-8",
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 sta chiamando uno strumento e si aspetta che tu lo esegua.
Per la maggior parte delle implementazioni di uso degli strumenti, usa il tool runner, che gestisce automaticamente l'esecuzione degli strumenti, la formattazione dei risultati e la gestione della conversazione.
from anthropic import Anthropic
client = Anthropic()
weather_tool = {
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and state"},
},
"required": ["location"],
},
}
def execute_tool(name, tool_input):
"""Execute a tool and return the result."""
return f"Weather in {tool_input.get('location', 'unknown')}: 72°F"
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What's the weather?"}],
)
if response.stop_reason == "tool_use":
# Estrai ed esegui lo strumento
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# Restituisci il risultato a Claude per la risposta finaleRestituito quando il ciclo di campionamento lato server raggiunge il suo limite di iterazioni durante l'esecuzione di strumenti server come la ricerca web o il recupero web. Il limite predefinito è di 10 iterazioni per richiesta.
Quando questo accade, la risposta potrebbe contenere un blocco server_tool_use senza un corrispondente server_tool_result. Per consentire a Claude di completare l'elaborazione, continua la conversazione rinviando la risposta così com'è.
response = client.messages.create(
model="claude-opus-4-8",
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":
# Continua la conversazione inviando la risposta
messages = [
{"role": "user", "content": original_query},
{"role": "assistant", "content": response.content},
]
continuation = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)La tua applicazione dovrebbe gestire pause_turn in qualsiasi ciclo agente che utilizza strumenti server. Aggiungi semplicemente la risposta dell'assistente al tuo array di messaggi ed effettua un'altra richiesta API per consentire a Claude di continuare.
Claude ha rifiutato di generare una risposta. Su Claude Fable 5, i classificatori di sicurezza restituiscono questo motivo di interruzione come una normale risposta HTTP 200, non come un errore.
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}],
)
if response.stop_reason == "refusal":
# Claude ha rifiutato di rispondere
print("Claude was unable to process this request")
# Considera di riformulare o modificare la richiestaSe incontri frequentemente motivi di interruzione refusal durante l'utilizzo di Claude Sonnet 4.5 o Opus 4.1 (deprecato), puoi provare ad aggiornare le tue chiamate API per utilizzare Haiku 4.5 (claude-haiku-4-5-20251001), che ha restrizioni d'uso diverse. Scopri di più su come comprendere i filtri di sicurezza dell'API di Sonnet 4.5.
In caso di rifiuto, l'oggetto stop_details identifica la categoria di policy che lo ha attivato. Le categorie e la struttura completa della risposta di rifiuto sono trattate in Rifiuti e fallback. stop_details è null per tutti i motivi di interruzione diversi da refusal.
Una richiesta rifiutata su Claude Fable 5 può solitamente essere servita riprovando su un altro modello Claude, e Rifiuti e fallback mostra come configurare quel nuovo tentativo, lato server o nel tuo client. Credito di fallback spiega come evitare di pagare due volte il costo della cache dei prompt quando costruisci tu stesso il nuovo tentativo.
Claude si è fermato perché ha raggiunto il limite della finestra di contesto del modello. Questo ti consente di richiedere il massimo numero possibile di token senza conoscere la dimensione esatta dell'input.
# Richiesta con il massimo dei token per ottenere il più possibile
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k (Opus 4.8 supports 128k with streaming)
messages=[
{"role": "user", "content": "Large input that uses most of context window..."}
],
)
if response.stop_reason == "model_context_window_exceeded":
# La risposta ha raggiunto il limite della finestra di contesto prima di max_tokens
print("Response reached model's context window limit")
# La risposta è comunque valida ma è stata limitata dalla finestra di contestoQuesto motivo di interruzione è disponibile per impostazione predefinita in Sonnet 4.5 e nei modelli più recenti. Per i modelli precedenti, usa l'header beta model-context-window-exceeded-2025-08-26 per abilitare questo comportamento.
Prendi l'abitudine di controllare stop_reason nella tua logica di gestione delle risposte:
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:
# Gestisci end_turn e altri casi
return response.content[0].textQuando una risposta viene troncata a causa dei limiti di token o della finestra di contesto:
def handle_truncated_response(response):
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
# Opzione 1: Avvisa l'utente del limite specifico
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}"
# Opzione 2: Continua la generazione
messages = [
{"role": "user", "content": original_prompt},
{"role": "assistant", "content": response.content[0].text},
]
continuation = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=messages + [{"role": "user", "content": "Please continue"}],
)
return response.content[0].text + continuation.content[0].textQuando usi strumenti server, l'API potrebbe restituire pause_turn se il ciclo di campionamento lato server raggiunge il suo limite di iterazioni (predefinito 10). Gestisci questo caso continuando la conversazione:
def handle_server_tool_conversation(client, user_query, tools, max_continuations=5):
"""
Handle server tool conversations that may require multiple continuations.
The server runs a sampling loop when executing server tools. If the loop
reaches its iteration limit, the API returns pause_turn. Continue the
conversation by sending the response back to let Claude finish.
"""
messages = [{"role": "user", "content": user_query}]
for _ in range(max_continuations):
response = client.messages.create(
model="claude-opus-4-8", max_tokens=1024, messages=messages, tools=tools
)
if response.stop_reason != "pause_turn":
# Claude ha terminato l'elaborazione - restituisci la risposta finale
return response
# pause_turn: sostituisci l'intero elenco di messaggi per mantenere l'alternanza dei ruoli
messages = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": response.content},
]
# Raggiunto il numero massimo di continuazioni - restituisci l'ultima risposta
return responseÈ importante distinguere tra i valori di stop_reason e gli errori veri e propri:
import anthropic
from anthropic import Anthropic
client = Anthropic()
try:
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Gestisci la risposta riuscita con stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIStatusError as e:
# Gestisci gli errori effettivi
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")Quando usi lo streaming, stop_reason è:
null nell'evento iniziale message_startmessage_deltafrom anthropic import Anthropic
client = Anthropic()
with client.messages.stream(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
) 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}")Più semplice con il tool runner: L'esempio seguente mostra la gestione manuale degli strumenti. Per la maggior parte dei casi d'uso, il tool runner gestisce automaticamente l'esecuzione degli strumenti con molto meno codice.
def complete_tool_workflow(client, user_query, tools):
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-4-8", max_tokens=1024, messages=messages, tools=tools
)
if response.stop_reason == "tool_use":
# Esegui gli strumenti e continua
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Risposta finale
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-8", messages=messages, max_tokens=4096
)
full_response += response.content[0].text
if response.stop_reason != "max_tokens":
break
# Continua da dove si era interrotto
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."},
]
return full_responseCon il motivo di interruzione model_context_window_exceeded, puoi richiedere il massimo numero possibile di token senza calcolare la dimensione dell'input:
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-8",
messages=[{"role": "user", "content": prompt}],
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k
)
if response.stop_reason == "model_context_window_exceeded":
# Ottenuto il massimo numero possibile di token data la dimensione dell'input
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
elif response.stop_reason == "max_tokens":
# Ottenuto esattamente il numero di token richiesto
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Completamento naturale
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return response.content[0].textGestendo correttamente i valori di stop_reason, puoi creare applicazioni più robuste che gestiscono in modo elegante diversi scenari di risposta e offrono migliori esperienze utente.
# Verifica se la risposta è stata troncata durante l'uso degli strumenti
if response.stop_reason == "max_tokens":
# Verifica se l'ultimo blocco di contenuto è un tool_use incompleto
last_block = response.content[-1]
if last_block.type == "tool_use":
# Invia la richiesta con un valore di max_tokens più alto
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=4096, # Increased limit
messages=messages,
tools=tools,
)