01Il punto in cui le catene si rompono
Chi ha messo in produzione un'automazione con dentro un modello linguistico riconosce questo pezzo di codice, perché ce l'ha scritto almeno dieci volte.
risposta = chiedi_al_modello('Rispondi solo con {"reparto": "..."} e nient\'altro')
try:
reparto = json.loads(risposta)["reparto"]
except (ValueError, KeyError):
reparto = "da_smistare_a_mano" # e vai a capire perchéIl giro è sempre lo stesso. Scrivi un prompt che chiede per favore un JSON, speri che il JSON sia valido, lo leggi, gestisci il caso in cui arriva con tre righe di preambolo davanti, e paghi dei token in uscita per farti restituire una parola che avevi già scritto tu nel prompt. Quando ne fai una al giorno regge. Quando ne fai quindicimila, il ramo da_smistare_a_mano diventa una cartella che nessuno apre.
La cosa che mi ha fatto scrivere Bivio è questa sproporzione. La decisione che stai chiedendo vale un bit: urgente o no. Per averla stai accendendo una macchina che compone testo parola per parola, e poi controlli che il testo abbia la forma di un dato.
02La risposta si legge dalle lettere
L'idea di partenza è di TypeSafe AI, che a settembre 2026 ha presentato Jev e l'ha chiamato System One model.
Un modello che scrive costruisce la risposta un token alla volta: gliela chiedi in JSON, lui te la produce carattere per carattere, e tu poi la controlli. Un modello che decide fa un'altra cosa. Gli dai lo stato, cioè il testo su cui si deve decidere, e delle domande chiuse: sì o no, oppure scegli fra queste opzioni, oppure dai un voto su questa scala. Torna indietro una risposta del tipo che hai dichiarato, con la sua probabilità attaccata. Niente da riparare, niente da validare.
Arriva un messaggio all'assistenza:
Il pacco è arrivato rotto, vorrei fare un reso.
Da quelle otto parole servono tre decisioni, e sono tutte chiuse: che tono ha (negativo), in che lingua è (italiano), cosa vuole chi scrive (un reso). Con quelle tre il software manda il ticket al gruppo che gestisce i resi. E se la probabilità su una delle tre è bassa, invece di tirare a indovinare lo mette nella fila di quelli che guarda una persona. È un esempio banale di proposito. Quelle tre domande tornano identiche su diecimila ticket, e allora quanto costano e quanto durano decidono se l'automazione si può fare o no.
I numeri di Jev li mette il suo venditore, e vanno letti sapendolo: TypeSafe dichiara da 40 a 200 volte più veloce e 444 volte meno caro di un modello che scrive, e nella stessa pagina scrive che quei guadagni stanno «sul lato alto». Quello che invece si controlla da fuori è il listino: dal 18 settembre 2026 Jev sta su OpenRouter, dove Jev 1.13 costa 0,042 dollari per milione di token in ingresso e zero in uscita, con 32.000 token di contesto. Zero in uscita perché di token in uscita non ce ne sono, ed è la parte dell'idea che conta.
Il limite è che Jev è chiuso: gira sui server di TypeSafe, il modello non te lo danno, e per l'API dell'azienda c'è ancora una lista d'attesa. Se il tuo stato è una cartella clinica, una busta paga o il messaggio di un tuo cliente, quel testo esce di casa a ogni decisione. Bivio fa lo stesso lavoro con un modello aperto sul tuo disco, parla la stessa API, e non apre nessuna connessione.
Il meccanismo sta in tre passaggi, e la parte bella è che sono semplici. Ogni risposta possibile prende una lettera maiuscola. Al caricamento si controlla sul tokenizzatore che quella lettera, nel punto esatto in cui verrà letta, valga un token solo: se il conto torna diverso il modello viene rifiutato, invece di darti numeri sbagliati in silenzio. Poi un passaggio in avanti, e si guardano i logit di quelle lettere. Il modello di decodifica resta spento: output_tokens: 0 è vero alla lettera.
r = b.decidi(
"Provo da tre giorni a collegare il conto Stripe e continua a fallire.",
{"urgente": {"tipo": "si_no", "istruzioni": "Il messaggio esprime urgenza"},
"reparto": {"tipo": "scelta", "istruzioni": "Chi prende in carico",
"opzioni": {"pagamenti": "Incassi, fatture, rimborsi",
"tecnico": "Errori e malfunzionamenti"}}})
r["reparto"].valore # 'tecnico'
r["reparto"].probabilita # 0.9999I tipi di domanda sono quattro: si_no, scelta fra opzioni che dichiari tu, voto su una scala ordinata, numero con delle ancore. Gli ultimi due tornano una media pesata, e per me sono la parte più utile. Una partita che sta fra «in bilico» e «persa» esce 1,5, con la sua dispersione accanto a dire se il modello era combattuto oppure stava davvero in mezzo. Un'etichetta secca quell'informazione la butta via.
Su un contratto da 1.559 token, otto domande fatte insieme costano 3,2 secondi; le stesse otto una per volta ne costano 18,4, cioè 5,75 volte tanto. Lo stato si legge una volta e resta in cache, poi cambia solo la coda della domanda. Su uno stato di due righe questa differenza sparisce: il vantaggio vive tutto nei documenti lunghi con tante domande sopra.
Sul mio MacBook con M5 una decisione su uno stato corto costa 187 millisecondi, e circa 95 se lo stato è già stato letto. A quel prezzo la domanda la puoi mettere davanti a ogni chiamata di un agente, che è uno degli esempi nel repository: prima che l'agente esegua uno strumento, gli chiedi se quella roba tocca dati già scritti e se manda qualcosa fuori. Un controllo che costa un decimo di secondo lo accendi sempre; un controllo che costa una chiamata a pagamento lo accendi quando te ne ricordi.
03L'italiano, e il modello italiano che perde
Implementazioni aperte di questa idea ce n'erano già due, e buone: SemIf di TheoLeeCJ e Rizzo Flow di Simone Rizzo, che ha misurato il suo contro il primo. Bivio l'ho scritto perché me ne servivano tre cose che lì mancano, e sono tutte e tre italiane.
La prima sono le domande già scritte. La libreria è la parte facile: il lavoro vero sta nel decidere quali domande fare e con che opzioni. Nel pacchetto ci sono quattro griglie pronte, e sono i mestieri che si fanno qui.
| Griglia | Che cosa chiede | Per chi |
|---|---|---|
assistenza | reparto, urgenza, quanto è scontento, se serve una persona | chi risponde ai clienti |
spese | voce di spesa, inerenza, ordine di grandezza | chi tiene la prima nota |
contratto | rinnovo tacito, termini, penali, foro, chi deve leggerlo | chi firma senza un legale in casa |
moderazione | offese, spam, dati personali, che farne | chi tiene una pagina o dei commenti |
La seconda sono i casi di prova. I 37 su cui l'ho misurato parlano di ticket, clausole di pagamento a sessanta giorni, documenti di trasporto e voci di spesa, in italiano. Le fixture di SemIf, che sono il metro del settore, stanno in inglese, e un modello che va bene lì qui va verificato lo stesso.
La terza è che i dati restano a casa. Studi, ambulatori, scuole e uffici, per mandare i testi dei loro utenti a un servizio estero, si mettono in una fila di adempimenti. Bivio gira senza rete: stacchi il wifi e continua a rispondere.
Poi c'è la parte che pensavo sarebbe andata in un altro modo. Il modello italiano per eccellenza è Minerva-7B della Sapienza, addestrato da zero sull'italiano, Apache-2.0, col suo GGUF ufficiale. L'ho messo nel catalogo e l'ho misurato sugli stessi casi.
| Qwen3-4B Q4 | Minerva-7B Q4 | |
|---|---|---|
| 31 casi etichettati | 30 giuste | 13 giuste |
| 6 casi senza risposta, riconosciuti | 5 | 0 |
| Per decisione | 188 ms | 334 ms |
| Il file da scaricare | 2,5 GB | 4,5 GB |
Il confronto è onesto su una cosa sola, lo stesso prompt, e quel prompt l'ho scelto guardando come rispondeva Qwen. Minerva-7B-instruct v1.0 è un modello del 2024 che nessuno ha messo a punto per seguire una scelta multipla, quindi con un prompt scritto per lui i numeri cambierebbero. Il caveat sta nel README e resta lì.
Con quel caveat in mano, la conclusione che regge è questa: per questo mestiere conta più l'obbedienza alle istruzioni della lingua di addestramento. Una buona notizia per chi deve scegliere un modello, e scomoda per chi dava per scontato il contrario.
04La grotta, e un guadagno venuto da una sottrazione
Per la lezione di un corso mi serviva un esempio in cui si vedesse cosa succede quando deleghi una decisione. Ho scritto un gioco a turni: un eroe, un mostro, quattro mosse, il bottino. Un gioco ha tre cose che un processo di lavoro all'inizio si sogna: le regole sono scritte, lo stato sta in una riga, e una mossa sbagliata fa danni a nessuno.
A giocare ho messo tre giocatori: uno a caso, quattro righe di if scritte da me, e Bivio che a ogni turno guarda lo stato e sceglie.
| Giocatore | Partite | Vinte | Fuggite | Morte |
|---|---|---|---|---|
| A caso | 100 | 0% | 94% | 6% |
Quattro righe di if | 100 | 63% | 35% | 2% |
| Bivio, tutte le mosse sempre offerte | 40 | 0% | 0% | 100% |
| Bivio, solo le mosse legali | 40 | 75% | 2% | 22% |
| Bivio legali, più una regola sul pericolo | 40 | 0% | 75% | 25% |
Guarda la terza riga contro la quarta, che è il motivo per cui questo laboratorio esiste. Offrendogli sempre tutte e quattro le mosse, il modello muore in ogni partita: sceglie il colpo forte anche mentre è in ricarica, cioè butta il turno, perché nella descrizione c'è scritto che toglie da 18 a 24 ed è il numero più grosso della lista. Sei righe di Python che guardano eroe.ricarica e eroe.pozioni gli tolgono dalle opzioni le mosse impossibili, e passa a vincere tre volte su quattro.
Quale mossa sia possibile è meccanico, e lo fa il codice. Quale mossa sia conveniente è giudizio, e resta al modello.
Il guadagno è venuto da quello che al modello ho tolto. Questa è la frase che mi porto dietro in aula quando qualcuno vuole dare a un agente tutti gli strumenti dell'azienda e vedere che succede.
Ci ho messo sopra una regola scritta a mano: se il modello dice che sono in pericolo, bevo o scappo. Il risultato crolla a zero vittorie, perché quel sì o no risponde sì quasi sempre. Una risposta non tarata, usata come se fosse tarata, fa più danni dell'averla ignorata. L'ho lasciata nella tabella apposta.
05Duecento strumenti, e la stessa sottrazione
Quella frase l'ho messa alla prova sul caso vero. Un agente con dieci server MCP addosso si porta dietro centomila token di soli schemi prima che qualcuno abbia scritto una parola, e più strumenti ha meno ci azzecca a sceglierli. Ho scritto una portineria che sta in mezzo: tiene i server dietro di sé, in contesto ne espone tre, e quando serve chiede a Bivio quali dei duecento servono a questa richiesta.
Le prime due versioni le ho buttate, e le ho buttate misurando. La prima faceva una domanda sì o no per strumento: duecento domande su una lettura sola, che è esattamente quello che Bivio fa bene. Ventinove secondi, e chiedendo «devo aprire una issue su GitHub» tornava cerca_contatto. Il secondo difetto conta più del primo: a «questo strumento serve?» il modello dice sì a qualunque cosa sia vagamente in tema, le probabilità si accalcano vicino a 1 e l'ordine che ne esce è rumore. Una scelta costringe al confronto, un sì o no no.
La seconda spezzava il catalogo in gironi da venticinque: nove domande invece di duecento. Le scelte sono migliorate, il tempo no, ventisei secondi. Non erano le domande a costare, erano gli undicimila token di descrizioni che il modello doveva leggere comunque.
Quello che funziona è la riga della grotta. Quali strumenti siano plausibili è meccanico: le parole della richiesta e quelle del nome si sovrappongono oppure no, e un set lo dice gratis. Quale sia giusto è giudizio, e resta al modello. Il filtro porta duecento a venticinque, il modello sceglie fra venticinque.
| Su 200 strumenti, 10 richieste | Primo giusto | Fra i primi 5 | Mediana |
|---|---|---|---|
| Solo il modello, legge tutto | 10/10 | 10/10 | 18,7 s |
| Filtro meccanico, poi il modello | 10/10 | 10/10 | 2,6 s |
Stessa accuratezza, sette volte più veloce. È il terzo posto in cui finisco sulla stessa frase partendo da un problema diverso, e a questo punto ho smesso di considerarla una coincidenza.
La portineria guarda anche le chiamate prima di passarle: è irreversibile? porta fuori dati personali? lo strumento fa quello che dichiara? Ma è un classificatore, cioè legge del testo e dice un numero, e chi controlla quel testo può provare a parlargli intorno. Prende le sviste e gli incidenti, che sono la maggioranza, non un avversario. Le cose che devono valere sempre stanno in un elenco di nomi vietati, che è meccanico e non si discute.
06Quanto ti puoi fidare
Poco, e lo dico io che l'ho scritto. Sono 37 casi che ho scritto io, misurati su una macchina sola. Ti dicono che gira e che risponde sensato. Per sapere se regge il confronto con Jev o con SemIf servirebbero le loro fixture e il loro valutatore, e quel lavoro qui manca.
Dei 31 casi etichettati ne sbaglia uno: un refuso nella pagina contatti, classificato come «fastidioso» invece che «trascurabile», con probabilità 1,000. Sicuro e sbagliato insieme. Tengo quel caso in prima fila perché spiega in tre righe che la confidenza di un modello è un numero, con tutti i difetti dei numeri che escono da una macchina non tarata.
Le tre cose da sapere prima di metterlo in produzione
- Appena acceso è sicurissimo di tutto. Dove la documentazione di Jev mostra 0,88, qui esce 0,9999. Finché ti prendi l'opzione più probabile funziona lo stesso; il giorno che ci metti una soglia, quella soglia va tarata sui tuoi dati con
bivio taratura. - La taratura è legata a un'impronta che tiene dentro i pesi, la versione del prompt e la taratura stessa. Cambi modello e il file si rifiuta di caricarsi, con un messaggio, al posto di darti numeri scollegati.
- L'astensione è spenta di serie. Accesa riconosce 5 casi su 6 in cui la risposta nello stato manca davvero, e insieme si tira indietro 6 volte su 31 con la risposta sotto gli occhi. Accendila se hai un ramo dove far finire i dubbi.
C'è poi il difetto che questo metodo si porta dietro per costruzione: un modello a scelta multipla pesa un'opzione anche per dove sta, e chi legge i logit delle lettere lo eredita intero. Era la cosa più imbarazzante da lasciare a occhio, quindi l'ho misurata facendo la stessa domanda con le opzioni girate in tutti i modi.
| Su 22 casi | Qwen3-4B | Minerva-7B |
|---|---|---|
| Di quanto si muove il logit di un'opzione spostandola | 2,43 | 1,35 |
| Margine più stretto fra prima e seconda | 7,88 | 0,01 |
| Risposte che cambiano solo girando le opzioni | 0 su 22 | 19 su 22 |
Le prime due righe vanno lette insieme. Il difetto c'è anche su Qwen, e vale due punti e mezzo; solo che lì la prima classificata stacca la seconda di quasi otto, quindi due e mezzo non ribaltano niente. Su Minerva il margine è un centesimo, e allora quel difetto decide da solo la risposta diciannove volte su ventidue. Si spiega così anche la sconfitta del modello italiano: in buona parte non sta leggendo male lo stato, sta scegliendo per posizione. Adesso si rimescola con --giri 3, che costa poco perché lo stato resta in cache e si ripaga solo la seconda metà del prompt, e su Minerva recupera tre casi su ventidue.
Restano fuori le opzioni per domanda, che si fermano a 26 perché tante sono le lettere, e il fatto che l'ho provato su un Mac con Apple Silicon e basta.
La misura che conta davvero è un'altra, e vale per qualunque strumento di questo tipo, compreso quello che stai valutando adesso: prendi cento decisioni che qualcuno in azienda ha già preso a mano, dagliele in pasto, e conta quante ne azzecca. Un'ora di lavoro, e ti risparmia sei mesi di automazione che smista male.
Bivio è su github.com/TheRealF/bivio, licenza Apache-2.0: pip install git+https://github.com/TheRealF/bivio, poi bivio scarica per il modello e bivio serve per il campo di prova. Dentro a un agente ci entra con bivio mcp, e bivio portineria è quella della sezione 05. Gira su llama.cpp con Qwen3-4B-Instruct-2507.
L'idea è di TypeSafe AI, che l'ha presentata in Introducing System One models and Jev. In aperto ci sono arrivati prima SemIf e Rizzo Flow, da cui ho preso il controllo sulle lettere e l'abitudine di pubblicare i numeri con tutti i loro se e ma.
Il laboratorio della grotta sta nella Lezione 6 del corso gratuito Gen AI per l'automazione dei processi, con il codice in pagina e il piano B per chi lascia stare i 2,5 GB del modello.
Federico Boggia