Avanzato 20 minAPI

Padroneggiare la chiamata di strumenti (Tool Calling) con Ollama e Python

La chiamata di strumenti (tool calling) trasforma un modello che si limita a generare testo in un agente capace di avviare l'esecuzione di codice reale: chiamare un'API meteo, interrogare una base di dati, eseguire un calcolo. Questa guida mostra come padroneggiare il tool calling di Ollama in Python dall'inizio alla fine — formato JSON degli strumenti, ciclo di esecuzione, streaming delle chiamate agli strumenti (serie 0.17) e output strutturati vincolati da un JSON Schema applicato direttamente durante la decodifica. Tutto viene eseguito in locale su http://localhost:11434, senza chiave API né fughe di dati.

Di Mohamed Meguedmi·Agg. 2026-08-27·Testato su Windows, macOS e Linux

#Perché l'invocazione degli strumenti (tool calling)?

Un LLM da solo non sa nulla del mondo reale dopo il suo addestramento: non conosce né il meteo di oggi, né il saldo di un conto, né il contenuto del tuo database. La chiamata a uno strumento colma questa lacuna. Descrivi al modello un elenco di funzioni disponibili; il modello decide quali chiamare e con quali argomenti, il tuo codice le esegue e poi restituisce il risultato al modello affinché formuli una risposta informata.

Punto cruciale da capire: il modello non esegue mai nulla da solo. Si limita a generare una richiesta strutturata — «chiama get_meteo con ville='Lyon'». È il tuo programma Python che esegue la funzione e mantiene il controllo totale. Questa separazione è ciò che rende il tool calling sicuro e prevedibile.

Dati aggiornati
Il modello interroga un'API in tempo reale invece di tirare a indovinare sulla base di ciò che ricorda dall'addestramento.
Azioni concrete
Creare un ticket, inviare un'e-mail, scrivere in un database — il LLM orchestra, il tuo codice agisce.
Affidabilità
I calcoli e le ricerche esatte vengono delegati a codice deterministico, anziché essere inventati dal modello sotto forma di allucinazioni.
100 % locale
Con Ollama, tutta la catena rimane sulla tua macchina: nessuna chiave API, nessuna richiesta in uscita, nessuna fatturazione per token.

#Come funziona la chiamata agli strumenti in Ollama

Il kit Copilota Locale

Questa guida ti porta al modello. Il kit ti porta al copilota che scrive codice nel tuo editor.

  • Spazio online a vita
  • PDF + file
  • Aggiornamenti a vita

Il ciclo completo si articola in cinque fasi. Visualizzarlo bene evita l’equivoco più comune: credere che basti una sola chiamata. Ne servono almeno due — una per ottenere la richiesta di utilizzo di uno strumento, una per ottenere la risposta finale.

  1. 01
    Invii la domanda + gli strumenti
    La richiesta chat contiene il messaggio utente e l'elenco degli strumenti disponibili (parametro tools).
  2. 02
    Il modello restituisce una richiesta di chiamata a uno strumento
    Invece di rispondere con del testo, restituisce uno o più tool_calls con il nome della funzione e gli argomenti.
  3. 03
    Il tuo codice esegue la funzione
    Recuperi name e arguments, chiami la funzione Python effettiva corrispondente e ottieni un risultato.
  4. 04
    Restituisci il risultato
    Il risultato viene aggiunto alla cronologia sotto forma di un messaggio con ruolo « tool », poi richiami chat.
  5. 05
    Il modello scrive la risposta finale
    Sulla base del risultato, questa volta produce una risposta in linguaggio naturale per l'utente.
i
Almeno due chiamate
Un ciclo completo di tool calling = almeno due passaggi attraverso il modello. Se il modello chiama più strumenti, si ripete fino a quando non ci sono più tool_calls nella sua risposta.

#Prerequisiti

Tre componenti: il daemon Ollama in esecuzione, un modello che supporta davvero gli strumenti e la libreria Python ufficiale. Attenzione al secondo punto: non tutti i modelli sanno fare tool calling. Punta sulle famiglie recenti progettate per questo.

