API de Tool Calling: padrão de nome e **args (kwargs)

Visão geral: o que significa “name + args/kwargs”

Em muitos sistemas modernos integrados a modelos de linguagem grandes (LLM, large language model), uma chamada de ferramenta (tool call) (também chamada de chamada de função (function call)) é representada como duas partes de dados:

  1. Um identificador da ferramenta (mais comumente um nome de função): name
  2. Um objeto de argumentos estruturado (frequentemente chamado de args ou kwargs): um objeto JSON contendo parâmetros nomeados

Conceitualmente, isso espelha uma chamada de função Python usando argumentos nomeados (keyword arguments, kwargs):

result = search(query="llm tool calling", top_k=5)

A representação na API de chamada de ferramentas (tool-calling API) normalmente é:

{
  "name": "search",
  "args": {
    "query": "llm tool calling",
    "top_k": 5
  }
}

Esse padrão é difundido porque ele é:

  • Independente de linguagem (JSON transporta bem)
  • Componível (objetos aninhados representam parâmetros aninhados)
  • Validável (esquemas podem definir campos permitidos, tipos e restrições)
  • Amigável ao modelo (LLMs se saem melhor quando solicitadas a produzir objetos estruturados do que quando solicitadas a escrever código arbitrário)

Dentro de Chamada de Função / Uso de Ferramentas, a convenção “name + args” é a base que torna o uso de ferramentas previsível o suficiente para sistemas de produção.

Por que o padrão existe (e por que ele funciona)

Ele separa intenção de parâmetros

Uma chamada de ferramenta responde a duas perguntas:

  • Qual capacidade deve ser invocada?name
  • Com quais entradas?args

Essa separação importa porque ela habilita:

  • Roteamento de ferramentas: despachar por name para o manipulador (handler) correto
  • Validação independente: validar args contra o esquema daquela ferramenta
  • Auditoria e observabilidade (observability): registrar “o que foi chamado” e “com quais parâmetros”

Ele mapeia bem para convenções de chamada de linguagens de programação

  • Python: func(**kwargs) é uma correspondência conceitual direta
  • JavaScript/TypeScript: func(args) comumente recebe um único argumento do tipo objeto
  • Sistemas RPC: se assemelha a nome do método + carga útil (payload) de mensagem (ex.: JSON-RPC)

Ele dá suporte à geração estruturada (structured generation)

Frequentemente, instrui-se o LLM a produzir um objeto JSON para chamadas de ferramenta. Isso aproveita a capacidade do modelo de seguir estrutura e reduz a ambiguidade em comparação com gerar código-fonte.

Isso é especialmente útil em pipelines de Geração Aumentada por Recuperação, em que ferramentas como retrieve, search, lookup_document ou sql_query precisam ser invocadas de forma confiável.

Formatos canônicos: representações comuns de API

Plataformas diferentes encapsulam chamadas de ferramenta de maneiras distintas, mas o núcleo permanece o mesmo.

Representação mínima e canônica

{ "name": "get_weather", "args": { "city": "Paris", "units": "c" } }

Com um envelope explícito de chamada de ferramenta (IDs, metadados)

{
  "id": "call_123",
  "type": "tool_call",
  "name": "get_weather",
  "args": { "city": "Paris", "units": "c" }
}

Várias chamadas de ferramenta em uma única resposta do modelo

[
  { "name": "search", "args": { "query": "Kyoto travel restrictions", "top_k": 5 } },
  { "name": "get_weather", "args": { "city": "Kyoto", "units": "c" } }
]

Se a chamada é entregue como uma lista, uma sequência em streaming, ou embutida em uma mensagem de chat, a lógica de despacho geralmente se reduz a:

  1. Ler name
  2. Validar e interpretar args
  3. Executar a ferramenta
  4. Retornar o resultado da ferramenta de volta para o modelo/usuário

Mapeamento para Esquema JSON (JSON Schema) (a base prática)

Para tornar args seguro e confiável, sistemas definem um esquema por ferramenta. A escolha mais comum é Esquema JSON (ou uma variante próxima).

Exemplo de definição de ferramenta com esquema

Ferramenta: search

Chamada Python desejada:

search(query: str, top_k: int = 5, filters: dict | None = None)

Esquema JSON correspondente para args:

{
  "type": "object",
  "properties": {
    "query": { "type": "string", "minLength": 1 },
    "top_k": { "type": "integer", "minimum": 1, "maximum": 50, "default": 5 },
    "filters": {
      "type": "object",
      "additionalProperties": { "type": "string" }
    }
  },
  "required": ["query"],
  "additionalProperties": false
}

