LangGraph e' diventato nel 2026 lo standard de facto per costruire agenti IA in Python: lo usano in produzione Klarna, LinkedIn, Uber e Replit, ed e' alla base di molti dei prodotti agentici che vediamo nelle suite enterprise di OpenAI, Anthropic e Google. Rispetto al primo LangChain, LangGraph porta un modello di esecuzione robusto: definisci uno stato tipizzato, nodi che leggono e scrivono quello stato, edge che dirigono il flusso, e ogni transizione viene salvata automaticamente su un checkpoint. Il risultato e' che l'agente sa sempre dov'e' arrivato, puoi metterlo in pausa e ripartire, mostrare i suoi passi all'utente, fare debug come su una macchina a stati.

In questo tutorial costruiamo - da zero - un agente di ricerca su prodotti tecnologici. Riceve una domanda dall'utente, decide se rispondere direttamente o se cercare informazioni sul web, esegue una ricerca con DuckDuckGo, ragiona sui risultati, eventualmente fa una seconda ricerca, e poi sintetizza la risposta. Il tutto con memoria persistente: se riavvii lo script, l'agente ricorda le conversazioni precedenti.

A chi serve, cosa otterrai, prerequisiti

Il tutorial e' pensato per sviluppatori Python che hanno familiarita' con virtualenv e pip, conoscono un LLM (GPT-5, Claude, Gemini, DeepSeek), e vogliono passare da chiamate isolate alle API alle pipeline strutturate. Al termine avrai uno script funzionante che fa girare un agente in locale, configurabile, esportabile e pronto a essere il cuore di un servizio piu' grande.

Prerequisiti reali:

  • Python 3.11 o 3.12 installato. Su macOS: brew install python@3.12. Su Linux: dal package manager. Su Windows: dall'installer ufficiale o da Microsoft Store.
  • Una chiave API per OpenAI o Anthropic. Useremo OpenAI come esempio, ma il codice e' identico in pratica per Anthropic - basta cambiare due righe.
  • Editor di testo, anche solo VS Code.
  • Un terminale: bash, zsh, PowerShell, indifferente.

Quale modello usare

LangGraph e' agnostico rispetto al modello: funziona con tutti quelli che hanno API. Per task agentici nel 2026 le tre prime scelte sono Claude Opus 4.7 (il piu' bravo nei task multi-step ma anche il piu' caro), GPT-5.5 (buon compromesso prezzo-velocita') e DeepSeek V4 (eccellente costo, buono il reasoning, ma piu' lento nei tool calls). Per imparare suggerisco GPT-5.5 mini o Claude Sonnet 4.5: pochi euro di credito permettono centinaia di esecuzioni del tutorial. Per esercizio finale, una volta che il codice funziona, prova a sostituire OpenAI con Anthropic - cambia solo l'import.

Passo 1 - Setup dell'ambiente

Crea una cartella e un virtualenv:

mkdir agente-langgraph && cd agente-langgraph
python3.12 -m venv .venv
source .venv/bin/activate  # su Windows: .venv\Scripts\activate
pip install -U pip
pip install langgraph langchain langchain-openai langchain-community duckduckgo-search python-dotenv

Crea un file .env:

OPENAI_API_KEY=sk-...

Mai committarlo. Se preferisci Anthropic: pip install langchain-anthropic e usa ANTHROPIC_API_KEY.

Passo 2 - Definire lo stato

Lo stato e' il cuore di LangGraph. E' un TypedDict che descrive cosa l'agente conosce a un certo punto dell'esecuzione. Crea agent.py:

from typing import Annotated, Sequence, TypedDict
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage

class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]
    iterations: int

Il campo messages usa l'helper add_messages che gestisce il merge dei messaggi tra le iterazioni. iterations tiene il conto delle volte che l'agente ha ragionato: utile per evitare loop infiniti.

Passo 3 - Definire i tool

I tool sono le funzioni che l'agente puo' chiamare. Aggiungiamo una ricerca web:

from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.tools import tool

search = DuckDuckGoSearchRun()

@tool
def web_search(query: str) -> str:
    """Cerca su DuckDuckGo. Usa parole chiave brevi, non frasi lunghe."""
    return search.invoke(query)

tools = [web_search]

La descrizione nei docstring conta: l'LLM la legge per capire quando chiamare il tool e come.

Passo 4 - Definire i nodi

Un nodo e' una funzione che prende lo stato e ne restituisce un aggiornamento parziale. Ne servono due: il "ragionatore" (l'LLM che decide cosa fare) e l'"esecutore tool".

from langchain_openai import ChatOpenAI
from langgraph.prebuilt import ToolNode
from dotenv import load_dotenv

load_dotenv()

model = ChatOpenAI(model="gpt-5.5", temperature=0).bind_tools(tools)

def reasoner(state: AgentState):
    response = model.invoke(state["messages"])
    return {"messages": [response], "iterations": state.get("iterations", 0) + 1}

tool_node = ToolNode(tools)

Il ToolNode e' un nodo pre-costruito che esegue tool chiamati dal modello e ne restituisce gli output come ToolMessage.

Passo 5 - Costruire il grafo

Adesso il pezzo importante: collegare i nodi. Aggiungiamo un edge condizionale per decidere se chiamare un tool o terminare.

from langgraph.graph import StateGraph, END, START