Ollama aggiornato
Serie 0.17 o successiva per sfruttare lo streaming delle chiamate agli strumenti. Il daemon ascolta su http://localhost:11434.
Un modello compatibile con gli strumenti
Qwen 3.5, Granite 4.2, Mistral Small, Devstral, gpt-oss. I modelli contrassegnati « tools » sulla pagina ollama.com/library.
Abbastanza VRAM
Un piccolo modello (Qwen 3.5 4B ≈ 3,4 GB) basta per testare; un Granite 4.2 8B (≈ 5,3 GB) o un Qwen 3.5 9B (≈ 6,6 GB) segue meglio le istruzioni multi-tool. RTX 3060 12 GB come entry level.
La libreria ollama
pip install -U ollama. Elle sait construire le schéma d'un outil directement depuis une fonction Python typée.
Preparare l'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
!
Modello senza supporto per strumenti
Passare un parametro tools a un modello che non lo supporta non genera sempre un errore chiaro: il modello ignora gli strumenti e risponde con del testo, oppure restituisce del falso JSON nel contenuto. Controlla sempre l'etichetta «Tools» del modello prima di scrivere codice.

#Il formato JSON degli strumenti

Uno strumento viene descritto con uno schema JSON rigorosamente allineato a quello di OpenAI: un oggetto type: "function" contenente un nome, una descrizione e un oggetto parameters in formato JSON Schema. La descrizione conta moltissimo — è ciò che il modello legge per decidere quando e come chiamare lo strumento. Sii esplicito.

Definizione di uno strumento (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"]
    }
  }
}
→
Lascia che la libreria scriva lo schema
In Python, non è obbligatorio scrivere manualmente questo JSON. Se si passa direttamente una funzione tipizzata con una docstring, la libreria ollama ne deduce automaticamente lo schema (nomi, tipi, descrizione). È il modo più sicuro per evitare errori di battitura nel JSON.

#Prima chiamata a uno strumento in Python

Partiamo dal caso più semplice: una funzione, una domanda, e osserviamo cosa decide il modello. Le annotazioni di tipo e la docstring servono a generare lo schema inviato al modello.

Un primo tool call
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'}

A questo punto, message.content è generalmente vuoto: il modello ha restituito la sua richiesta in message.tool_calls. Ogni tool_call espone function.name (una stringa) e function.arguments (già deserializzato in un dizionario Python dalla libreria). Non resta che eseguire la chiamata e restituire il risultato.

#Il ciclo di esecuzione completo

Ecco lo scheletro riutilizzabile di un agente che usa il tool calling: un dizionario che associa ogni nome di strumento alla relativa funzione, l'esecuzione delle chiamate richieste, l'aggiunta dei risultati alla cronologia, poi una seconda chiamata per la risposta finale. Si racchiude il tutto in un ciclo per gestire il caso in cui il modello richiami più strumenti in successione.

Ciclo completo dell'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),
        })
!
Mai fidarsi degli argomenti
Gli argomenti provengono dal modello: possono essere incompleti, avere tipi errati o essere fuori dai limiti consentiti. Validali prima di eseguire la funzione, soprattutto se interviene su un file system, un database o un comando shell. Un tool calling non validato apre la porta all'injection.

Il messaggio di risposta contiene il ruolo tool e un campo tool_name che indica a quale chiamata risponde. Il content deve essere una stringa: serializza gli oggetti (json.dumps) prima di restituirli. Il modello interpreta questo contenuto come se fosse un'osservazione del mondo.

#Parità OpenAI: lo stesso codice con il client openai

Ollama espone un endpoint compatibile con OpenAI su /v1. Se il tuo codice utilizza già il client openai, non devi cambiare quasi nulla: punta base_url a Ollama e imposta una chiave API fittizia. Il formato degli strumenti e dei tool_calls è identico: è la «parità OpenAI» che rende la migrazione banale.

Chiamata di strumenti tramite il client 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
Una differenza da conoscere
Tramite il client openai, function.arguments arriva sotto forma di stringa JSON (da parsare con json.loads), mentre la libreria ollama nativa ti fornisce già un dizionario. Pensaci quando passi da un client all'altro.

#Streaming dei tool calls (serie 0.17)

Storicamente, attivare lo streaming disabilitava il tool calling: bisognava scegliere. A partire dalla serie 0.17, Ollama può trasmettere in streaming le chiamate agli strumenti durante la generazione. In pratica, ricevi i tool_calls nei blocchi (chunk) del flusso, insieme all'eventuale testo, il che permette di mostrare una risposta fluida e al tempo stesso attivare gli strumenti.

