~ / tutoriais /tool-calling-nao-funciona $ _

Tool calling não funciona? Por que seu agente chama a ferramenta errada (e como consertar)

Lucas Souza Lucas Souza 11 min de leitura Tutoriais
Tool calling não funciona? Por que seu agente chama a ferramenta errada (e como consertar)

O agente tinha a ferramenta certa na mão. E escolheu a errada.

Você definiu seis tools, escreveu o prompt, rodou o teste. Em vez de chamar get_customer, o modelo chamou search_customers com um argumento que nem existia no schema. Aí veio o reflexo: "esse modelo é burro". Quase nunca é. Quando o tool calling não funciona, na imensa maioria dos casos o problema está na descrição da ferramenta, no schema frouxo e no nome ambíguo que você escreveu.

Tool calling é o que transforma um chat em agente. É o pedaço que mais cresceu em interesse no último ano, e também o que mais gente coloca em produção sem entender por que falha. Neste tutorial você vai aprender a diagnosticar e consertar os três pontos onde o tool calling não funciona: descrição, schema e fallback. Com código que roda na API da Claude e os números que a própria Anthropic publicou medindo cada ajuste.

TL;DR

  • O que é: guia prático para consertar tool calling (function calling) quando o agente escolhe a ferramenta errada ou passa argumento inválido.
  • Stack/Modelos: Python, Anthropic SDK, claude-opus-4-8. Os princípios valem para qualquer provider com function calling.
  • Custo/Acesso: requer chave da API da Claude. Os ajustes principais (descrição, schema, strict, tool_choice) são GA, sem header beta.
  • Link útil: Writing effective tools for AI agents e Advanced tool use, da engenharia da Anthropic.

Por que o tool calling não funciona (e o problema quase nunca é o modelo)

Existem só dois jeitos de o tool calling quebrar:

  1. O modelo escolhe a ferramenta errada.
  2. O modelo escolhe a certa, mas passa argumento inválido — JSON malformado, campo obrigatório faltando, tipo errado.

A Anthropic é direta sobre isso: as falhas mais comuns em tool calling são exatamente "wrong tool selection and incorrect parameters" (Advanced tool use). E a causa raiz dessas duas falhas raramente é a capacidade do modelo. É ambiguidade.

Pensa pelo lado do modelo. Ele não enxerga sua arquitetura. Ele enxerga uma lista de tools com nome, descrição e schema, e tem que decidir. Se você tem notification-send-user e notification-send-channel lado a lado com descrições genéricas, o modelo vai chutar. Se o parâmetro se chama user em vez de user_id, ele vai mandar um nome quando você esperava um ID. A informação que faltava para decidir certo não estava no modelo. Estava no contrato que você definiu.

A boa notícia: como o problema é o contrato, o conserto está sob seu controle. A Anthropic mostrou que refinar só as descrições das tools levou o Claude Sonnet 3.5 ao topo do SWE-bench Verified, derrubando a taxa de erro (Advanced tool use). Não mudaram o modelo. Mudaram o texto. É engenharia de contrato, não sorte com prompt.

Pré-requisitos

Para acompanhar os exemplos você vai precisar de:

  • [ ] Chave de API da Claude (ANTHROPIC_API_KEY no ambiente)
  • [ ] pip install anthropic
  • [ ] Conhecimento básico de tool use: como declarar uma tool e devolver o tool_result. Se isso ainda é novo, comece pelo tool calling na prática e volta aqui.
  • [ ] Python 3.10+

Mão na massa: consertando o tool calling

Passo 1: a descrição que escolhe a ferramenta por você

A regra que mais entrega resultado: escreva a descrição como se estivesse treinando um funcionário novo, e seja prescritivo sobre quando chamar, não só sobre o que a tool faz.

Modelos recentes (Opus 4.7, 4.8) acionam tools com mais cautela e seguem a descrição mais à risca. Isso significa que uma condição de gatilho explícita na descrição dá ganho mensurável de acerto. Compare:

# Ruim: o modelo tem que adivinhar quando usar
{
    "name": "search",
    "description": "Busca dados.",
    "input_schema": {
        "type": "object",
        "properties": {"user": {"type": "string"}},
        "required": ["user"],
    },
}

