Avançado 20 minAPI

Dominar a chamada de ferramentas (Tool Calling) com Ollama e Python

A chamada de ferramentas (tool calling) transforma um modelo que apenas gera texto em um agente capaz de acionar código real: chamar uma API de previsão do tempo, consultar uma base de dados, executar um cálculo. Este guia mostra como dominar a chamada de ferramentas com Ollama em Python de ponta a ponta — formato JSON das ferramentas, laço de execução, streaming das chamadas de ferramentas (série 0.17) e saídas estruturadas restritas por um JSON Schema aplicado diretamente à decodificação. Tudo roda localmente em http://localhost:11434, sem chave de API nem vazamento de dados.

Por Mohamed Meguedmi·Atualização 2026-08-27·Testado no Windows, macOS e Linux

#Por que a chamada de ferramenta (tool calling)?

Um LLM, por si só, não sabe nada do mundo real após seu treinamento: ele não conhece o tempo de hoje, o saldo de uma conta nem o conteúdo do seu banco de dados. A chamada de ferramenta preenche essa lacuna. Você descreve ao modelo uma lista de funções disponíveis, ele decide quais chamar e com quais argumentos, seu código as executa e depois retorna o resultado ao modelo para que ele redija uma resposta informada.

O ponto crucial a entender: o modelo nunca executa nada por si só. Ele apenas gera uma solicitação estruturada — “chame get_meteo com ville='Lyon'”. É o seu programa Python que executa a função e mantém o controle total. Essa separação é o que torna a chamada de ferramentas segura e previsível.

Dados atualizados
O modelo consulta uma API em tempo real, em vez de adivinhar com base em suas lembranças do treinamento.
Ações concretas
Criar um ticket, enviar um e-mail, escrever em um banco de dados — o LLM coordena, seu código age.
Confiabilidade
Os cálculos e as buscas exatas são delegados a código determinístico, em vez de serem inventados pelo modelo.
100 % local
Com Ollama, toda a cadeia permanece na sua máquina: sem chave API, sem requisição de saída, sem fatura por token.

#Como funciona a chamada de ferramentas no Ollama

O kit Copiloto Local

Este guia leva você ao modelo. O kit leva você ao copiloto que programa no seu editor.

  • Espaço online vitalício
  • PDF + arquivos
  • Atualizações vitalícias

O ciclo completo é composto por cinco etapas. Visualizá-lo bem evita a confusão mais comum: acreditar que uma única chamada é suficiente. São necessárias pelo menos duas — uma para obter a solicitação de ferramenta, outra para obter a resposta final.

  1. 01
    Você envia a pergunta + as ferramentas
    A requisição de chat contém a mensagem do usuário e a lista das ferramentas disponíveis (parâmetro tools).
  2. 02
    O modelo retorna uma solicitação de ferramenta
    Em vez de responder em texto, ele retorna um ou mais tool_calls com o nome da função e os argumentos.
  3. 03
    Seu código executa a função
    Você recupera name e arguments, chama a função Python real correspondente e obtém um resultado.
  4. 04
    Você retorna o resultado
    O resultado é adicionado ao histórico como uma mensagem com o papel “tool”, depois você chama chat novamente.
  5. 05
    O modelo redige a resposta final
    Com base no resultado, ele produz, desta vez, uma resposta em linguagem natural para o usuário.
i
No mínimo duas chamadas
Um ciclo completo de chamada de ferramentas = pelo menos duas passagens pelo modelo. Se o modelo chamar várias ferramentas em sequência, o processo se repete até que não haja mais tool_calls na resposta.

#Pré-requisitos

Três componentes: o daemon Ollama em execução, um modelo que realmente suporte ferramentas e a biblioteca Python oficial. Atenção ao segundo ponto — nem todos os modelos sabem fazer chamadas de ferramentas. Prefira famílias recentes projetadas para isso.

Ollama atualizado
Série 0.17 ou mais recente para aproveitar o streaming das chamadas de ferramentas. O daemon escuta em http://localhost:11434.
Um modelo compatível com ferramentas
Qwen 3.5, Granite 4.2, Mistral Small, Devstral, gpt-oss. Modelos marcados como "tools" no ollama.com/library.
VRAM suficiente
Um pequeno modelo (Qwen 3.5 4B ≈ 3,4 GB) é suficiente para testes; um Granite 4.2 8B (≈ 5,3 GB) ou um Qwen 3.5 9B (≈ 6,6 GB) segue melhor as instruções multi-ferramenta. RTX 3060 12 GB como entrada de nível básico.
A biblioteca ollama
pip install -U ollama. Ela sabe construir o esquema de uma ferramenta diretamente a partir de uma função Python tipada.
Preparar o ambiente
# Le daemon Ollama doit tourner (souvent déjà lancé en service)
ollama serve