Chiamate a strumenti in 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 streamare
Lo streaming è eccellente per le interfacce conversazionali in cui l'utente vede la risposta costruirsi. Per elaborazioni batch o estrazioni di dati, rimani in modalità non-streaming: è più semplice da gestire e ottieni la risposta completa in una volta sola.

#Output strutturati: forzare uno schema JSON

Il tool calling serve ad agire; gli output strutturati servono a garantire la forma della risposta. Con il parametro format, passi un JSON Schema che Ollama applica durante la decodifica: il modello è vincolato, token per token, a produrre solo un output valido rispetto allo schema. Basta con il parsing fragile di JSON approssimativo: la struttura è garantita per costruzione.

Il modo più pratico in Python è descrivere la struttura con un modello Pydantic, poi estrarre lo schema tramite model_json_schema(). Successivamente ottieni un oggetto tipizzato e validato.

Uscita strutturata validata da 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 schema completo
format="json" impone soltanto JSON valido, senza imporre una struttura. Passare uno schema JSON completo va molto oltre: vincola i campi, i tipi e i valori consentiti durante la decodifica. Preferisci sempre lo schema esplicito quando conosci la struttura attesa.
→
La combinazione vincente
Chiamata di strumenti per recuperare i dati, output strutturato per restituirli in modo corretto. Un agente che chiama un'API e restituisce un oggetto Pydantic validato è molto più robusto di un modello a cui si chiede «rispondi in JSON» sperando che funzioni.

#Gestione degli errori e insidie comuni

L'invocazione dei tool fallisce raramente in modo evidente: di solito, il modello «si disorienta» in modo silenzioso. Ecco i problemi più comuni e come affrontarli.

Nessun tool_call restituito
Il modello ha risposto con del testo quando era necessario usare uno strumento. Migliora la descrizione dello strumento oppure cambia modello: i modelli piccoli spesso sbagliano nel decidere se chiamare uno strumento.
Argomenti mancanti o errati
function.arguments può omettere un campo required o assegnargli un tipo errato. Valida gli argomenti con Pydantic o un try/except prima di chiamare la funzione effettiva e restituisci l'errore al modello come risultato dello strumento.
Strumento allucinato
Il modello inventa un nome di funzione inesistente. Per questo si usa OUTILS.get(name) che restituisce un messaggio di errore invece di crashare — il modello può quindi correggersi nel turno successivo.
Loop infinito di chiamate agli strumenti
Un modello può continuare a richiedere lo stesso strumento all'infinito. Aggiungi un contatore delle iterazioni con un limite massimo (es.: 5) per interrompere il ciclo ed evitare che continui a girare a vuoto.
Contesto troncato
Ollama a volte limita il contesto a 2048 token per impostazione predefinita, facendo perdere la cronologia degli strumenti nelle sessioni lunghe. Aumenta num_ctx tramite le opzioni del modello.
Risultato non serializzato
Restituire un oggetto Python non serializzato come content fa fallire la richiesta. Serializzalo sempre come stringa (json.dumps o str) prima di aggiungerlo ai messaggi.
Esecuzione difensiva di un tool
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)

L'idea chiave: non lasciare mai che un errore di uno strumento mandi in crash l'agente. Rimanda il messaggio di errore al modello come se fosse un risultato. Un buon modello legge «strumento sconosciuto» o «argomento mancante» e adatta da solo la chiamata successiva.


#Per approfondire

Ora sai usare il tool calling con Ollama in Python: formato JSON degli strumenti, ciclo di esecuzione, parità con OpenAI, streaming e output strutturati vincolati da uno schema. Queste guide approfondiscono naturalmente l'argomento.

L'API REST di Ollama
« Integrare Ollama in un'applicazione Python tramite l'API REST » — le basi dell'endpoint :11434, dello streaming e della modalità JSON, fondamento di tutta questa guida.
Agenti con LangChain
«Creare un agente IA locale in Python con LangChain e Ollama» — orchestrare più strumenti e una memoria su un livello superiore al semplice tool calling.
Scegliere la quantizzazione
« Scegliere la quantizzazione (Q4, Q5, Q8, FP16) » — per bilanciare la VRAM e la qualità del modello che controllerà i tuoi strumenti.
Questa guida ti è stata utile?

Un feedback, un errore, una precisazione? Facci sapere, così la guida migliora per tutti.