# Bom: o "quando" está no texto, o parâmetro é específico
{
    "name": "get_customer_context",
    "description": (
        "Retorna o perfil do cliente, últimas transações e notas internas. "
        "Chame esta tool quando o usuário perguntar sobre o histórico, status "
        "ou pendências de um cliente específico. NÃO use para listar vários "
        "clientes — para isso use search_customers."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "customer_id": {
                "type": "string",
                "description": "ID do cliente, formato cus_XXXX (não é o nome).",
            }
        },
        "required": ["customer_id"],
    },
}

Duas mudanças carregam o resultado. Primeiro: a descrição diz quando usar e quando não usar, apontando a alternativa. Segundo: user virou customer_id com formato explícito no description do parâmetro. Nome genérico convida argumento genérico. Se você quer descer ao design das tools em si — contrato, exemplos few-shot, erros úteis —, o tool use na prática destrincha isso.

Passo 2: o schema que rejeita argumento inválido na origem

Descrição resolve a escolha. Schema resolve o argumento. E aqui tem uma alavanca que muita gente ignora: strict tool use. Com strict: true, o modelo só consegue gerar tool calls que batem com o seu schema — chega de campo faltando ou tipo errado.

import anthropic

client = anthropic.Anthropic()

tools = [{
    "name": "book_flight",
    "description": "Reserva um voo. Chame quando o usuário confirmar destino e data.",
    "strict": True,
    "input_schema": {
        "type": "object",
        "properties": {
            "destination": {"type": "string"},
            "date": {"type": "string", "format": "date"},
            "passengers": {"type": "integer", "enum": [1, 2, 3, 4, 5, 6]},
        },
        "required": ["destination", "date", "passengers"],
        "additionalProperties": False,
    },
}]

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Quero voar pra Tóquio dia 15/03, 2 pessoas"}],
)

Três detalhes que fazem diferença: enum fecha o conjunto de valores aceitos (o modelo não inventa passengers: 9), format: "date" orienta o formato, e additionalProperties: false é obrigatório no modo strict.

Para tools com input complexo — objetos aninhados, formatos sensíveis — o JSON Schema tem um limite: ele define estrutura, mas não consegue expressar padrões de uso. É aí que entra o campo input_examples, com exemplos de chamada validados contra o schema. O número da Anthropic é difícil de ignorar: o acerto em parâmetros complexos subiu de 72% para 90% com exemplos (Advanced tool use).

E uma regra de ouro que vale para qualquer modelo: três níveis de aninhamento é o limite prático para tool calls confiáveis. Se o seu schema tem objetos dentro de objetos dentro de objetos, achate. Promova os campos críticos para o topo e empacote o resto de config opcional em um único parâmetro que a tool desempacota por dentro.

Passo 3: nomes ambíguos e excesso de tools

O modelo se confunde quando duas tools se sobrepõem. notification-send-user versus notification-send-channel com descrições parecidas é pedir para errar. Dois consertos:

Namespacing por prefixo. Quando suas tools cruzam vários serviços, prefixe pelo serviço: asana_search, jira_search, slack_send_message. A escolha entre prefixo e sufixo teve impacto mensurável nas avaliações da Anthropic — prefixo na frente ajuda o modelo a navegar a biblioteca.

Consolidação. Em vez de list_users, list_events e create_event, exponha um schedule_event. Em vez de get_customer_by_id, list_transactions e list_notes, um get_customer_context que já devolve tudo enriquecido. Menos tools, mais capazes — menos ambiguidade na escolha e menos round-trips.

E se você tem dezenas de tools? O custo aparece antes da conversa começar: um caso real tinha 58 tools consumindo ~55.000 tokens só de definição. A Tool Search Tool carrega só as tools relevantes sob demanda, e o ganho de acerto foi concreto: o Opus 4.5 saltou de 79,5% para 88,1% (Advanced tool use). É o padrão de progressive disclosure: carregar tool sob demanda em vez de afogar o modelo em cinquenta definições. Tool demais não é robustez. É distração.

Passo 4: o fallback para quando falha mesmo assim

Nenhum schema é perfeito. Quando a tool falha em runtime — ID não existe, API externa fora do ar — devolva um erro que o modelo consegue usar para se corrigir, não um código críptico:

