"Fine-grained tool streaming" (streaming alat berbutir halus) mengirimkan input alat ke klien Anda saat Claude menghasilkannya, tanpa buffering di sisi server atau validasi JSON. Melewati langkah buffering mengurangi waktu hingga fragmen pertama dari parameter besar, seperti dokumen atau blok kode, dan fragmen-fragmen tersebut tiba melalui event Streaming messages yang sama seperti penggunaan alat standar.
Semua model mendukung streaming alat berbutir halus di Claude API, Amazon Bedrock, Claude Platform on AWS, Google Cloud, dan Microsoft Foundry. Untuk menggunakannya, atur eager_input_streaming ke true pada alat yang didefinisikan pengguna di mana Anda ingin streaming berbutir halus diaktifkan, dan aktifkan streaming pada permintaan Anda.
Field eager_input_streaming bersifat opsional. Mengaturnya ke true mengaktifkan streaming berbutir halus untuk alat tersebut, dan menghilangkannya memberi Anda streaming buffered standar, di mana API melakukan buffering dan memvalidasi setiap nilai parameter sebelum melakukan streaming kembali. Pengecualiannya adalah permintaan yang masih mengirimkan header beta lama fine-grained-tool-streaming-2025-05-14, yang mengaktifkan streaming berbutir halus untuk alat yang membiarkan field tersebut tidak diatur. Field per-alat menggantikan header tersebut, dan false eksplisit mempertahankan streaming buffered untuk sebuah alat bahkan ketika permintaan masih mengirimkannya. Lihat Referensi alat untuk definisi field.
Contoh berikut mengaktifkan streaming berbutir halus untuk alat make_file dan meminta Claude membuat puisi panjang, sehingga input alat cukup besar untuk melihatnya melakukan streaming:
client = anthropic.Anthropic()
with client.messages.stream(
max_tokens=65536,
model="claude-opus-5",
tools=[
{
"name": "make_file",
"description": "Write text to a file",
"eager_input_streaming": True,
"input_schema": {
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "The filename to write text to",
},
"lines_of_text": {
"type": "array",
"description": "An array of lines of text to write to the file",
},
},
"required": ["filename", "lines_of_text"],
},
}
],
messages=[
{
"role": "user",
"content": "Can you write a long poem and make a file called poem.txt?",
}
],
) as stream:
for event in stream:
if event.type == "input_json":
print(event.partial_json, end="", flush=True)
final_message = stream.get_final_message()
print()
for block in final_message.content:
if block.type == "tool_use":
print(f"Complete tool input: {block.input}")Setiap tab mengaktifkan streaming berbutir halus untuk alat make_file. Tab SDK mencetak setiap fragmen input saat tiba, lalu mencetak input terakumulasi lengkap setelah stream berakhir. Tab cURL menunjukkan event stream mentah, dan tab CLI menggunakan jq untuk mencetak hanya fragmen-fragmennya. Karena fragmen yang dicetak bergabung menjadi input alat lengkap, puisi tersebut memenuhi terminal Anda saat Claude menulisnya:
{"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", "", "I.", "", "Beneath the vast and star-strewn sky,", "Where silver moonbeams softly lie,", ...
Complete tool input: {"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", ...]}Tanpa eager_input_streaming, API melakukan buffering dan memvalidasi setiap nilai parameter sebelum melakukan streaming kembali, sehingga tidak ada yang dicetak untuk parameter besar sampai Claude selesai menghasilkannya. Dengannya, fragmen mulai tiba segera setelah Claude memulai parameter, dan biasanya lebih panjang, dengan lebih sedikit pemotongan di tengah kata.
Kontrak akumulasi sama dengan streaming penggunaan alat standar, jadi bagian ini berlaku dengan dan tanpa eager_input_streaming. Lihat Input JSON delta di Streaming messages untuk format event. Streaming alat berbutir halus mengubah apa yang dapat Anda asumsikan tentang hasilnya: server melakukan streaming fragmen tanpa memvalidasinya, sehingga string yang terakumulasi mungkin bukan JSON yang valid.
Ketika blok konten tool_use di-streaming, event content_block_start awal berisi input: {} (objek kosong). Ini adalah placeholder. Input sebenarnya tiba sebagai serangkaian event input_json_delta, masing-masing membawa fragmen string partial_json. Untuk merakit input lengkap, gabungkan fragmen-fragmen ini dan parse hasilnya ketika blok ditutup.
Jika SDK Anda menyediakan helper akumulator (seperti yang dilakukan tab Python, TypeScript, Go, Java, dan Ruby pada contoh sebelumnya), helper tersebut menanganinya untuk Anda. Pola manual ditujukan untuk SDK tanpa helper, atau ketika Anda menginginkan kontrol penuh atas cara input dirakit.
Kontrak akumulasi:
content_block_start dengan type: "tool_use", inisialisasi string kosong: input_json = ""content_block_delta dengan type: "input_json_delta", tambahkan: input_json += event.delta.partial_jsoncontent_block_stop, parse string yang terakumulasiLindungi proses parsing, seperti yang dilakukan contoh SDK berikut. Respons juga dapat berhenti pada max_tokens di tengah parameter. Periksa stop reason dan putuskan apakah akan mencoba ulang permintaan dengan max_tokens yang lebih tinggi atau memperbaiki input parsial.
Ketidakcocokan tipe antara input: {} awal (objek) dan partial_json (string) memang disengaja. Objek kosong menandai slot dalam array konten. String delta membangun nilai sebenarnya.
client = anthropic.Anthropic()
tool_inputs: dict[int, str] = {} # index -> accumulated JSON string
with client.messages.stream(
model="claude-opus-5",
max_tokens=1024,
tools=[
{
"name": "get_weather",
"description": "Get current weather for a city",
"eager_input_streaming": True,
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
],
messages=[{"role": "user", "content": "Weather in Paris?"}],
) as stream:
for event in stream:
match event.type:
case "content_block_start" if event.content_block.type == "tool_use":
tool_inputs[event.index] = ""
case "content_block_delta" if event.delta.type == "input_json_delta":
tool_inputs[event.index] += event.delta.partial_json
case "content_block_stop" if event.index in tool_inputs:
raw_input = tool_inputs[event.index]
try:
parsed = json.loads(raw_input)
except json.JSONDecodeError:
# String yang terakumulasi tidak dijamin merupakan JSON yang valid.
# Lihat "Menangani JSON tidak valid dalam respons alat" di halaman ini.
print(f"Invalid tool input: {raw_input}")
else:
print(f"Tool input: {parsed}")Dengan streaming alat berbutir halus, input terakumulasi untuk pemanggilan alat mungkin berupa JSON yang tidak valid atau tidak lengkap. Ketika itu terjadi, Anda tidak dapat menjalankan alat tersebut, jadi laporkan kegagalannya kembali ke Claude. content dari hasil alat tidak harus berupa JSON, tetapi membungkus string mentah dalam objek JSON di bawah satu kunci membuatnya tidak ambigu bagi Claude bahwa Anda menerima JSON yang tidak valid, dan mempertahankan input asli untuk debugging:
{
"INVALID_JSON": "<the unparseable input you received>"
}Kembalikan pembungkus tersebut, yang diserialisasi menjadi string, sebagai content dari blok konten tool result dengan is_error diatur ke true:
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"is_error": true,
"content": "{\"INVALID_JSON\": \"<the unparseable input you received>\"}"
}Pahami cara kerja jendela konteks, bagaimana pemikiran diperpanjang dan penggunaan alat dihitung di dalamnya, dan cara mengelola konteks saat percakapan berkembang.
Streaming respons Messages API secara bertahap dengan server-sent events, termasuk teks, penggunaan alat, dan delta pemikiran diperpanjang.
Parse blok tool_use, format respons tool_result, dan tangani error dengan is_error.
Direktori alat yang disediakan Anthropic dan referensi untuk properti definisi alat opsional.
Was this page helpful?