def should_continue(state: AgentState):
    last_msg = state["messages"][-1]
    if state.get("iterations", 0) > 5:
        return END  # safety: dopo 5 giri, basta
    if last_msg.tool_calls:
        return "tools"
    return END

workflow = StateGraph(AgentState)
workflow.add_node("reasoner", reasoner)
workflow.add_node("tools", tool_node)
workflow.add_edge(START, "reasoner")
workflow.add_conditional_edges("reasoner", should_continue)
workflow.add_edge("tools", "reasoner")

Passo 6 - Aggiungere memoria persistente

LangGraph offre dei "checkpoint saver". Usiamo SQLite per salvare lo stato fra esecuzioni:

from langgraph.checkpoint.sqlite import SqliteSaver

checkpointer = SqliteSaver.from_conn_string("agent_memory.db")
app = workflow.compile(checkpointer=checkpointer)

Il database agent_memory.db verra' creato al primo run. Ogni conversazione e' identificata da un thread_id: usando lo stesso thread, l'agente "ricorda" tutto.

Passo 7 - Eseguire l'agente

Aggiungiamo l'eseguibile in fondo a agent.py:

from langchain_core.messages import HumanMessage

if __name__ == "__main__":
    config = {"configurable": {"thread_id": "sessione-test-1"}}
    while True:
        q = input("\n> ")
        if q.lower() in ("exit", "quit", ""):
            break
        result = app.invoke(
            {"messages": [HumanMessage(content=q)]},
            config=config,
        )
        print("\nAgente:", result["messages"][-1].content)

Lancia:

python agent.py

Prova un prompt:

Qual e' la versione piu' recente di Python e quali sono le novita' chiave introdotte negli ultimi sei mesi?

L'agente capira' che la sua conoscenza non e' aggiornata, chiamera' web_search("Python latest version 2026 release notes"), leggera' i risultati, magari fara' una seconda ricerca per dettagli, e poi sintetizzera'. Tu vedrai solo l'output finale; per debuggare i passi intermedi attiva il tracing.

LangGraph trasforma la chiamata LLM in un grafo a stati: piu' robusto, piu' osservabile. Foto: Christina Morillo / Pexels.

Passo 8 - Streaming e debugging

Per vedere i passi in tempo reale - utili per debug - sostituisci app.invoke con app.stream:

for chunk in app.stream({"messages": [HumanMessage(content=q)]}, config=config):
    for node, value in chunk.items():
        print(f"\n[{node}]", value.get("messages", [""])[-1].content[:300])

Per il tracing piu' avanzato, registrati gratis su LangSmith e aggiungi LANGSMITH_API_KEY al .env: ogni run sara' visualizzata in dashboard con tempi, costi e payload.

Varianti e casi avanzati

Una volta che il base agent gira, ecco alcune evoluzioni utili:

  • Multi-agente: due nodi LLM con prompt diversi (es. "ricercatore" e "verificatore") che si passano lo stato.
  • Strumenti reali: sostituisci DuckDuckGo con Tavily (piu' affidabile) o aggiungi una tool su un database aziendale (con SQL parametrizzato).
  • Human-in-the-loop: usa workflow.interrupt_before=["tools"] per chiedere conferma all'utente prima di eseguire un tool sensibile (es. inviare una mail, fare un acquisto).
  • Postgres invece di SQLite: per piu' utenti concorrenti, usa PostgresSaver della libreria langgraph-checkpoint-postgres.
  • Sostituire OpenAI con Anthropic: from langchain_anthropic import ChatAnthropic e model = ChatAnthropic(model="claude-opus-4-7").bind_tools(tools).

Errori comuni

  • "Tool not found": il modello cita un tool che non esiste. Spesso e' perche' la docstring non e' chiara. Migliora la descrizione e l'esempio nel docstring del tool.
  • Loop infinito di tool calls: l'agente continua a chiamare lo stesso tool. Il limite iterations > 5 nello should_continue impedisce il disastro. Per evitarlo, dai prompt al modello con istruzioni chiare su quando smettere.
  • Output del tool troppo grande: se DuckDuckGo restituisce 10 KB di testo, il contesto LLM si satura. Trunc-a il risultato a 2-3 KB nel tool.
  • Costi che esplodono: ogni iterazione spedisce tutta la storia al modello. Per agenti lunghi conviene fare prompt-caching (su Claude e' nativo) o riassumere i messaggi vecchi con un "reducer" node.

Alternative e quando NON usare LangGraph

LangGraph e' giusto quando il flusso e' complesso, ramificato, con stato. Se devi fare una sola chiamata a un modello, non serve. Per pipeline lineari basta langchain base. Per agenti molto verticali esistono framework piu' opinati (CrewAI per multi-agente, AutoGen di Microsoft, OpenAI Agents SDK). LangGraph rimane la scelta piu' flessibile e produzione-ready nel 2026.

Come proseguire

Adesso che il primo agente funziona, prova a renderlo utile: integralo dietro a un endpoint Flask o FastAPI per esporlo come API, collegalo a Slack o Telegram per parlarci da chat, deploya il tutto su Render o Railway. La documentazione ufficiale di LangGraph ha esempi completi per ogni pattern. Una settimana di esperimenti e' il tempo medio per arrivare a un agente "abbastanza buono" da risparmiarti due ore al giorno di lavoro ripetitivo. Inizia con un caso d'uso piccolo (es. uno script che ti riassume la posta del giorno) e cresci da li'.