Pontos-chave:

  • properties define os kwargs permitidos
  • required espelha os parâmetros obrigatórios
  • default pode representar valores padrão do Python (mas exige tratamento em tempo de execução (runtime) — mais abaixo)
  • additionalProperties: false impede a aceitação silenciosa de erros de digitação como "topK"

“args” é a carga útil do esquema, não a chamada inteira

Normalmente, o esquema descreve apenas o objeto de argumentos, não o envelope externo:

  • Externo: { "name": "...", "args": {...} }
  • O esquema se aplica a: args

Essa separação é útil: você pode rotear por name primeiro e, depois, validar args com o esquema correspondente.

Mapeamento para chamada no estilo Python (`**kwargs`) e outras linguagens

Python: despacho direto via `**kwargs`

Um registro de ferramentas (tool registry) típico em Python:

from typing import Any, Callable

TOOLS: dict[str, Callable[..., Any]] = {}

def tool(name: str):
    def decorator(fn: Callable[..., Any]):
        TOOLS[name] = fn
        return fn
    return decorator

@tool("get_weather")
def get_weather(city: str, units: str = "c") -> dict:
    # ... call API
    return {"city": city, "units": units, "temp": 18}

def dispatch(call: dict) -> Any:
    name = call["name"]
    args = call.get("args", {})
    fn = TOOLS[name]
    return fn(**args)  # Python kwargs

Isso é elegante, mas perigoso se args não for validado: fn(**args) vai levantar erros (ou pior — aceitar argumentos inesperados em funções no estilo **kwargs).

TypeScript/JavaScript: estilo de parâmetro-objeto

JS frequentemente modela ferramentas como recebendo um objeto:

type SearchArgs = { query: string; top_k?: number };

async function search(args: SearchArgs) {
  const topK = args.top_k ?? 5;
  // ...
}

function dispatch(call: { name: string; args: any }) {
  if (call.name === "search") return search(call.args);
  throw new Error("Unknown tool");
}

Isso evita completamente o problema de argumentos posicionais — tudo é nomeado.

Por que args posicionais são raros em chamada de ferramentas

Embora Python suporte *args, APIs de chamada de ferramentas geralmente evitam arrays como args: ["Paris", "c"] porque:

  • arrays perdem os nomes dos parâmetros
  • a ordem vira uma dependência oculta
  • validação por esquema e geração parcial ficam mais difíceis

Para uso de ferramentas por LLMs, parâmetros nomeados são muito mais robustos do que argumentos posicionais.

Armadilhas práticas (e como lidar com elas)

1) Incompatibilidades de tipagem e coerção

LLMs frequentemente produzem tipos plausíveis, porém errados:

  • "top_k": "5" (string em vez de inteiro)
  • "include_sources": "true" (string em vez de booleano)
  • "date": "tomorrow" quando uma data ISO é exigida

Estratégias:

  • Validação estrita (rejeitar e pedir que o modelo corrija)
  • Coerção segura (converter "5"5 quando não houver ambiguidade)
  • Híbrida (coagir primitivos, rejeitar incompatibilidades estruturais)

Uma abordagem pragmática é:

  • Coagir apenas casos bem definidos (string → int se corresponder a ^\d+$)
  • Rejeitar todo o resto com um erro estruturado que o modelo consiga reparar

2) Lacunas de validação e “aceitação silenciosa”

Se você permitir chaves arbitrárias, erros de digitação viram bugs em produção:

{ "name": "search", "args": { "query": "x", "topK": 5 } }

Se o seu código lê top_k, ele vai usar o padrão 5 e ignorar o valor pretendido pelo usuário. Evite isso usando:

  • additionalProperties: false no esquema
  • parsing estrito (falhar ao encontrar chaves desconhecidas)

3) Valores padrão: defaults do esquema não são aplicados automaticamente

Um equívoco muito comum: "default": 5 do Esquema JSON não força o produtor (o LLM) a incluir esse campo, e muitos validadores não alteram os dados para inserir defaults.

Então você precisa decidir:

  • Você quer que o modelo sempre produza defaults explícitos?
  • Ou quer que o runtime preencha campos ausentes?

Preencher no runtime costuma ser melhor:

  • chamadas de ferramenta menores
  • menos oportunidades para o modelo “escolher” um default errado
  • comportamento consistente entre modelos

Em Python, você pode confiar nos defaults da função se chamar fn(**args) e garantir que as chaves ausentes estejam realmente ausentes (não null).

