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 beendet hat. Das Verständnis dieser Werte ist entscheidend für die Erstellung robuster Anwendungen, die verschiedene Antworttypen angemessen verarbeiten.
Weitere Informationen zu stop_reason in der API-Antwort finden Sie in der Messages API-Referenz.
Das stop_reason-Feld ist Teil jeder erfolgreichen Messages API-Antwort. Im Gegensatz zu Fehlern, die auf Fehler bei der Verarbeitung Ihrer Anfrage hindeuten, teilt Ihnen stop_reason mit, warum Claude die Generierung seiner Antwort erfolgreich abgeschlossen hat.
{
"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
}
}Der häufigste Stop-Grund. Zeigt an, dass Claude seine Antwort natürlich beendet hat.
if response.stop_reason == "end_turn":
# Process the complete response
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 geschieht normalerweise, wenn Claude interpretiert, dass der Assistent-Turn abgeschlossen ist, besonders nach Tool-Ergebnissen.
Häufige Ursachen:
So verhindern Sie leere Antworten:
# INCORRECT: Adding text immediately after 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
}
]}
]
# CORRECT: Send tool results directly without additional text
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
]
# If you still get empty responses after fixing the above:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
# Check if response is empty
if (response.stop_reason == "end_turn" and
not response.content):
# INCORRECT: Don't just retry with the empty response
# This won't work because Claude already decided it's done
# CORRECT: Add a continuation prompt in a NEW user message
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=messages
)
return responseBest Practices:
Claude hat gestoppt, weil es das in Ihrer Anfrage angegebene max_tokens-Limit erreicht hat.
# Request with limited tokens
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=10,
messages=[{"role": "user", "content": "Explain quantum physics"}]
)
if response.stop_reason == "max_tokens":
# Response was truncated
print("Response was cut off at token limit")
# Consider making another request to continueClaude ist auf eine Ihrer benutzerdefinierten Stop-Sequenzen gestoßen.
response = client.messages.create(
model="claude-sonnet-4-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}")Claude ruft ein Tool auf und erwartet, dass Sie es ausführen.
Für die meisten Tool-Use-Implementierungen empfehlen wir die Verwendung des Tool-Runners, der die Tool-Ausführung, Ergebnis-Formatierung und Gesprächsverwaltung automatisch verarbeitet.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What's the weather?"}]
)
if response.stop_reason == "tool_use":
# Extract and execute the tool
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# Return result to Claude for final responseWird mit Server-Tools wie Web-Suche verwendet, wenn Claude einen langwierigen Vorgang unterbrechen 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": "Search for latest AI news"}]
)
if response.stop_reason == "pause_turn":
# Continue the conversation
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 hat sich geweigert, eine Antwort zu generieren, da Sicherheitsbedenken bestehen.
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}]
)
if response.stop_reason == "refusal":
# Claude declined to respond
print("Claude was unable to process this request")
# Consider rephrasing or modifying the requestWenn Sie bei der Verwendung von Claude Sonnet 4.5 oder Opus 4.1 häufig auf refusal-Stop-Gründe stoßen, können Sie versuchen, Ihre API-Aufrufe auf Sonnet 4 (claude-sonnet-4-20250514) zu aktualisieren, das unterschiedliche Nutzungsbeschränkungen hat. Erfahren Sie mehr über Verständnis der API-Sicherheitsfilter von Sonnet 4.5.
Um mehr über Ablehnungen zu erfahren, die durch API-Sicherheitsfilter für Claude Sonnet 4.5 ausgelöst werden, siehe Verständnis der API-Sicherheitsfilter von Sonnet 4.5.
Claude hat gestoppt, weil es das Kontext-Fenster-Limit des Modells erreicht hat. Dies ermöglicht es Ihnen, die maximale Anzahl von Token anzufordern, ohne die genaue Eingabegröße zu kennen.
# Request with maximum tokens to get as much as possible
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=64000, # Model's maximum output tokens
messages=[{"role": "user", "content": "Large input that uses most of context window..."}]
)
if response.stop_reason == "model_context_window_exceeded":
# Response hit context window limit before max_tokens
print("Response reached model's context window limit")
# The response is still valid but was limited by context windowDieser 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 überprü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:
# Handle end_turn and other cases
return response.content[0].textWenn eine Antwort aufgrund von Token-Limits oder Kontext-Fenster gekürzt wird:
def handle_truncated_response(response):
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
# Option 1: Warn the user about the specific limit
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}"
# Option 2: Continue generation
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": "Please continue"}]
)
return response.content[0].text + continuation.content[0].textFür Server-Tools, die möglicherweise unterbrochen werden:
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(...)
# Handle successful response with stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIError as e:
# Handle actual errors
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")Bei Verwendung von Streaming ist stop_reason:
null im initialen message_start-Eventmessage_delta-Event vorhandenwith 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 ended with: {stop_reason}")Einfacher mit Tool-Runner: Das folgende Beispiel zeigt manuelle Tool-Handhabung. Für die meisten Anwendungsfälle verarbeitet der Tool-Runner die Tool-Ausführung automatisch mit viel weniger Code.
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":
# Execute tools and continue
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Final response
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
# Continue from where it left off
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."}
]
return full_responseMit dem model_context_window_exceeded-Stop-Grund können Sie die maximale Anzahl von Token anfordern, ohne die Eingabe-Token-Anzahl zu berechnen:
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-sonnet-4-5",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000 # Set to model's maximum output tokens
)
if response.stop_reason == "model_context_window_exceeded":
# Got the maximum possible tokens given input size
print(f"Generated {response.usage.output_tokens} tokens (context limit reached)")
elif response.stop_reason == "max_tokens":
# Got exactly the requested tokens
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Natural completion
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return response.content[0].textDurch die ordnungsgemäße Behandlung von stop_reason-Werten können Sie robustere Anwendungen erstellen, die verschiedene Antwortszenarien elegant verarbeiten und bessere Benutzererfahrungen bieten.