Каждый ответ 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-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
# Обработка полного ответа
print(response.content[0].text)Claude остановился, потому что достиг лимита max_tokens, указанного в вашем запросе.
client = anthropic.Anthropic()
# Запрос с ограниченным числом токенов
response = client.messages.create(
model="claude-opus-4-8",
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-4-8",
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-4-8",
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_fetch",
"input": { "url": "https://example.com/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_fetch` 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_fetch` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_fetch_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-4-8",
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-4-8",
max_tokens=4096,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)Ваше приложение должно обрабатывать pause_turn в любом агентном цикле, использующем серверные инструменты. Добавьте ответ ассистента в ваш массив сообщений и выполните ещё один запрос к API, чтобы позволить Claude продолжить.
Claude отказался генерировать ответ. В Claude Fable 5 классификаторы безопасности возвращают эту причину остановки как обычный ответ HTTP 200, а не как ошибку.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
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, и страница Отказы и резервные варианты показывает, как настроить такой повтор — на стороне сервера или в вашем клиенте. Страница Кредит за резервный вариант описывает, как избежать двойной оплаты стоимости кэширования подсказок, когда вы реализуете повтор самостоятельно.
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-4-8",
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k (Opus 4.8 supports 128k with streaming)
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 response.content[0].textКогда ответ усечён из-за лимитов токенов или контекстного окна, добавьте уведомление, чтобы читатель знал, что вывод неполный. Чтобы вместо этого продолжить генерацию с места, где ответ прервался, см. Обеспечение полных ответов.
def handle_truncated_response(response):
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"{response.content[0].text}\n\n{note}"
return response.content[0].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-4-8", 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-4-8",
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-4-8",
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-4-8", 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-4-8", messages=messages, max_tokens=4096
)
full_response += response.content[0].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-4-8",
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 response.content[0].textПовторяйте отклонённые запросы на резервной модели — на стороне сервера или в вашем клиенте.
Позвольте SDK управлять циклом tool_use, форматированием результатов и повторами за вас.
Считывайте stop_reason из события message_delta при потоковой передаче.
Обрабатывайте HTTP-ошибки 4xx и 5xx, которые отличаются от причин остановки.
Was this page helpful?