Каждый ответ Messages API включает поле stop_reason, которое сообщает, почему Claude прекратил генерацию. Проверяйте это поле, чтобы решить, использовать ли ответ как есть, продолжить разговор, повторить запрос или переключиться на другую модель.
Полную схему ответа см. в справочнике Messages API.
| Значение | Когда возникает | Что делать |
|---|---|---|
end_turn | Claude естественным образом завершил свой ответ. | Используйте ответ. |
max_tokens | Ответ достиг вашего лимита max_tokens. | Увеличьте max_tokens или продолжите ответ. |
stop_sequence | Claude выдал одну из ваших stop_sequences. | Прочитайте stop_sequence, чтобы узнать, какая из них сработала. |
tool_use | Claude вызывает инструмент. | Запустите инструмент и верните результат. Вызов серверного инструмента, для которого ещё нет блока результата, завершается в последующем ответе. |
pause_turn | Цикл серверных инструментов достиг лимита итераций. | Отправьте содержимое ассистента обратно, чтобы продолжить. |
refusal | Claude отказался отвечать. | Прочитайте stop_details и повторите запрос на резервной модели. |
model_context_window_exceeded | Ответ заполнил контекстное окно модели. | Считайте ответ усечённым. |
Поле stop_reason является частью каждого успешного ответа Messages API. В отличие от ошибок, которые указывают на сбои при обработке вашего запроса, stop_reason сообщает, почему Claude завершил генерацию ответа.
{
"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
}
}Наиболее распространённая причина остановки. Указывает, что Claude естественным образом завершил свой ответ.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
# Обработка полного ответа
for block in response.content:
if block.type == "text":
print(block.text)Claude остановился, потому что достиг лимита max_tokens, указанного в вашем запросе.
client = anthropic.Anthropic()
# Запрос с ограниченным числом токенов
response = client.messages.create(
model="claude-opus-5",
max_tokens=10,
messages=[{"role": "user", "content": "Explain quantum physics"}],
)
if response.stop_reason == "max_tokens":
# Ответ был усечён
print("Response was cut off at token limit")
# Рассмотрите возможность отправить ещё один запрос для продолженияClaude встретил одну из ваших пользовательских последовательностей остановки.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-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 вызывает инструмент и ожидает, что вы его запустите.
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",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
)
if response.stop_reason == "tool_use":
# Извлечь и выполнить инструмент
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Вернуть результат Claude для окончательного ответаОтвет tool_use также может содержать блок server_tool_use, чей id не имеет соответствующего блока результата. Этот вызов серверного инструмента не завершён, и данный ответ не содержит его результата. В типичном случае Claude вызывает серверный инструмент и один из ваших клиентских инструментов в одной группе параллельных вызовов инструментов: API возвращает ответ, не запуская серверный инструмент, чтобы вы могли сначала запустить клиентские инструменты. Другого маркера для этого состояния нет; определяйте его, проверяя id каждого блока server_tool_use или mcp_tool_use на наличие соответствующего блока результата.
{
"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" }
}
]
}Продолжение — это пользовательское сообщение из блоков tool_result, по одному на каждый блок tool_use в ответе (см. Обработка вызовов инструментов), с двумя дополнительными правилами: это сообщение не должно содержать ничего, кроме блоков tool_result, а запрос должен сохранять тот же массив tools. Запрос на возобновление, который больше не определяет ожидающий серверный инструмент, завершается ошибкой 400, сообщение которой заканчивается на but no `web_search` tool was provided. API присоединяет ваши результаты к ещё открытому ходу ассистента, запускает отложенный серверный инструмент (для приостановленного выполнения кода — возобновляет его) и продолжает ход. Для серверного инструмента, вызванного Claude напрямую, content следующего ответа начинается с блока результата, который отвечает на id блока server_tool_use из предыдущего ответа.
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
"content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
}
]
}Добавление чего-либо после блоков tool_result в этом пользовательском сообщении, например текста, завершает ход ассистента; для серверного инструмента, вызванного Claude напрямую, запрос затем завершается ошибкой 400 invalid_request_error, в которой указан неразрешённый серверный инструмент:
`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` blockПропуск tool_result или размещение его после другого содержимого приводит к более ранней ошибке — стандартной tool_use ids were found without tool_result blocks immediately after. Чтобы передать Claude дополнительные входные данные, отправьте их отдельным пользовательским сообщением после завершения хода.
Возвращается, когда цикл выборки на стороне сервера достигает лимита итераций при выполнении серверных инструментов, таких как веб-поиск. Лимит по умолчанию — 10 итераций на запрос.
Когда это происходит, ответ может содержать блок server_tool_use без соответствующего блока результата. Чтобы позволить Claude завершить обработку, продолжите разговор, отправив ответ обратно как есть. Ответ, который оставляет клиентский блок tool_use в ожидании вас, никогда не имеет stop_reason со значением pause_turn: когда Claude останавливается для вызова ваших инструментов, stop_reason равен tool_use, и вы продолжаете его, отправляя клиентские блоки tool_result, а не сам ответ.
response = client.messages.create(
model="claude-opus-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":
# Продолжите разговор, отправив ответ обратно
messages = [
{"role": "user", "content": "Search for latest AI news"},
{"role": "assistant", "content": response.content},
]
continuation = client.messages.create(
model="claude-opus-5",
max_tokens=4096,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)Claude отказался генерировать ответ. Классификаторы безопасности возвращают эту причину остановки как обычный ответ HTTP 200, а не как ошибку.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}],
)
if response.stop_reason == "refusal":
# Claude отказался отвечать
print("Claude was unable to process this request")
# Рассмотрите возможность переформулировать или изменить запросПри отказе объект stop_details идентифицирует категорию политики, которая его вызвала. Категории и полная форма ответа при отказе описаны на странице Отказы и резервные варианты. stop_details равен null для всех причин остановки, кроме refusal.
Отклонённый запрос на Claude Fable 5 или Claude Opus 5 обычно можно обслужить, повторив его на другой модели Claude, и страница Отказы и резервные варианты показывает, как настроить такой повтор — на стороне сервера или в вашем клиенте. Страница Кредит за резервный запрос описывает, как избежать двойной оплаты стоимости кэша подсказок, когда вы реализуете повтор самостоятельно.
Claude остановился, потому что достиг лимита контекстного окна модели. Это позволяет вам запрашивать максимально возможное количество токенов, не зная точного размера входных данных.
# Запрос с максимальным числом токенов, чтобы получить как можно больше
response = client.beta.messages.create(
model="claude-opus-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":
# Ответ достиг предела контекстного окна раньше, чем max_tokens
print("Response reached model's context window limit")
# Ответ по-прежнему корректен, но был ограничен контекстным окномВозьмите за правило проверять stop_reason в вашей логике обработки ответов:
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 и других случаев
return next(
(block.text for block in response.content if block.type == "text"), ""
)Когда ответ усечён из-за лимитов токенов или контекстного окна, добавьте уведомление, чтобы читатель знал, что вывод неполный. Чтобы вместо этого продолжить генерацию с места, где ответ прервался, см. Обеспечение полных ответов.
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 textПри использовании серверных инструментов API может вернуть pause_turn, если цикл выборки на стороне сервера достигает лимита итераций (по умолчанию 10). Обработайте это, продолжив разговор:
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", max_tokens=4096, messages=messages, tools=tools
)
if response.stop_reason != "pause_turn":
# Claude завершил обработку — возвращаем итоговый ответ
return response
# pause_turn: заменяем весь список сообщений, чтобы сохранить чередование ролей
messages = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": response.content},
]
# Достигнут максимум продолжений — возвращаем последний ответ
return responseВажно различать значения stop_reason и фактические ошибки:
client = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Обработка успешного ответа со stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIStatusError as e:
# Обработка фактических ошибок
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")При использовании потоковой передачи stop_reason:
null в начальном событии message_startmessage_deltaclient = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-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}")def complete_tool_workflow(client, user_query, tools):
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-5", max_tokens=1024, messages=messages, tools=tools
)
if response.stop_reason == "tool_use":
# Выполнить инструменты и продолжить
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Окончательный ответ
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-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
# Продолжить с места остановки
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."},
]
return full_responseС причиной остановки model_context_window_exceeded вы можете запрашивать максимально возможное количество токенов без вычисления размера входных данных:
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",
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":
# Получено максимально возможное число токенов при данном размере входных данных
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
elif response.stop_reason == "max_tokens":
# Получено ровно запрошенное число токенов
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Естественное завершение
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return next((block.text for block in response.content if block.type == "text"), "")Повторяйте отклонённые запросы на резервной модели — на стороне сервера или в вашем клиенте.
Позвольте SDK управлять циклом tool_use, форматированием результатов и повторами за вас.
Считывайте stop_reason из события message_delta при потоковой передаче.
Обрабатывайте HTTP-ошибки 4xx и 5xx, которые отличаются от причин остановки.
Was this page helpful?