Guida agli Agenti AI

11. Framework: LangChain, AutoGen, CrewAI

I framework forniscono astrazioni di alto livello per costruire agenti senza scrivere il loop a mano. Ti danno: chiamate ai modelli unificate, tool pronti, memoria, multi-agente, observability.

Tradeoff classico: velocità di sviluppo vs. controllo. Bene da sapere quando scegliere quale, e quando non usare un framework.

11.1 Panoramica

Framework Focus Punti di forza Quando usarlo
LangChain Composizione di chain e agent Ecosistema enorme, integrazioni con tutto Pipeline RAG, prototipi rapidi, dev che vogliono tool pronti
LangGraph Workflow as graph Controllo fine, stato esplicito, debug Agenti complessi che richiedono branching, retry, human-in-the-loop
AutoGen Multi-agente conversazionale Pattern "agenti che parlano tra loro" Simulazioni, debate, problemi che beneficiano di più ruoli
CrewAI Multi-agente orientato a "team" Astrazione "ruolo + task + tool" Workflow business-like (ricerca → scrittura → editing)
LiteLLM Adapter unificato per LLM Uniforma l'API tra OpenAI, Anthropic, locale, ecc. Quando vuoi provider-agnostic
Pydantic AI Type-safe agents in Python Rigore typing, validazione strutturata Backend Python che valuta type-safety
Vercel AI SDK Agent in TypeScript per app web Streaming UI, hook React Frontend/full-stack JS

Non sono mutuamente esclusivi. Uno stack tipico potrebbe essere: LiteLLM (provider-agnostic) + LangGraph (orchestrazione) + pgvector (memoria) + Langfuse (observability).

11.2 LangChain: lo stato dell'arte (con riserve)

LangChain è il framework più popolare. Ha avuto un'evoluzione travagliata: la prima versione ("classic chains") è stata superata da una basata su LCEL (LangChain Expression Language) e poi affiancata da LangGraph per gli agent.

Esempio: chain RAG con LangChain

from langchain_anthropic import ChatAnthropic
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

# Vector store già popolato
vstore = Chroma(persist_directory="./db", embedding_function=OpenAIEmbeddings())
retriever = vstore.as_retriever(search_kwargs={"k": 4})

prompt = ChatPromptTemplate.from_template("""
Rispondi usando solo il contesto fornito.

Contesto: {context}

Domanda: {question}
""")

chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | ChatAnthropic(model="claude-opus-4-7")
)

print(chain.invoke("Qual è la policy sui rimborsi viaggi?"))

Pro:
- 5 righe per RAG funzionante.
- Cambi modello cambiando 1 import.
- Ecosistema con centinaia di integrazioni (DB, API, vector store, parsers).

Contro:
- API instabile (breaking changes frequenti).
- "Magia" che nasconde problemi: quando qualcosa non va, debug è un viaggio.
- Astrazioni a volte forzate (LCEL può diventare poco leggibile).

Verdetto: ottimo per prototipi e per usare integrazioni che non vuoi reimplementare. Per agenti di produzione, preferisci LangGraph (sotto).

11.3 LangGraph: workflow as graph

LangGraph è il fratello "grown-up" di LangChain. L'agente si modella come un grafo di nodi dove ogni nodo è una funzione e gli archi sono transizioni condizionate.

from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator

class AgentState(TypedDict):
    messages: Annotated[list, operator.add]
    iterations: int

def call_model(state: AgentState):
    response = llm.invoke(state["messages"])
    return {"messages": [response], "iterations": state["iterations"] + 1}

def call_tools(state: AgentState):
    last = state["messages"][-1]
    results = [execute_tool(call) for call in last.tool_calls]
    return {"messages": results}

def should_continue(state: AgentState):
    if state["iterations"] >= 10:
        return END
    last = state["messages"][-1]
    return "tools" if last.tool_calls else END

graph = StateGraph(AgentState)
graph.add_node("agent", call_model)
graph.add_node("tools", call_tools)
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
graph.add_edge("tools", "agent")

app = graph.compile()
result = app.invoke({"messages": [...], "iterations": 0})

Pro:
- Stato esplicito → debug e replay facili.
- Branching condizionato, cicli, human-in-the-loop nativo.
- Persistenza dello stato tra run (checkpointing).
- Production-ready.

Contro:
- Curva di apprendimento più ripida.
- Overkill per task semplici.

Quando usarlo: agente complesso con più branch, retry, approvazioni umane, replay del workflow.

