Due modelli mentali, non due liste di funzioni
La differenza si dice in una frase: CrewAI descrive una squadra di ruoli, LangGraph descrive un grafo di stati con nodi e archi. Tutto il resto, dalla curva di apprendimento al consumo di token, discende da qui.
In CrewAI scrivi chi lavora: un agente ha un ruolo, un obiettivo e una storia, un compito ha una descrizione e la forma dell'output attesa. Il framework traduce questi campi in prompt e cicli di ragionamento, e il percorso concreto emerge durante l'esecuzione. In LangGraph scrivi come si muove l'esecuzione: ogni nodo è una funzione Python che riceve lo stato e ne restituisce gli aggiornamenti, ogni arco è una transizione dichiarata, e il modello linguistico interviene solo dove tu lo chiami.
La domanda giusta quindi non è quale dei due sia più capace, perché con entrambi si costruiscono gli stessi sistemi. È chi tiene il volante: l'agente, con la libertà e l'imprevedibilità che ne seguono, o il tuo codice, con la verbosità e il controllo che ne seguono.
Lo stesso compito, scritto nei due
Il modo più onesto di confrontarli è fare la stessa cosa due volte: un passo di ricerca che raccoglie fatti su un tema, un passo di scrittura che li condensa. Prima la versione CrewAI.
from crewai import Agent, Task, Crew, Process
ricercatore = Agent(
role="Ricercatore",
goal="Raccogliere fatti verificabili su {tema}",
backstory="Separa i fatti dalle opinioni.",
llm="gpt-5.4-mini",
)
redattore = Agent(
role="Redattore",
goal="Condensare i fatti in una sintesi utile",
backstory="Scrive breve e taglia gli aggettivi.",
llm="gpt-5.4-mini",
)
ricerca = Task(
description="Raccogli i fatti principali su {tema}.",
expected_output="Elenco puntato di fatti, con i numeri.",
agent=ricercatore,
)
sintesi = Task(
description="Condensa i fatti raccolti in 5 righe.",
expected_output="5 righe di testo, senza premesse.",
agent=redattore,
)
crew = Crew(
agents=[ricercatore, redattore],
tasks=[ricerca, sintesi],
process=Process.sequential,
)
esito = crew.kickoff(inputs={"tema": "tariffe UE 2026"})
print(esito.raw)Ora la stessa pipeline in LangGraph: uno stato tipizzato, due funzioni come nodi, gli archi che li collegano.
from typing_extensions import TypedDict
from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, START, END
llm = init_chat_model("openai:gpt-5.4-mini")
class Stato(TypedDict):
tema: str
fatti: str
testo: str
def ricerca(stato: Stato):
r = llm.invoke(f"Elenca fatti verificabili su {stato['tema']}.")
return {"fatti": r.content}
def scrittura(stato: Stato):
r = llm.invoke(f"Condensa in 5 righe:\n{stato['fatti']}")
return {"testo": r.content}
builder = StateGraph(Stato)
builder.add_node("ricerca", ricerca)
builder.add_node("scrittura", scrittura)
builder.add_edge(START, "ricerca")
builder.add_edge("ricerca", "scrittura")
builder.add_edge("scrittura", END)
grafo = builder.compile()
esito = grafo.invoke({"tema": "tariffe UE 2026"})
print(esito["testo"])Le due versioni fanno la stessa cosa, ma dicono cose diverse. Nel file CrewAI non c'è un percorso: c'è una lista di compiti e un processo che la percorre, e i campi testuali diventano prompt. Quanti giri fa ogni agente per arrivare al risultato lo decide l'agente. Nel file LangGraph non c'è niente di implicito: lo stato è dichiarato campo per campo, ogni chiamata al modello sta scritta dentro un nodo, e il prompt lo componi tu. È più codice, ed è tutto ispezionabile. Su un caso lineare come questo la differenza sembra estetica; cresce appena entrano ramificazioni, cicli e riprese dopo errore.
Dieci criteri a confronto
I criteri sotto sono quelli che pesano davvero quando il progetto passa dal prototipo alla produzione. Il segno di spunta indica chi è più forte su quel terreno, non chi vince il confronto.
| Criterio | CrewAI | LangGraph |
|---|---|---|
| Curva di apprendimento | Ruoli e compiti in linguaggio naturale: il primo crew gira in un pomeriggio e si legge come una descrizione del lavoro. | Chiede di pensare per stati, nodi e archi prima di vedere il primo risultato. L'investimento ripaga sui processi complessi. |
| Controllo del flusso | Nel crew i passaggi li decide l'agente; diventano espliciti solo nei Flow, con @start e @listen. | Ogni transizione è un arco dichiarato: il percorso possibile si legge nel codice, prima di eseguirlo. |
| Gestione dello stato | Contesto passato di task in task nel crew; stato strutturato con Pydantic solo nei Flow. | Lo stato tipizzato è il centro del modello: ogni nodo lo riceve e ne restituisce gli aggiornamenti. |
| Ramificazioni condizionali | Il decoratore @router dei Flow sceglie il ramo in base al valore restituito da un passo. | add_conditional_edges con funzione di routing e mappa dei rami: è il terreno di casa del grafo. |
| Ripresa dopo errore e durabilità | Nei Flow lo stato si salva con @persist e sopravvive ai riavvii; è la parte del framework pensata per riprendere. | Checkpointer a ogni passo (memoria, SQLite, Postgres): con un thread_id si riparte dal punto esatto. |
| Verificabilità e test | L'esito di un crew varia fra esecuzioni: si testano strumenti e confini, non il percorso interno. | I nodi sono funzioni Python: si testano una a una, anche senza chiamare il modello. |
| Uso dei token | Il crew decide da sé quanti giri fare: consumo più alto e variabile. Un Flow riduce la differenza. | I passaggi li fissa il grafo: a parità di compito, consumo più basso e più prevedibile. |
| Osservabilità | Log dettagliati con verbose e conteggio token in token_usage; tracciamento esteso sulla piattaforma commerciale. | Integrazione stretta con LangSmith, la piattaforma di tracciamento dell'ecosistema LangChain. |
| Ecosistema e dipendenze | Autonomo: niente LangChain, modelli instradati via LiteLLM. Meno ampiezza, meno peso. | Eredita l'ampiezza dell'ecosistema LangChain, e insieme il suo peso in dipendenze. |
| Maturità delle API | 1.0 il 20 ottobre 2025; la documentazione a volte resta indietro rispetto al codice. | 1.0 il 22 ottobre 2025; documentazione appena migrata su docs.langchain.com. |
nota
Non contare le spunte
Il punto che quasi nessuno spiega: Crew, Flow o grafo
La maggior parte dei confronti in circolazione mette il crew autonomo contro il grafo esplicito e si ferma lì. È un confronto vero ma parziale, perché CrewAI ha due metà: i Crew, dove gli agenti decidono il percorso, e i Flow, dove il percorso lo scrivi tu con decoratori su metodi Python. E i Flow coprono buona parte del terreno di LangGraph.
| Concetto LangGraph | Equivalente nei Flow di CrewAI |
|---|---|
| Nodi e archi | @start e @listen: un metodo parte, un altro ascolta il suo esito |
Archi condizionali (add_conditional_edges) | @router, che sceglie il ramo in base al valore restituito |
Stato tipizzato (TypedDict) | Stato strutturato con modelli Pydantic |
| Checkpointer | @persist, che salva lo stato fra i riavvii |
La sovrapposizione non è totale. LangGraph resta più basso di livello e più generale: i checkpoint sono per passo su backend a scelta, con ispezione dello stato a ogni snapshot e la possibilità di riportare l'esecuzione a un punto precedente. I Flow sono più compatti e restano dentro un framework che ha anche i crew. Ma la conseguenza pratica è chiara: nel 2026 il confronto vero non è "CrewAI contro LangGraph" ma due confronti distinti, "Crew contro grafo" sul terreno dell'autonomia e "Flow contro grafo" sul terreno del controllo. Se stai valutando LangGraph solo per avere passaggi espliciti, prima leggi la guida ai Flows: forse quello che cerchi è già nella metà deterministica di CrewAI.
Costi e prestazioni: si paga in token
Sul prezzo dei framework non c'è partita perché non c'è prezzo: entrambi sono librerie con licenza MIT, senza limiti d'uso. Il costo vero sono i token del modello, e qui la differenza di modello mentale diventa differenza di bolletta.
Un crew autonomo tende a consumare più di un grafo esplicito, perché i passaggi non sono fissati: l'agente può fare giri di ragionamento in più, ripetere una chiamata a uno strumento, allungare il contesto a ogni scambio. Due esecuzioni identiche possono costare cifre diverse. In un grafo il numero di chiamate lo fissa la struttura, e il costo per esecuzione diventa stimabile in anticipo. CrewAI aiuta a misurare il fenomeno: l'oggetto restituito da kickoff() espone token_usage con il conteggio dell'intera esecuzione, ed è il primo numero da guardare prima di mettere un crew in produzione. I Flow, di nuovo, stanno nel mezzo: passaggi espliciti, consumo paragonabile a un grafo.
Il fattore ecosistema
LangGraph è sviluppato dal team di LangChain e vive dentro quel mondo: la documentazione sta su docs.langchain.com, i modelli si istanziano di solito con init_chat_model delle librerie LangChain, e l'osservabilità naturale è LangSmith. La documentazione precisa che LangGraph si può usare senza LangChain, ed è vero; in pratica ne eredita l'ampiezza, cioè integrazioni per quasi tutto, e il peso, cioè un albero di dipendenze e di concetti che arriva insieme alle integrazioni. LangChain è il progetto più stellato della categoria, 144.400 stelle contro le 39.900 di LangGraph al 17 agosto 2026, e fa da moltiplicatore di tutorial, esempi e risposte già scritte.
CrewAI ha fatto la scelta opposta: nato sopra LangChain, ne ha rimosso la dipendenza ed è oggi autonomo. I modelli passano da LiteLLM, che fa da traduttore verso decine di fornitori. Il repository conta circa 57.200stelle, più di LangGraph preso da solo, meno dell'ecosistema in cui LangGraph è immerso. Se la tua squadra usa già componenti LangChain, LangGraph è in casa; se vuoi un albero di dipendenze corto e un solo vocabolario da imparare, il punto va a CrewAI.
Si possono usare insieme?
Tecnicamente sì, e senza acrobazie: sono due librerie Python nello stesso interprete. Un crew si avvia con una chiamata di metodo, quindi un nodo LangGraph può eseguire crew.kickoff() e scrivere il risultato nello stato del grafo; allo stesso modo un passo di un Flow può invocare un grafo compilato. Nessun ostacolo tecnico.
La risposta onesta però è che di solito non conviene. Orchestratore dentro orchestratore significa due modelli di stato da tenere allineati, due superfici di debug quando qualcosa si rompe, due insiemi di versioni da aggiornare. Il caso in cui ha senso è preciso: un processo governato da un grafo LangGraph che delega un solo sottocompito aperto, per esempio una ricerca multi passaggio, a un piccolo crew, trattandolo come una funzione costosa che restituisce testo. Se invece ti accorgi di volere i crew dentro una struttura esplicita, la strada corta è restare in CrewAI e usare un Flow che chiama i crew nei punti giusti: è lo schema per cui i Flow sono nati.
Il verdetto
Nessuno dei due è la scelta giusta in assoluto, e chi lo sostiene sta vendendo qualcosa. La discriminante è il tipo di processo che devi costruire e quanto controllo pretende.
Scegli
CrewAI
- Il problema si descrive bene come ruoli che collaborano: ricerca, analisi, scrittura, revisione.
- Vuoi un prototipo funzionante in un pomeriggio, per capire se l'idea regge prima di investirci.
- Il team conosce Python ma non vuole ereditare l'ecosistema LangChain e il suo vocabolario.
- Ti serve una via di crescita nello stesso framework: crew autonomi all'inizio, Flow quando arriva il bisogno di controllo.
Scegli
LangGraph
- Il processo è regolamentato o critico: ogni ramo, ogni ripetizione e ogni stato devono essere espliciti e ispezionabili.
- Serve riprendere un'esecuzione interrotta dal punto esatto, con lo stato salvato passo per passo su un database.
- Cicli, ramificazioni e ritorni indietro sono la norma del tuo dominio, non l'eccezione.
- Lavori già con LangChain e LangSmith e vuoi orchestrazione e osservabilità nello stesso ecosistema.