Claude Platform Docs
Messages使用 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伺服器工具迴圈達到了迭代上限。將 assistant 內容送回以繼續。
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()
# 限制 token 數量的請求
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 區塊組成的 user 訊息,回應中的每個 tool_use 區塊各對應一個(請參閱處理工具呼叫),並附帶兩條額外規則:該訊息除了 tool_result 區塊之外不得包含任何其他內容,且請求必須保留相同的 tools 陣列。若恢復請求不再定義正在等待的伺服器工具,將會失敗並回傳 400,其訊息以 but no `web_search` tool was provided 結尾。API 會將您的結果附加到仍開啟的 assistant 回合,執行延後的伺服器工具(對於暫停的程式碼執行則是恢復它),並繼續該回合。對於 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"
    }
  ]
}

在該 user 訊息的 tool_result 區塊之後加入任何內容(例如文字)都會結束 assistant 回合;對於 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 更多輸入,請在回合完成後以另一則獨立的 user 訊息送出。

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 因達到模型的上下文視窗上限而停止。這讓您可以在不知道確切輸入大小的情況下請求最大可能的 token 數。

# 以最大 token 數發出請求,以盡可能取得最多內容
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"),
                "",
            )

妥善處理被截斷的回應

當回應因 token 上限或上下文視窗而被截斷時,請附加一則通知,讓讀者知道輸出不完整。若要改為從回應中斷處繼續生成,請參閱確保完整回應。

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

在不知道輸入大小的情況下取得最大 token 數

藉由 model_context_window_exceeded 停止原因,您可以在不計算輸入大小的情況下請求最大可能的 token 數:

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":
            # 已取得輸入大小所允許的最大 token 數
            print(
                f"Generated {response.usage.output_tokens} tokens (context limit reached)"
            )
        case "max_tokens":
            # 已取得恰好等於請求數量的 token
            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?