Otto modelli, tre servizi AI e un README che non si lasciava scrivere
Come ho portato lo sviluppo su specifiche delle Sentinelle di Casa dal cloud alla mia LAN — e cosa ho capito quando l'agente diceva di scrivere ma non scriveva.
![]()
Ho scritto la documentazione OpenSpec delle feature delle “Sentinelle di Casa” con modelli che girano dentro casa: nessun file del repository è uscito dalla mia LAN in quella fase. Il codice che quelle specifiche documentano, invece, era nato prima nel cloud — e questa storia comincia proprio lì. Per farlo ho messo in fila tre servizi AI diversi e almeno otto modelli linguistici. Il primo file scritto in locale è arrivato alla fine di una serata in cui ho litigato con un agente che parlava benissimo di modifiche che non faceva.
Questa è la storia di quella serata.
Da dove arrivo
Riassunto per chi non ha letto il post precedente: la notte del 25 ottobre 2026 un blackout ha spento tre nodi della mia rete domestica e nessuno si è riavviato da solo. Dal fastidio di quell'evento è nato un sistema distribuito di monitoraggio — sentinelle che pubblicano heartbeat su MQTT, un collector che archivia in SQLite, una dashboard che racconta lo stato della casa.
Non so programmare, e il codice di quel sistema è nato con Lumo, via interfaccia web. Io descrivevo, l'assistente generava file di codice e markdown, e io copiavo il codice a mano, dai tab delle creazioni di Lumo al computer su cui doveva girare. Funzionava. Ma il passaggio umano era obbligatorio, e tutto avveniva via web.
Il metodo però, quello, era già quello giusto: usavo il ciclo di definizione e validazione delle specifiche di OpenSpec.
Il giro circolare
L'esperimento di questa serata è stato provare a chiudere il cerchio dentro la LAN. Il risultato è un flusso che mi diverte molto perché è circolare e non ha un solo padrone:
- Lumo (con modelli utilizzati via interfaccia web) ha generato la base: codice e markdown delle Sentinelle.
- Copilot in VS Code (con gli agenti che orchestra) mi ha fatto da cicerone: mi ha aiutato a configurare l'editor e a mettere in piedi Continue. L'ho usato solo per le configurazioni di VS Code, dicendogli espressamente di non modificare o leggere il codice nei repository (in pratica l'ho usato per aiutarmi a sostituirlo).
- Continue (versione 2.0.0), puntato sui modelli serviti da Ollama su geekmonio nella mia rete locale, legge il codice nel repository e scrive la documentazione OpenSpec delle specifiche, feature per feature, senza far passare il codice per sistemi esterni.
I modelli locali che ho sulla mia macchina dedicata alle inferenze sono cinque: GPT-OSS 20B, Qwen 2.5 Coder 7B, Mistral Nemo 12B, Llama 3.2 3B e Gemma 3 12B. A questi si sommano quelli di Copilot, quelli di Lumo e — per qualche correzione di codice — quelli di Leo AI di Brave.
Ognuno dei modelli locali ha uno scopo preciso: chat, scrittura di codice, orchestrazione degli agenti o autocompletamento.
Ho scelto Leo AI di Brave per le correzioni di codice per quello che Brave dichiara sulla privacy: che le conversazioni con Leo non vengono conservate, condivise né usate per addestrare i modelli, e che non serve un account. Non ho verificato la cosa di persona: mi baso sulla loro dichiarazione, che è il massimo che posso fare come utente. Per me era una scelta coerente con il tipo di dati e di attività che gli affidavo.
Allo stesso modo, con GitHub Copilot ho posto un vincolo esplicito: non leggere o modificare il repository, ma intervenire solo sulle configurazioni di VS Code. Il mio criterio personale, per questo progetto, è questo: usare servizi cloud solo quando la riservatezza è parte dei termini del servizio che sto usando — e mai per il codice del repository.
Facendo il conto: tre servizi AI nel percorso principale, e almeno otto modelli linguistici — cinque locali certificati, gli altri contati per affidamento, uno per servizio. Leo AI è fuori dal percorso principale: entra solo per revisioni.
L'istanza Ollama gira su geekmonio, la macchina nella mia LAN che fa da hub alla rete delle sentinelle, sulla porta 11434 che era già lì prima. Durante la generazione della documentazione, nessun dato del repository è stato inviato a servizi esterni.
OpenSpec l'ho inizializzato nella root del repository: configurazione in openspec/config.yaml, schema spec-driven, e sei prompt pronti in .continue/prompts/ — /opsx-explore, /opsx-propose, /opsx-apply, /opsx-update, /opsx-sync, /opsx-archive. openspec validate --all è passato al primo colpo. Cosa abbia effettivamente verificato con un registro ancora vuoto, onestamente, non l'ho indagato. La CLI sta in ~/.local/bin/openspec e non ha bisogno di Node.js.
Fin qui la teoria. Poi ho provato a far scrivere qualcosa perché, seppure avessi generato il codice seguendo il ciclo OpenSpec su Lumo, non avevo documentato le singole feature.
La documentazione delle singole feature serve perché, con le feature documentate, è possibile definirne di nuove e generare codice nuovo senza modificare il comportamento del software già rilasciato (come era successo nel mio esperimento con la centralina meteo, raccontato nel post qui, con i widget degli agenti sui nodi che restituivano le informazioni sui nodi).
L'agente che parlava benissimo
Obiettivo dichiarato: una modifica minima al README del repository. Una nota che dicesse che la documentazione del progetto è mantenuta con GPT-OSS self-hosted. Roba da trenta secondi, in teoria.
Modalità Agent, modello Geekmonio GPT-OSS 20B, chat nuova. Chiedo la modifica.
L'agente risponde. Parla di strumenti, nomina edit_file, spiega cosa farebbe, a tratti dichiara di non avere accesso a strumenti di modifica. Il README, nel frattempo, resta esattamente com'era.
Il dettaglio che mi ha fatto perdere mezz'ora: nessuna di quelle risposte era un evento. Erano testo in chat. Il modello raccontava di usare strumenti, non li stava usando. E io stavo leggendo la trascrizione di un monologo teatrale credendo di leggere un log.
![]()
La temperatura non bastava come colpevole
Prima ipotesi, la più comoda: il modello è piccolo, la temperatura è troppo elevata e il modello inventa quando non sa, GPT-OSS 20B non è fatto per gli agenti. Ipotesi sbagliata, e lo dico perché all'inizio ne ero proprio convinto.
Guardando meglio la sessione, i problemi erano tre, molto banali, e nessuno riguardava i parametri di sampling:
- Il workspace era la cartella sbagliata. Continue risultava collegato alla cartella padre del repository, non alla root. Percorsi ambigui, contesto ambiguo.
- Gli strumenti chiamati non esistevano. L'agente ha provato
read_filecon un argomento che Continue ha rifiutato, poireaddir, che Continue non riconosce propriamente. Continue documentaread_file,ls,create_new_fileededit_existing_file: nonfile_read, nonfile_write, nonreaddir. - Tool call fantasma. Il modello descriveva l'azione invece di compierla. E il fatto che
grep_searchfunzionasse regolarmente rende il tutto più insidioso: un pezzo di integrazione andava, l'altro no.
Che queste tre anomalie fossero la causa del fallimento resta un'ipotesi — com'è un'ipotesi, all'inverso, darla alla temperatura. Ma almeno le tre si vedevano nei log.
Appunto per le prossime volte: quando un agente “non vuole” modificare i file, prima di dare la colpa al modello conviene guardare dove punta l'editor e come si chiamano gli strumenti che il modello prova a invocare.
Non sapevo che il funzionamento degli agenti fosse vincolato alla capacità del modello di usare gli strumenti.
Chi sa usare gli strumenti, e chi lo dice
Per capire se il problema fosse il modello che non sa usare gli agenti o l'integrazione, ho interrogato direttamente l'API di Ollama, fuori da Continue. Risultati:
- GPT-OSS 20B ha restituito una vera
tool_call. Il modello, insomma, sa farlo. - Qwen 2.5 Coder 7B ha restituito JSON come testo, non una chiamata strutturata.
- Gemma 3 12B ha risposto, senza giri di parole, che non supporta gli strumenti.
Utile, ma non conclusivo: il test positivo su GPT-OSS dimostra che il modello è capace, non che l'integrazione dentro Continue funzioni. Sono due cose diverse.
Non fidarti di nessuno, soprattutto degli agenti
Questo è il punto che mi porto a casa, e vale oltre questa serata.
Un modello che scrive “sto usando lo strumento X per modificare il file” non ha modificato niente. Per sapere cosa è successo davvero servono quattro cose: il nome esatto dello strumento invocato, gli argomenti passati, l'eventuale richiesta di autorizzazione, e il risultato o l'errore restituito. Senza quelle, si sta interpretando della prosa.
Aggiungo un dettaglio di metodo: io volevo esattamente questo comportamento. Nell'obiettivo della sessione avevo scritto che l'assistente non deve modificare i file al posto mio senza chiedermene prima il permesso. Peccato che per chiedere il permesso bisogna prima essere capaci di chiamare lo strumento — e quella capacità, in quella sessione, non c'era.
La luce
Stessa sera, poco dopo: apro VS Code sulla root corretta del repository, verifico con pwd di essere nel posto giusto, controllo che il README sia visibile nell'Explorer. Nuova chat Continue, stesso modello, stessa modalità Agent.
E dopo qualche @agent Try Again, la nota compare nel README.
Il giro funziona: un modello che gira sul mio server ha letto un file del mio repository e ne ha scritto un altro, senza cloud e senza copia-incolla. Per un progetto che esiste per non morire in silenzio, mi sembra un buon modo di iniziare.
La parte che non so
Qui devo essere onesto, perché è il senso di questo blog.
Fatti osservati: il workspace corretto era aperto; dopo i nuovi tentativi la nota è comparsa nel README.
Ipotesi non verificate: aprire la root corretta può aver eliminato l'ambiguità dei percorsi; un nuovo tentativo può aver prodotto una chiamata valida dopo chiamate non valide.
Non ho la trascrizione dettagliata del tentativo riuscito. Non so quale strumento abbia usato, con quali argomenti, se ci sia stata una richiesta di autorizzazione. Quindi non posso attribuire il successo al cambio di workspace, al retry, o a entrambi.
E c'è una piccola ironia: dei fallimenti ho pagine di trascrizioni, del successo niente. Registriamo ossessivamente ciò che va storto e quasi mai il momento in cui le cose iniziano ad andare bene, perché quando funzionano sembra normale. Poi ti serve capire cosa è cambiato, e ti accorgi che l'unico evento che conta non è documentato.
Vabbè.
Cosa ho imparato
Che il metodo regge, ma solo se il robot posa davvero le rotaie che gli ho disegnato io. Che il supporto agli strumenti di un modello e il funzionamento dell'integrazione sono due cose separate, e vanno testate separatamente. Che il workspace è la radice di tutto, in senso quasi letterale: se l'editor punta alla cartella padre, l'agente vive in un mondo che non è il tuo.
Postilla: l'allucinazione è capitata anche qui
Questo post l'ho scritto e revisionato con Lumo, lo stesso assistente con cui era nato il codice delle Sentinelle. E mentre correggevo la bozza di questo post — che, ricordiamolo, parla di agenti che dicono di usare strumenti che non usano — Lumo ha chiamato, in mezzo alla revisione, uno strumento che nessuno aveva chiesto: ha controllato il prezzo del Bitcoin in euro. Il tool ha risposto 71.665,78 € — il valore restituito da quella singola chiamata, il 29 ottobre 2026, non un dato di mercato. Non c'era alcun rapporto con la bozza, con le Sentinelle, con niente.
Non ho modo di sapere cosa sia successo dentro il modello: se un'associazione spuria, un passaggio di elaborazione finito fuori pista, o altro. So quello che ho visto: una chiamata a uno strumento, senza motivo, mentre il compito era un altro. È esattamente la fenomenologia descritta sopra, capitata nel momento peggiore possibile — l'assistente che editava il post sulle tool call fantasma ha prodotto una tool call fantasma.
La differenza, rispetto alla serata con Continue, è che qui la chiamata era reale: uno strumento invocato, un risultato, un dato. Solo che il dato non c'entrava niente. Il problema non è solo l'agente che dice e non fa: è anche quello che fa quello che non gli hai chiesto. E a posteriori la cosa peggiore è che, se non avessi guardato la trascrizione della chiamata al tool, quel numero sarebbe semplicemente finito nel nulla. Semplice, silenzioso, non documentato — come il mio README riuscito.
Non fidarsi, verificare, guardare i log. Vale per i modelli locali e vale per quelli nel cloud. Soprattutto per quelli nel cloud.
“La tecnologia libera non è solo codice, è la libertà di capire cosa c'è sotto il cofano.”
👤 Lorenzo | ItsMeLarry 🧪 Esperimenti AI & Open Source | 🕹️ Retro Gaming & Graphic Novel 🎸 Rock, Folk, Jazz, Punk | 🔒 Privacy First 📍 Toscana / Digitale
🔗 noblogo.org | 🐙 Codeberg