Каждый ответ 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 вызывает инструмент и ожидает, что вы его запустите.
Для большинства реализаций использования инструментов используйте tool runner, который автоматически обрабатывает выполнение инструментов, форматирование результатов и управление разговором.
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 на наличие соответствующего блока результата.
При программном вызове инструментов та же форма ответа означает нечто иное. Клиентский блок tool_use исходит из кода, выполняющегося в инструменте code_execution, а не напрямую от Claude, и его поле caller указывает блок code_execution, который его вызвал. Этот код уже запущен: он приостановлен в ожидании ваших блоков tool_result, и их отправка возобновляет выполнение вместо запуска отложенного инструмента. Собственный блок результата блока code_execution приходит после завершения кода, что может занять более одного раунда результатов инструментов. Последующее пользовательское сообщение само по себе одинаково в обоих случаях; при программном вызове инструментов также передайте обратно id из поля container ответа, как показано на этой странице.
{
"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"}],
)Ваше приложение должно обрабатывать pause_turn в любом агентном цикле, использующем серверные инструменты. Добавьте ответ ассистента в ваш массив сообщений и сделайте ещё один запрос к API, чтобы позволить Claude продолжить.
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")
# Попробуйте переформулировать или изменить запросЕсли вы часто сталкиваетесь с причинами остановки refusal при использовании Claude Sonnet 4.5 или Opus 4.1 (устарела; см. Устаревание моделей), вы можете попробовать обновить ваши вызовы API для использования Haiku 4.5 (claude-haiku-4-5-20251001), у которой другие ограничения использования. Узнайте больше о понимании фильтров безопасности API Sonnet 4.5.
При отказе объект stop_details идентифицирует категорию политики, которая его вызвала. Категории и полная форма ответа с отказом описаны на странице Отказы и резервный переход. stop_details равен null для всех причин остановки, кроме refusal.
Отклонённый запрос на Claude Fable 5 или Claude Opus 5 обычно может быть обслужен повторной попыткой на другой модели Claude, и страница Отказы и резервный переход показывает, как настроить эту повторную попытку на стороне сервера или в вашем клиенте. Страница Резервный кредит описывает, как избежать двойной оплаты стоимости кэша подсказок, когда вы реализуете повторную попытку самостоятельно.
Claude остановился, потому что достиг лимита контекстного окна модели. Это позволяет вам запрашивать максимально возможное количество токенов, не зная точного размера входных данных.
Эта причина остановки в настоящее время типизирована только в пространстве имён beta SDK, поэтому следующие примеры вызывают client.beta.messages и используют типы с префиксом Beta. На Sonnet 4.5 и более новых моделях API возвращает это значение без бета-заголовка. Для более ранних моделей добавьте бета-заголовок model-context-window-exceeded-2025-08-26, чтобы включить его.
# Запрос с максимальным числом токенов, чтобы получить как можно больше
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}")Проще с tool runner: Следующий пример показывает ручную обработку инструментов. Для большинства случаев использования tool runner автоматически обрабатывает выполнение инструментов с гораздо меньшим количеством кода.
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?