Dopo aver accantonato un pò di conoscenze sul Langchain ed aver acquisito un pò di operatività è il momento di conoscere LangGraph, un framework integrato con Langchain di cui avevamo già parlato presentandone lo stack.
Langchain vs LangGraph: perchè servono entrambi
Abbiamo visto che Langchain permette di usare comodamente tutto ciò che ci aiuta a costruire delle applicazioni con AI. Non solo modelli ma anche ogni componente di gestione dell'input e dell'output. Ciò che realizziamo con essa è in fin dei conti un programma, con tante belle funzionalità ma che a livello architetturale sono affidate a normali funzioni e oggetti Python.
LangGraph invece non aggiunge particolarità da un punto di vista dell'Intelligenza Artificiale ma offre importantissimo e fondamentale (come scopriremo) sostegno all'architettura dell'applicazione con A.I. soprattutto mettendo a disposizione:
- struttura a grafo, composta da archi e nodi, con cui definiremo il flusso di esecuzione della nostra applicazione soprattutto andando a strutturare componenti finalizzate a specifici scopi come chatbot, RAG e agenti;
- gestione dello stato ovvero una struttura dati onnipresente nel flusso di esecuzione dell'applicazione in cui inseriremo dati in input, risultati e valori da passare tra un nodo e l'altro;
- supporto alla gestione della memoria e dei dati con una serie di classi e strumenti che permettono la conservazione dei "messaggi" tra nodi diversi sia in memoria sia in maniera persistente.
Primo esempio con LangGraph
Per conoscere LangGraph iniziamo strutturando un esempio con ciò che abbiamo già imparato usando però la sua comoda struttura a grafo. I passaggi base sono questi:
- definiamo lo stato del grafo, generalmente una classe derivante da un dizionario Python in cui identificheremo tutte le variabili che saranno coinvolte nel flusso di lavoro;
- definiamo funzioni che incorporano, ognuna, una componente della nostra applicazione (ad esempio: una riceve input, una chiama l'LLM, una produce output);
- definiamo il grafo (tipicamente un oggetto
StateGraph) a cui collegheremo tutta la struttura; - creiamo nodi dove ognuno è agganciato ad una delle funzioni del punto precedente. Questi saranno gli elementi cardine del grafo
- creiamo archi (detti edge) che tracceranno il percorso tra nodi che al momento sarà lineare ma scopriremo poter essere molto più ramificabile;
- compiliamo il grafo passando dal workflow di nodi e archi appena creato all'applicazione vera e propria.
Possiamo importare il necessario (baseremo l'esempio su OpenAI) così:
pip install --upgrade langchain-openai langchain-core langgraph
e predisporre un semplice grafo lineare in cui il primo nodo raccoglie l'input utente, il secondo lo passa al modello, il terzo lo scrive in output. Notiamo che non c'è passaggio diretto di valori tra nodi ma uno stato che deve essere restituito alla fine di ogni funzione. Con i valori che serviranno ai nodi successivi.
Creeremo uno stato iniziale per impostare il numero di token (che serve al secondo nodo) mentre lo stato finale sarà resituito dal grafo stesso al termine dell'esecuzione.
Supporremo che la chiave OpenAI sia stata passata come variabile d'ambiente tuttavia in questo esempio si può usare il modello che si preferisce.
import os
# supporremo che la chiave sia stata passata come variabile di ambiente
os.environ["OPENAI_API_KEY"] = "LA VOSTRA CHIAVE API"
Il codice del nostro esempio
Vediamo il codice di esempio dove nei commenti citeremo i numeri dell'elenco precedente:
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langgraph.graph import StateGraph
from typing import TypedDict
# punto 1: questa classe rappresenta lo stato del grafo.
# Da qui sapremo quali elementi richiamare in ogni funzione
class AppState(TypedDict):
richiesta_utente: str
risposta_AI: str
limite_token: int
# punto 2: le funzioni che rappresentano i nodi.
# Unico legame con LangGraph lo stato passato in input e restituito in output
def nodo_richiesta(state: AppState) -> AppState:
richiesta = input("Cosa vuoi sapere? ")
return {
'richiesta_utente': richiesta,
'limite_token': state['limite_token']
}
def nodo_AI(state: AppState) -> AppState:
modello_gpt = init_chat_model(
model="gpt-4.1-nano",
model_provider="openai",
max_tokens=state['limite_token']
)
chain = modello_gpt | StrOutputParser()
risposta = chain.invoke(state['richiesta_utente'])
return {
'richiesta_utente': state['richiesta_utente'],
'risposta_AI': risposta
}
def nodo_risposta(state: AppState):
print(state['richiesta_utente'])
print(state['risposta_AI'])
# punto 3: definiamo il grafo
workflow = StateGraph(AppState)
# punto 4: aggiungiamo al grafo i nodi assegnando loro un'etichetta e legandoli ad una delle funzioni precedenti
workflow.add_node("inizio", nodo_richiesta)
workflow.add_node("ragionamento", nodo_AI)
workflow.add_node("risposta", nodo_risposta)
# punto 5: creiamo gli archi facendo attenzione a specificare nodo di inizio (entry) e nodo di fine (finish)
workflow.set_entry_point("inizio")
workflow.add_edge("inizio", "ragionamento")
workflow.add_edge("ragionamento", "risposta")
workflow.set_finish_point("risposta")
# punto 6: compiliamo il grafo
app = workflow.compile()
# esecuzione con passaggio dello stato iniziale e ottenimento di quello finale
stato_iniziale = AppState(limite_token=500)
stato_finale = app.invoke(AppState(stato_iniziale))
Potremo a questo punto provare il funzionamento e alla fine sarà interessante vedere il contenuto di stato_finale con tutte le variabili compilate con il contenuto finale.
Da oggi LangGraph diventerà un nostro nuovo, utilissimo, compagno di viaggio.
Se vuoi aggiornamenti su Il framework LangGraph di Langchain inserisci la tua email nel box qui sotto: