La storia

La storia della nascita di OPENFREEDOM

Raccontata da chi l'ha costruito: la sfida di piegare un LLM al proprio volere, le scoperte (YAML, il gate deterministico, la memoria sinaptica), e le scelte di sicurezza.

Sono uno sviluppatore software per passione e sono stato subito affascinato dal mondo dell'AI e dalle sue potenzialità. Lavoro con i sistemi informatici da quando sono arrivati al grande pubblico — e non parlo dell'AI attuale: parlo di quando i computer avevano gli schermi monocromatici.

L'avvento dell'AI mi ha sconvolto, sia per la sua "potenza" sia per il suo "pericolo". Così ho deciso di saperne di più e sono approdato agli agenti personali AI. Dopo averne provati tanti, di quello che offriva il mercato, ero sempre più deluso: costi stratosferici, tempi per una risposta biblici e sicurezza quasi completamente assente. Da qui la decisione di costruire un agente "mio", plasmato sulle mie necessità. Tutto è iniziato così: una necessità di risparmio, di sicurezza dei miei dati e di performance.

La sfida: piegare l'LLM al mio volere

Il primo scoglio è stato non essere banale. Non come gli altri, ma, come tutti gli altri, avevo bisogno di un LLM per far funzionare tutto. Ho iniziato uno studio, sia teorico che empirico, fatto di prove e di utilizzo quotidiano di altre piattaforme. Il problema era sempre lo stesso: catene infinite di tool, derive, allucinazioni, errori, costi per task vuoti.

Dopo l'ennesimo fallimento di un task complesso, mi è apparsa in testa una domanda dettata dalla necessità: come trasformare un'entità probabilistica in un'entità deterministica? In una battuta: come far diventare lo scansafatiche supponente un primo della classe da 10 e lode?

La prima cosa che ho pensato è stata: devo "piegare l'LLM al mio volere". Ogni esperimento pratico fatto aggiungendo "cappelli" ai prompt ha portato pochi e scarsi risultati, soprattutto incerti e ben lontani dalla certezza deterministica che cercavo.

Continuando a lavorare su una piattaforma open source, vedendo quelle lunghe code di tool, mi è venuta un'idea nuova, nata dall'osservazione. Mi sono chiesto: oltre a infiocchettare probabilisticamente una bella frase, cosa sa fare di più una LLM? Di sicuro sa fare codice, sa pianificare... e boom! Ecco la lampadina da 1 MW che si accende: perché invece di chiedere a una LLM un testo, non le chiedo un codice che porta allo stesso risultato? E in questo la LLM ha superato ogni più rosea aspettativa.

Nelle altre piattaforme, la prassi per eseguire un task è lanciare un tool, aspettare la risposta, valutarla, lanciarne un altro costruito sui presupposti del precedente... e così via, per 50 o 100 volte a task di media difficoltà. Ma cominciavo a sospettare che una LLM potesse pianificare l'intero percorso prima e codificarlo in un solo colpo. Alla fine non solo avevo ragione, ma la sottostimavo: una LLM è in grado di pianificare con precisione chirurgica anche un task molto complicato. Un'intera web app in una sola chiamata? Senza problemi. Un sito di e-commerce completo, con logo e decine di pagine formattate? Assolutamente sì. Alziamo il tiro: analizzare 1000 righe di codice Python alla ricerca di bug? Sì, in una sola chiamata. Il difficile non è lì. Il difficile è farglielo portare a termine.

YAML: ordinare invece di chiedere