4) Ausente vs `null` (e por que isso importa)

LLMs às vezes produzem:

{ "filters": null }

Em Python isso vira None, o que pode ser diferente de “não fornecido”.

Orientação de design:

  • Trate ausente como “use o comportamento padrão”
  • Trate null como “explicitamente sem valor” apenas se sua ferramenta der suporte significativo a isso

Em esquemas, seja explícito:

  • Se null é permitido: "type": ["object", "null"]
  • Caso contrário: mantenha "type": "object" e rejeite null

5) Parâmetros aninhados e esquemas profundos

Ferramentas frequentemente exigem dados aninhados:

{
  "name": "create_calendar_event",
  "args": {
    "title": "Design review",
    "time": { "start": "2026-01-08T10:00:00Z", "end": "2026-01-08T10:30:00Z" },
    "attendees": [
      { "email": "a@example.com" },
      { "email": "b@example.com" }
    ]
  }
}

Armadilhas:

  • Aninhamento profundo aumenta a chance de faltar subcampos obrigatórios (time.end)
  • Modelos podem trocar formatos (objeto vs lista) a menos que o esquema seja muito claro

Mitigações:

  • Manter os esquemas tão rasos quanto possível
  • Adicionar restrições (required em cada nível aninhado)
  • Fornecer exemplos curtos nas descrições das ferramentas (orientação few-shot)

6) Uniões ambíguas (`oneOf`, “string ou objeto”)

Esquemas que aceitam múltiplos formatos podem confundir tanto LLMs quanto validadores:

  • oneOf: [{type: "string"}, {type: "object", ...}]

O modelo pode escolher um ramo não pretendido. Prefira um único formato canônico.

Se você precisar de múltiplos modos, torne o modo explícito:

{
  "type": "object",
  "properties": {
    "mode": { "type": "string", "enum": ["by_id", "by_query"] },
    "id": { "type": "string" },
    "query": { "type": "string" }
  },
  "required": ["mode"],
  "additionalProperties": false
}

Depois, valide requisitos condicionais no código ou com condicionais do Esquema JSON.

7) Faixas numéricas, enums e deriva de restrições

Restrições ajudam o modelo, mas podem divergir das regras reais do seu backend:

  • backend suporta top_k <= 100, o esquema diz <= 50
  • backend exige units in {"c","f"}, o esquema esquece o enum

Trate o esquema como um contrato:

  • gere o esquema a partir de código tipado quando possível (ou vice-versa)
  • adicione testes que comparam restrições do esquema com restrições da implementação

8) Segurança e riscos de injeção (injection)

Chamada de ferramentas reduz alguns riscos (porque você não está executando código gerado pelo modelo), mas argumentos ainda podem ser perigosos:

  • Ferramenta SQL: query pode conter comandos destrutivos
  • Ferramenta de shell: injeção de comando
  • Ferramenta de busca de URL: SSRF (Server-Side Request Forgery) para endpoints internos
  • Ferramentas de arquivos: travessia de caminho (../../etc/passwd)

Mitigue com:

  • listas de permissões (allowlists) (ex.: apenas consultas SELECT; apenas domínios específicos)
  • consultas parametrizadas (parameterized queries) (evite strings SQL brutas quando possível)
  • esquemas estritos + sanitização
  • escopo de capacidades (capability scoping) (ferramentas com privilégios mínimos)
  • registro de logs (logging) e alertas (alerting)

Essa postura de segurança é um tema importante em Engenharia de Prompts e práticas de confiabilidade no uso de ferramentas.

Boas práticas de design para esquemas de ferramentas e objetos args

Prefira “object args” mesmo para ferramentas de um único argumento

Em vez de:

{ "name": "lookup", "args": "doc_123" }

Prefira:

{ "name": "lookup", "args": { "doc_id": "doc_123" } }

Benefícios:

  • extensível sem mudanças incompatíveis
  • autoexplicativo
  • código de despacho consistente entre ferramentas

Seja explícito sobre obrigatório vs opcional

  • Marque campos genuinamente obrigatórios em required
  • Mantenha campos opcionais como opcionais; confie em defaults no runtime
  • Evite “opcional, mas na prática obrigatório”

Use `additionalProperties: false` por segurança

Isso impede chaves desconhecidas e dá suporte à autocorreção do modelo (você pode retornar erros de validação como “campo desconhecido: topK; você quis dizer top_k?”).

