Lo streaming granulare degli strumenti (fine-grained tool streaming) consegna l'input di uno strumento al tuo client man mano che Claude lo genera, senza buffering lato server o validazione JSON. Saltare la fase di buffering riduce il tempo necessario per ricevere il primo frammento di un parametro di grandi dimensioni, come un documento o un blocco di codice, e i frammenti arrivano attraverso gli stessi eventi di Streaming dei messaggi dell'uso degli strumenti standard.
Tutti i modelli supportano lo streaming granulare degli strumenti sulla Claude API, Amazon Bedrock, Claude Platform on AWS, Google Cloud e Microsoft Foundry. Per usarlo, imposta eager_input_streaming su true su qualsiasi strumento definito dall'utente per cui vuoi abilitare lo streaming granulare, e abilita lo streaming nella tua richiesta.
Il campo eager_input_streaming è opzionale. Impostarlo su true attiva lo streaming granulare per quello strumento, mentre ometterlo ti dà lo streaming bufferizzato standard, in cui l'API esegue il buffering e valida il valore di ciascun parametro prima di trasmetterlo in streaming. L'eccezione è una richiesta che invia ancora l'header beta legacy fine-grained-tool-streaming-2025-05-14, che attiva lo streaming granulare per gli strumenti che lasciano il campo non impostato. Il campo per singolo strumento sostituisce quell'header, e un false esplicito mantiene lo streaming bufferizzato per uno strumento anche quando una richiesta lo invia ancora. Consulta il Riferimento degli strumenti per la definizione del campo.
L'esempio seguente attiva lo streaming granulare per uno strumento make_file e chiede a Claude una poesia lunga, in modo che l'input dello strumento sia abbastanza grande da poterne osservare lo 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}")Ogni scheda attiva lo streaming granulare per lo strumento make_file. Le schede degli SDK stampano ogni frammento di input nel momento in cui arriva, poi stampano l'input completo accumulato una volta terminato lo stream. La scheda cURL mostra il flusso di eventi grezzo, e la scheda CLI usa jq per stampare solo i frammenti. Poiché i frammenti stampati si uniscono nell'input completo dello strumento, la poesia riempie il tuo terminale man mano che Claude la scrive:
{"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", ...]}Senza eager_input_streaming, l'API esegue il buffering e valida il valore di ciascun parametro prima di trasmetterlo in streaming, quindi per un parametro di grandi dimensioni non viene stampato nulla finché Claude non ha finito di generarlo. Con esso, i frammenti iniziano ad arrivare non appena Claude inizia il parametro, e sono tipicamente più lunghi, con meno interruzioni a metà parola.
Il contratto di accumulo è lo stesso dello streaming standard dell'uso degli strumenti, quindi questa sezione si applica con e senza eager_input_streaming. Consulta Input JSON delta in Streaming dei messaggi per il formato degli eventi. Lo streaming granulare degli strumenti cambia ciò che puoi assumere sul risultato: il server trasmette i frammenti senza validarli, quindi la stringa accumulata potrebbe non essere JSON valido.
Quando un blocco di contenuto tool_use viene trasmesso in streaming, l'evento iniziale content_block_start contiene input: {} (un oggetto vuoto). Questo è un segnaposto. L'input effettivo arriva come una serie di eventi input_json_delta, ciascuno contenente un frammento di stringa partial_json. Per assemblare l'input completo, concatena questi frammenti e analizza il risultato quando il blocco si chiude.
Dove il tuo SDK fornisce un helper di accumulo (come fanno le schede Python, TypeScript, Go, Java e Ruby nell'esempio precedente), questo se ne occupa per te. Il pattern manuale è per gli SDK senza helper, o quando vuoi il pieno controllo su come l'input viene assemblato.
Il contratto di accumulo:
content_block_start con type: "tool_use", inizializza una stringa vuota: input_json = ""content_block_delta con type: "input_json_delta", aggiungi in coda: input_json += event.delta.partial_jsoncontent_block_stop, analizza la stringa accumulataProteggi il parsing, come fanno i seguenti esempi degli SDK. Una risposta può anche fermarsi a max_tokens a metà di un parametro. Controlla lo stop reason e decidi se ritentare la richiesta con un max_tokens più alto o riparare l'input parziale.
La discrepanza di tipo tra l'input: {} iniziale (oggetto) e partial_json (stringa) è intenzionale. L'oggetto vuoto segna lo slot nell'array dei contenuti. Le stringhe delta costruiscono il valore reale.
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:
# Non è garantito che la stringa accumulata sia JSON valido.
# Vedi "Gestione di JSON non valido nelle risposte degli strumenti" in questa pagina.
print(f"Invalid tool input: {raw_input}")
else:
print(f"Tool input: {parsed}")Con lo streaming granulare degli strumenti, l'input accumulato per una chiamata a uno strumento potrebbe essere JSON non valido o incompleto. Quando lo è, non puoi eseguire lo strumento, quindi segnala invece il fallimento a Claude. Il content di un risultato di uno strumento non deve necessariamente essere JSON, ma racchiudere la stringa grezza in un oggetto JSON sotto una singola chiave rende inequivocabile per Claude che hai ricevuto JSON non valido, e preserva l'input originale per il debug:
{
"INVALID_JSON": "<the unparseable input you received>"
}Restituisci il wrapper, serializzato in una stringa, come content di un blocco di contenuto tool result con is_error impostato su true:
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"is_error": true,
"content": "{\"INVALID_JSON\": \"<the unparseable input you received>\"}"
}Comprendi come funziona la finestra di contesto, come il pensiero esteso e l'uso degli strumenti vi contribuiscono, e come gestire il contesto man mano che le conversazioni crescono.
Trasmetti in streaming le risposte della Messages API in modo incrementale con eventi server-sent, inclusi testo, uso degli strumenti e delta del pensiero esteso.
Analizza i blocchi tool_use, formatta le risposte tool_result e gestisci gli errori con is_error.
Elenco degli strumenti forniti da Anthropic e riferimento per le proprietà opzionali di definizione degli strumenti.
Was this page helpful?