Claude Platform Docs
СообщенияРазработка с Claude

Причины остановки и резервные модели

Узнайте, что означает каждое значение stop_reason и как обрабатывать усечение ответов, использование инструментов, приостановленные ходы и отказы в вашем приложении.

Каждый ответ Messages API содержит поле stop_reason, которое сообщает, почему Claude прекратил генерацию. Проверяйте это поле, чтобы решить, что делать дальше: использовать ответ как есть, продолжить разговор, повторить запрос или выполнить «fallback» (переключение на резервную модель).

Полную схему ответа см. в справочнике по Messages API.

Краткий справочник

ЗначениеКогда возникаетЧто делать
end_turnClaude естественным образом завершил свой ответ.Используйте ответ.
max_tokensОтвет достиг заданного вами лимита max_tokens.Увеличьте max_tokens или продолжите ответ.
stop_sequenceClaude выдал одну из ваших stop_sequences.Прочитайте stop_sequence, чтобы узнать, какая из них сработала.
tool_useClaude вызывает инструмент.Выполните инструмент и верните результат. Вызов серверного инструмента, для которого ещё нет блока результата, завершается в одном из последующих ответов.
pause_turnЦикл серверных инструментов достиг лимита итераций.Отправьте содержимое ассистента обратно, чтобы продолжить.
refusalClaude отказался отвечать.Прочитайте stop_details и повторите запрос на резервной модели.
model_context_window_exceededОтвет заполнил «context window» (контекстное окно) модели.Считайте ответ усечённым.

Поле stop_reason

Поле stop_reason входит в каждый успешный ответ Messages API. В отличие от ошибок, которые указывают на сбои при обработке вашего запроса, stop_reason сообщает, почему Claude завершил генерацию ответа.

Example response
{
  "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
  }
}

Значения причин остановки

end_turn

Самая распространённая «stop reason» (причина остановки). Указывает, что Claude естественным образом завершил свой ответ.

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-5-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)

max_tokens

Claude остановился, потому что достиг лимита max_tokens, указанного в вашем запросе.

client = anthropic.Anthropic()
# Запрос с ограниченным числом токенов
response = client.messages.create(
    model="claude-opus-5-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")
    # Можно отправить ещё один запрос, чтобы продолжить

stop_sequence

Claude встретил одну из ваших пользовательских стоп-последовательностей.

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-opus-5-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}")

tool_use

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-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 наличие соответствующего блока результата.

A mixed tool_use response
{
  "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 из предыдущего ответа.

The follow-up user message
{
  "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 дополнительные входные данные, отправьте их отдельным пользовательским сообщением после завершения хода.

pause_turn

Возвращается, когда серверный цикл сэмплирования достигает лимита итераций при выполнении серверных инструментов, таких как веб-поиск. Лимит по умолчанию — 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-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-5",
        max_tokens=4096,
        messages=messages,
        tools=[{"type": "web_search_20250305", "name": "web_search"}],
    )

refusal

Claude отказался генерировать ответ. Классификаторы безопасности возвращают эту причину остановки как обычный ответ HTTP 200, а не как ошибку.

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-opus-5-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 указывает категорию политики, которая его вызвала. Категории и полная форма ответа с отказом описаны на странице Отказы и резервные модели. Для всех причин остановки, кроме refusal, stop_details равен null.

Отклонённый запрос к Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Sonnet 5.5 или Claude Haiku 5.5 обычно можно обслужить, повторив его на другой модели Claude. На странице Отказы и резервная обработка показано, как настроить такой повтор — на стороне сервера или в вашем клиенте. У Claude Haiku 5.5 нет серверной резервной обработки, поэтому настройте повтор для неё в вашем клиенте. Если вы реализуете повтор самостоятельно для запросов к Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5 или Claude Sonnet 5.5, на странице Кредит при резервном переключении описано, как избежать двойной оплаты кэша подсказок.

model_context_window_exceeded

Claude остановился, потому что достиг предела контекстного окна модели. Это позволяет запрашивать максимально возможное количество токенов, не зная точного размера входных данных.

# Запрос с максимальным числом токенов, чтобы получить как можно больше
response = client.beta.messages.create(
    model="claude-opus-5-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

Возьмите за правило проверять stop_reason в логике обработки ответов:

def handle_response(response):
    match response.stop_reason:
        case "tool_use":
            return handle_tool_use(response)
        case "max_tokens":
            return handle_truncation(response)
        case "model_context_window_exceeded":
            return handle_context_limit(response)
        case "pause_turn":
            return handle_pause(response)
        case "refusal":
            return handle_refusal(response)
        case _:
            # Обработка 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

Реализуйте логику повтора для pause_turn

При использовании серверных инструментов 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-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 и настоящие ошибки:

Причины остановки (успешные ответы)

  • Являются частью тела ответа
  • Указывают, почему генерация штатно остановилась
  • Ответ содержит корректное содержимое

Ошибки (неудачные запросы)

  • Коды состояния HTTP 4xx или 5xx
  • Указывают на сбои при обработке запроса
  • Ответ содержит сведения об ошибке
client = anthropic.Anthropic()

try:
    response = client.messages.create(
        model="claude-opus-5-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:
    # Обработка реальных ошибок
    match e.status_code:
        case 429:
            print("Rate limit exceeded")
        case 500:
            print("Server error")

Особенности потоковой передачи

При использовании «streaming» (потоковая передача) stop_reason:

  • равен null в начальном событии message_start
  • передаётся в событии message_delta
  • не передаётся ни в каких других событиях
client = anthropic.Anthropic()

with client.messages.stream(
    model="claude-opus-5-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-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 response

Обеспечение полных ответов

def 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-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-5",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=20000,  # Python SDK requires streaming for max_tokens above ~21k
    )

    match response.stop_reason:
        case "model_context_window_exceeded":
            # Получено максимально возможное число токенов с учётом размера входных данных
            print(
                f"Generated {response.usage.output_tokens} tokens (context limit reached)"
            )
        case "max_tokens":
            # Получено ровно запрошенное число токенов
            print(
                f"Generated {response.usage.output_tokens} tokens (max_tokens reached)"
            )
        case _:
            # Естественное завершение
            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?