# Un modèle qui supporte les outils
ollama pull qwen3.5:4b

# La librairie Python officielle
pip install -U ollama
!
Modelo sem suporte a ferramentas
Passar um parâmetro tools para um modelo que não o suporta não gera sempre um erro claro: o modelo ignora as ferramentas e responde em texto, ou retorna JSON falso no conteúdo. Verifique sempre a etiqueta «Tools» do modelo antes de codificar.

#O formato JSON das ferramentas

Uma ferramenta é descrita com um esquema JSON estritamente alinhado ao da OpenAI: um objeto type: "function" contendo um nome, uma descrição e um objeto parameters no formato JSON Schema. A descrição é fundamental — é ela que o modelo lê para decidir quando e como chamar a ferramenta. Seja explícito.

Definição de uma ferramenta (formato OpenAI)
{
  "type": "function",
  "function": {
    "name": "get_meteo",
    "description": "Renvoie la météo actuelle pour une ville donnée",
    "parameters": {
      "type": "object",
      "properties": {
        "ville": {
          "type": "string",
          "description": "Nom de la ville, ex : Lyon"
        },
        "unite": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "Unité de température souhaitée"
        }
      },
      "required": ["ville"]
    }
  }
}
→
Deixe a biblioteca escrever o esquema
Em Python, você não precisa escrever manualmente esse JSON. Se passar diretamente uma função tipada com uma docstring, a biblioteca ollama deduz automaticamente o esquema (nomes, tipos, descrição). É a forma mais segura de evitar erros de digitação no JSON.

#Primeira chamada de ferramenta em Python

Vamos começar pelo caso mais simples: uma função, uma pergunta, e observamos o que o modelo decide. As anotações de tipo e a docstring servem para gerar o esquema enviado ao modelo.

Uma primeira chamada de ferramenta
import ollama

def get_meteo(ville: str, unite: str = "celsius") -> str:
    """Renvoie la météo actuelle pour une ville donnée.

    Args:
        ville: Nom de la ville (ex : Lyon).
        unite: Unité de température, celsius ou fahrenheit.
    """
    # Ici, un vrai appel à une API météo. On simule le retour.
    return f"21 degrés, ciel dégagé à {ville} ({unite})."

reponse = ollama.chat(
    model="qwen3.5:4b",
    messages=[{"role": "user", "content": "Quel temps fait-il à Lyon ?"}],
    tools=[get_meteo],  # la lib introspecte signature + docstring
)

# Le modèle n'a pas répondu en texte : il demande un outil
for appel in reponse.message.tool_calls or []:
    print(appel.function.name)       # -> get_meteo
    print(appel.function.arguments)  # -> {'ville': 'Lyon'}

Nesta etapa, message.content geralmente está vazio: o modelo retornou sua solicitação em message.tool_calls. Cada tool_call expõe function.name (uma string) e function.arguments (já desserializado em um dicionário Python pela biblioteca). Resta apenas executar e retornar.

#O loop completo de execução

Aqui está a estrutura reutilizável de um agente que faz chamadas de ferramentas: um dicionário que associa cada nome de ferramenta à sua função, a execução das chamadas solicitadas, a adição dos resultados ao histórico e, em seguida, uma segunda chamada para a resposta final. Envolvemos tudo em um laço para lidar com o caso em que o modelo encadeia chamadas a várias ferramentas.

Loop completo do agente
import ollama

def get_meteo(ville: str, unite: str = "celsius") -> str:
    """Météo actuelle d'une ville."""
    return f"21 degrés, ciel dégagé à {ville}."

# Registre nom -> fonction réelle
OUTILS = {"get_meteo": get_meteo}

messages = [{"role": "user", "content": "Météo à Lyon puis à Marseille ?"}]

