Wenn Sie eine Anfrage an die Messages API stellen, enthält Claudes Antwort ein stop_reason-Feld, das angibt, warum das Modell die Generierung seiner Antwort gestoppt hat. Das Verständnis dieser Werte ist entscheidend für die Erstellung robuster Anwendungen, die verschiedene Antworttypen angemessen handhaben.
Für Details über stop_reason in der API-Antwort siehe die Messages API-Referenz.
Das stop_reason-Feld ist Teil jeder erfolgreichen Messages API-Antwort. Im Gegensatz zu Fehlern, die Ausfälle bei der Verarbeitung Ihrer Anfrage anzeigen, teilt Ihnen stop_reason mit, warum Claude seine Antwortgenerierung erfolgreich abgeschlossen hat.
{
"id": "msg_01234",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Hier ist die Antwort auf Ihre Frage..."
}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}Der häufigste Stop-Grund. Zeigt an, dass Claude seine Antwort natürlich beendet hat.
if response.stop_reason == "end_turn":
# Verarbeite die vollständige Antwort
print(response.content[0].text)Manchmal gibt Claude eine leere Antwort zurück (genau 2-3 Token ohne Inhalt) mit stop_reason: "end_turn". Dies passiert typischerweise, wenn Claude interpretiert, dass der Assistenten-Turn abgeschlossen ist, insbesondere nach Tool-Ergebnissen.
Häufige Ursachen:
Wie man leere Antworten verhindert:
# FALSCH: Text unmittelbar nach tool_result hinzufügen
messages = [
{"role": "user", "content": "Berechne die Summe von 1234 und 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": "Hier ist das Ergebnis" # Fügen Sie keinen Text nach tool_result hinzu
}
]}
]
# RICHTIG: Tool-Ergebnisse direkt ohne zusätzlichen Text senden
messages = [
{"role": "user", "content": "Berechne die Summe von 1234 und 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"
}
]} # Nur das tool_result, kein zusätzlicher Text
]
# Wenn Sie nach der Behebung des oben genannten Problems immer noch leere Antworten erhalten:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
# Prüfen, ob die Antwort leer ist
if (response.stop_reason == "end_turn" and
not response.content):
# FALSCH: Nicht einfach mit der leeren Antwort wiederholen
# Das funktioniert nicht, weil Claude bereits entschieden hat, dass es fertig ist
# RICHTIG: Eine Fortsetzungsaufforderung in einer NEUEN Benutzernachricht hinzufügen
messages.append({"role": "user", "content": "Bitte fortfahren"})
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
return responseBest Practices:
Claude stoppte, weil es das in Ihrer Anfrage angegebene max_tokens-Limit erreicht hat.
# Anfrage mit begrenzten Token
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=10,
messages=[{"role": "user", "content": "Erkläre Quantenphysik"}]
)
if response.stop_reason == "max_tokens":
# Antwort wurde abgeschnitten
print("Antwort wurde am Token-Limit abgeschnitten")
# Erwägen Sie eine weitere Anfrage zum FortfahrenClaude stieß auf eine Ihrer benutzerdefinierten Stop-Sequenzen.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
stop_sequences=["ENDE", "STOPP"],
messages=[{"role": "user", "content": "Generiere Text bis du ENDE sagst"}]
)
if response.stop_reason == "stop_sequence":
print(f"Gestoppt bei Sequenz: {response.stop_sequence}")Claude ruft ein Tool auf und erwartet, dass Sie es ausführen.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "Wie ist das Wetter?"}]
)
if response.stop_reason == "tool_use":
# Tool extrahieren und ausführen
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# Ergebnis an Claude für finale Antwort zurückgebenWird mit Server-Tools wie Websuche verwendet, wenn Claude eine langwierige Operation pausieren muss.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
messages=[{"role": "user", "content": "Suche nach den neuesten KI-Nachrichten"}]
)
if response.stop_reason == "pause_turn":
# Unterhaltung fortsetzen
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 weigerte sich, eine Antwort aufgrund von Sicherheitsbedenken zu generieren.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsichere Anfrage]"}]
)
if response.stop_reason == "refusal":
# Claude lehnte eine Antwort ab
print("Claude konnte diese Anfrage nicht verarbeiten")
# Erwägen Sie eine Umformulierung oder Änderung der AnfrageWenn Sie häufig refusal Stop-Gründe bei der Verwendung von Claude Sonnet 4.5 oder Opus 4.1 antreffen, können Sie versuchen, Ihre API-Aufrufe zu aktualisieren, um Sonnet 4 (claude-sonnet-4-20250514) zu verwenden, das andere Nutzungsbeschränkungen hat.
Claude stoppte, weil es das Kontextfenster-Limit des Modells erreicht hat. Dies ermöglicht es Ihnen, die maximal möglichen Token anzufordern, ohne die genaue Eingabegröße zu kennen.
# Anfrage mit maximalen Token, um so viel wie möglich zu erhalten
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=64000, # Maximale Ausgabe-Token des Modells
messages=[{"role": "user", "content": "Große Eingabe, die den größten Teil des Kontextfensters nutzt..."}]
)
if response.stop_reason == "model_context_window_exceeded":
# Antwort erreichte Kontextfenster-Limit vor max_tokens
print("Antwort erreichte das Kontextfenster-Limit des Modells")
# Die Antwort ist immer noch gültig, wurde aber durch das Kontextfenster begrenztDieser Stop-Grund ist standardmäßig in Sonnet 4.5 und neueren Modellen verfügbar. Für frühere Modelle verwenden Sie den Beta-Header model-context-window-exceeded-2025-08-26, um dieses Verhalten zu aktivieren.
Machen Sie es sich zur Gewohnheit, den stop_reason in Ihrer Antwortbehandlungslogik zu prüfen:
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:
# end_turn und andere Fälle behandeln
return response.content[0].textWenn eine Antwort aufgrund von Token-Limits oder Kontextfenster abgeschnitten wird:
def handle_truncated_response(response):
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
# Option 1: Benutzer über das spezifische Limit warnen
if response.stop_reason == "max_tokens":
message = "[Antwort aufgrund von max_tokens-Limit abgeschnitten]"
else:
message = "[Antwort aufgrund von Kontextfenster-Limit abgeschnitten]"
return f"{response.content[0].text}\n\n{message}"
# Option 2: Generierung fortsetzen
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": "Bitte fortfahren"}]
)
return response.content[0].text + continuation.content[0].textFür Server-Tools, die pausieren können:
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 responseEs ist wichtig, zwischen stop_reason-Werten und tatsächlichen Fehlern zu unterscheiden:
try:
response = client.messages.create(...)
# Erfolgreiche Antwort mit stop_reason behandeln
if response.stop_reason == "max_tokens":
print("Antwort wurde abgeschnitten")
except anthropic.APIError as e:
# Tatsächliche Fehler behandeln
if e.status_code == 429:
print("Rate-Limit überschritten")
elif e.status_code == 500:
print("Server-Fehler")Bei der Verwendung von Streaming ist stop_reason:
null im anfänglichen message_start-Eventmessage_delta-Eventwith 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 endete mit: {stop_reason}")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":
# Tools ausführen und fortfahren
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Finale Antwort
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
# Von dort fortfahren, wo aufgehört wurde
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Bitte fahren Sie dort fort, wo Sie aufgehört haben."}
]
return full_responseMit dem model_context_window_exceeded Stop-Grund können Sie die maximal möglichen Token anfordern, ohne die Eingabegröße zu berechnen:
def get_max_possible_tokens(client, prompt):
"""
So viele Token wie möglich innerhalb des Kontextfensters des Modells erhalten
ohne die Eingabe-Token-Anzahl berechnen zu müssen
"""
response = client.messages.create(
model="claude-sonnet-4-5",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000 # Auf maximale Ausgabe-Token des Modells setzen
)
if response.stop_reason == "model_context_window_exceeded":
# Maximale mögliche Token bei gegebener Eingabegröße erhalten
print(f"{response.usage.output_tokens} Token generiert (Kontextlimit erreicht)")
elif response.stop_reason == "max_tokens":
# Genau die angeforderten Token erhalten
print(f"{response.usage.output_tokens} Token generiert (max_tokens erreicht)")
else:
# Natürliche Vervollständigung
print(f"{response.usage.output_tokens} Token generiert (natürliche Vervollständigung)")
return response.content[0].textDurch die ordnungsgemäße Behandlung von stop_reason-Werten können Sie robustere Anwendungen erstellen, die verschiedene Antwortszenarien elegant handhaben und bessere Benutzererfahrungen bieten.