11.4 AutoGen: agenti che parlano tra loro

AutoGen (Microsoft Research) si focalizza su conversational multi-agent: definisci agenti con ruoli, falli dialogare, lascia emergere la soluzione.

from autogen import AssistantAgent, UserProxyAgent

assistant = AssistantAgent(
    name="coder",
    llm_config={"config_list": [{"model": "gpt-5", "api_key": "..."}]},
    system_message="Sei un programmatore Python esperto."
)

reviewer = AssistantAgent(
    name="reviewer",
    llm_config={...},
    system_message="Sei un code reviewer rigoroso."
)

user = UserProxyAgent(
    name="user",
    human_input_mode="NEVER",
    code_execution_config={"work_dir": "./tmp"}
)

user.initiate_chat(
    assistant,
    message="Scrivi una funzione che calcola Fibonacci con memoizzazione"
)
# poi reviewer commenta, coder rivede, user esegue, ecc.

Pro:
- Pattern naturale per problemi che beneficiano di "più punti di vista".
- Gestione conversazione multi-agent gratis.
- Ottimo per simulazioni e debate.

Contro:
- Agenti che chiacchierano = molti token. Costo alto.
- Convergere a una risposta "buona" richiede tuning fine dei system prompt.
- Spesso un singolo agente con reflection fa lo stesso a 1/3 del costo.

Quando usarlo: simulazioni educational, generazione di dialoghi, problemi di team in cui i ruoli sono davvero distinti.

11.5 CrewAI: team di agenti task-driven

CrewAI usa la metafora del "team aziendale": definisci agenti (ruoli), task (cose da fare), tool (strumenti), e li componi in una crew.

from crewai import Agent, Task, Crew

researcher = Agent(
    role="Senior Researcher",
    goal="Trovare informazioni accurate sul tema X",
    backstory="Sei una ricercatrice meticolosa con anni di esperienza...",
    tools=[web_search_tool, scrape_tool]
)

writer = Agent(
    role="Tech Writer",
    goal="Scrivere articoli chiari basati su ricerca solida",
    backstory="...",
    tools=[]
)

research_task = Task(
    description="Indaga gli sviluppi recenti in {topic}",
    expected_output="Lista puntata con 5-7 fatti chiave e fonti",
    agent=researcher
)

write_task = Task(
    description="Scrivi un articolo divulgativo basato sui risultati",
    expected_output="Articolo di 800 parole, tono chiaro",
    agent=writer,
    context=[research_task]   # passa output di research come context
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    verbose=True
)

result = crew.kickoff(inputs={"topic": "agenti AI nel 2026"})

Pro:
- Astrazione mentale facile per chi non viene da ML.
- Buono per workflow lineari (research → write → review).
- Comunità attiva, molti template.

Contro:
- Magia che a volte produce risultati mediocri.
- Personalità prompt ("backstory") può diventare romanzata e sprecare token.
- Per task brevi, un singolo agent è più efficiente.

Quando usarlo: automazione di workflow business con passi chiari, prototipo di un "team AI" da mostrare a chi non programma.

11.6 LiteLLM: l'adapter universale

Non è un framework di agenti, ma un wrapper unificato per tutti i provider. Stessa API, modelli diversi:

from litellm import completion

# Cambi solo "model"
r1 = completion(model="claude-opus-4-7", messages=[...])
r2 = completion(model="gpt-5", messages=[...])
r3 = completion(model="gemini/gemini-2-pro", messages=[...])
r4 = completion(model="ollama/llama4", messages=[...])  # locale!

Ti dà:
- Provider switching semplice.
- Routing fallback (se Anthropic è down, tenta OpenAI).
- Telemetry e cost tracking.
- Proxy server per centralizzare le chiamate da più servizi.

Quando usarlo: sempre che ti aspetti di provare più provider o vuoi ridurre il vendor lock-in.

11.7 Pydantic AI: type-safe agents

Da Pydantic (libreria di validazione Python più amata). Approccio: definisci I/O con tipi Python, il framework gestisce serializzazione/parsing.

from pydantic_ai import Agent
from pydantic import BaseModel

class WeatherResponse(BaseModel):
    city: str
    temperature_celsius: float
    conditions: str

agent = Agent(
    "claude-opus-4-7",
    result_type=WeatherResponse,
    system_prompt="Sei un agente meteo."
)

result = agent.run_sync("Che tempo fa a Roma?")
print(result.data.city)               # str
print(result.data.temperature_celsius) # float

