Il termine output parser è apparso sin dall'inizio nei nostri esempi soprattutto applicato alle chain rappresentandone l'elemento finale. In questa lezione, cerchiamo di capire cosa sono in generale e quali tipi esistono.
String Output Parser
Quello che abbiamo incontrato sinora è lo String Output Parser interpretato dalla classe StrOutputParser
. Il suo contributo è quello di trasformare in una stringa la risposta del modello AI. Infatti quando interroghiamo un modello quello che otteniamo è un oggetto di tipo AIMessage
. Se, ad esempio, facciamo questa interrogazione:
res=model.invoke("Qual è la capitale della Spagna?")
otteniamo un oggetto di questo tipo che conserva, nel campo content
, il vero risultato:
AIMessage(content='La capitale della Spagna è Madrid', ...)
Pertanto per restituire all'utente la risposta del modello dovremmo usare il codice res.content
. In alternativa, potremmo usare una chain di questo tipo in cui il messaggio di tipo AIMessage
viene direttamente dirottato all'output parser che estrae la stringa:
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
model = ChatOpenAI()
chain= model | parser
chain.invoke("Qual è la capitale della Spagna")
tanto che si ottiene direttamente:
La capitale della Spagna è Madrid.
Ma quanti altri output parser esistono? Vediamone alcuni altri importanti ai fini della costruzione di un'applicazione.
CommaSeparatedListOutputParser
Il CommaSeparatedListOutputParser
fa in modo che si possa avere il risultato in una lista di elementi. Facciamo una richiesta al modello che possa essere compatibile con questo tipo di formato:
from langchain.output_parsers import CommaSeparatedListOutputParser
richiesta="dimmi alcuni importanti attori italiani indicandone solo nome e cognome"
chain=model | CommaSeparatedListOutputParser()
print(chain.invoke(richiesta))
il cui risultato è il seguente:
['Marcello Mastroianni', 'Sophia Loren', 'Giancarlo Giannini', 'Monica Bellucci', 'Roberto Benigni']
L'aspetto interessante consiste nel fatto che il risultato ottenuto è una reale lista Python pertanto integrabile in un programma. Ne richiedessimo ad esempio la componente in terza posizione ci verrebbe restituita la stringa 'Giancarlo Giannini'.
JsonOutputParser
Il JsonOutputParser
restituisce una struttura in formato JSON che poi, in Python, viene mappata in analoga struttura dati. Esempio:
from langchain_core.output_parsers import JsonOutputParser
richiesta="dimmi cinque importanti attori italiani indicando in una lista di oggetti JSON nome e cognome in proprietà diverse"
chain=model | JsonOutputParser()
res=chain.invoke(richiesta)
print(res)
propone:
[{'nome': 'Marcello', 'cognome': 'Mastroianni'},
{'nome': 'Sophia', 'cognome': 'Loren'},
{'nome': 'Monica', 'cognome': 'Bellucci'},
{'nome': 'Giancarlo', 'cognome': 'Giannini'},
{'nome': 'Alberto', 'cognome': 'Sordi'}]
Come nel caso precedente, trovandoci in un programma Python quello che otteniamo in res
è l'analogo di una struttura JSON ma fatta di liste e dizionari. Potremmo infatti chiedere res[2]['cognome']
per farci restituire la stringa 'Bellucci'.
Possiamo immaginare quanto un output parser di questo tipo potrebbe rivelarsi utile nello sviluppo di un'API REST, tanto per citare un caso piuttosto noto, per restituire in JSON una risposta attraverso la Rete. Questo come sappiamo aprirebbe scenari molto interessanti come mettere a disposizione di app mobile, siti web e applicazioni di vario genere delle risposte di un modello AI a distanza.
Ne esistono altri?
Tantissimi! Attraverso la documentazione si potrà scoprire che ne esistono molti già pronti che possono essere impiegati seguendo, per ognuno, le istruzioni del caso come EnumOutputParser
che cerca di incastonare la risposta nei valori di una enumerazione definita, DateTimeOutputParser
che tenta di ricondurre la risposta ad un'informazione oraria o PydanticOutputParser
che si rifà a classi Pydantic. Soprattutto però possiamo creare i nostri scegliendo come trattare l'output fornito dal modello e offrirne una versione specifica per gli utenti delle nostre applicazioni.
Un'ultima nota molto importante per ricordare che comunque si tratta di un parsing, un'operazione quindi in cui un oggetto (il parser appunto) tenterà di interpretare il risultato di un modello e convertirlo nel formato richiesto. Potranno quindi capitare errori di conversione che saranno del tutto naturali e che dovranno, in primis, essere controllati per evitare il crash dell'applicazione e soprattutto evitati cercando di configurare al meglio la richiesta espressa nel prompt.