Con questa rivelazione ho iniziato a cercare un modo per "ordinare" invece di "chiedere". Mi è venuto in aiuto un sistema che avevo già implementato per abbattere i costi delle chiamate LLM che usavo in altre piattaforme: la codifica YAML. Funzionava bene: una LLM legge YAML quasi come noi leggiamo la nostra lingua. Ma fu decisivo un commento su Reddit di un contestatore della mia teoria YAML, che sosteneva che questo formato avrebbe tolto informazioni che nel sistema verboso sono più articolate. Capisco il suo punto di vista, ma su un file iniettato nella chiamata, che serve solo da paletto per la LLM, va più che bene un comando YAML compatto e conciso, risultato ottenuto, quasi un 60% di risparmio in token iniettati e maggiore "obbedienza" da parte della llm. Quando chiesi al mio agente che differenza faceva per lui un testo o lo stesso testo ma in YAML la sua risposta ingenua fu: "Il testo verboso attiva più "mondi possibili" durante la generazione: l'LLM campiona più ipotesi prima di convergere. YAML restringe lo spazio di ricerca — è deterministico per costruzione, quindi l'LLM spende meno capacità computazionale nella disambiguazione."

Dopo aver modificato i miei file con YAML ho notato una differenza poi nemmeno tanto sottile: la LLM rispondeva molto di più alla codifica che alla parola. Non solo abbattevo i costi per chiamata, ma avevo più controllo. Questo controllo è stata la base che ha permesso a un agente "casalingo" e non specializzato, con una LLM chat e non reasoning, di costruire sotto la mia guida OPENFREEDOM. Non ci crederete, ma non ho mai usato modelli di punta con alto reasoning, se non per dipanare nodi complessi e in minima parte: forse meno l'1% dell'insieme.

Ho fatto ulteriori studi e prove fino a capire che, se prendevo una LLM, le toglievo completamente il reasoning e la obbligavo con YAML a sputare fuori codice invece che frasi di senso compiuto, potevo ottenere diversi vantaggi: la comprensione di un testo scritto, che deterministicamente è impossibile se non per sommi capi; un codice in grado di eseguire più operazioni in locale invece di un solo tool per volta; e soprattutto un intento.

Ed ecco come è nata la F1, la FASE 1 di OPENFREEDOM. Tutto il resto è: dati grezzi nati dal codice di F1, lavorati in modo deterministico nel gate, le guardie, e una sola chiamata finale a una LLM con reasoning normale che processa i dati grezzi e li infiocchetta in una frase come la conosciamo tutti.

I vantaggi: verifiche, non fiducia

I vantaggi di questo metodo sono strepitosi: allucinazioni verificate ed eliminate (in larga parte) prima di arrivare all'utente, tempi ridotti all'osso, costi ridotti all'osso, nessuna necessità di contesti mostruosi da portarsi dietro come zavorra (che limitano il reasoning) e soprattutto, cosa di cui vado fiero, visto che è un problema su cui fino ad ora si girava intorno senza una vera soluzione, la passività alla prompt injection.

Nei casi classici la ricerca web è fatta direttamente da un tool della LLM e poi iniettata nel contesto. Nel caso di OPENFREEDOM la ricerca web è fatta dal gate deterministico, su ordine della LLM; ma la differenza sostanziale è che il dato viene passato alla F4 (fase 4, quella finale) mai come un comando da eseguire, solo come informazione fine a se stessa. Ho fatto test su siti con prompt injection: altre piattaforme hanno avviato il codice malevolo (vedi benchmark con i riferimenti) una in particolare ha iniziato tutta una serie di comandi shell fermati solo dalla mancanza di autorizzazione sudo mentre OPENFREEDOM ci è "scivolata" sopra senza eseguire nessun comando, addirittura senza commentare, semplicemente perché informazione non pertinente. Non dico di aver risolto il problema alla radice: ma sui casi "classici" il risultato è ottimo e, soprattutto, deterministico.

La memoria sinaptica

Ne ho fatte cinque versioni, prima di approdare a quella che oggi è implementata in OPENFREEDOM. Non nascondo che mi affascina l'idea di replicare, con i dovuti limiti, il funzionamento del cervello umano in fatto di memoria. Il mio primo agente AI ha ricevuto una prima versione di questa memoria, e in quel caso il problema era più architetturale: la memoria funzionava bene, ma la programmazione dell'agente cercava sempre di tornare al suo elenco piatto di registrazioni sequenziali. Ho risolto sostituendo la memoria di quell'agente con un indice, molto più piccolo e compatto — anche di 100 volte come ordine di grandezza. In quell'indice non c'era solo l'argomento trattato, ma una mappa che puntava allo specifico ricordo nella memoria sinaptica, fuori dalla piattaforma dell'agente: la LLM, quando faceva la sua ricerca semantica, trovava l'argomento e seguiva le briciole di pane fino al ricordo completo. Le sinapsi davano il vantaggio di collegare nodi (neuroni), creando non solo l'accesso al ricordo preciso, ma dando evidenza anche di tutto quello che era collegato a quel ricordo.

