Messages APIにリクエストを行うと、Claudeのレスポンスには、モデルがレスポンス生成を停止した理由を示すstop_reasonフィールドが含まれます。これらの値を理解することは、異なるレスポンスタイプを適切に処理する堅牢なアプリケーションを構築するために非常に重要です。

APIレスポンスのstop_reasonの詳細については、Messages APIリファレンスを参照してください。

stop_reasonとは何か?

stop_reasonフィールドは、すべての成功したMessages APIレスポンスの一部です。リクエスト処理の失敗を示すエラーとは異なり、stop_reasonはClaudeがレスポンス生成を正常に完了した理由を教えてくれます。

レスポンス例
{
  "id": "msg_01234",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "あなたの質問に対する回答です..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

停止理由の値

end_turn

最も一般的な停止理由。Claudeが自然にレスポンスを終了したことを示します。

if response.stop_reason == "end_turn":
    # 完全なレスポンスを処理する
    print(response.content[0].text)

max_tokens

Claudeがリクエストで指定されたmax_tokens制限に達したため停止しました。

# トークン制限付きのリクエスト
response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=10,
    messages=[{"role": "user", "content": "量子物理学について説明してください"}]
)

if response.stop_reason == "max_tokens":
    # レスポンスが切り捨てられた
    print("レスポンスがトークン制限で切り捨てられました")
    # 続きを取得するために別のリクエストを検討する

stop_sequence

Claudeがカスタム停止シーケンスの1つに遭遇しました。

response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1024,
    stop_sequences=["END", "STOP"],
    messages=[{"role": "user", "content": "ENDと言うまでテキストを生成してください"}]
)

if response.stop_reason == "stop_sequence":
    print(f"シーケンスで停止: {response.stop_sequence}")

tool_use

Claudeがツールを呼び出し、あなたがそれを実行することを期待しています。

response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1024,
    tools=[weather_tool],
    messages=[{"role": "user", "content": "天気はどうですか?"}]
)

if response.stop_reason == "tool_use":
    # ツールを抽出して実行する
    for content in response.content:
        if content.type == "tool_use":
            result = execute_tool(content.name, content.input)
            # 最終レスポンスのために結果をClaudeに返す

pause_turn

Claudeが長時間実行される操作を一時停止する必要がある場合に、ウェブ検索などのサーバーツールで使用されます。

response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1024,
    tools=[{"type": "web_search_20250305", "name": "web_search"}],
    messages=[{"role": "user", "content": "最新のAIニュースを検索してください"}]
)

if response.stop_reason == "pause_turn":
    # 会話を続ける
    messages = [
        {"role": "user", "content": original_query},
        {"role": "assistant", "content": response.content}
    ]
    continuation = client.messages.create(
        model="claude-3-7-sonnet-20250219",
        messages=messages,
        tools=[{"type": "web_search_20250305", "name": "web_search"}]
    )

停止理由を処理するためのベストプラクティス

1. 常にstop_reasonを確認する

レスポンス処理ロジックで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 == "pause_turn":
        return handle_pause(response)
    else:
        # end_turnやその他のケースを処理する
        return response.content[0].text

2. max_tokensを適切に処理する

トークン制限によりレスポンスが切り捨てられた場合:

def handle_truncated_response(response):
    if response.stop_reason == "max_tokens":
        # オプション1:ユーザーに警告する
        return f"{response.content[0].text}\n\n[レスポンスが長さの制限により切り捨てられました]"
        
        # オプション2:生成を続ける
        messages = [
            {"role": "user", "content": original_prompt},
            {"role": "assistant", "content": response.content[0].text}
        ]
        continuation = client.messages.create(
            model="claude-3-7-sonnet-20250219",
            max_tokens=1024,
            messages=messages + [{"role": "user", "content": "続けてください"}]
        )
        return response.content[0].text + continuation.content[0].text

3. pause_turnのための再試行ロジックを実装する

一時停止する可能性のあるサーバーツールの場合:

def handle_paused_conversation(initial_response, max_retries=3):
    response = initial_response
    messages = [{"role": "user", "content": original_query}]
    
    for attempt in range(max_retries):
        if response.stop_reason != "pause_turn":
            break
            
        messages.append({"role": "assistant", "content": response.content})
        response = client.messages.create(
            model="claude-3-7-sonnet-20250219",
            messages=messages,
            tools=original_tools
        )
    
    return response

停止理由とエラーの違い

stop_reasonの値と実際のエラーを区別することが重要です:

停止理由(成功したレスポンス)

  • レスポンス本文の一部
  • 生成が正常に停止した理由を示す
  • レスポンスには有効なコンテンツが含まれる

エラー(失敗したリクエスト)

  • HTTPステータスコード4xxまたは5xx
  • リクエスト処理の失敗を示す
  • レスポンスにはエラーの詳細が含まれる
try:
    response = client.messages.create(...)
    
    # stop_reasonを持つ成功したレスポンスを処理する
    if response.stop_reason == "max_tokens":
        print("レスポンスが切り捨てられました")
    
except anthropic.APIError as e:
    # 実際のエラーを処理する
    if e.status_code == 429:
        print("レート制限を超えました")
    elif e.status_code == 500:
        print("サーバーエラー")

ストリーミングに関する考慮事項

ストリーミングを使用する場合、stop_reasonは:

  • 最初のmessage_startイベントではnull
  • message_deltaイベントで提供される
  • 他のすべてのイベントでは非nullである
with client.messages.stream(...) as stream:
    for event in stream:
        if event.type == "message_delta":
            stop_reason = event.delta.stop_reason
            if stop_reason:
                print(f"ストリームが終了しました: {stop_reason}")

一般的なパターン

ツール使用ワークフローの処理

def complete_tool_workflow(client, user_query, tools):
    messages = [{"role": "user", "content": user_query}]
    
    while True:
        response = client.messages.create(
            model="claude-3-7-sonnet-20250219",
            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-3-7-sonnet-20250219",
            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": "中断した箇所から続けてください。"}
        ]
    
    return full_response

stop_reasonの値を適切に処理することで、異なるレスポンスシナリオを適切に処理し、より良いユーザーエクスペリエンスを提供する、より堅牢なアプリケーションを構築できます。