Pro:
- Validazione automatica dell'output.
- Ottima esperienza developer in Python (autocomplete, mypy).
- Lightweight, zero magia.

Contro:
- Più giovane di LangChain, meno integrazioni.
- Multi-agent meno sviluppato.

Quando usarlo: backend Python dove il rigore typing conta (FastAPI, microservizi).

11.8 Vercel AI SDK: agent in TypeScript

Per app web (Next.js, Svelte, ecc.), il Vercel AI SDK è di fatto lo standard.

import { streamText, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';

const result = await streamText({
  model: anthropic('claude-opus-4-7'),
  tools: {
    weather: tool({
      description: 'Get weather for a city',
      parameters: z.object({ city: z.string() }),
      execute: async ({ city }) => fetchWeather(city),
    }),
  },
  prompt: 'What is the weather in Rome?',
});

Ti dà streaming, tool, structured output, hook React (useChat, useCompletion). Per chat UI in app web, è la via più rapida.

11.9 Quando NON usare un framework

I framework hanno un costo: complessità nascosta + dipendenze + lock-in.

Considera di farne a meno se:

  • Il tuo agente è semplice (1-2 tool, loop diretto). 100 righe di Python con SDK puro sono più chiare di 30 righe di LangChain.
  • Sei in un contesto con vincoli stringenti di dipendenze (embedded, pacchetti minimal).
  • Devi debuggare un comportamento sottile e la magia del framework rende tutto opaco.
  • La tua azienda ha policy di sicurezza che impediscono dipendenze npm/pip non verificate.

Regola pragmatica: inizia con SDK puro. Quando ti accorgi di stare scrivendo per la terza volta lo stesso boilerplate (RAG, retry, multi-agent), è il momento di considerare un framework.

11.10 Confronto rapido: scegliere

Hai un workflow semplice? → SDK puro (Cap. 10).
Vuoi RAG rapido? → LangChain.
Agente complesso, branching, human-in-the-loop? → LangGraph.
Multi-agente conversazionale? → AutoGen.
Workflow business "team-style"? → CrewAI.
Backend type-safe Python? → Pydantic AI.
App web TS/JS? → Vercel AI SDK.
Vuoi cambiare provider? → LiteLLM ovunque.
Observability? → Langfuse o LangSmith (orthogonal a tutti).

11.11 Observability: il pezzo che non puoi saltare

Una volta in produzione, gli agenti si rompono in modi imprevedibili. Senza tracing strutturato sei cieco.

Strumenti che integrano i framework sopra:

  • Langfuse (open-source, self-hostable). Traccia chain, agent, costi, errori.
  • LangSmith (di LangChain Inc, hosted). Equivalente commerciale, integrazione perfetta con LangChain/LangGraph.
  • Helicone (proxy + observability). Si infila tra te e l'API.

Ti permettono di:
- Vedere ogni call con prompt, output, token usati, latency.
- Replay di sessioni problematiche.
- A/B test su prompt.
- Alert su errori o costi anomali.

Spendere 1 ora a setupare observability all'inizio risparmia giorni di debug dopo.

11.12 Pratica: rifare l'agente del Cap. 10 in LangGraph

Esercizio: prendi l'agente di research del Cap. 10 e riscrivilo in LangGraph. Vedrai:
- Quanto codice "loop" sparisce.
- Quanto guadagni in observability (traccia ogni step).
- Quanto perdi in semplicità.

Confronta i due e decidi quale ti convince di più per il tuo uso.

11.13 Da ricordare

  • Inizia con SDK puro. Capisci come funziona davvero.
  • LangChain per prototipi e RAG, LangGraph per agent in produzione.
  • AutoGen / CrewAI per multi-agente, ma valuta se serve davvero.
  • LiteLLM ovunque per provider-switching.
  • Vercel AI SDK per app web TS.
  • Observability (Langfuse o simili) non è opzionale.

11.14 Errori tipici

  • Saltare l'SDK puro. Senza capire i fondamenti, i framework diventano scatole nere magiche.
  • Adottare framework instabili in produzione. LangChain ha breaking changes ogni 6 mesi.
  • Multi-agente perché "fa figo". Un agente con reflection spesso supera un team di agenti chiacchieroni.
  • Niente observability. "Funziona in locale" → in produzione esplode senza che capisci perché.
  • Non bloccare le versioni delle dipendenze. Update automatico = sorprese sgradevoli.

Sai costruire e orchestrare agenti. Adesso parliamo di come lavorarci bene: pratiche, workflow, qualità del lavoro.