Intro

Per chi non lo conoscesse, LangChain è un framework per lo sviluppo di applicazioni che fanno uso di LLMs.

Come si evince dal nome stesso, LangChain si basa sul concetto di Catena LLM, la quale combina 3 elementi:

  • I Prompt Templates: fanno riferimento ad un modo riproducibile per generare un prompt. Contiene una stringa di testo (“il modello”), che può accettare una serie di parametri dall’utente finale e genera il prompt definitivo che viene passato in input al modello
  • Il modello linguistico (LLM): in particolare, LangChain si integra con i provider più importanti (OpenAI, Cohere, Hugging Face, etc)
  • Gli Output Parsers: consentono di estrarre dati in forma strutturata dalle risposte restituite dal modello linguistico

I Prompt Templates: fanno riferimento ad un modo riproducibile per generare un prompt. Contiene una stringa di testo (“il modello”), che può accettare una serie di parametri dall’utente finale e genera il prompt definitivo che viene passato in input al modello Il modello linguistico (LLM): in particolare, LangChain si integra con i provider più importanti (OpenAI, Cohere, Hugging Face, etc) Gli Output Parsers: consentono di estrarre dati in forma strutturata dalle risposte restituite dal modello linguistico

Il framework ha 2 caratteristiche molto interessanti:

  1. può integrare le capability dei LLM con una propria base dati, partendo da dati strutturati e non
  2. permette di implementare il concetto di “Agente”, attraverso cui la sequenza di azioni da compiere è determinata anch’essa come output del language model

Relativamente al punto 1, ero piuttosto curioso così ho deciso di fare alcuni test. L’obiettivo non è tanto quello di fare un’analisi critica relativa alle performance dei modelli, ma piuttosto verificare la facilità con cui è possibile integrare il framework all’interno di una propria base dati.

Integrazione con i dati non strutturati

Non sapendo da dove partire, ho dato un occhio ai casi d’uso più documentati su internet. Nella fattispecie, ho trovato molta documentazione relativa all’analisi dei file PDF. Se c’è una cosa che non manca su Internet, sono appunto i file PDF, quindi mi sembrava un ambito su cui avrei potuto sperimentare parecchio.

Nella documentazione ufficiale c’è una sezione apposita relativa alla “Data Connection”, che ho trovato incredibilmente chiara ed intuitiva e di cui provo a riassumere qui i punti principali.

I building blocks messi a disposizione da LangChain sono i seguenti:

  • Document: è un’astrazione contenente i dati in forma testuale e i metadati associati
  • Document loaders: Sono delle classi che consentono di estrarre il testo e i metadati da una specifica tipologia di dati per costruire il “Document”
  • Document transformers: utilizzato per processare i Document. Poiché i LLM solitamente hanno delle limitazioni importanti in termini di token processabili, la trasformazione più comune è lo splitting in chunk, attraverso cui è possibile sottomettere le chiamate verso il provider del LLM in serie o in parallelo. Esistono anche altre tipologie di transformer, per esempio: riduzione di ridondandanza, traduzione, estrazione di metadati etc…
  • Text embedding: L’embedding è l’operazione di traduzione di una porzione di testo in un modello vettoriale N-dimensionale, che poi è alla base delle operazioni di ricerca semantica basate su indici di similarità ed implementate tramite calcolo delle distanze vettoriali
  • Vector stores: memorizza gli embedding all’interno di un DB Engine in grado di restituire efficientemente i “vettori” più “vicini” (e dunque le porzioni di testo più simili) al testo passato in input (anch’esso opportunamente vettorializzato tramite embedding). In particolare, è possibile sfruttare alcuni engine open source per far girare tutto in locale, oppure integrarsi con alcuni prodotti di mercato che ovviamente offrono performance molto migliori (es: Pinecone)
  • Retrievers: è un’interfaccia che restituisce documenti a partire da una query non strutturata. È un concetto un po’ più generale di un Vector Store, ma a differenza di quest’ultimo, consente unicamente di restituire i documenti e non necessariamente di memorizzarli

Chains

E adesso veniamo ai componenti principali: le catene.

LangChain introduce questo concetto che rappresenta un’astrazione utile per implementare in maniera semplice e modulare le applicazioni che fanno uso di LLMs. Esistono molte Chain predefinite, le più comuni sono:

  • RetrievalQA: risponde ad un input utente partendo dal’output restituito da un retriever
  • ConversationalRetrievalChain: simile a RetrievalQA, aggiunge la capacità di costruire una esperienza conversazionale attraverso la storia dei messaggi scambiati
  • Summarize: come si evince dal nome, fa una sintesi dei documenti passati in input

L’esperimento

Ho preso un paper di ricerca del 2017, scritto da alcuni ricercatori dell’Oak Ridge National Laboratory (ORNL) e di altri istituti universitari, che propone una implementazione di un algoritmo di quantum computing per un problema di Portfolio Optimization.

