pular para o conteúdo
voltar ao mapa
técnica
etapa: act
4 min de leitura

Tool calling

Tool calling é o contrato entre o LLM e o mundo: o modelo devolve JSON estruturado (nome + argumentos), o runtime valida contra schema e executa. É o que transforma texto em ação.

OpenAI Function CallingAnthropic Tool UseJSON SchemaPydantic

O que é

Tool calling (também chamado de ) é a interface pela qual um LLM devolve, em vez de (ou além de) texto, uma chamada de função estruturada: nome da ferramenta + argumentos em JSON. O runtime recebe, valida os argumentos contra um schema, executa e devolve o resultado ao contexto.

O modelo nunca executa nada por conta própria. Ele só propõe — 'eu chamaria get_weather com { city: 'São Paulo' }'. A execução, autorização, retry e auditoria ficam com o runtime. Esquecer quem executa é o erro mais caro que aparece em produção.

O schema JSON: o contrato

Cada ferramenta é declarada para o modelo por um schema JSON com nome, descrição e parâmetros tipados. O LLM usa esse schema como gramática: ele sabe o nome, sabe o formato dos argumentos e usa a descrição para decidir quando faz sentido chamar.

A descrição é a parte mais negligenciada. O LLM não lê código: ele lê a string. 'Obtém o clima de uma cidade' é melhor que 'get_weather', porque o modelo decide quando chamar baseado em prosa. Schemas com descrições ruins geram chamadas certas ou erradas.

schema da ferramenta get_weatherjson
{
  "name": "get_weather",
  "description": "Obtém a temperatura atual e a previsão para os próximos 3 dias em uma cidade.",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "Nome da cidade, em português ou inglês (ex: 'São Paulo', 'Lisbon')."
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "default": "celsius"
      }
    },
    "required": ["city"]
  }
}

Validação de argumentos: a sua responsabilidade

Quando o modelo devolve uma chamada de função, o runtime recebe um JSON que pode estar errado: tipo errado, campo faltando, valor fora do enum, string onde devia ser número. Validar antes de executar é não-negociável — é a fronteira entre o modelo, que propõe, e o seu código, que executa com permissões reais.

Em Python, Pydantic é o caminho padrão: declare um modelo, chame model_validate(model_dump), e o Pydantic levanta ValidationError com a lista exata de campos e o motivo. Em TypeScript, o par natural é Zod, com safeParse retornando { success, data, error }.

validacao.pypython
from pydantic import BaseModel, Field, ValidationError


class GetWeatherArgs(BaseModel):
    city: str = Field(..., min_length=1)
    unit: str = Field("celsius", pattern=r"^(celsius|fahrenheit)$")


def call_tool(name: str, raw_args: dict):
    if name != "get_weather":
        raise ValueError(f"ferramenta desconhecida: {name}")
    try:
        args = GetWeatherArgs.model_validate(raw_args)
    except ValidationError as e:
        return {"error": "invalid_args", "details": e.errors()}
    return fetch_weather(args.city, args.unit)

O fluxo completo, fim a fim

A chamada de ferramenta atravessa cinco etapas, cada uma com uma resposta possível e um caminho de erro distinto. Sem ver os cinco, parece mágica — e os bugs não-estão.

1. declaração: a lista de schemas é entregue ao LLM junto do prompt.

2. proposta: o LLM devolve tool_calls com name + JSON arguments.

3. validação: o runtime parseia e valida contra o schema — falha aqui significa 'o modelo alucinou'; trate como erro e devolva o motivo ao LLM.

4. execução: o runtime chama a função real (HTTP, DB, comando) com permissões e timeout.

5. observação: o resultado vira uma mensagem tool no contexto; o LLM usa para a próxima decisão.

runtime.tsxtypescript
import Anthropic from '@anthropic-ai/sdk';
import { z } from 'zod';

const GetWeather = z.object({
  city: z.string().min(1),
  unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
});

async function run(anthropic: Anthropic, userMessage: string) {
  const tools = [{
    name: 'get_weather',
    description: 'Obtém o clima atual de uma cidade.',
    input_schema: /* shape JSON do schema */,
  }];

  let msg = await anthropic.messages.create({
    model: 'claude-opus-4', max_tokens: 1024, tools,
    messages: [{ role: 'user', content: userMessage }],
  });

  if (msg.stop_reason === 'tool_use') {
    const call = msg.content.find((b) => b.type === 'tool_use');
    const parsed = GetWeather.safeParse(call.input);
    if (!parsed.success) return { error: 'invalid_args', issues: parsed.error.issues };
    const result = await fetchWeather(parsed.data.city, parsed.data.unit);
    msg = await anthropic.messages.create({
      model: 'claude-opus-4', max_tokens: 1024, tools,
      messages: [
        { role: 'user',     content: userMessage },
        { role: 'assistant',content: msg.content },
        { role: 'user',     content: [{ type: 'tool_result', tool_use_id: call.id, content: JSON.stringify(result) }] },
      ],
    });
  }
  return msg;
}

Por que não dá para pular a validação

  • o modelo pode inventar campos: ele viu 1000 schemas diferentes, não garante consistência
  • tipos são garantidos, valores não: ele sabe que 'unit' é string, mas pode mandar 'celcius' (typo) por 2s
  • enums são a fronteira mais comum de erro: o modelo raramente respeita o enum sem prompt explícito
  • validação é barata: custa microssegundos; execução não validada pode permitir ordem de execução crítica

Pontos fortes e limitações

  • forte: o schema força estrutura — o modelo não precisa 'tentar' produzir JSON válido; ele está produzindo tokens dentro de uma gramática
  • forte: erros de validação devolvem mensagem rica para o modelo, que quase sempre se corrige no próximo turno
  • fraco: descrição ruim leva a chamadas certas ou erradas — escrever schema bom é trabalho de design
  • fraco: a chamada é opaca para o usuário — se ele não ler o trace, não sabe que ferramenta rodou
  • fraco: paralelismo depende do provedor — alguns permitem várias tool_calls em uma resposta, outros só uma
Function callingtool useMecanismo pelo qual o LLM pede, em vez de só texto, para executar uma função com parâmetros estruturados: o modelo emite JSON, seu código roda a função, e o resultado volta para a conversa. É o que transforma um modelo puro em agente capaz de agir no mundo, e o que precisa de guardrails para evitar o call errado com o argumento errado.ver no glossário →

conexões no mapa

atualizado em 14/10/2026 · fontes para aprofundar · reportar erro

em outros domínios

Estude antes

Desbloqueia

teste o que entendeu

Carregando perguntas…