while True:
    reponse = ollama.chat(model="qwen3.5:4b", messages=messages, tools=[get_meteo])
    messages.append(reponse.message)  # on garde la demande dans l'historique

    if not reponse.message.tool_calls:
        # Plus d'outil demandé : c'est la réponse finale
        print(reponse.message.content)
        break

    for appel in reponse.message.tool_calls:
        fonction = OUTILS.get(appel.function.name)
        if fonction is None:
            resultat = f"Erreur : outil inconnu '{appel.function.name}'"
        else:
            resultat = fonction(**appel.function.arguments)
        messages.append({
            "role": "tool",
            "tool_name": appel.function.name,
            "content": str(resultat),
        })
!
Nunca confiar nos argumentos
Os argumentos vêm do modelo: podem estar incompletos, ter tipos incorretos ou estar fora dos limites. Valide-os antes de executar a função, especialmente se ela acessar um sistema de arquivos, um banco de dados ou um comando shell. Uma chamada de ferramenta não validada é uma porta aberta para injeção.

A mensagem de resultado tem o papel tool e um campo tool_name que indica a qual chamada ela responde. O campo content deve ser uma string: serialize seus objetos (json.dumps) antes de retorná-los. O modelo relê esse conteúdo como se fosse uma observação do mundo.

#Paridade com a OpenAI: o mesmo código com o cliente openai

Ollama expõe um endpoint compatível com OpenAI em /v1. Se seu código já utiliza o cliente openai, você quase não precisa mudar nada: aponte base_url para Ollama e use uma chave de API fictícia. O formato das ferramentas e dos tool_calls é idêntico — é a “paridade OpenAI” que torna a migração trivial.

Chamada de ferramentas via cliente OpenAI
from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

reponse = client.chat.completions.create(
    model="qwen3.5:4b",
    messages=[{"role": "user", "content": "Météo à Lyon ?"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_meteo",
            "description": "Météo actuelle d'une ville",
            "parameters": {
                "type": "object",
                "properties": {"ville": {"type": "string"}},
                "required": ["ville"],
            },
        },
    }],
)

print(reponse.choices[0].message.tool_calls)
i
Uma diferença que você precisa conhecer
Através do cliente openai, function.arguments chega na forma de uma string JSON (que deve ser parseada com json.loads), enquanto a biblioteca nativa ollama já entrega um dicionário. Pense nisso ao migrar de um cliente para o outro.

#Streaming de chamadas de ferramentas (série 0.17)

Historicamente, ativar o streaming desativava o tool calling: era necessário escolher. Desde a série 0.17, o Ollama consegue transmitir chamadas de ferramentas em streaming ao longo da geração. Na prática, você recebe os tool_calls nos blocos (chunks) do fluxo, junto com o texto, se houver, o que permite exibir uma resposta fluida enquanto aciona ferramentas.

Chamadas de ferramenta em streaming
import ollama

flux = ollama.chat(
    model="qwen3.5:4b",
    messages=[{"role": "user", "content": "Météo à Nice ?"}],
    tools=[get_meteo],
    stream=True,
)

for morceau in flux:
    # Le texte arrive token par token
    if morceau.message.content:
        print(morceau.message.content, end="", flush=True)
    # Les appels d'outils arrivent aussi dans le flux
    for appel in morceau.message.tool_calls or []:
        print("\n[outil]", appel.function.name, appel.function.arguments)
→
Quando usar streaming
O streaming é excelente para interfaces conversacionais, onde o usuário vê a resposta se construindo. Para processamento em lote ou extração de dados, mantenha o modo não-streaming: é mais fácil de gerenciar e você recebe a resposta completa de uma vez.

#Saídas estruturadas: forçar um JSON Schema

O tool calling serve para agir; as saídas estruturadas servem para garantir o formato da resposta. Com o parâmetro format, você fornece um JSON Schema que o Ollama aplica durante a decodificação: o modelo é limitado, token por token, a produzir apenas uma saída válida de acordo com o esquema. Chega de parsing frágil de JSON aproximado — a estrutura é garantida por construção.

O mais prático em Python é definir a estrutura com um modelo Pydantic, depois obter o esquema por meio de model_json_schema(). Em seguida, você recebe um objeto tipado e validado.

Saída estruturada validada por Pydantic
from pydantic import BaseModel
import ollama

class Facture(BaseModel):
    numero: str
    montant_ttc: float
    devise: str
    lignes: list[str]

