Da quando gli scenari moderni hanno reso centrale il ruolo degli dell’Intelligenza Artificiale, la sfida principale è diventata quella di riuscire a integrarla nelle proprie applicazioni aziendali. Questo richiede una serie di passaggi per poter generare un’architettura adeguata non solo a sfruttare la potenza del Large Language Model prescelto, ma anche a riuscire a integrarlo con la logica di business del servizio, la gestione dei dati e l’interazione con l’utente.

In questo processo di evoluzione, LangChain rappresenta sicuramente una pietra miliare. Si tratta di un framework che si pone proprio questo obiettivo: permettere di creare applicazioni basate sulla potenza di Large Language Model. Come tutti i cicli di vita dello sviluppo di applicazioni, quello di un’applicazione con LangChain può essere schematizzato in quattro fasi principali:

  • sviluppo dell’applicazione stessa;
  • test del suo funzionamento;
  • deployment, ovvero messa in produzione;
  • monitoraggio del suo funzionamento.

Tali fasi procedono nel senso indicato, ma spesso alcune di esse forniscono spunti per riattivare quelle precedenti. Per esempio, quando durante il monitoraggio si capisce come sviluppare ulteriori funzionalità o migliorare quelle esistenti.

L’approccio principale di questo framework sembra proprio l’incontro tra i tipici processi di creazione di applicazioni informatiche e l’Intelligenza Artificiale tanto che, come vedremo, LangChain offre importanti meccanismi di comunicazione tra componenti, test, monitoraggio e deployment che potrebbero essere utili anche per la produzione di software che non richieda l’impiego di un LLM.

In questo articolo iniziamo a conoscerlo e vedremo come abbia portato alla creazione di un vero e proprio ecosistema molto completo. Come sempre, cercheremo di veicolare il nostro processo di comprensione passando per un esempio pratico.

Quella che appare in figura è probabilmente una delle immagini più importanti della documentazione di LangChain.

langchain

Mostra un’architettura a strati che evidenzia come tutto ciò che costituisce l’ecosistema di LangChain serva a creare lo stack necessario a mettere in piedi un ambiente che porti dal concepimento dell’idea fino alla sua messa in produzione. Percorriamone velocemente la struttura.

Nella parte bassa troviamo il core con tutti i blocchi principali che riguardano il flusso dei dati, la gestione delle attività asincrone e la parallelizzazione delle operazioni. Il tutto è potenziato da un originale linguaggio di espressione. Notiamo inoltre in figura che ogni porzione è etichettata dal linguaggio di programmazione che si può utilizzare. Appaiono i nomi dei principali linguaggi per uno sviluppo veloce di applicazioni di ogni genere, Python e Javascript, entrambi riferiti più volte nel blocco inferiore di colore più scuro mentre gli strati superiori sono contraddistinti essenzialmente dall’esclusivo impiego del linguaggio Python.

Oltre al core, troviamo l’infrastruttura di base per la gestione dei dati e l’interazione all’interno dell’applicazione. Sono elementi come il Model I/O (ne riparleremo nell’illustrazione del nostro esempio) che include anche il Prompt, elemento centrale del flusso di input/output, il Retrieval per la gestione dei documenti da usare come fonte di informazioni, l’embedding e lo storing vettoriale, nonché tutto ciò che concerne gli Agent.

Proseguendo, troviamo i template e LangServe, quest’ultimo per l’esposizione delle funzionalità della nostra applicazione tramite REST API. Come sappiamo, una volta che le funzionalità di un sistema vengono pubblicate mediante il paradigma REST, avremo a portata di mano l’integrazione con qualsiasi tipo di interfaccia, applicazione e mezzo di comunicazione.

LangSmith, invece, è l’elemento che domina verticalmente tutto il sistema come una sorta di piattaforma unica di DevOps.

Cosa sono le chain di LangChain

Per iniziare a lavorare con LangChain, come il nome stesso fa intuire, dobbiamo iniziare a conoscerne le chain ovvero le sue catene. Si tratta di elementi che inanellano una serie di componenti che nel complesso trattano un dato in ingresso (ad esempio, un input utente) e lo trasformano fino a produrre qualcosa che viene inviato in uscita, un output, che potrà variare molto a seconda del tipo di applicazione che stiamo creando.

Uno dei blocchi di queste catene sarà proprio un LLM che fornirà la logica dell’AI durante il flusso di lavorazione. Tali strutture sono estremamente flessibili, tanto che potrebbero anche non contenere un LLM ed essere utilizzate per generici processi di trasformazione di dati, tipo pipeline, alienando del tutto dal loro procedimento l’Intelligenza Artificiale.

