Perché far girare gli agenti in locale
Tre motivi concreti spingono verso l'esecuzione locale, e in Italia il primo pesa più degli altri due messi insieme.
- I dati restano nel perimetro. Prompt, documenti e output non transitano dai server di un fornitore di modelli. Per chi lavora su contratti, cartelle cliniche o dati di clienti, è la differenza fra una valutazione privacy complessa e una molto più corta.
- Il costo per token è zero. Un crew che gira cento volte al giorno su un'API commerciale produce una fattura che cresce con l'uso. In locale paghi l'hardware una volta e la corrente sempre, ma il costo marginale della singola esecuzione sparisce.
- La latenza è prevedibile. Niente limiti di richieste, niente errori 429, niente code nelle ore di punta. Il modello risponde alla velocità del tuo hardware, sempre la stessa.
I due prezzi da pagare vanno detti subito, perché sono reali. Primo: la qualità è inferiore. Un modello da 8 miliardi di parametri quantizzato non ragiona come i modelli di punta serviti via API, e negli agenti la differenza si vede soprattutto nelle chiamate agli strumenti. Secondo: serve hardware. Senza una GPU con abbastanza memoria video, l'inferenza scende su CPU e diventa lenta al punto da cambiare il modo in cui usi il sistema. Se questi due vincoli non sono accettabili per il tuo caso, l'esecuzione locale non fa per te, e saperlo prima costa meno che scoprirlo dopo.
Installare Ollama e scaricare un modello
Ollama è il runtime che scarica i modelli e li espone con un'API HTTP locale sulla porta 11434. Si installa dal sito ufficiale su Linux, macOS e Windows. Servono due modelli, non uno: quello di chat che farà ragionare gli agenti e un modello di embedding, che servirà più avanti per memoria e knowledge.
# Scarica i due modelli: chat ed embedding
ollama pull llama3.1
ollama pull mxbai-embed-large
ollama ls # elenca i modelli scaricati
# Avvia il server, se non gira già come servizio
ollama serve
# Verifica che risponda sulla porta 11434
curl http://localhost:11434/api/chat -d '{
"model": "llama3.1",
"messages": [{"role": "user", "content": "Rispondi solo: ok"}]
}'Se il curl restituisce una risposta del modello, il server funziona e puoi passare a CrewAI. Se Ollama è stato installato come applicazione desktop, il servizio parte da solo e ollama serve non serve: un errore di porta già occupata in quel caso è normale.
La classe LLM: il punto dove quasi tutti sbagliano
CrewAI non parla direttamente con Ollama: instrada ogni chiamata attraverso LiteLLM, che smista verso decine di fornitori in base al prefisso del nome del modello. La configurazione corretta ha tre pezzi, e mancarne uno produce errori poco leggibili.
- La classe
LLM, importata dacrewai. - Il prefisso
ollama/davanti al nome del modello: è l'istruzione di instradamento per LiteLLM. Senza prefisso, il nome viene interpretato come un modello OpenAI. - Il
base_urldel server, che in locale èhttp://localhost:11434.
from crewai import Agent, LLM
llm_locale = LLM(
model="ollama/llama3.1", # il prefisso instrada verso Ollama
base_url="http://localhost:11434",
)
analista = Agent(
role="Analista documentale",
goal="Estrarre i punti chiave da {documento}",
backstory="Sintetizza senza inventare.",
llm=llm_locale, # senza questa riga cerca OpenAI
)Un dettaglio utile che sta nella documentazione LiteLLM e non in quella CrewAI: il prefisso ollama/ chiama l'endpoint /api/generate, mentre ollama_chat/ chiama /api/chat, e LiteLLM consiglia il secondo per la qualità delle risposte. Gli esempi nella documentazione CrewAI usano ollama/: parti da quello, e prova l'altro solo se le risposte deludono.
Il problema numero uno: chiede ancora la chiave OpenAI
errore
Hai configurato Ollama e l'errore parla di OPENAI_API_KEY
La diagnosi è una lista di controllo, da percorrere tutta anche quando il primo punto sembra risolvere.
- Un agente senza
llm. Il caso più frequente: in un crew di quattro agenti, tre hanno il modello locale e uno è rimasto senza parametro. Basta quello per far partire la richiesta di chiave. Controlla ogni singoloAgent, non solo il primo. - Il processo gerarchico senza
manager_llm. Conprocess=Process.hierarchicalCrewAI crea un agente manager in più, che non è nella tua lista e quindi non eredita niente: il suo modello va dichiarato a parte nel parametromanager_llmdel crew. - Memoria e knowledge usano un altro modello. Quando attivi
memory=Trueo passi delleknowledge_sources, entra in gioco un modello di embedding, separato da quello di chat, che per impostazione predefinita è di OpenAI anche se tutti gli agenti girano su Ollama. Si configura con il parametroembedder, come nel codice della prossima sezione.
Il modo più rapido per stanare il colpevole: leggi il traceback fino in fondo, perché la riga che nomina embedding o memory distingue subito il terzo caso dai primi due. Se il traceback non basta, togli memoria e knowledge, verifica che il crew giri pulito, e riaggiungili con l'embedder configurato. Altri errori tipici di questo passaggio sono raccolti nella pagina sugli errori comuni.
Un crew interamente locale, completo
Questo è il codice di riferimento: due agenti, memoria attiva, nessuna chiamata esterna. Ogni punto in cui un modello può intrufolarsi è coperto in modo esplicito.
from crewai import Agent, Task, Crew, Process, LLM
llm_locale = LLM(
model="ollama/llama3.1",
base_url="http://localhost:11434",
)
analista = Agent(
role="Analista",
goal="Isolare i 3 punti critici in {testo}",
backstory="Separa i fatti dalle opinioni.",
llm=llm_locale,
)
redattore = Agent(
role="Redattore",
goal="Scrivere una sintesi ordinata dei punti critici",
backstory="Scrive frasi brevi, senza aggettivi inutili.",
llm=llm_locale, # su OGNI agente, nessuno escluso
)
analisi = Task(
description="Analizza {testo} e isola 3 punti critici.",
expected_output="3 punti: problema, evidenza, gravità.",
agent=analista,
)
sintesi = Task(
description="Trasforma l'analisi in una sintesi di 150 parole.",
expected_output="Un paragrafo, massimo 150 parole.",
agent=redattore,
)
crew = Crew(
agents=[analista, redattore],
tasks=[analisi, sintesi],
process=Process.sequential,
memory=True,
embedder={ # anche gli embedding restano in locale
"provider": "ollama",
"config": {
"model": "mxbai-embed-large",
"url": "http://localhost:11434/api/embeddings",
},
},
)
esito = crew.kickoff(inputs={"testo": "..."})
print(esito.raw)attenzione
La documentazione non è uniforme sulla chiave del modello
model nella sezione knowledge e con model_name in quella sulla memoria. È uno dei punti in cui la documentazione CrewAI risulta disallineata fra le sue stesse pagine: se una forma produce errori di validazione, prova l'altra prima di cercare cause più esotiche.Quali modelli reggono un agente
Un agente non è una chat. A ogni passo il modello deve decidere se rispondere o usare uno strumento, e in quel secondo caso deve produrre la chiamata nel formato esatto che il framework si aspetta. È qui che i modelli piccoli cadono: la prosa la reggono, la sintassi delle chiamate agli strumenti no. Una parentesi fuori posto o un nome di parametro inventato, e l'agente entra in un ciclo di tentativi che brucia iterazioni fino al limite di max_iter. Le chiamate a strumenti sbagliate con modelli non OpenAI sono fra gli errori più segnalati dagli utenti del framework, e in locale il fenomeno si amplifica.
Non pubblichiamo classifiche di modelli locali: cambiano troppo in fretta e i risultati dipendono da quantizzazione, hardware e tipo di compito. Il metodo conta più della classifica, e il metodo è questo:
- Parti senza strumenti. Un crew di sola lettura, analisi e scrittura mette alla prova il modello sul ragionamento senza esporlo al suo punto debole.
- Aggiungi uno strumento alla volta, e guarda i log: se le chiamate falliscono o vengono ripetute con argomenti a caso, il modello non regge quel formato e serve una taglia superiore.
- Tieni i compiti stretti. Descrizioni corte e un
expected_outputrigido compensano in parte la minore capacità del modello. Su un modello piccolo, un compito vago non produce un risultato mediocre: non produce proprio.
Quanta memoria serve: ordini di grandezza
I numeri che seguono sono stime indicative per i modelli quantizzati a 4 bit, il formato in cui Ollama distribuisce di solito le sue varianti predefinite. Non sono specifiche: il consumo reale dipende dalla quantizzazione scelta, dalla finestra di contesto usata (il contesto occupa memoria oltre ai pesi) e dal runtime.
| Parametri del modello | Memoria video indicativa | Dove gira |
|---|---|---|
| 7-8 miliardi | 5-6 GB | GPU consumer di fascia media |
| 13-14 miliardi | 9-11 GB | GPU consumer di fascia alta |
| circa 30 miliardi | 18-22 GB | GPU da 24 GB |
| 70 miliardi | 40-48 GB | Due GPU, o hardware da data center |
Senza GPU l'inferenza gira su CPU e RAM: funziona, ed è un modo legittimo di provare il sistema, ma i tempi si allungano di un ordine di grandezza. Per un crew, dove ogni compito genera più chiamate al modello in sequenza, la differenza fra 20 secondi e 4 minuti a chiamata decide se il sistema è usabile. I Mac con memoria unificata sono un caso a parte favorevole: la memoria di sistema fa anche da memoria per il modello. Prima di comprare hardware, misura: fai girare il crew di prova su quello che hai e leggi i tempi reali.
GDPR e dati in casa: cosa cambia davvero
Il motivo per cui molte aziende italiane guardano a questa architettura è la riservatezza, e su questo serve precisione, non slogan. Quello che l'esecuzione locale cambia davvero: i dati trattati dagli agenti non vengono comunicati a un fornitore di modelli, quindi sparisce quel responsabile del trattamento, sparisce la valutazione del trasferimento extra UE per quel flusso, e i log delle conversazioni restano su macchine tue. Per dati sanitari, legali o finanziari è una riduzione concreta della superficie di rischio.
Quello che non cambia: tutto il resto. Il trattamento resta un trattamento, con la sua base giuridica, le informative, la minimizzazione, i tempi di conservazione. Anzi, un pezzo di responsabilità torna indietro: la sicurezza che prima era coperta dal contratto con il fornitore ora è tua, e un server mal protetto con dentro i log di tutte le conversazioni aziendali è un problema peggiore di un'API ben contrattualizzata. Eseguire in locale riduce il trasferimento di dati a terzi, ma non rende un sistema conforme da solo, e chi lo promette sta vendendo qualcosa.
C'è poi un livello ulteriore: gli obblighi normativi sugli agenti non finiscono con il GDPR, e l'AI Act introduce requisiti che non dipendono da dove gira il modello. La pagina su AI Act e agenti tratta quel pezzo del quadro.