Claude Platform Docs
Messages使用 Claude 构建

停止原因与回退

了解每个 stop_reason 值的含义,以及如何在您的应用程序中处理截断、工具使用、暂停的轮次和拒绝。

每个 Messages API 响应都包含一个 stop_reason 字段,用于告诉您 Claude 停止生成的原因。检查此字段以决定是按原样使用响应、继续对话、重试,还是回退到另一个模型。

有关完整的响应模式,请参阅 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响应填满了模型的上下文窗口。将响应视为已截断。

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

最常见的停止原因。表示 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 在不运行服务器工具的情况下返回,以便您可以先运行客户端工具。该状态没有其他标记;请通过检查每个 server_tool_use 或 mcp_tool_use 块的 id 是否有匹配的结果块来检测它。

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 以回应上一个响应中 server_tool_use id 的结果块开头。

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 模型上重试来处理。拒绝与回退介绍了如何在服务器端或您的客户端中设置这种重试。如果您自行构建从 Claude Fable 5.1、Claude Fable 5、Claude Opus 5.5、Claude Opus 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 之前已触及 context window(上下文窗口)限制
    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 实现重试逻辑

使用服务器工具时,如果服务器端采样循环达到其迭代上限(默认为 10),API 可能会返回 pause_turn。通过继续对话来处理这种情况:

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:

  • 在初始的 message_start 事件中为 null
  • 在 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 循环、结果格式化和重试。

在流式传输时从 message_delta 事件中读取 stop_reason。

处理 4xx 和 5xx HTTP 错误,它们与停止原因不同。

Was this page helpful?