Riguardando la figura iniziale, partiremo dal Model I/O, che è di fatto impiegato in qualsiasi progetto, visto che gestisce tutto il flusso di informazioni in entrata ed in uscita.

In pratica ciò che dovremo fare sarà incastonare in una chain i seguenti elementi:

  • un prompt che riceve l’input utente;
  • un LLM;
  • un parser in uscita.

La cosa sorprendente è la semplicità con cui questi elementi possono comunicare tra loro. Supponendo che rispettivamente si chiamino prompt, llm e parser la nostra chain potrà essere costruita in Python con una concatenazione di tali oggetti, separati dal simbolo pipe (|):

chain = prompt | llm | parser

e per utilizzarla sarà sufficiente la sua invocazione con:

chain.invoke(...)

Andiamo a definire i nostri elementi.
Per prima cosa, abbiamo bisogno di installare le librerie Python necessarie: a noi serviranno langchain e il suo connettore con OpenAI:

pip install langchain-openai langchain

Per il prompt abbiamo formato un oggetto che riceverà un input, in questo caso il nome di un attore e lo invierà all’LLM, chiedendo qual è il film più famoso dell’artista.

prompt = PromptTemplate(
    input_variables=["attore"],
    template="Qual è il film più famoso di {attore}?"
)

Notiamo che finora non vi è traccia indicazioni sul Large Language Model da usare. Ciò perché l’oggetto prompt si occupa solo di ricevere in input un parametro passato come argomento, integrarlo con la vera domanda (che potrebbe includere altri elementi di contesto) e passarlo al blocco successivo in catena, ovvero l’LLM.

Come LLM usiamo GPT 3.5 Turbo di OpenAI. Ci sarebbe a questo punto da aprire una grossa parentesi, discutendo sulle varie modalità di selezione del modello da utilizzare, ripercorrendo tutto ciò che riguarda il confronto tra quelli open source e proprietari, installabili in locale oppure accessibili da remoto. Diciamo che qui abbiamo scelto GPT 3.5 T perché rappresenta uno dei principali e più validi attori della scena e le sue API ben documentate e affidabili.

Soprattutto quando non si conosce ancora bene il framework perché lo si sta studiando, può essere conveniente affidarsi come primo banco di prova a OpenAI proprio perché ci libera da qualsiasi altra difficoltà e limitazione hardware. Infatti, come sappiamo, per utilizzare le API di OpenAI è sufficiente predisporre una chiave, che può essere disponibile gratuitamente con dei limiti di utilizzo oppure a pagamento, e metterla a disposizione del nostro codice.

chatopenai = ChatOpenAI(api_key="sk-XXXYYYZZZ", model_name="gpt-3.5-turbo")

Il risultato dell’intervento di un LLM produrrà un’oggetto AIMessage che sarà passato al parser. In questo caso utilizzeremo la forma di parser più semplice in assoluto, cioè una funzione in grado di ricevere un AIMessage e di utilizzarlo accedendo ai suoi contenuti, rappresentati dalla property content.

Ci limiteremo semplicemente a estrarre il contenuto della risposta di OpenAI e presentarla con la frase “Scopri il suo film più famoso”. Il ruolo di parser sarà semplicemente svolto dalla seguente funzione:

def parse(ai_message: AIMessage) -> str:
    """Parse the AI message."""
    return f"Scopri il suo film più famoso:\n{ai_message.content}"

La catena verrà formata proprio con i simboli pipe come detto:

mychain= prompt | llm | parse

prenderà il nome di mychain e potrà essere invocata nel seguente modo per fornire la prima risposta della nostra applicazione:

print(mychain.invoke("Carlo Verdone"))

Il risultato prodotto in output sarà:

Scopri il suo film più famoso:
Il film più famoso di Carlo Verdone è probabilmente "Bianco, rosso e Verdone" del 1981, 
una commedia che ha ottenuto un enorme successo di pubblico e critica.

Cosa ci rimane da fare?

Prompt avviato, sperimentazione completata, il tutto in modo estremamente semplice grazie al set di funzionalità che LangChain mette a nostra disposizione per preparare applicazioni integrate con un LLM. Come sempre, questo non è che l’inizio. Ci aspetta l’esplorazione di tutte le sue altre componenti per implementare le principali architetture a disposizione, il tutto ancora a vantaggio dei nostri servizi. Approfondiremo il tema nel prossimo articolo.

(Immagine di apertura: Mojahid Mottakin / Shutterstock.com)