Alasan berhenti dan fallback
Pelajari arti setiap nilai stop_reason dan cara menangani pemotongan, penggunaan alat, giliran yang dijeda, dan penolakan dalam aplikasi Anda.
Setiap respons Messages API menyertakan field stop_reason yang memberi tahu Anda mengapa Claude berhenti menghasilkan output. Periksa field ini untuk memutuskan apakah akan menggunakan respons apa adanya, melanjutkan percakapan, mencoba ulang, atau beralih (fallback) ke model lain.
Untuk skema respons lengkap, lihat referensi Messages API.
Referensi cepat
| Nilai | Kapan terjadi | Apa yang harus dilakukan |
|---|---|---|
end_turn | Claude menyelesaikan responsnya secara alami. | Gunakan respons tersebut. |
max_tokens | Respons mencapai batas max_tokens Anda. | Naikkan max_tokens atau lanjutkan respons. |
stop_sequence | Claude mengeluarkan salah satu stop_sequences Anda. | Baca stop_sequence untuk melihat mana yang terpicu. |
tool_use | Claude sedang memanggil alat. | Jalankan alat dan kembalikan hasilnya. Panggilan alat server yang masih belum memiliki blok hasilnya akan selesai dalam respons berikutnya. |
pause_turn | Loop alat server mencapai batas iterasinya. | Kirim kembali konten asisten untuk melanjutkan. |
refusal | Claude menolak untuk merespons. | Baca stop_details dan coba ulang pada model fallback. |
model_context_window_exceeded | Respons memenuhi jendela konteks model. | Perlakukan respons sebagai terpotong. |
Field stop_reason
Field stop_reason adalah bagian dari setiap respons Messages API yang berhasil. Tidak seperti error, yang menunjukkan kegagalan dalam memproses permintaan Anda, stop_reason memberi tahu Anda mengapa Claude menyelesaikan pembuatan responsnya.
{
"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
}
}Nilai stop reason
end_turn
Alasan berhenti yang paling umum. Menunjukkan bahwa Claude menyelesaikan responsnya secara alami.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
# Proses respons lengkap
for block in response.content:
if block.type == "text":
print(block.text)Terkadang Claude mengembalikan respons kosong (tepat 2–3 token tanpa konten) dengan stop_reason: "end_turn". Ini biasanya terjadi ketika Claude menafsirkan bahwa giliran asisten telah selesai, terutama setelah hasil alat.
Penyebab umum:
- Menambahkan blok teks tepat setelah hasil alat (Claude belajar untuk mengharapkan pengguna selalu menyisipkan teks setelah hasil alat, sehingga ia mengakhiri gilirannya untuk mengikuti pola tersebut)
- Mengirim kembali respons Claude yang sudah selesai tanpa menambahkan apa pun (Claude sudah menentukan bahwa ia selesai, jadi ia akan tetap selesai)
Cara mencegah respons kosong:
# SALAH: Menambahkan teks langsung setelah 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", # Don't add text after tool_result
},
],
},
]
# BENAR: Kirim hasil alat secara langsung tanpa teks tambahan
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"}
],
}, # Just the tool_result, no additional text
]Jika Anda masih mendapatkan respons kosong setelah memperbaiki struktur pesan, tambahkan prompt lanjutan dalam pesan pengguna baru alih-alih mencoba ulang dengan respons kosong tersebut:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-5", max_tokens=1024, messages=messages
)
# Periksa apakah respons kosong
if response.stop_reason == "end_turn" and not response.content:
# SALAH: Jangan hanya mencoba ulang dengan respons kosong
# Ini tidak akan berhasil karena Claude sudah memutuskan bahwa ia telah selesai
# BENAR: Tambahkan prompt lanjutan dalam pesan pengguna BARU
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-opus-5", max_tokens=1024, messages=messages
)
return responsePraktik terbaik:
- Jangan pernah menambahkan blok teks tepat setelah hasil alat: Ini mengajarkan Claude untuk mengharapkan input pengguna setelah setiap penggunaan alat.
- Jangan mencoba ulang respons kosong tanpa modifikasi: Mengirim kembali respons kosong tidak akan membantu.
- Gunakan prompt lanjutan sebagai upaya terakhir: Hanya jika perbaikan ini tidak menyelesaikan masalah.
max_tokens
Claude berhenti karena mencapai batas max_tokens yang ditentukan dalam permintaan Anda.
client = anthropic.Anthropic()
# Permintaan dengan token terbatas
response = client.messages.create(
model="claude-opus-5",
max_tokens=10,
messages=[{"role": "user", "content": "Explain quantum physics"}],
)
if response.stop_reason == "max_tokens":
# Respons terpotong
print("Response was cut off at token limit")
# Pertimbangkan untuk membuat permintaan lain untuk melanjutkanJika respons Claude terpotong karena mencapai batas max_tokens, dan respons yang terpotong tersebut berisi blok penggunaan alat yang tidak lengkap, Anda perlu mencoba ulang permintaan dengan nilai max_tokens yang lebih tinggi untuk mendapatkan penggunaan alat secara lengkap.
# Periksa apakah respons terpotong selama penggunaan alat
if response.stop_reason == "max_tokens":
# Periksa apakah blok konten terakhir adalah tool_use yang tidak lengkap
last_block = response.content[-1]
if last_block.type == "tool_use":
# Kirim permintaan dengan max_tokens yang lebih tinggi
response = client.messages.create(
model="claude-opus-5",
max_tokens=4096, # Increased limit
messages=messages,
tools=tools,
)stop_sequence
Claude menemukan salah satu stop sequence kustom Anda.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-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 sedang memanggil alat dan mengharapkan Anda menjalankannya.
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",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
)
if response.stop_reason == "tool_use":
# Ekstrak dan jalankan alat
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Kembalikan hasil ke Claude untuk respons akhirRespons tool_use juga dapat berisi blok server_tool_use yang id-nya tidak memiliki blok hasil yang cocok. Panggilan alat server tersebut belum selesai, dan respons ini tidak membawa hasilnya. Dalam kasus umum, Claude memanggil alat server dan salah satu alat klien Anda dalam kelompok panggilan alat paralel yang sama: API kembali tanpa menjalankan alat server sehingga Anda dapat menjalankan alat klien terlebih dahulu. Tidak ada penanda lain untuk status ini; deteksi dengan memeriksa id setiap blok server_tool_use atau mcp_tool_use untuk mencari blok hasil yang cocok.
{
"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" }
}
]
}Kelanjutannya adalah pesan pengguna berisi blok tool_result, satu untuk setiap blok tool_use dalam respons (lihat Menangani panggilan alat), dengan dua aturan tambahan: pesan tersebut tidak boleh berisi apa pun selain blok tool_result, dan permintaan harus mempertahankan array tools yang sama. Permintaan lanjutan yang tidak lagi mendefinisikan alat server yang sedang menunggu akan gagal dengan 400 yang pesannya diakhiri but no `web_search` tool was provided. API melampirkan hasil Anda ke giliran asisten yang masih terbuka, menjalankan alat server yang ditangguhkan (untuk eksekusi kode yang dijeda, melanjutkannya), dan meneruskan giliran tersebut. Untuk alat server yang dipanggil Claude secara langsung, content respons berikutnya dimulai dengan blok hasil yang menjawab id server_tool_use dari respons sebelumnya.
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
"content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
}
]
}Menambahkan apa pun setelah blok tool_result dalam pesan pengguna tersebut, seperti teks, akan mengakhiri giliran asisten; untuk alat server yang dipanggil Claude secara langsung, permintaan kemudian gagal dengan 400 invalid_request_error yang menyebutkan alat server yang belum terselesaikan:
`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` blockMenghilangkan tool_result, atau menempatkannya setelah konten lain, akan gagal lebih awal dengan error standar tool_use ids were found without tool_result blocks immediately after. Untuk memberi Claude input tambahan, kirimkan sebagai pesan pengguna terpisah setelah giliran selesai.
pause_turn
Dikembalikan ketika loop sampling sisi server mencapai batas iterasinya saat mengeksekusi alat server seperti pencarian web. Batas default adalah 10 iterasi per permintaan.
Ketika ini terjadi, respons mungkin berisi blok server_tool_use tanpa blok hasil yang sesuai. Agar Claude dapat menyelesaikan pemrosesan, lanjutkan percakapan dengan mengirim kembali respons apa adanya. Respons yang membiarkan blok tool_use klien menunggu Anda tidak pernah memiliki stop_reason berupa pause_turn: ketika Claude berhenti untuk memanggil alat Anda, stop_reason-nya adalah tool_use, dan Anda melanjutkannya dengan mengirim blok tool_result klien alih-alih respons itu sendiri.
response = client.messages.create(
model="claude-opus-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":
# Lanjutkan percakapan dengan mengirim kembali respons tersebut
messages = [
{"role": "user", "content": "Search for latest AI news"},
{"role": "assistant", "content": response.content},
]
continuation = client.messages.create(
model="claude-opus-5",
max_tokens=4096,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)refusal
Claude menolak untuk menghasilkan respons. Pengklasifikasi keamanan mengembalikan alasan berhenti ini sebagai respons HTTP 200 normal, bukan error.
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}],
)
if response.stop_reason == "refusal":
# Claude menolak untuk merespons
print("Claude was unable to process this request")
# Pertimbangkan untuk menyusun ulang atau memodifikasi permintaanPada penolakan, objek stop_details mengidentifikasi kategori kebijakan yang memicunya. Kategori-kategori tersebut dan bentuk respons penolakan lengkap dibahas di Penolakan dan fallback. stop_details bernilai null untuk semua alasan berhenti selain refusal.
Permintaan yang ditolak pada Claude Fable 5.1, Claude Fable 5, atau Claude Opus 5 biasanya dapat dilayani dengan mencoba ulang pada model Claude lain. Penolakan dan fallback menunjukkan cara menyiapkan percobaan ulang tersebut, di sisi server atau di klien Anda. Jika Anda membangun percobaan ulang sendiri dari Claude Fable 5.1, Claude Fable 5, atau Claude Opus 5, kredit fallback membahas cara menghindari membayar biaya prompt-cache dua kali.
model_context_window_exceeded
Claude berhenti karena mencapai batas "context window" (jendela konteks) model. Ini memungkinkan Anda meminta token maksimum yang mungkin tanpa mengetahui ukuran input yang tepat.
# Permintaan dengan token maksimum untuk mendapatkan sebanyak mungkin
response = client.beta.messages.create(
model="claude-opus-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":
# Respons mencapai batas jendela konteks sebelum max_tokens
print("Response reached model's context window limit")
# Respons tetap valid tetapi dibatasi oleh jendela konteksPraktik terbaik untuk menangani alasan berhenti
Selalu periksa stop_reason
Biasakan untuk memeriksa stop_reason dalam logika penanganan respons Anda:
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:
# Tangani end_turn dan kasus lainnya
return next(
(block.text for block in response.content if block.type == "text"), ""
)Tangani respons terpotong dengan baik
Ketika respons terpotong karena batas token atau jendela konteks, tambahkan pemberitahuan agar pembaca tahu bahwa output tidak lengkap. Untuk melanjutkan pembuatan dari titik respons terhenti, lihat Memastikan respons lengkap.
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 textImplementasikan logika percobaan ulang untuk pause_turn
Saat menggunakan alat server, API dapat mengembalikan pause_turn jika loop sampling sisi server mencapai batas iterasinya (default 10). Tangani ini dengan melanjutkan percakapan:
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", max_tokens=4096, messages=messages, tools=tools
)
if response.stop_reason != "pause_turn":
# Claude selesai memproses - kembalikan respons akhir
return response
# pause_turn: ganti seluruh daftar pesan untuk mempertahankan peran yang bergantian
messages = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": response.content},
]
# Batas maksimum kelanjutan tercapai - kembalikan respons terakhir
return responseAlasan berhenti vs. error
Penting untuk membedakan antara nilai stop_reason dan error yang sebenarnya:
Alasan berhenti (respons berhasil)
- Bagian dari body respons
- Menunjukkan mengapa pembuatan berhenti secara normal
- Respons berisi konten yang valid
Error (permintaan gagal)
- Kode status HTTP 4xx atau 5xx
- Menunjukkan kegagalan pemrosesan permintaan
- Respons berisi detail error
client = anthropic.Anthropic()
try:
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Tangani respons yang berhasil dengan stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIStatusError as e:
# Tangani error yang sebenarnya
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")Pertimbangan streaming
Saat menggunakan streaming, stop_reason:
- Bernilai
nulldalam eventmessage_startawal - Disediakan dalam event
message_delta - Tidak disediakan dalam event lainnya
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-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}")Pola umum
Menangani alur kerja penggunaan alat
def complete_tool_workflow(client, user_query, tools):
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-5", max_tokens=1024, messages=messages, tools=tools
)
if response.stop_reason == "tool_use":
# Jalankan alat dan lanjutkan
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Respons akhir
return responseMemastikan respons lengkap
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", 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
# Lanjutkan dari titik terakhir
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."},
]
return full_responseMendapatkan token maksimum tanpa mengetahui ukuran input
Dengan alasan berhenti model_context_window_exceeded, Anda dapat meminta token maksimum yang mungkin tanpa menghitung ukuran input:
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",
messages=[{"role": "user", "content": prompt}],
max_tokens=20000, # Python SDK requires streaming for max_tokens above ~21k
)
if response.stop_reason == "model_context_window_exceeded":
# Mendapat token maksimum yang mungkin berdasarkan ukuran input
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
elif response.stop_reason == "max_tokens":
# Mendapat token persis sesuai yang diminta
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Penyelesaian alami
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return next((block.text for block in response.content if block.type == "text"), "")Langkah selanjutnya
Coba ulang permintaan yang ditolak pada model fallback, di sisi server atau di klien Anda.
Biarkan SDK mengelola loop tool_use, pemformatan hasil, dan percobaan ulang untuk Anda.
Baca stop_reason dari event message_delta saat streaming.
Tangani error HTTP 4xx dan 5xx, yang berbeda dari alasan berhenti.
Was this page helpful?