Un 'agente' IA non e' magia: e' un modello linguistico a cui diamo degli strumenti (funzioni che scrivi tu) e la liberta' di decidere quando usarli per portare a termine un compito. In questa guida costruiremo da zero, in Python, un agente funzionante che sa fare calcoli, leggere file e cercare informazioni, gestendo il ciclo in cui il modello chiede di eseguire uno strumento e noi gli restituiamo il risultato. Il meccanismo si chiama tool use (o function calling) ed e' il cuore di tutti gli assistenti evoluti.
A chi serve e cosa devi sapere gia'
Questa e' una guida di livello intermedio-avanzato. Ti serve: Python 3.10 o superiore, dimestichezza di base con il linguaggio (funzioni, dizionari), e una chiave API. Useremo l'SDK compatibile con lo standard OpenAI, che funziona con l'API di OpenAI ma anche con DeepSeek e con modelli locali via Ollama: cosi' potrai scegliere se pagare a consumo o eseguire tutto gratis sul tuo computer. Al termine avrai un agente completo e capirai il ciclo che sta sotto ai grandi framework.
Quale modello usare: le tre opzioni
- OpenAI (GPT-4o / GPT-4o-mini): tool use molto affidabile, ottima documentazione. A pagamento a consumo, ma i modelli 'mini' costano pochissimo. Prima scelta se vuoi risultati solidi senza complicazioni.
- DeepSeek: API compatibile con lo standard OpenAI, costi molto bassi. Buona per contenere la spesa mantenendo prestazioni discrete.
- Ollama (locale, gratis): esegui un modello aperto (es. Llama, Qwen, Mistral) sul tuo PC, senza costi e senza inviare dati all'esterno. Richiede un computer con RAM/VRAM sufficiente e il tool use funziona meglio con i modelli piu' recenti. Prima scelta per privacy e costo zero.
Il bello e' che, grazie all'interfaccia compatibile, il codice non cambia: cambiano solo l'indirizzo del server e il nome del modello.
Passo 1 — Installa e configura l'ambiente
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install openaiImposta la chiave API come variabile d'ambiente (con OpenAI o DeepSeek). Con Ollama non serve una chiave reale:
export OPENAI_API_KEY="la-tua-chiave"Se usi Ollama, prima scarica ed esegui un modello che supporta gli strumenti:
ollama pull qwen2.5
ollama serve # avvia il server locale sulla porta 11434Passo 2 — Definisci gli strumenti (le funzioni Python)
Uno strumento e' una normale funzione Python piu' una descrizione in formato JSON che spiega al modello a cosa serve e quali argomenti accetta. Creiamo tre strumenti: un calcolatore sicuro, un lettore di file e una finta ricerca (che potrai sostituire con una vera API).
import json, ast, operator
# --- calcolatrice sicura (niente eval pericoloso) ---
_OPS = {ast.Add: operator.add, ast.Sub: operator.sub,
ast.Mult: operator.mul, ast.Div: operator.truediv,
ast.Pow: operator.pow, ast.USub: operator.neg}
def _valuta(n):
if isinstance(n, ast.Constant): return n.value
if isinstance(n, ast.BinOp): return _OPS[type(n.op)](_valuta(n.left), _valuta(n.right))
if isinstance(n, ast.UnaryOp): return _OPS[type(n.op)](_valuta(n.operand))
raise ValueError('espressione non valida')
def calcola(espressione: str) -> str:
return str(_valuta(ast.parse(espressione, mode='eval').body))
def leggi_file(percorso: str) -> str:
with open(percorso, encoding='utf-8') as f:
return f.read()[:4000]
def cerca(query: str) -> str:
# Sostituisci con una vera API di ricerca. Qui restituiamo un placeholder.
return f"Risultati simulati per: {query}"
FUNZIONI = {'calcola': calcola, 'leggi_file': leggi_file, 'cerca': cerca}Ora descriviamo gli strumenti nel formato che il modello capisce:
TOOLS = [
{'type':'function','function':{
'name':'calcola',
'description':'Valuta un\'espressione aritmetica, es. "3*(4+2)".',
'parameters':{'type':'object','properties':{
'espressione':{'type':'string'}}, 'required':['espressione']}}},
{'type':'function','function':{
'name':'leggi_file',
'description':'Restituisce il contenuto testuale di un file locale.',
'parameters':{'type':'object','properties':{
'percorso':{'type':'string'}}, 'required':['percorso']}}},
{'type':'function','function':{
'name':'cerca',
'description':'Cerca informazioni su un argomento e restituisce un riassunto.',
'parameters':{'type':'object','properties':{
'query':{'type':'string'}}, 'required':['query']}}},
]Passo 3 — Scrivi il ciclo dell'agente
Il cuore dell'agente e' un ciclo: inviamo il messaggio al modello; se il modello chiede di usare uno strumento, lo eseguiamo e gli restituiamo il risultato; ripetiamo finche' il modello produce una risposta finale. Ecco il codice completo:
from openai import OpenAI
# OpenAI: client = OpenAI()
# DeepSeek: client = OpenAI(base_url='https://api.deepseek.com', api_key='...')
# Ollama: client = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')
client = OpenAI()
MODELLO = 'gpt-4o-mini' # o 'deepseek-chat', o 'qwen2.5' con Ollama
def agente(domanda: str) -> str:
messaggi = [
{'role':'system','content':'Sei un assistente che usa gli strumenti quando servono. Rispondi in italiano.'},
{'role':'user','content':domanda},
]
for _ in range(6): # massimo 6 giri, per sicurezza
resp = client.chat.completions.create(
model=MODELLO, messages=messaggi, tools=TOOLS)
msg = resp.choices[0].message
messaggi.append(msg)
if not msg.tool_calls:
return msg.content # risposta finale
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
try:
risultato = FUNZIONI[tc.function.name](**args)
except Exception as e:
risultato = f'ERRORE: {e}'
messaggi.append({'role':'tool','tool_call_id':tc.id,
'content':str(risultato)})
return 'Limite di passaggi raggiunto.'
print(agente('Quanto fa 1234 * 5678? E poi leggi note.txt e riassumilo.'))Cosa succede all'esecuzione: il modello capisce che deve prima chiamare calcola con l'espressione 1234*5678, riceve 7006652, poi chiama leggi_file su note.txt, riceve il testo e infine produce una risposta che unisce il calcolo e il riassunto. Il ciclo for con un tetto di 6 giri evita che l'agente resti bloccato in un loop infinito.
Passo 4 — Provalo e osserva le decisioni
Crea un file note.txt con qualche riga di testo e lancia lo script. Per capire cosa sta facendo l'agente, aggiungi una stampa dentro il ciclo, subito dopo aver ottenuto msg:
if msg.tool_calls:
for tc in msg.tool_calls:
print(' -> uso lo strumento', tc.function.name, tc.function.arguments)Vedrai in tempo reale quali strumenti il modello sceglie e con quali argomenti: e' il modo migliore per capire e correggere il comportamento dell'agente.
Errori comuni e soluzioni
- 'openai.AuthenticationError': la chiave API non e' impostata o e' errata. Verifica la variabile d'ambiente. Con Ollama usa una chiave fittizia ma non vuota.
- Il modello non chiama mai gli strumenti: spesso le descrizioni sono vaghe. Rendile piu' chiare e specifiche; con modelli locali, assicurati di usarne uno che supporta il tool use.
- 'KeyError' sul nome della funzione: il nome nello schema TOOLS deve coincidere esattamente con la chiave in FUNZIONI.
- 'JSONDecodeError' sugli argomenti: raramente il modello produce JSON malformato. Avvolgi
json.loadsin un try/except e, in caso di errore, restituisci al modello un messaggio che lo invita a riprovare. - Loop che non termina: mantieni sempre un tetto massimo di passaggi, come nel nostro ciclo.
Varianti e come renderlo davvero utile
Da qui puoi far crescere l'agente in molte direzioni. Sostituisci la funzione cerca con una vera API di ricerca web per dargli accesso a informazioni aggiornate. Aggiungi strumenti che salvano appunti, inviano email o interrogano un database. Puoi collegarlo a un sistema RAG per rispondere sui tuoi documenti, o esporlo come piccola API con FastAPI per usarlo da altre applicazioni. Lo stesso schema, con differenze minime, vale anche con lo strumento di tool use di Claude di Anthropic: cambia la forma delle chiamate, ma il ciclo 'chiedi, esegui, restituisci' resta identico (vedi la documentazione ufficiale).
Quando non costruirlo da zero
Capire questo ciclo e' fondamentale, ma per progetti complessi in produzione — con molti strumenti, memoria, piu' agenti che collaborano — conviene appoggiarsi a framework gia' pronti che gestiscono per te ripetizioni, gestione degli errori e osservabilita'. Costruire l'agente a mano, come abbiamo fatto qui, ti serve pero' a non trattarli come scatole nere: saprai sempre cosa succede sotto il cofano e potrai correggere quando qualcosa non va. Il prossimo passo naturale e' dare al tuo agente uno strumento reale utile al tuo lavoro e vederlo all'opera sui tuoi dati.
Il codice di questa guida e' didattico: prima di dare a un agente la capacita' di eseguire azioni reali (file, rete, comandi), esegui tutto in un ambiente isolato e con permessi limitati.
Passo 5 — Dai all'agente una memoria di lavoro
L'agente che abbiamo costruito 'dimentica' tutto a fine funzione. Per compiti piu' lunghi puoi conservare la lista dei messaggi tra una chiamata e l'altra, cosi' l'agente ricorda cosa e' gia' successo. Basta spostare messaggi fuori dalla funzione e riutilizzarlo, aggiungendo di volta in volta la nuova domanda dell'utente. Attenzione pero': la conversazione cresce, e con essa il numero di token inviati a ogni chiamata, quindi il costo. Una strategia comune e' 'riassumere' periodicamente i messaggi piu' vecchi in un unico messaggio di sintesi, mantenendo il contesto essenziale senza trascinare l'intera cronologia.
Questo e' esattamente il tipo di problema che i framework professionali risolvono per te, ma vederlo dal vivo ti fa capire perche' gli agenti 'lunghi' costano di piu' e come tenerne sotto controllo la spesa.
Sicurezza: perche' isolare sempre l'agente
Dare a un modello la possibilita' di eseguire azioni reali e' potente ma rischioso. Uno strumento che legge file, esegue comandi o accede alla rete puo', se guidato da un prompt malevolo o da un errore del modello, compiere azioni indesiderate. Alcune regole minime: esegui l'agente in un ambiente isolato (una cartella dedicata, meglio ancora un container), non dargli mai accesso a directory sensibili, limita gli strumenti allo stretto necessario e valida sempre gli argomenti prima di eseguirli. Nel nostro esempio, la funzione leggi_file potrebbe leggere qualsiasi file del sistema: in un contesto reale andrebbe vincolata a una cartella specifica. La comodita' non deve mai prevalere sulla prudenza.
Con queste basi hai in mano il vero mattone su cui sono costruiti gli assistenti IA moderni. Il passo successivo e' scegliere un compito concreto del tuo lavoro — controllare un log, riordinare file, interrogare i tuoi appunti — e dare al tuo agente lo strumento giusto per svolgerlo, partendo sempre da permessi minimi e ampliandoli solo quando ti fidi del comportamento.




