O "fine-grained tool streaming" (streaming refinado de ferramentas) entrega a entrada de uma ferramenta ao seu cliente à medida que Claude a gera, sem buffering no lado do servidor ou validação de JSON. Pular a etapa de buffering reduz o tempo até o primeiro fragmento de um parâmetro grande, como um documento ou um bloco de código, e os fragmentos chegam através dos mesmos eventos de Streaming de mensagens que o uso de ferramentas padrão.
Todos os modelos suportam streaming refinado de ferramentas na API Claude, Amazon Bedrock, Claude Platform na AWS, Google Cloud e Microsoft Foundry. Para usá-lo, defina eager_input_streaming como true em qualquer ferramenta definida pelo usuário em que você queira habilitar o streaming refinado, e habilite o streaming na sua requisição.
O campo eager_input_streaming é opcional. Defini-lo como true ativa o streaming refinado para aquela ferramenta, e omiti-lo fornece o streaming padrão com buffering, no qual a API faz buffering e valida cada valor de parâmetro antes de fazer o streaming de volta. A exceção é uma requisição que ainda envia o cabeçalho beta legado fine-grained-tool-streaming-2025-05-14, que ativa o streaming refinado para ferramentas que deixam o campo não definido. O campo por ferramenta substitui esse cabeçalho, e um false explícito mantém o streaming com buffering para uma ferramenta mesmo quando uma requisição ainda o envia. Consulte a Referência de ferramentas para a definição do campo.
O exemplo a seguir ativa o streaming refinado para uma ferramenta make_file e pede a Claude um poema longo, para que a entrada da ferramenta seja grande o suficiente para observá-la chegar via 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}")Cada aba ativa o streaming refinado para a ferramenta make_file. As abas de SDK imprimem cada fragmento de entrada no momento em que ele chega e, em seguida, imprimem a entrada completa acumulada quando o stream termina. A aba cURL mostra o fluxo de eventos bruto, e a aba CLI usa jq para imprimir apenas os fragmentos. Como os fragmentos impressos se juntam na entrada completa da ferramenta, o poema preenche seu terminal à medida que Claude o escreve:
{"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", ...]}Sem eager_input_streaming, a API faz buffering e valida cada valor de parâmetro antes de fazer o streaming de volta, então nada é impresso para um parâmetro grande até que Claude tenha terminado de gerá-lo. Com ele, os fragmentos começam a chegar assim que Claude inicia o parâmetro, e eles são tipicamente mais longos, com menos quebras no meio de palavras.
O contrato de acumulação é o mesmo do streaming padrão de uso de ferramentas, então esta seção se aplica com e sem eager_input_streaming. Consulte Input JSON delta em Streaming de mensagens para o formato do evento. O streaming refinado de ferramentas muda o que você pode assumir sobre o resultado: o servidor faz streaming dos fragmentos sem validá-los, então a string acumulada pode não ser JSON válido.
Quando um bloco de conteúdo tool_use é transmitido via streaming, o evento inicial content_block_start contém input: {} (um objeto vazio). Isso é um placeholder. A entrada real chega como uma série de eventos input_json_delta, cada um carregando um fragmento de string partial_json. Para montar a entrada completa, concatene esses fragmentos e faça o parse do resultado quando o bloco fechar.
Onde seu SDK fornece um helper acumulador (como fazem as abas de Python, TypeScript, Go, Java e Ruby no exemplo anterior), ele cuida disso para você. O padrão manual é para SDKs sem um helper, ou quando você quer controle total sobre como a entrada é montada.
O contrato de acumulação:
content_block_start com type: "tool_use", inicialize uma string vazia: input_json = ""content_block_delta com type: "input_json_delta", anexe: input_json += event.delta.partial_jsoncontent_block_stop, faça o parse da string acumuladaProteja o parse, como fazem os exemplos de SDK a seguir. Uma resposta também pode parar em max_tokens no meio de um parâmetro. Verifique o motivo de parada e decida se deve tentar novamente a requisição com um max_tokens maior ou reparar a entrada parcial.
A incompatibilidade de tipo entre o input: {} inicial (objeto) e partial_json (string) é intencional. O objeto vazio marca a posição no array de conteúdo. As strings de delta constroem o valor real.
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:
# Não há garantia de que a string acumulada seja um JSON válido.
# Consulte "Lidando com JSON inválido em respostas de ferramentas" nesta página.
print(f"Invalid tool input: {raw_input}")
else:
print(f"Tool input: {parsed}")Com o streaming refinado de ferramentas, a entrada acumulada para uma chamada de ferramenta pode ser JSON inválido ou incompleto. Quando isso acontece, você não pode executar a ferramenta, então reporte a falha de volta para Claude. O content de um resultado de ferramenta não precisa ser JSON, mas envolver a string bruta em um objeto JSON sob uma única chave torna inequívoco para Claude que você recebeu JSON inválido, e preserva a entrada original para depuração:
{
"INVALID_JSON": "<the unparseable input you received>"
}Retorne o wrapper, serializado como uma string, como o content de um bloco de conteúdo de resultado de ferramenta com is_error definido como true:
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"is_error": true,
"content": "{\"INVALID_JSON\": \"<the unparseable input you received>\"}"
}Entenda como a janela de contexto funciona, como o pensamento estendido e o uso de ferramentas contam para ela, e como gerenciar o contexto à medida que as conversas crescem.
Faça streaming de respostas da API de Mensagens de forma incremental com eventos enviados pelo servidor, incluindo deltas de texto, uso de ferramentas e pensamento estendido.
Faça o parse de blocos tool_use, formate respostas tool_result e lide com erros usando is_error.
Diretório de ferramentas fornecidas pela Anthropic e referência para propriedades opcionais de definição de ferramentas.
Was this page helpful?