Da qualche tempo mi chiedo: cosa ho davvero chiesto a ChatGPT in questi anni? Ho scaricato l’archivio completo delle mie conversazioni e ho deciso di analizzarlo — non con un servizio cloud, ma con un LLM in esecuzione sul mio PC. Questo post racconta l’esperimento, le scelte tecniche, i risultati e le riflessioni che ne sono emerse.
Perché Ollama, e perché in locale
La scelta di usare Ollama come runtime per il modello non è stata casuale. Ollama è un progetto open source scritto in Go che espone un server HTTP locale (sulla porta 11434) attraverso il quale è possibile interagire con modelli LLM esattamente come si farebbe con le API di OpenAI o Anthropic — ma senza inviare dati all’esterno e senza costi per token.
L’installazione su Linux è banale:
curl -fsSL https://ollama.com/install.sh | sh ollama pull phi3:mini ollama serve
Da quel momento, il modello è raggiungibile via HTTP esattamente come un’API cloud:
POST http://localhost:11434/api/generate
{
"model": "phi3:mini",
"prompt": "...",
"stream": false
}
Ovviamente il prompt può essere anche scritto da linea di comando, come si farebbe con un chatbot normale, ma in questo caso il prompt viene inoltrato al motore LLM da uno script Python che ripete tante volte lo stesso prompt con parametri diversi.
Questo è il cuore del paradigma: scrivi codice per la struttura, delega al LLM tutto ciò che richiede comprensione del linguaggio naturale.
La scelta del modello: phi3:mini e i vincoli hardware
Il mio PC è un HP Laptop 15s con CPU Intel i5 a 12 core e alimentatore da 45W — nessuno slot PCIe, nessuna possibilità di aggiungere una GPU dedicata. L’intera elaborazione avviene su CPU.
In questo contesto, phi3:mini di Microsoft è stata la scelta obbligata ma non scontata. Si tratta di un modello da 3.8 miliardi di parametri, ottimizzato per ragionamento e instruction following, con un peso di circa 2.2 GB in VRAM (o RAM, nel mio caso). Le alternative come LLaMA 3 8B o Mistral 7B avrebbero richiesto il doppio della memoria e tempi di inferenza ancora più lunghi.
Il log di Ollama durante il caricamento mostra chiaramente il contesto di esecuzione:
llama_kv_cache: CPU KV buffer size = 1536.00 MiB llama_context: Flash Attention was auto, set to enabled llama_runner started in 2.30 seconds
Il warning n_ctx_seq (4096) < n_ctx_train (131072) indica che il modello è stato addestrato per gestire contesti fino a 131k token, ma Ollama lo esegue con una finestra di 4096 — più che sufficiente per i nostri prompt.
Il formato dei dati: l’export di ChatGPT
ChatGPT permette di scaricare l’intero archivio delle conversazioni in formato JSON. La struttura non è banale: ogni conversazione è rappresentata come un grafo di nodi collegati da relazioni parent/child, non come una semplice lista di messaggi.
{
"id": "00b6fac4-...",
"title": "Invert Square Matrix Python",
"create_time": 1720644750.545788,
"current_node": "51aa9598-...",
"mapping": {
"51aa9598-...": {
"message": {
"author": { "role": "assistant" },
"content": { "parts": ["Sì, potresti avere ragione..."] }
},
"parent": "aaa2f4c3-...",
"children": []
}
}
}
Il campo create_time è un Unix timestamp — i secondi trascorsi dal 1° gennaio 1970 (Unix Epoch). Per ricostruire la conversazione in ordine cronologico è necessario risalire la catena dei nodi a partire da current_node fino alla radice, poi invertire il percorso.
L’architettura dello script
Lo script Python è organizzato in tre livelli funzionali:
- Estrazione: legge i file JSON, ricostruisce la catena di messaggi da ogni conversazione
- Classificazione: per ogni conversazione costruisce un prompt e lo invia a Ollama via HTTP
- Checkpoint: salva i progressi dopo ogni classificazione, permettendo di riprendere in caso di interruzione
Il punto cruciale è la generazione del prompt:
prompt = f"""Analizza questo frammento di conversazione e rispondi SOLO
con un oggetto JSON con questi campi:
- "argomento": una stringa breve (3-6 parole)
- "parole_chiave": una lista di 3-5 parole chiave
Conversazione:
{estratto}
Rispondi SOLO con il JSON, senza testo aggiuntivo."""
Cambiare questo prompt è tutto ciò che serve per trasformare lo script in un analizzatore di sentiment, un estrattore di entità, un sistema di moderazione. Il codice è la struttura, il prompt è la logica.
La chiamata HTTP a Ollama è altrettanto semplice:
response = requests.post(
"http://localhost:11434/api/generate",
json={"model": "phi3:mini", "prompt": prompt, "stream": False},
timeout=300
)
t_elapsed = round(time.time() - t_start, 2)
Questo è uno dei miei primi esperimenti di interazione tra software e LLM. Mi sono fatto aiutare da Anthropic – Claude nella stesura del codice, che però ho modificato e modulato a mio piacemento, ad esempio abbiamo aggiunto il meccaismo di checkpoint solo successivamente, dopo che ho visto che si doveva ripetere l’analisi dei prompt GPT da capo ogni volta che accadeva un errore o che femavo lo script per altri motivi. Così ho ottimizzato il tempo.
Ovviamente lasciar fare tutto ad un chatbot sarbbe stato più veloce ma così mi sari perso la parte divertente.
I risultati: 50 conversazioni analizzate
Ho analizzato un campione di 50 conversazioni estratte da un archivio di 1812. I numeri sono istruttivi.
Statistiche di elaborazione
| Metrica | Valore |
|---|---|
| Conversazioni analizzate | 50 |
| Tempo totale | 1471s (24,5 minuti) |
| Tempo medio per conversazione | 42,0s |
| Tempo minimo | 11,1s |
| Tempo massimo | 258,7s |
Distribuzione dei tempi
| Fascia | Conversazioni |
|---|---|
| Meno di 20s | 12 (24%) |
| 20–40s | 19 (38%) |
| 40–60s | 15 (30%) |
| Oltre 60s | 4 (8%) |
La variabilità è significativa: una conversazione breve richiede 11 secondi, una lunga o complessa può arrivare a quasi 4 minuti. Il collo di bottiglia è la generazione dei token su CPU — ogni token prodotto richiede un forward pass completo sulla rete neurale.
Qualità delle classificazioni
- 74% delle classificazioni sono pulite e corrette (37/50)
- 26% contengono JSON grezzo non parsato (13/50) — phi3:mini non ha sempre rispettato l’istruzione di rispondere solo con JSON
Questo è un problema noto nel prompt engineering: i modelli piccoli tendono a “ragionare ad alta voce” aggiungendo testo prima o dopo il JSON richiesto, rompendo il parsing. Le soluzioni possibili includono few-shot examples nel prompt, output structuring, o modelli più grandi.
Gli argomenti emersi
La distribuzione tematica delle mie conversazioni con ChatGPT rispecchia abbastanza fedelmente i miei interessi:
- Fisica teorica: meccanica quantistica, Lagrangiana del Modello Standard, dilatazione del tempo, invarianti adiabatici, polarizzazione ottica
- Programmazione: Python, JavaScript, PHP, Oracle SQL, Laravel, XML parsing, SQL injection, exploit di servizi remoti
- Astronomia: coordinate celesti, sonde solari, visibilità dei poli lunari
- Linux e IT: identificazione hardware, gestione della batteria, VPN
- Musica: Cubase, Yamaha, importazione partiture MIDI
- Storia della scienza: Galileo e il Copernicanesimo
- Varie: tossicità delle piante per i gatti, lavatrice Samsung, il fiume Sile
Riflessioni e sviluppi futuri
Questo esperimento ha confermato qualcosa di importante: usare un LLM come componente di un programma è sorprendentemente accessibile. Non serve GPU, non servono librerie complesse, non serve conoscere i dettagli interni del modello. Basta una chiamata HTTP e un prompt ben formulato.
Il vero cambio di paradigma è concettuale: invece di scrivere codice per ogni singola regola di classificazione, si scrive codice per la struttura del problema e si delega la comprensione del linguaggio naturale al modello. Questo apre possibilità enormi — analisi di email, estrazione di dati da documenti non strutturati, moderazione di contenuti, generazione di dati di test — con una quantità di codice sorprendentemente piccola.
I limiti emersi sono altrettanto istruttivi:
- Velocità su CPU: 42 secondi per conversazione rendono l’analisi dell’intero archivio (1812 conversazioni) un’operazione da 21 ore. Una GPU entry-level ridurrebbe questo tempo di un fattore 10-20x.
- Affidabilità del formato di output: phi3:mini non rispetta sempre le istruzioni di formato. Modelli più grandi o tecniche di structured output migliorerebbero il 74% di successo.
- Qualità semantica: le classificazioni corrette sono generalmente buone, ma alcuni argomenti sono descritti in modo troppo specifico per essere utili in un raggruppamento tematico.
I prossimi passi naturali sono estendere l’analisi all’intero archivio (magari con una GPU), aggiungere una modalità di ricerca semantica, e sperimentare con modelli più capaci come LLaMA 3 8B o Mistral 7B quando l’hardware lo permetterà.
Avere un LLM direttamente sulla macchina mi aprirà inoltre la possibilità di guardare sotto il cofano: capire come funzionano gli embeddings e la tokenizzazione, come viene applicato il softmax, come il modello costruisce la risposta token per token. Cose che si possono leggere sui paper, ma che diventano molto più concrete quando il modello gira davanti al tuo naso. È anche un modo per creare un primo mattoncino per la realizzazione di un vero e proprio Agente.
Il codice completo è disponibile su richiesta.










Commenti recenti