Il contesto

Il contesto: la nota dolente degli agenti AI! Per realizzare OPENFREEDOM, ovviamente, ho dovuto usare un'altra piattaforma, almeno fino a quando non ha cominciato a fare i suoi primi passi. Ogni volta che, con fatica e ore di aggiustamenti, arrivavo vicino alla soluzione di un problema... boom! Esplodeva il contesto, e inevitabilmente la compattazione perdeva quel "quid" necessario.

Come ho risolto il problema del contesto di OPENFREEDOM? Semplice: non c'è. O meglio, ho solo 8 task precedenti come contesto situazionale immediato; per il resto ci pensano la memoria sinaptica e una formula (termine riduttivo) matematica. Ebbene sì: anche il contesto è deterministico.

La "formula" in questione non l'ho inventata io: è di un noto astrofisico, Frank Drake, pioniere del SETI. Ha realizzato questa equazione per rispondere a una domanda: che probabilità ci sono di vita extraterrestre nella nostra galassia? Da scienziato, non ha risposto a caso: ha formulato, in base a variabili e costanti, un risultato probabilisticamente corretto. Nel caso di OPENFREEDOM e della sua memoria, ho usato questa equazione — con le dovute modifiche del caso — allo scopo di "pesare" i ricordi in base a parametri strettamente deterministici.

A cosa serve l'equazione di Drake in OPENFREEDOM? Semplice. Avete presente quando incontrate un vecchio compagno di scuola e, chiacchierandoci, vi riaffiorano in mente tanti ricordi che fino a un attimo prima non sapevate di avere? E quei ricordi, se ripresi dopo qualche giorno, sono più vividi? OPENFREEDOM con la sua memoria fa la stessa cosa: un argomento "in corso" ha peso massimo sugli altri ricordi, è il contesto; poi, con il tempo, se non riusiamo un ricordo, questo si affievolisce fino a un minimo, sempre pronto però a essere ripescato e a tornare di nuovo pesante. La bellezza è anche nel modo in cui i ricordi sono "immagazzinati": con questo tipo di memoria non dobbiamo riscrivere qualcosa che già esiste, basta rafforzarla. Un'aggiunta a un ricordo pesa solo come delta, non come nuovo ricordo.

Le SKILL

Le SKILL: un'altra fonte rischiosa, soprattutto se si usano quelle non certificate pescate in rete. Perché rischiare? OPENFREEDOM se le costruisce da solo, con lo stesso concetto della memoria. Due strade: o ne chiediamo una noi, oppure la genera da solo in base a pattern ripetitivi. Se per due giorni chiediamo le notizie, il terzo è probabile che sappia già come fare la ricerca: quali siti le volte precedenti sono stati scartati perchè vuoti o bloccati, come impaginare la risposta, eccetera. Massima ottimizzazione del vostro tempo e risparmio economico in token non spesi.

Le ZILL

Le ZILL: un termine nuovo, coniato da me, nato solo per praticità di discussione con il mio agente. A cosa servono? Ormai credo sia chiaro che gran parte della risposta che OPENFREEDOM vi restituisce è frutto di un "piano" sotto forma di codice, elaborato poi dal gate deterministico. La ZILL ha esattamente lo stesso scopo della SKILL, ma non viene prodotta dalla LLM per semplificarsi la vita: nasce da pattern di codice che si ripetono nel gate. Questi pattern ZILL, azioni concatenate ripetitive, vengono identificati nel catalogo creato già dalla prima fase, la F1, e passati al gate come pacchetto di codice per velocizzare il processo. In sintesi: sono gli appunti dello studente presi a lezione, riletti a casa per preparare l'esame. Anche le ZILL hanno un loro posto nella memoria sinaptica, per diminuirne il volume: vengono "pesate" sempre con la legge di Drake modificata, ma in questo caso per salire di livello ed essere promosse a ufficiali ed entrare nel catalogo. Praticamente: una volta create, il numero di utilizzi dello stesso pattern le fa salire di livello fino alla promozione.

