Motivi di arresto e fallback
Scopri cosa significa ciascun valore di stop_reason e come gestire il troncamento, l'uso degli strumenti, i turni in pausa e i rifiuti nella tua applicazione.
Ogni risposta della Messages API include un campo stop_reason che ti indica perché Claude ha smesso di generare. Controlla questo campo per decidere se usare la risposta così com'è, continuare la conversazione, riprovare o ripiegare su un altro modello.
Per lo schema completo della risposta, consulta il riferimento della Messages API.
Riferimento rapido
| Valore | Quando si verifica | Cosa fare |
|---|---|---|
end_turn | Claude ha terminato la sua risposta in modo naturale. | Usa la risposta. |
max_tokens | La risposta ha raggiunto il tuo limite max_tokens. | Aumenta max_tokens oppure continua la risposta. |
stop_sequence | Claude ha emesso una delle tue stop_sequences. | Leggi stop_sequence per vedere quale si è attivata. |
tool_use | Claude sta chiamando uno strumento. | Esegui lo strumento e restituisci il risultato. Una chiamata a uno strumento server a cui manca ancora il blocco di risultato viene completata in una risposta successiva. |
pause_turn | Un ciclo di strumenti server ha raggiunto il suo limite di iterazioni. | Rimanda indietro il contenuto dell'assistente per continuare. |
refusal | Claude ha rifiutato di rispondere. | Leggi stop_details e riprova su un modello di fallback. |
model_context_window_exceeded | La risposta ha riempito la finestra di contesto del modello. | Tratta la risposta come troncata. |
Il campo stop_reason
Il campo stop_reason fa parte di ogni risposta riuscita della Messages API. A differenza degli errori, che indicano fallimenti nell'elaborazione della tua richiesta, stop_reason ti indica 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,
"stop_details": null,
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}Valori di stop reason
end_turn
Il motivo di arresto più comune. Indica che Claude ha terminato la sua risposta in modo naturale.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
# Elabora la risposta completa
for block in response.content:
if block.type == "text":
print(block.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:
- Aggiungere blocchi di testo immediatamente dopo i risultati degli strumenti (Claude impara ad aspettarsi che l'utente inserisca sempre del testo dopo i risultati degli strumenti, quindi termina il suo turno per seguire lo schema)
- Rimandare indietro la risposta completata di Claude senza aggiungere nulla (Claude ha già stabilito di aver finito, quindi rimarrà tale)
Come prevenire le risposte vuote:
# ERRATO: aggiunta di 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, aggiungi un prompt di continuazione in un nuovo messaggio utente invece di riprovare con la risposta vuota:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-5-5", 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
# 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-5-5", max_tokens=1024, messages=messages
)
return responseBest practice:
- Non aggiungere mai blocchi di testo immediatamente dopo i risultati degli strumenti: Questo insegna a Claude ad aspettarsi un input dell'utente dopo ogni uso degli strumenti.
- Non riprovare le risposte vuote senza modifiche: Rimandare indietro la risposta vuota non aiuterà.
- Usa i prompt di continuazione come ultima risorsa: Solo se queste correzioni non risolvono il problema.
max_tokens
Claude si è fermato perché ha raggiunto il limite max_tokens specificato nella tua richiesta.
client = anthropic.Anthropic()
# Richiesta con token limitati
response = client.messages.create(
model="claude-opus-5-5",
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")
# Valuta di effettuare un'altra richiesta per continuareSe la risposta di Claude viene interrotta perché ha raggiunto il 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.
# 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-5-5",
max_tokens=4096, # Increased limit
messages=messages,
tools=tools,
)stop_sequence
Claude ha incontrato una delle tue sequenze di arresto personalizzate.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
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}")tool_use
Claude sta chiamando uno strumento e si aspetta che tu lo esegua.
client = anthropic.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-opus-5-5",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
)
if response.stop_reason == "tool_use":
# Estrai ed esegui lo strumento
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Restituisci il risultato a Claude per la risposta finaleUna risposta tool_use può anche contenere un blocco server_tool_use il cui id non ha un blocco di risultato corrispondente. Quella chiamata allo strumento server non è terminata, e questa risposta non ne contiene il risultato. Nel caso comune, Claude chiama uno strumento server e uno dei tuoi strumenti client nello stesso gruppo di chiamate parallele agli strumenti: l'API restituisce la risposta senza eseguire lo strumento server in modo che tu possa eseguire prima gli strumenti client. Non esiste alcun altro indicatore per questo stato; rilevalo controllando, per ogni blocco server_tool_use o mcp_tool_use, se il suo id ha un blocco di risultato corrispondente.
{
"stop_reason": "tool_use",
"content": [
{
"type": "server_tool_use",
"id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
"name": "web_search",
"input": { "query": "example article" }
},
{
"type": "tool_use",
"id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
"name": "run_command",
"input": { "command": "uname -a" }
}
]
}La continuazione è un messaggio utente di blocchi tool_result, uno per ogni blocco tool_use nella risposta (vedi Gestire le chiamate agli strumenti), con due regole aggiuntive: quel messaggio non deve contenere nient'altro che i blocchi tool_result, e la richiesta deve mantenere lo stesso array tools. Una richiesta di ripresa che non definisce più lo strumento server in attesa fallisce con un 400 il cui messaggio termina con but no `web_search` tool was provided. L'API allega i tuoi risultati al turno dell'assistente ancora aperto, esegue lo strumento server differito (per l'esecuzione di codice in pausa, la riprende) e continua il turno. Per uno strumento server chiamato direttamente da Claude, il content della risposta successiva inizia con il blocco di risultato che risponde all'id del server_tool_use della risposta precedente.
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
"content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
}
]
}Aggiungere qualsiasi cosa dopo i blocchi tool_result in quel messaggio utente, come del testo, termina il turno dell'assistente; per uno strumento server chiamato direttamente da Claude, la richiesta fallisce quindi con un 400 invalid_request_error che indica lo strumento server non risolto:
`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` blockOmettere un tool_result, o metterne uno dopo altro contenuto, fallisce invece prima con l'errore standard tool_use ids were found without tool_result blocks immediately after. Per fornire a Claude ulteriore input, invialo come messaggio utente separato dopo il completamento del turno.
pause_turn
Restituito quando il ciclo di campionamento lato server raggiunge il suo limite di iterazioni durante l'esecuzione di strumenti server come la ricerca web. Il limite predefinito è di 10 iterazioni per richiesta.
Quando ciò accade, la risposta può contenere un blocco server_tool_use senza un blocco di risultato corrispondente. Per consentire a Claude di terminare l'elaborazione, continua la conversazione rimandando indietro la risposta così com'è. Una risposta che lascia un blocco tool_use client in attesa di te non ha mai uno stop_reason pari a pause_turn: quando Claude si ferma per chiamare i tuoi strumenti, stop_reason è tool_use, e la continui inviando i blocchi tool_result client invece della risposta stessa.
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=4096,
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 di nuovo la risposta
messages = [
{"role": "user", "content": "Search for latest AI news"},
{"role": "assistant", "content": response.content},
]
continuation = client.messages.create(
model="claude-opus-5-5",
max_tokens=4096,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)refusal
Claude ha rifiutato di generare una risposta. I classificatori di sicurezza restituiscono questo motivo di arresto come una normale risposta HTTP 200, non come un errore.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}],
)
if response.stop_reason == "refusal":
# Claude si è rifiutato di rispondere
print("Claude was unable to process this request")
# Valuta di riformulare o modificare la richiestaIn caso di rifiuto, l'oggetto stop_details identifica la categoria di policy che lo ha attivato. Le categorie e la forma completa della risposta di rifiuto sono trattate in Rifiuti e fallback. stop_details è null per tutti i motivi di arresto diversi da refusal.
Una richiesta rifiutata su Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5 o Claude Sonnet 5.5 può solitamente essere servita riprovando su un altro modello Claude. Rifiuti e fallback mostra come configurare quel nuovo tentativo, lato server o nel tuo client. Se costruisci tu stesso il nuovo tentativo a partire da Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5 o Claude Sonnet 5.5, la pagina sul credito di fallback spiega come evitare di pagare due volte il costo della cache dei prompt.
model_context_window_exceeded
Claude si è fermato perché ha raggiunto il limite della "context window" (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 numero massimo di token per ottenere il più possibile
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k
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 contestoBest practice per la gestione dei motivi di arresto
Controlla sempre stop_reason
Prendi l'abitudine di controllare lo stop_reason nella tua logica di gestione delle risposte:
def handle_response(response):
match response.stop_reason:
case "tool_use":
return handle_tool_use(response)
case "max_tokens":
return handle_truncation(response)
case "model_context_window_exceeded":
return handle_context_limit(response)
case "pause_turn":
return handle_pause(response)
case "refusal":
return handle_refusal(response)
case _:
# Gestisci end_turn e gli altri casi
return next(
(block.text for block in response.content if block.type == "text"),
"",
)Gestisci le risposte troncate in modo appropriato
Quando una risposta viene troncata a causa dei limiti di token o della finestra di contesto, aggiungi un avviso in modo che il lettore sappia che l'output è incompleto. Per continuare invece a generare dal punto in cui la risposta si è interrotta, consulta Garantire risposte complete.
def handle_truncated_response(response):
text = next((block.text for block in response.content if block.type == "text"), "")
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
if response.stop_reason == "max_tokens":
note = "[Response truncated due to max_tokens limit]"
else:
note = "[Response truncated due to context window limit]"
return f"{text}\n\n{note}"
return textImplementa la logica di nuovo tentativo per pause_turn
Quando usi gli strumenti server, l'API può restituire pause_turn se il ciclo di campionamento lato server raggiunge il suo limite di iterazioni (predefinito 10). Gestiscilo 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-5-5",
max_tokens=4096,
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 dei 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 responseMotivi di arresto vs. errori
È importante distinguere tra i valori di stop_reason e gli errori veri e propri:
Motivi di arresto (risposte riuscite)
- Fanno 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 i dettagli dell'errore
client = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-opus-5-5",
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
match e.status_code:
case 429:
print("Rate limit exceeded")
case 500:
print("Server error")Considerazioni sullo streaming
Quando usi lo streaming, stop_reason è:
nullnell'evento inizialemessage_start- Fornito nell'evento
message_delta - Non fornito in nessun altro evento
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-5-5",
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}")Pattern comuni
Gestione dei flussi di lavoro con uso degli strumenti
def complete_tool_workflow(client, user_query, tools):
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-5-5",
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 responseGarantire 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-opus-5-5", messages=messages, max_tokens=4096
)
full_response += next(
(block.text for block in response.content if block.type == "text"), ""
)
if response.stop_reason != "max_tokens":
break
# Riprendi 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_responseOttenere il massimo numero di token senza conoscere la dimensione dell'input
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):
"""
Get as many tokens as possible within the model's context window
without needing to calculate input token count
"""
response = client.beta.messages.create(
model="claude-opus-5-5",
messages=[{"role": "user", "content": prompt}],
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k
)
match response.stop_reason:
case "model_context_window_exceeded":
# Ottenuto il numero massimo di token possibile data la dimensione dell'input
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
case "max_tokens":
# Ottenuto esattamente il numero di token richiesto
print(
f"Generated {response.usage.output_tokens} tokens (max_tokens reached)"
)
case _:
# Completamento naturale
print(
f"Generated {response.usage.output_tokens} tokens (natural completion)"
)
return next((block.text for block in response.content if block.type == "text"), "")Prossimi passi
Riprova le richieste rifiutate su un modello di fallback, lato server o nel tuo client.
Lascia che l'SDK gestisca per te il ciclo tool_use, la formattazione dei risultati e i nuovi tentativi.
Leggi stop_reason dall'evento message_delta durante lo streaming.
Gestisci gli errori HTTP 4xx e 5xx, che sono distinti dai motivi di arresto.
Was this page helpful?