In particolare, l’articolo descrive i vantaggi derivanti dall’utilizzo di una variante del modello di Markowitz (QUBO) su device quantistici di tipo D-Wave.

L’articolo completo lo trovate a questo link.

Essendo appassionato di questi temi, ma non avendo una solida base teorica, riesco a capire i punti principali del paper, ma non ho alcuna competenza per valutarne l’attendibilità o la bontà dei risultati, così decido di chiedere un’analisi critica ad OpenAI, passando attraverso LangChain.

Sorprendentemente, ho impiegato solo qualche ora e meno di 20 righe di codice per ottenere un prototipo funzionante, con un risultato che reputo soddisfacente.

Il codice

Ecco di seguito il codice prodotto.

Il codice è praticamente autoesplicativo, ma aggiungo nel seguito alcune note e commenti a margine.

from langchain.llms import OpenAI
from langchain.document_loaders import PyPDFLoader
from langchain.chains.summarize import load_summarize_chain
from langchain import OpenAI, PromptTemplate
from dotenv import load_dotenv

load_dotenv()

loader = PyPDFLoader("docs/pdf/102.pdf")
docs = []
docs.extend(loader.load())

prompt_template = """Scrivi una critica negativa di questo articolo di ricerca, mettendone in dubbio i risultati e l'applicabilità:


{text}


CRITICA:"""
PROMPT = PromptTemplate(template=prompt_template, input_variables=["text"])
chain = load_summarize_chain(OpenAI(temperature=0.5), chain_type="map_reduce", return_intermediate_steps=False, map_prompt=PROMPT, combine_prompt=PROMPT)
chain({"input_documents": docs}, return_only_outputs=True)

La chiamata a load_dotenv() serve unicamente per caricare la variabile d’ambiente OPENAI_API_KEY, associata al mio account di OpenAI. Le prime istruzioni servono per inizializzare il loader e creare i “Document” associati. Subito dopo, definisco il template di Prompt e chiamo la Chain di “Summarize”. Il primo parametro passato alla Chain è una istanziazione di LLM di tipo OpenAI, che ho volutamente configurato con una temperatura di 0.5, per spingere un po’ con la creatività. La chain_type è di tipo “map_reduce” perché il testo è sufficientemente lungo da superare il limite dei 4096 token imposti dal provider che sto usando (ChatGPT 3.5).

Risultati

Questi alcuni dei risultati che ho ottenuto:

Questo articolo di ricerca presenta una varietà di metodologie per l’analisi della selezione del portafoglio in presenza di più criteri. Tuttavia, i risultati sono basati su teorie matematiche e modelli di simulazione piuttosto che su dati reali. Inoltre, l’uso di un budget fisso di $100 non è realistico e non rappresenta le condizioni del mondo reale. Inoltre, la maggior parte delle tecniche descritte in questo articolo non sono applicabili ai mercati finanziari reali, poiché non prendono in considerazione fattori come la volatilità dei mercati, i cicli economici, ecc. Di conseguenza, i risultati e l’applicabilità di questo articolo di ricerca sono messi in dubbio.

Questo articolo di ricerca non fornisce sufficienti prove dei risultati ottenuti, né dimostra l’applicabilità del modello proposto nella pratica. La teoria di Markowitz sulla selezione del portafoglio non è applicabile a tutti gli investitori, poiché non considera le complicazioni che si possono verificare nella pratica, come le tasse o le commissioni. Inoltre, l’uso dell’ottimizzatore quantico D-Wave non è stato adeguatamente dimostrato. La formulazione rilassata non prevede l’acquisto di quote di azioni, il che significa che il risultato ottimale non è realistico. Inoltre, l’abbondanza di zeri nei dati non è realistica. La maggior parte dei codici di esempio e delle applicazioni menzionate nell’articolo sono relativi al calcolo

Conclusioni

Anche dopo aver letto il responso di OpenAI, non riesco ad entrare nel merito per capire se le conclusioni tratte hanno delle basi solide o meno, ma in prima analisi sembrerebbero condivisibili dal lettore medio (cioè me stesso in questo caso).

Il caso emblematico è, ad esempio, il commento sui soli 100$ di investimento. Se andate a leggere l’articolo, vedrete che questo è effettivamente lo scenario semplificato che è stato considerato dai ricercatori, ma oggettivamente non ho elementi per dire se effettivamente questo fattore può mettere in discussione i risultati.

In generale, la cosa che mi ha stupito è la facilità con cui il framework mette a disposizione dello sviluppatore i building block per sviluppare applicazioni di AI, senza reinventare la ruota ed integrandosi molto bene con i principali provider e prodotti di mercato.

Mi rendo conto che l’esempio illustrato è veramente banale, ma apre un mondo di possibilità. Sto facendo delle altre prove ampliando il dataset e cercando di rispondere a domande un po’ più articolate. Stay tuned