Quando fai una richiesta all’API Messages, la risposta di Claude include un campo stop_reason che indica perché il modello ha smesso di generare la sua risposta. Comprendere questi valori è cruciale per costruire applicazioni robuste che gestiscono diversi tipi di risposta in modo appropriato.
Per i dettagli su stop_reason nella risposta dell’API, vedi il riferimento API Messages.
Cos’è stop_reason?
Il campo stop_reason fa parte di ogni risposta di successo dell’API Messages. A differenza degli errori, che indicano fallimenti nell’elaborazione della tua richiesta, stop_reason ti dice perché Claude ha completato con successo la generazione della sua risposta.
{
"id": "msg_01234",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Ecco la risposta alla tua domanda..."
}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}
Valori del motivo di arresto
end_turn
Il motivo di arresto più comune. Indica che Claude ha finito la sua risposta naturalmente.
if response.stop_reason == "end_turn":
# Elabora la risposta completa
print(response.content[0].text)
Risposte vuote con end_turn
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, particolarmente dopo i risultati degli strumenti.
Cause comuni:
- Aggiungere blocchi di testo immediatamente dopo i risultati degli strumenti (Claude impara ad aspettarsi che l’utente inserisca sempre testo dopo i risultati degli strumenti, quindi termina il suo turno per seguire il pattern)
- Inviare indietro la risposta completata di Claude senza aggiungere nulla (Claude ha già deciso che ha finito, quindi rimarrà finito)
Come prevenire risposte vuote:
# SCORRETTO: Aggiungere testo immediatamente dopo tool_result
messages = [
{"role": "user", "content": "Calcola la somma di 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": "Ecco il risultato" # Non aggiungere testo dopo tool_result
}
]}
]
# CORRETTO: Invia i risultati degli strumenti direttamente senza testo aggiuntivo
messages = [
{"role": "user", "content": "Calcola la somma di 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"
}
]} # Solo il tool_result, nessun testo aggiuntivo
]
# Se ottieni ancora risposte vuote dopo aver corretto quanto sopra:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
# Controlla se la risposta è vuota
if (response.stop_reason == "end_turn" and
not response.content:
# SCORRETTO: Non riprovare semplicemente con la risposta vuota
# Questo non funzionerà perché Claude ha già deciso che ha finito
# CORRETTO: Aggiungi un prompt di continuazione in un NUOVO messaggio utente
messages.append({"role": "user", "content": "Per favore continua"})
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
return response
Migliori pratiche:
- Non aggiungere mai blocchi di testo immediatamente dopo i risultati degli strumenti - Questo insegna a Claude ad aspettarsi input dell’utente dopo ogni uso di strumento
- Non riprovare risposte vuote senza modifiche - Semplicemente inviare indietro la risposta vuota non aiuterà
- Usa prompt di continuazione come ultima risorsa - Solo se le correzioni sopra non risolvono il problema
max_tokens
Claude si è fermato perché ha raggiunto il limite max_tokens specificato nella tua richiesta.
# Richiesta con token limitati
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=10,
messages=[{"role": "user", "content": "Spiega la fisica quantistica"}]
)
if response.stop_reason == "max_tokens":
# La risposta è stata troncata
print("La risposta è stata tagliata al limite di token")
# Considera di fare un'altra richiesta per continuare
stop_sequence
Claude ha incontrato una delle tue sequenze di arresto personalizzate.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
stop_sequences=["END", "STOP"],
messages=[{"role": "user", "content": "Genera testo finché non dici END"}]
)
if response.stop_reason == "stop_sequence":
print(f"Fermato alla sequenza: {response.stop_sequence}")
Claude sta chiamando uno strumento e si aspetta che tu lo esegua.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "Che tempo fa?"}]
)
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 finale
pause_turn
Usato con strumenti server come la ricerca web quando Claude deve mettere in pausa un’operazione di lunga durata.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
messages=[{"role": "user", "content": "Cerca le ultime notizie sull'IA"}]
)
if response.stop_reason == "pause_turn":
# Continua la conversazione
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
Claude ha rifiutato di generare una risposta a causa di preoccupazioni di sicurezza.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "[Richiesta non sicura]"}]
)
if response.stop_reason == "refusal":
# Claude ha rifiutato di rispondere
print("Claude non è stato in grado di elaborare questa richiesta")
# Considera di riformulare o modificare la richiesta
Se incontri frequentemente motivi di arresto refusal mentre usi Claude Sonnet 4.5 o Opus 4.1, puoi provare ad aggiornare le tue chiamate API per usare Sonnet 4 (claude-sonnet-4-20250514), che ha diverse restrizioni d’uso. Scopri di più su comprendere i filtri di sicurezza API di Sonnet 4.5.
model_context_window_exceeded
Claude si è fermato perché ha raggiunto il limite della finestra di contesto del modello. Questo ti permette di richiedere il massimo numero possibile di token senza conoscere la dimensione esatta dell’input.
# Richiesta con token massimi per ottenere il più possibile
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=64000, # Token di output massimi del modello
messages=[{"role": "user", "content": "Input grande che usa la maggior parte della finestra di contesto..."}]
)
if response.stop_reason == "model_context_window_exceeded":
# La risposta ha raggiunto il limite della finestra di contesto prima di max_tokens
print("La risposta ha raggiunto il limite della finestra di contesto del modello")
# La risposta è ancora valida ma è stata limitata dalla finestra di contesto
Questo motivo di arresto è disponibile di default in Sonnet 4.5 e modelli più recenti. Per modelli precedenti, usa l’header beta model-context-window-exceeded-2025-08-26 per abilitare questo comportamento.
Migliori pratiche per gestire i motivi di arresto
1. Controlla sempre stop_reason
Prendi l’abitudine di controllare il 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].text
2. Gestisci le risposte troncate con grazia
Quando una risposta è troncata a causa di limiti di token o 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 = "[Risposta troncata a causa del limite max_tokens]"
else:
message = "[Risposta troncata a causa del limite della finestra di contesto]"
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-sonnet-4-5",
max_tokens=1024,
messages=messages + [{"role": "user", "content": "Per favore continua"}]
)
return response.content[0].text + continuation.content[0].text
3. Implementa logica di retry per pause_turn
Per strumenti server che potrebbero mettere in pausa:
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
Motivi di arresto vs. errori
È importante distinguere tra valori stop_reason ed errori effettivi:
Motivi di arresto (risposte di successo)
- Parte del corpo della risposta
- Indicano perché la generazione si è fermata normalmente
- La risposta contiene contenuto valido
Errori (richieste fallite)
- Codici di stato HTTP 4xx o 5xx
- Indicano fallimenti nell’elaborazione della richiesta
- La risposta contiene dettagli dell’errore
try:
response = client.messages.create(...)
# Gestisci risposta di successo con stop_reason
if response.stop_reason == "max_tokens":
print("La risposta è stata troncata")
except anthropic.APIError as e:
# Gestisci errori effettivi
if e.status_code == 429:
print("Limite di velocità superato")
elif e.status_code == 500:
print("Errore del server")
Considerazioni sullo streaming
Quando usi lo streaming, stop_reason è:
null nell’evento iniziale message_start
- Fornito nell’evento
message_delta
- Non fornito in nessun altro 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 terminato con: {stop_reason}")
Pattern comuni
Gestione dei flussi di lavoro degli strumenti
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":
# Esegui 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 response
Assicurare risposte complete
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
# Continua da dove si era fermato
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Per favore continua da dove ti eri fermato."}
]
return full_response
Con il motivo di arresto 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):
"""
Ottieni il maggior numero possibile di token entro la finestra di contesto del modello
senza dover calcolare il conteggio dei token di input
"""
response = client.messages.create(
model="claude-sonnet-4-5",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000 # Imposta ai token di output massimi del modello
)
if response.stop_reason == "model_context_window_exceeded":
# Ottenuto il massimo numero possibile di token data la dimensione dell'input
print(f"Generati {response.usage.output_tokens} token (limite di contesto raggiunto)")
elif response.stop_reason == "max_tokens":
# Ottenuto esattamente i token richiesti
print(f"Generati {response.usage.output_tokens} token (max_tokens raggiunto)")
else:
# Completamento naturale
print(f"Generati {response.usage.output_tokens} token (completamento naturale)")
return response.content[0].text
Gestendo correttamente i valori stop_reason, puoi costruire applicazioni più robuste che gestiscono con grazia diversi scenari di risposta e forniscono migliori esperienze utente.