Implementare un framework per realizzare assistenti intelligenti basati su intelligenza artificiale generativa come Oraculum e Sibylla mi ha offerto un’opportunità unica di osservare il ruolo che i modelli LLM giocano nel contesto di un software che ne vuole usare le funzioni integrandole con dati esterni.
Le sfide per chi deve introdurre un modello LLM all’interno di un sistema
I sistemi RAG (Retrieval Augmented Generative systems) come Sibylla arricchiscono il prompt di un LLM come GPT con informazioni reperite da un database vettoriale che supporta la ricerca delle informazioni usando vettori generati dal modello di AI e che in qualche modo rappresentano il significato dell’informazione indicizzata.
Nello sviluppo di Sibylla mi sono reso conto che, dovendo superare il limite della finestra del prompt, il codice comincia a scimmiottare concetti tipici del cervello umano, come ad esempio la memoria a lungo termine, prevedendo meccanismi per indebolire i ricordi e mantenere l’attenzione sul centro del discorso. Anche se non sono informazioni pubbliche, anche ChatGPT implementa euristiche analoghe, e questo spiega perché pur usando lo stesso modello (i.e. GPT-4) Bing chat sia così diversa nel funzionamento di ChatGPT.
Cerchiamo di capire quali sfide deve affrontare chi deve introdurre un modello LLM all’interno di un sistema, anche per sfatare il mito che l’AI faccia tutto da sola.
L’API di OpenAI
Attualmente OpenAI raccomanda di utilizzare l’API di chat per realizzare sistemi basati sui modelli GPT. Ecco un esempio dalla documentazione:
completion = openai.ChatCompletion.create(
model=”gpt-3.5-turbo”,
messages=[
{“role”: “system”, “content”: “You are a helpful assistant.”},
{“role”: “user”, “content”: “Hello!”}
]
)
Come si può osservare al modello si passa attraverso la struttura dati messages che è una sequenza di messaggi in cui sono riportate le interazioni etichettate col ruolo (che può essere system, user, e assistant a titolo di esempio) che richiamano la struttura di una chat.
Appare subito evidente però che se si prova ad usare una chat reale di una conversazione con ChatGPT si sfora immediatamente il limite dei token che si possono inserire in un prompt. E allora come è possibile che ChatGPT lo faccia?
Dopo qualche esperimento la risposta è quasi ovvia: la chat con i messaggi che vediamo non è quella che il software usa per interrogare il modello e non solo per aggirare il limite di spazio.
Il pappagallo stocastico
Sebbene sia stato scritto più volte è facile scordare che un LLM alla fine non è che un enorme modello probabilistico che appende la parola più probabile al prompt. Non ha assolutamente idea di cosa stia leggendo nonostante il comportamento che esibisce lasci pensare il contrario. La crescita della dimensione del prompt condiziona inevitabilmente la selezione dei prossimo token ed è impressione condivisa che all’allungarsi del prompt le singole affermazioni in esso contenuto si “diluiscono” poiché alterano la distribuzione calcolata nel modello.
Può capitare quindi che affermazioni perentorie si perdano al crescere della conversazione arrivando ad aggirare anche le indicazioni volte a determinare il comportamento del modello e consentendo ad eventuali attaccanti di aggirare le restrizioni imposte per ottenere comportamenti indesiderati.
La gestione della memoria
Un sistema che fa uso del modello LLM cercando di avere un’interazione con l’utente e non solo una risposta secca deve quindi tenere conto di questi comportamenti e della dimensione complessiva del prompt. In particolare i sistemi che recuperano informazioni da un database e le usano per arricchire il prompt sono particolarmente sensibili a questi aspetti poiché la conoscenza di un particolare dominio applicativo del modello è inserita in poco spazio che va usato efficacemente.
Per questo motivo se la conoscenza che si inserisce del prompt non è molto specifica o è “diluita” (ovverosia viene espressa attraverso affermazioni generiche e poco assertive) le risposte che il modello produrrà tenderanno ad essere altrettanto vaghe e poco utili.
L’alternativa del raffinamento del modello per introdurre nuova conoscenza non è sempre adeguata, soprattutto per il controllo delle risposte, e inoltre c’è un rischio più elevato di allucinazioni da parte dell’AI.
Molti sistemi open source di tipo RAG, oltre a Sibylla, come Haystack e Llama Index implementano un concetto di memoria. Si tratta di una struttura dati responsabile di “ricordare” fatti rilevanti relativi alla conoscenza specifica usata per arricchire il common sense del modello LLM e una memoria dei fatti salienti della conversazione corrente affinché l’utente abbia l’impressione che l’AI risponda avendo in mente l’intera conversazione.
Nella memoria è quindi naturale memorizzare anche l’intera conversazione, in modo da poterla analizzare per poter estrare le informazioni rilevanti da includere nel vero prompt che viene passato al modello LLM.
La memoria diviene quindi un asse portante di questi sistemi ed offre una soluzione elegante per cecare di mantenere il modello LLM focalizzato senza che le interazioni precedenti possano alterarne troppo il comportamento. In Sibylla la memoria è
var (xml, msg) = await _memory.Recall(message, filter);
_chat.Messages.Clear();
_chat.Messages.Add(new ChatMessage(Actor.System, _conf.BaseSystemPrompt!));
_chat.Messages.Add(new ChatMessage(Actor.System, xml.OuterXml));
_chat.Messages.Add(new ChatMessage(Actor.Assistant, _conf.BaseAssistantPrompt!));
foreach (var m in msg)
_chat.Messages.Add(m);
_chat.Messages.Add(new ChatMessage(Actor.User, message));
_memory.History.Add(new ChatMessage(Actor.User, message));
Anche senza entrare nel dettaglio del codice, la memoria viene utilizzata per recuperare la conoscenza dal vector database relativa al messaggio corrente (message), e anche per ottenere alcuni messaggi salienti estratti dalla conversazione corrente. Usando queste due componenti il prompt, rappresentato da _chat.Messages, viene ricostruito ad ogni interazione.
Interagire col sistema da però l’impressione di sostenere una conversazione con un assistente che ha memoria anche se, nella versione corrente, vengono tenuti solo gli ultimi quattro messaggi della chat ed ignorati i messaggi che hanno portato ad una risposta negativa da parte dell’assistente.
Confinare il comportamento
Dall’esperienza che mi sono fatto finora nel fornire conoscenza in Sibylla, la selezione delle informazioni da includere nel prompt è determinante per la definizione del suo comportamento, e in particolare dalla capacità di restare focalizzata sul discorso rispondendo al bisogno informativo dell’utente.
Inoltre, mantenere un prompt “corto” aiuta il modello a non “dimenticare” le prime istruzioni, abbiamo infatti osservato che insistendo nella richiesta al di fuori degli argomenti indicati all’AI come consentiti porta il modello ad ignorare la direttiva originale (nel seguente esempio la Sibylla includeva tutta la conversazione nel prompt):
Evitando di fornire al modello AI l’intera conversazione, ma solo gli aspetti salienti, avendo cura di ignorare le interazioni che portano ad un messaggio di diniego da parte dell’assistente, si scongiura il rischio di superamento del perimetro indicato dal prompt che per l’assistente inizia:
“Sei un operatore che risponde alle domande degli utenti relativamente alle procedure e al sistema UWeb missioni dell’Università di Pisa. Rispondi solamente a domande riguardanti le missioni per conto dell’Università di Pisa, a tutte le altre domande risponderai ‘sono autorizzato a rispondere solo a questioni riguardanti le missioni‘.”
Conclusioni
I sistemi come ChatGPT che noi battezziamo come AI in realtà sembrano non coincidere con le capacità apprese dal modello LLM. Uno strato di codice si occupa di interagire con l’utente e decidere quali informazioni passare al modello sottostante affinché il comportamento sia quello atteso. Si tratta di un fatto rilevante e da considerare quando si valuta l’introduzione di queste tecnologie in sistemi di produzione.
Come sempre la natura, e in particolare il funzionamento del nostro cervello, è un’inesauribile fonte di ispirazione, anche nel modo di organizzare i dati in modo che ricorda sia la memoria di lungo periodo che quella temporanea che tutti sappiamo essere di circa sei elementi per gli esseri umani.
Più approfondisco questi sistemi e più mi chiedo come impiegarli amplificandone le qualità e mitigando i non pochi rischi dovuti alla difficoltà di avere un controllo completo sul comportamento.