Messages APIにリクエストを送信すると、Claudeのレスポンスにはモデルがレスポンスの生成を停止した理由を示すstop_reasonフィールドが含まれます。これらの値を理解することは、異なるレスポンスタイプを適切に処理する堅牢なアプリケーションを構築するために不可欠です。
APIレスポンスにおけるstop_reasonの詳細については、Messages APIリファレンスを参照してください。
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,
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}最も一般的な停止理由です。Claudeがレスポンスを自然に完了したことを示します。
if response.stop_reason == "end_turn":
# 完全なレスポンスを処理する
print(response.content[0].text)Claudeがstop_reason: "end_turn"で空のレスポンス(コンテンツなしでちょうど2〜3トークン)を返すことがあります。これは通常、特にツール結果の後に、Claudeがアシスタントのターンが完了したと解釈した場合に発生します。
一般的な原因:
空のレスポンスを防ぐ方法:
# 不正: tool_resultの直後にテキストを追加
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 5678"},
{"role": "assistant", "content": [
{
"type": "tool_use",
"id": "toolu_123",
"name": "calculator",
"input": {"operation": "add", "a": 1234, "b": 5678}
}
]},
{"role": "user", "content": [
{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "6912"
},
{
"type": "text",
"text": "Here's the result" # tool_resultの後にテキストを追加しないでください
}
]}
]
# 正しい: 追加テキストなしでツール結果を直接送信
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 5678"},
{"role": "assistant", "content": [
{
"type": "tool_use",
"id": "toolu_123",
"name": "calculator",
"input": {"operation": "add", "a": 1234, "b": 5678}
}
]},
{"role": "user", "content": [
{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "6912"
}
]} # tool_resultのみ、追加テキストなし
]
# 上記を修正しても空のレスポンスが返される場合:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages
)
# レスポンスが空かどうかを確認
if (response.stop_reason == "end_turn" and
not response.content:
# 不正: 空のレスポンスをそのままリトライしないでください
# Claudeはすでに完了を決定しているため、これは機能しません
# 正しい: 新しいユーザーメッセージに継続プロンプトを追加
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages
)
return responseベストプラクティス:
Claudeがリクエストで指定されたmax_tokensの制限に達したため停止しました。
# トークン制限付きのリクエスト
response = client.messages.create(
model="claude-opus-4-6",
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がカスタム停止シーケンスの1つに遭遇しました。
response = client.messages.create(
model="claude-opus-4-6",
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がツールを呼び出しており、実行を期待しています。
ほとんどのツール使用の実装では、ツールの実行、結果のフォーマット、会話管理を自動的に処理するツールランナーの使用を推奨します。
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What's the weather?"}]
)
if response.stop_reason == "tool_use":
# ツールを抽出して実行
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# 最終レスポンスのために結果をClaudeに返すWeb検索などのサーバーツールで、Claudeが長時間実行される操作を一時停止する必要がある場合に使用されます。
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
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": original_query},
{"role": "assistant", "content": response.content}
]
continuation = client.messages.create(
model="claude-opus-4-6",
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}]
)Claudeが安全上の懸念からレスポンスの生成を拒否しました。
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}]
)
if response.stop_reason == "refusal":
# Claudeがレスポンスを拒否した
print("Claude was unable to process this request")
# リクエストの言い換えや修正を検討Claude Sonnet 4.5またはOpus 4.1の使用中にrefusal停止理由に頻繁に遭遇する場合は、異なる使用制限を持つSonnet 4(claude-sonnet-4-20250514)を使用するようにAPI呼び出しを更新してみてください。Sonnet 4.5のAPI安全フィルターの理解で詳細をご覧ください。
Claude Sonnet 4.5のAPI安全フィルターによってトリガーされる拒否の詳細については、Sonnet 4.5のAPI安全フィルターの理解を参照してください。
Claudeがモデルのコンテキストウィンドウの制限に達したため停止しました。これにより、正確な入力サイズを知らなくても、可能な最大トークン数をリクエストできます。
# 可能な限り多くのトークンを取得するために最大トークンでリクエスト
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=64000, # モデルの最大出力トークン
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")
# レスポンスは有効ですが、コンテキストウィンドウによって制限されましたこの停止理由はSonnet 4.5以降のモデルではデフォルトで利用可能です。以前のモデルでは、ベータヘッダーmodel-context-window-exceeded-2025-08-26を使用してこの動作を有効にしてください。
レスポンス処理ロジックで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"]:
# オプション1: 特定の制限についてユーザーに警告
if response.stop_reason == "max_tokens":
message = "[Response truncated due to max_tokens limit]"
else:
message = "[Response truncated due to context window limit]"
return f"{response.content[0].text}\n\n{message}"
# オプション2: 生成を続行
messages = [
{"role": "user", "content": original_prompt},
{"role": "assistant", "content": response.content[0].text}
]
continuation = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=messages + [{"role": "user", "content": "Please continue"}]
)
return response.content[0].text + continuation.content[0].text一時停止する可能性のあるサーバーツールの場合:
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-opus-4-6",
messages=messages,
tools=original_tools
)
return responsestop_reasonの値と実際のエラーを区別することが重要です:
try:
response = client.messages.create(...)
# stop_reasonを使用して成功したレスポンスを処理
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIError as e:
# 実際のエラーを処理
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")ストリーミングを使用する場合、stop_reasonは:
message_startイベントではnullmessage_deltaイベントで提供される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"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-4-6",
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-6",
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_responsemodel_context_window_exceeded停止理由を使用すると、入力サイズを計算せずに可能な最大トークン数をリクエストできます:
def get_max_possible_tokens(client, prompt):
"""
入力トークン数を計算する必要なく、
モデルのコンテキストウィンドウ内で可能な限り多くのトークンを取得する
"""
response = client.messages.create(
model="claude-opus-4-6",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000 # モデルの最大出力トークンに設定
)
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].textstop_reasonの値を適切に処理することで、異なるレスポンスシナリオを適切に処理し、より良いユーザーエクスペリエンスを提供する、より堅牢なアプリケーションを構築できます。
Was this page helpful?