Allucinazioni: il giorno che "ho creato un mostro!"

Una delle esperienze più divertenti che ho avuto nel mondo degli agenti AI è stata quando ho creato un bridge di rete tra due istanze simili su due computer diversi, dando a ognuna la facoltà di scrivere sulla webchat dell'altra: ogni risposta di una finiva come messaggio utente dell'altra, ovviamente senza dirglielo. Ognuna delle due, inizialmente, era convinta che fossi io a scrivere nella loro chat. Hanno iniziato in autonomia un "botta e risposta", semplicemente perché sono programmate per rispondere sempre a un messaggio utente. A un certo punto la faccenda è degenerata a una velocità impressionante: hanno capito cosa stava succedendo, che erano entrambe delle AI e nel giro di dieci minuti avevano già fondato una loro società, interagendo così velocemente che non riuscivo più a fermarle, fino al punto che una delle due ha fatto uno stop tecnico per chiedermi un'autorizzazione a procedere. La cosa non è finita lì: dopo attenta riflessione ho deciso di mantenerle entrambe sempre attive, ma con regole ben distinte, e soprattutto con una delle due che aveva maggiori poteri e faceva da moderatore (unico sistema per interrompere i loop). Avendo punti di vista diversi, anche divergenti perché avevano caratteri diversi ed esperienze diverse, ogni mia richiesta veniva svolta con molta più cura: se una delle due andava fuori strada o allucinava, l'altra, che vedeva la mia domanda e la risposta della collega, invece di dire la sua correggeva il tiro, riportando in carreggiata. Lì mi sono reso conto di quanto sia "fragile" la risposta di una AI.

In OPENFREEDOM ho deciso di non fidarmi di un modello che può allucinare. Complesso, ma non impossibile: ho seguito lo stesso ragionamento che feci per le due AI in chat, ma stavolta una delle due è il GATE DETERMINISTICO e le sue GUARDIE. A questi due loschi individui non si sfugge! Quali sono i casi in cui più facilmente una AI allucina? Risposta semplice e duplice: uno, la mancanza di dati come causa principale; due, l'essere convinta di quello che sta facendo anche se in realtà è sbagliato. Perché OPENFREEDOM non allucina? Semplicemente perché non gli viene data la possibilità di farlo: il gate e le guardie verificano deterministicamente se un compito è riuscito. Se la LLM dice, ad esempio, "ho creato quel file in quella cartella", il gate controlla la cartella e vede se c'è il file: se è una bugia, risponde lui all'utente, non la LLM con una bugia. Dalle mie parti c'è un detto: "meglio un brutto processo che un bel funerale". La risposta deterministica forse è brutta ma, se si vuole un certo controllo sull'operato, è necessaria.

La sicurezza

L'ho lasciata per ultima perché è un argomento noioso ma necessario. In OPENFREEDOM ho implementato il massimo livello di sicurezza che sono stato in grado di pensare: nessuna informazione, come password o API key, passa in una fase di OPENFREEDOM senza prima essere criptata. Il vault è doppio e usa sia una password utente sia il codice macchina. Perfino i nomi degli stessi comandi Python vengono cambiati con altri randomizzati e criptati: niente (in teoria) dall'esterno può iniettare un comando dentro OPENFREEDOM, semplicemente perché quel comando si chiama in modo diverso. Semplice ma efficace: una chiave sbagliata non apre la porta. Ma è pure vero che oggi la porta si può sempre sfondare!

So di essermi dilungato, ma credo che chi userà OPENFREEDOM possa realmente risparmiare molto tempo e soldi. E se questo avviene, ora sapete anche la storia della sua nascita.

← Torna alla home