Mantenha nomes de ferramentas estáveis e inequívocos

  • Use estilo verbo-substantivo: search_documents, get_weather, create_ticket
  • Evite sinônimos entre ferramentas (search, find, lookup significando a mesma coisa)
  • Trate nomes de ferramentas como superfície de API: versione-os se necessário (search_v2)

Forneça descrições e exemplos curtos e concretos

Muitos frameworks de chamada de ferramentas permitem uma descrição em linguagem natural por ferramenta e por parâmetro. Isso não é perfumaria: é dado instrucional para o modelo.

Exemplo:

  • top_k: “Número de resultados a retornar (1–50). Use 5 para a maioria das consultas.”

Tratamento de erros: tornando args reparáveis pelo modelo

Um padrão forte é:

  1. Validar chamada de ferramenta e args
  2. Se inválida, retornar um erro estruturado que o modelo possa usar para corrigir a chamada
  3. Deixar o modelo tentar de novo com argumentos corrigidos

Exemplo de carga de erro:

{
  "error": "validation_error",
  "tool": "search",
  "details": [
    { "path": "/top_k", "message": "Expected integer, got string" },
    { "path": "", "message": "Unknown property: topK" }
  ]
}

Evite retornar apenas um stack trace; é difícil para o modelo reparar.

Exemplo prático: uma ferramenta de recuperação em um pipeline RAG

Uma chamada típica de ferramenta em RAG pode se parecer com:

{
  "name": "retrieve",
  "args": {
    "query": "What are the side effects of metformin?",
    "k": 8,
    "collection": "medical_guidelines",
    "metadata_filter": {
      "source": "FDA",
      "year_gte": 2018
    }
  }
}

Observações:

  • Filtros aninhados são comuns; valide-os com cuidado.
  • Considere se valores de filtro devem ser tipados (year_gte como inteiro) em vez de tipagem implícita por string (stringly-typed).

Em RAG de produção, a confiabilidade depende fortemente de formatos de argumentos consistentes — especialmente para filtros — porque a qualidade da recuperação pode degradar silenciosamente se filtros estiverem malformados.

Padrões de implementação: stacks de parsing e validação

Python: validação no estilo Pydantic (comum na prática)

Mesmo que suas ferramentas sejam funções Python, muitas vezes é melhor validar args via um modelo:

from pydantic import BaseModel, Field, ValidationError

class SearchArgs(BaseModel):
    query: str = Field(min_length=1)
    top_k: int = Field(default=5, ge=1, le=50)

def search(args: SearchArgs):
    return {"results": [], "used_top_k": args.top_k}

def dispatch(call: dict):
    if call["name"] == "search":
        try:
            args = SearchArgs.model_validate(call.get("args", {}))
        except ValidationError as e:
            return {"error": "validation_error", "details": e.errors()}
        return search(args)
    return {"error": "unknown_tool"}

Essa abordagem:

  • centraliza a validação
  • aplica defaults de forma determinística
  • produz detalhes de erro legíveis por máquina

TypeScript: validação em runtime (ex.: Zod) para args não confiáveis

Tipagem estática por si só não protege você em runtime — args vem de um LLM. Use validação em runtime.

Testes e observabilidade: trate chamadas de ferramenta como uma API

Como name + args é efetivamente uma fronteira de RPC (remote procedure call), aplique disciplina de API:

  • Testes unitários para esquemas e validadores
  • Testes de referência (golden tests) para chamadas de ferramenta comuns
  • Registro de logs de:
    • nome da ferramenta
    • args validados (com tarja/remoção se sensíveis)
    • latência e tipos de erro
  • Métricas:
    • taxa de sucesso de chamadas de ferramenta
    • taxa de falha de validação
    • contagem de “loop de reparo” (repair loop) (com que frequência o modelo precisou tentar novamente)

Isso frequentemente é a diferença entre uma demonstração e um sistema robusto.

Resumo: modelo mental e principais conclusões

  • Uma chamada de ferramenta como name + args (kwargs) é uma representação prática e independente de linguagem de “chame esta função com estes parâmetros nomeados”.
  • O padrão se alinha naturalmente com **kwargs do Python e argumentos-objeto do JavaScript.
  • Esquema JSON (ou equivalentes) torna args validável e orientável para o modelo, mas defaults e coerções exigem tratamento deliberado em runtime.
  • Os problemas mais difíceis no mundo real não são o formato do envelope — são tipagem, validação, defaults, estruturas aninhadas e restrições de segurança.
  • Trate a chamada de ferramentas como um contrato de API: esquemas estritos, erros claros, despacho seguro e boa observabilidade são o que torna o uso de ferramentas confiável em sistemas de produção.