tool_result = {
    "type": "tool_result",
    "tool_use_id": tool_use_id,
    "content": "Erro: cliente 'cus_999' não encontrado. Confirme o ID com o usuário ou use search_customers para localizar pelo nome.",
    "is_error": True,
}

A mensagem aponta o problema e a saída. O modelo lê isso, pede confirmação ou troca de estratégia. Erro acionável fecha o loop; código de erro nu trava o agente.

Quando você precisa de garantia de que uma tool específica vai ser chamada, force com tool_choice:

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "book_flight"},  # obriga essa tool
    messages=[{"role": "user", "content": "..."}],
)

As opções são auto (padrão), any (qualquer tool, mas obriga uma), tool (essa tool) e none (proíbe tools). Para o caminho onde o argumento ainda escapa, a estratégia de repair funciona: reenvie o histórico mais o schema para um modelo mais forte gerar o input válido, ou use structured outputs para constranger a saída.

Limitações e pontos de atenção

Onde você ainda vai se queimar:

  • Strict tem limites de schema. Modo strict não suporta schema recursivo, nem restrições numéricas (minimum/maximum) ou de string (minLength). Valide essas regras do seu lado.
  • Tier do modelo importa. Modelos da faixa mini/nano/flash/Haiku têm confiabilidade de tool call mensuravelmente menor. Se a precisão é crítica no agente, não economize justo na decisão de qual tool chamar.
  • Cache e contexto estourado. Mudou o schema e não reiniciou o processo? O agente pode estar usando a versão antiga enquanto a validação roda contra a nova. E se prompt + tools + mensagem estouram o contexto, o modelo pode ver um schema parcial e gerar uma chamada que não bate com a definição completa.
  • Não exagere na descrição. Nos modelos recentes, instrução agressiva tipo "CRITICAL: você DEVE usar esta tool sempre" faz a tool disparar quando não devia. Seja prescritivo, não autoritário.

E o ponto que sustenta credibilidade: monte um harness de avaliação. Rode 50 a 100 prompts representativos pelo caminho de tool calling e valide a saída contra o schema. Tool com menos de 95% de sucesso nesse teste não sobe para produção. Sem isso, você está consertando no escuro.

FAQ rápido

Por que o modelo chama a ferramenta certa mas com argumento errado? Quase sempre o schema está frouxo. Adicione strict: true, feche valores com enum, marque os required e dê description específico em cada parâmetro. Para inputs complexos, input_examples resolve o que o schema sozinho não expressa.

Mais tools deixam o agente mais capaz? Não. Tools demais distraem o modelo e inflam o contexto. Consolide tools relacionadas e, acima de algumas dezenas, use Tool Search para carregar só o que importa por chamada.

Strict mode resolve tudo? Resolve a validade do argumento, não a escolha da tool. Escolha errada é problema de descrição e nome — strict não toca nisso. Os dois andam juntos: descrição para escolher, schema para preencher.

Funciona com OpenAI, Gemini e outros? O princípio é o mesmo em qualquer function calling: nome claro, descrição prescritiva, schema apertado, fallback acionável. A sintaxe muda; a causa raiz da falha, não.

Conclusão

O modelo raramente é o gargalo do seu tool calling. O contrato é. Descrição que diz quando usar, schema que rejeita lixo na origem, nomes que não se confundem e fallback que ensina o modelo a se corrigir — esses quatro ajustes derrubam a maior parte das falhas de "chamou a ferramenta errada". E o melhor: dá para medir cada um deles, do jeito que a Anthropic mediu.

Esse é exatamente o tipo de decisão que separa um agente de demo de um agente de produção, e é o que destrinchamos com código rodando no Workshop Arquitetando Soluções de IA: como arquitetar soluções reais com agents, sem terceirizar o raciocínio para a sorte. Depois de fechar o tool calling, o próximo passo é avaliação contínua — porque schema bom hoje não garante schema bom depois da próxima tool que você adicionar.

Lucas Souza
Lucas Souza

{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.

Você também pode gostar

VirguIA

beer & code assistant

conectando…

Não foi possível iniciar o chat agora.

tocando