reponse = ollama.chat(
    model="qwen3.5:4b",
    messages=[{
        "role": "user",
        "content": "Extrais numéro, montant TTC, devise et lignes de : "
                   "Facture F-2026-0042, total 149,90 EUR, "
                   "prestations : audit, rédaction.",
    }],
    # Le schéma est appliqué au décodage : sortie garantie conforme
    format=Facture.model_json_schema(),
)

facture = Facture.model_validate_json(reponse.message.content)
print(facture.montant_ttc)  # -> 149.9
i
format="json" vs esquema completo
format="json" força apenas JSON válido, sem impor uma estrutura. Passar um JSON Schema completo vai muito além: ele restringe os campos, os tipos e os valores permitidos no momento da decodificação. Prefira sempre o esquema explícito quando você conhece a forma esperada.
→
A combinação vencedora
Chamada de ferramenta para buscar os dados e saída estruturada para retorná-los de forma organizada. Um agente que chama uma API e depois retorna um objeto Pydantic validado é muito mais robusto do que um modelo ao qual você pede “responda em JSON” esperando que funcione.

#Tratamento de erros e armadilhas comuns

A chamada de ferramentas raramente falha de forma evidente: na maioria das vezes, o modelo 'sai dos trilhos' silenciosamente. Veja as falhas frequentes e como lidar com elas.

Nenhum tool_call retornado
O modelo respondeu em texto quando deveria ter usado uma ferramenta. Melhore a descrição da ferramenta ou mude de modelo: os modelos pequenos costumam errar ao decidir se devem chamar uma ferramenta.
Argumentos faltando ou incorretos
function.arguments pode omitir um campo required ou atribuir a ele um tipo incorreto. Valide com Pydantic ou com um try/except antes de chamar a função real e retorne o erro ao modelo como resultado da ferramenta.
Ferramenta alucinada
O modelo cria um nome de função inexistente. Por isso, o OUTILS.get(name) retorna uma mensagem de erro em vez de travar — assim, o modelo pode se corrigir na rodada seguinte.
Loop infinito de ferramentas
Um modelo pode solicitar a mesma ferramenta indefinidamente. Adicione um contador com um limite máximo de iterações (ex.: 5) para interromper o loop e evitar que ele continue executando sem produzir resultados.
Contexto truncado
Ollama limita às vezes o contexto a 2048 tokens por padrão, o que apaga o histórico de ferramentas em sessões longas. Aumente num_ctx através das opções do modelo.
Resultado não serializado
Retornar um objeto Python bruto como content quebra a requisição. Sempre serialize em uma string (json.dumps ou str) antes de adicioná-lo às mensagens.
Execução defensiva de uma ferramenta
import json

def executer_outil(appel, registre, garde_fou=5):
    nom = appel.function.name
    fonction = registre.get(nom)
    if fonction is None:
        return f"Erreur : outil inconnu '{nom}'."
    try:
        resultat = fonction(**appel.function.arguments)
    except TypeError as e:
        return f"Erreur d'arguments pour {nom} : {e}"
    except Exception as e:
        return f"Échec de {nom} : {e}"
    return json.dumps(resultat, ensure_ascii=False, default=str)

A ideia principal: nunca deixar um erro de ferramenta fazer o agente travar. Retorne a mensagem de erro ao modelo como se fosse um resultado. Um bom modelo lê “ferramenta desconhecida” ou “argumento ausente” e ajusta sua próxima chamada por conta própria.


#Para se aprofundar

Agora você sabe fazer chamadas de ferramentas com o Ollama em Python: formato JSON das ferramentas, loop de execução, paridade com a OpenAI, streaming e saídas estruturadas restritas por um esquema. Estes guias dão continuidade ao tema naturalmente.

A API REST do Ollama
« Integrar Ollama em uma aplicação Python via API REST » — os fundamentos do endpoint :11434, do streaming e do modo JSON, que servem de base para todo este guia.
Agentes com LangChain
« Criar um agente de IA local em Python com LangChain e Ollama » — orquestrar várias ferramentas e memória em uma camada acima das chamadas diretas a ferramentas.
Escolher sua quantização
« Escolher a quantização (Q4, Q5, Q8, FP16) » — para equilibrar VRAM e qualidade do modelo que controlará suas ferramentas.
Este guia ajudou você?

Um comentário, um erro ou uma observação? Avise-nos; isso ajuda a melhorar o guia para todos.