Jangada AIJangada AI

Tools (function calling)

O modelo pode pedir para chamar ferramentas (funções). A API é de baixo nível: o complete() devolve as chamadas pedidas em Completion.tool_calls, você executa e reenvia o resultado. Suportado em OpenAI, Groq, Anthropic e Gemini — a mesma interface nos quatro.

from jangada_ai import LLM, Message

def get_weather(city: str, units: str = "metric") -> str:
    """Retorna o clima atual de uma cidade."""
    return "25°C, ensolarado"

llm = LLM("openai", "gpt-4o-mini")

# 1) o modelo decide chamar a ferramenta
comp = llm.complete("Como está o tempo em Recife?", tools=[get_weather])

# 2) você executa cada chamada e monta os resultados
results = []
for call in comp.tool_calls:        # call.name, call.args (dict)
    saida = get_weather(**call.args)
    results.append(call.result(saida))

# 3) reenvia: histórico = pergunta + resposta-com-tool-calls + resultados
comp2 = llm.complete(
    "Como está o tempo em Recife?",
    history=[comp.assistant_message(), Message.tool_results(*results)],
    tools=[get_weather],
)
print(comp2.text)

Definindo ferramentas

tools=[...] aceita:

  • função Python — o schema sai da assinatura (type hints) + docstring;
  • modelo Pydantic — vira o schema dos argumentos;
  • dict {"name", "description", "parameters"} (JSON Schema) já pronto;
  • um Tool (via to_tool(...)).

tool_choice controla a escolha: "auto" (padrão), "none", "required", ou o nome de uma ferramenta para forçá-la.

Peças

  • Completion.tool_calls: lista de ToolCall(id, name, args).
  • comp.assistant_message(): reconstrói a mensagem do assistant (texto + tool calls) para o histórico.
  • call.result(saida): cria o ToolResultPart correspondente.
  • Message.tool_results(*parts): empacota os resultados numa mensagem.

Ferramentas pré-prontas

A jangada traz tools prontas em jangada_ai.prebuilt:

from jangada_ai.prebuilt import tavily_search   # busca na web (Tavily)

llm.complete("Qual a cotação do dólar hoje?", tools=[tavily_search])
# execute: tavily_search(**call.args)  (precisa de TAVILY_API_KEY no ambiente)

Sem dependência e sem chave:

ToolO que faz
calculatoravalia expressões aritméticas (seguro, via ast)
current_datetimedata/hora atuais (fuso IANA)
fetch_urlbaixa uma página e devolve o texto legível (bloqueia rede interna por padrão)
wikipedia_searchresumo da Wikipedia (sem chave)
http_requestrequisição HTTP genérica (GET/POST/...; bloqueia rede interna por padrão)

Com chave (lê do ambiente, ou passe api_key=):

ToolChave
tavily_searchTAVILY_API_KEY (ou tavily_tool(api_key=...))
brave_searchBRAVE_API_KEY
openweatherOPENWEATHER_API_KEY

Parâmetros keyword-only (após *, como api_key/timeout) são config de runtime e não aparecem no schema que o modelo vê.

As tools que chamam API externa tratam todos os casos de resposta: rate limit (429, respeitando Retry-After), auth (401/403), 404, 5xx, timeout/ conexão e corpo não-JSON. Erros transitórios (429/5xx/timeout) têm retry leve com backoff; ao esgotar, a tool devolve uma mensagem de erro como texto (em vez de levantar exceção), para o modelo decidir o que fazer.

Veja também Structured output (que no Anthropic já usa tool-forcing por baixo) e Observabilidade (as tool calls aparecem no trace).

O que mudou na 1.9.0

  • Tipos de parâmetro. int | None (sintaxe do Python 3.10+) vira o tipo opcional correto no schema também no Python 3.10–3.13; Union de vários tipos vira anyOf; um parâmetro anotado com um BaseModel do Pydantic vira o schema do modelo, e jangada_ai.coerce_args(fn, args) converte o dict recebido na instância (o Agent já faz isso sozinho). Anotações que não resolvem (forward ref quebrada) não derrubam mais a função inteira.
  • Argumentos com JSON inválido não viram {} em silêncio: o ToolCall traz metadata["raw_arguments"] e metadata["args_error"] = True.
  • Tools nativas (web search, code execution…) entram na mesma lista tools= — veja Tools nativas.

Ferramentas prontas mais seguras

  • fetch_url / http_request bloqueiam rede interna por padrão. Só http/ https; o host é resolvido e loopback, rede privada, link-local (inclui o metadata da nuvem, 169.254.169.254), reservado e multicast são recusados — também em cada redirect (máx. 5). O corpo é lido até 5 MB. Isso impede que um prompt injection numa página faça o agente ler seu .env ou a rede da empresa. Para chamar um serviço interno de propósito: allow_private=True na chamada ou a env JANGADA_HTTP_ALLOW_PRIVATE=1.
  • Erros de rede viram mensagem para o modelo (URL malformada, conexão resetada, resposta incompleta), em vez de exceção que derruba o loop.
  • fetch_url respeita o charset do servidor e decodifica entidades HTML.
  • calculator limita expoente (±1000) e tamanho dos números (10 mil dígitos) — 9**9**9 não trava mais o processo.
  • CNPJ alfanumérico. validar_cnpj, formatar_cnpj, validar_documento e consultar_cnpj aceitam o formato novo da Receita (letras nas 12 primeiras posições, dígitos verificadores numéricos), ex.: 12.ABC.345/01DE-35.

Exemplo

examples/tools_example.py — script executável.

On this page