04 - Multi-Agent Coding: LangGraph, CrewAI e AutoGen
Immagina di affrontare un task complesso: implementare da zero un sistema di autenticazione per un'applicazione enterprise, completo di OAuth2, RBAC, audit log, test di integrazione e documentazione. Un singolo agente AI, per quanto capace, si troverebbe a gestire un context window sovraffollato, responsabilità conflittuali e un rischio elevato di errori a cascata. La soluzione non e un agente più potente: e una squadra di agenti specializzati che collaborano, si verificano a vicenda e parallelizzano il lavoro.
Questo e il paradigma del Multi-Agent Coding: sistemi in cui più agenti AI autonomi cooperano per completare task di sviluppo software che superano la capacità di un singolo modello. Nel 2025, tre framework hanno dominato questo spazio: LangGraph con la sua architettura a grafo stateful, CrewAI con il suo modello role-based intuitivo, e AutoGen (ora AG2) con il suo approccio conversazionale. A questi si aggiunge il sistema nativo di Claude Code con i suoi sub-agenti paralleli.
Questo articolo e un deep dive avanzato su tutti e quattro i sistemi: architetture, code examples funzionanti, confronto dettagliato e guida pratica per scegliere quello giusto per il tuo caso d'uso. Se hai già familiarita con i concetti base del vibe coding e vuoi portare i tuoi workflow agentici al livello successivo, sei nel posto giusto.
Cosa Imparerai
- perchè un singolo agente AI non basta per task di sviluppo complessi
- LangGraph: architettura a grafo, StateGraph, Nodes, Edges e Checkpointing
- CrewAI: role-based agents, Tasks, Process sequenziale e parallelo, Tools
- AutoGen/AG2: conversational agents, GroupChat, code execution sandbox
- Claude Code: sub-agents, Task tool e pattern di esecuzione parallela
- Confronto dettagliato: quando usare quale framework
- Architettura production-ready per team reali
- Sfide critiche: context pollution, error propagation, cost management
- Best practices per agenti specializzati e strategie di fallback
perchè un Singolo Agente Non Basta
Prima di immergersi nei framework, e importante capire il problema fondamentale che il multi-agent coding risolve. Un singolo agente AI, per quanto avanzato, ha limitazioni strutturali che emergono chiaramente nei task di sviluppo complessi.
Il primo limite e il context window. Anche con modelli che supportano 200K token, un task enterprise tipico richiede di tenere contemporaneamente in mente: il codebase esistente, le specifiche funzionali, i pattern architetturali, i test da scrivere, la documentazione da aggiornare e i vincoli di sicurezza. Tutto questo supera rapidamente le capacità di un singolo contesto coerente.
Il secondo limite e la specializzazione. Un agente generalista tende a fare tutto in modo mediocre piuttosto che alcune cose in modo eccellente. Un agente specializzato in security sa esattamente quali pattern cercare e quali standard applicare, mentre uno specializzato in testing conosce i pattern di test per ogni tipo di componente.
Il terzo limite e la verifica incrociata. Un singolo agente che scrive codice e poi lo "testa" sta fondamentalmente controllando il proprio lavoro con lo stesso bias cognitivo con cui l'ha prodotto. Due agenti separati, uno che implementa e uno che rivede, portano prospettive genuinamente diverse.
Il Principio della Separazione dei Ruoli
Nei team di sviluppo umani, la separazione dei ruoli (developer, code reviewer, QA, security engineer, tech writer) non e burocrazia: e una salvaguardia cognitiva. I multi-agent systems applicano lo stesso principio al codice generato da AI, riducendo sistematicamente i punti ciechi di ogni singolo agente.
Le ricerche del 2025 confermano questa intuizione: il codice AI-generated ha tassi di vulnerabilità significativamente più alti quando prodotto da agenti singoli non supervisionati (Veracode 2025 riporta 2.74x più vulnerabilità rispetto al codice umano). I sistemi multi-agent con agenti di review dedicati riducono questo gap in modo sostanziale.
LangGraph: Orchestrazione a Grafo Stateful
LangGraph, sviluppato dal team di LangChain, rappresenta l'evoluzione naturale dell'approccio a catene lineari verso grafi stateful ciclici. L'intuizione fondamentale e che i workflow agentici complessi non sono lineari: richiedono loop, biforcazioni condizionali, parallelismo e persistenza dello stato tra un passaggio e l'altro.
Il team stesso di LangChain ha comunicato esplicitamente nel 2025: "Use LangGraph for agents, not LangChain." Questa scelta riflette una verita architetturale importante: i sistemi agentici moderni sono fondamentalmente state machines, non pipeline sequenziali.
Concetti Fondamentali di LangGraph
LangGraph si basa su quattro concetti chiave che e necessario padroneggiare prima di costruire sistemi multi-agent efficaci:
- StateGraph: il grafo principale che definisce la struttura del workflow e il tipo di stato condiviso tra i nodi
- State: un TypedDict (o Pydantic model) che rappresenta l'informazione condivisa tra tutti i nodi del grafo
- Nodes: funzioni Python che ricevono lo stato, eseguono un'operazione (spesso una chiamata a un LLM) e restituiscono aggiornamenti allo stato
- Edges: connessioni tra nodi, che possono essere fisse o condizionali basate sullo stato corrente
from typing import TypedDict, Annotated, List
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, AIMessage
import operator
# ============================================================
# 1. DEFINIZIONE DELLO STATO CONDIVISO
# ============================================================
class CodingState(TypedDict):
"""Stato condiviso tra tutti gli agenti del sistema."""
task_description: str
requirements: List[str]
generated_code: str
test_code: str
review_comments: List[str]
security_issues: List[str]
final_code: str
iteration_count: int
status: str # "planning", "coding", "testing", "reviewing", "done"
# ============================================================
# 2. INIZIALIZZAZIONE DEI MODELLI
# ============================================================
# Planner: usa Opus per ragionamento complesso
planner_model = ChatAnthropic(model="claude-opus-4-6")
# Developer: usa Sonnet per code generation veloce
developer_model = ChatAnthropic(model="claude-sonnet-4-6")
# Reviewer: usa Sonnet per analisi critica
reviewer_model = ChatAnthropic(model="claude-sonnet-4-6")
# ============================================================
# 3. DEFINIZIONE DEI NODI (AGENTI)
# ============================================================
def planner_agent(state: CodingState) -> dict:
"""Agente pianificatore: decompone il task in requirements."""
prompt = f"""Sei un software architect senior.
Task: {state['task_description']}
Analizza il task e produci una lista di requisiti tecnici specifici.
Formato: una lista puntata di requisiti chiari e implementabili.
"""
response = planner_model.invoke([HumanMessage(content=prompt)])
requirements = [
line.strip().lstrip("- ")
for line in response.content.split("\n")
if line.strip().startswith("-")
]
return {
"requirements": requirements,
"status": "coding"
}
def developer_agent(state: CodingState) -> dict:
"""Agente sviluppatore: implementa il codice dai requirements."""
requirements_text = "\n".join(f"- {r}" for r in state["requirements"])
prompt = f"""Sei un senior Python developer.
Implementa il seguente codice rispettando tutti i requisiti:
Task originale: {state['task_description']}
Requisiti:
{requirements_text}
Codice esistente (se presente):
{state.get('generated_code', 'Nessun codice precedente')}
Note del reviewer (se presenti):
{chr(10).join(state.get('review_comments', []))}
Produci SOLO il codice Python, senza spiegazioni.
"""
response = developer_model.invoke([HumanMessage(content=prompt)])
return {
"generated_code": response.content,
"status": "testing"
}
def test_writer_agent(state: CodingState) -> dict:
"""Agente test writer: scrive unit test per il codice generato."""
prompt = f"""Sei un QA engineer specializzato in Python testing.
Scrivi unit test completi per il seguente codice usando pytest:
{state['generated_code']}
Requisiti dei test:
- Copertura minima: 80%
- Testa happy path e edge cases
- Include test per casi di errore
- Usa fixtures dove appropriato
Produci SOLO il codice dei test, senza spiegazioni.
"""
response = developer_model.invoke([HumanMessage(content=prompt)])
return {
"test_code": response.content,
"status": "reviewing"
}
def code_reviewer_agent(state: CodingState) -> dict:
"""Agente code reviewer: analizza codice e test per qualità."""
prompt = f"""Sei un senior code reviewer con 10+ anni di esperienza.
Rivedi il seguente codice e i suoi test:
CODICE:
{state['generated_code']}
TEST:
{state['test_code']}
Analizza per:
1. Correttezza logica
2. qualità del codice (SOLID, DRY, KISS)
3. Completezza dei test
4. Performance
5. Leggibilita e manutenibilità
Se ci sono problemi critici, rispondimi con "NEEDS_REVISION:" seguito dai problemi.
Se il codice e accettabile, rispondimi con "APPROVED:" seguito da eventuali suggerimenti minor.
"""
response = reviewer_model.invoke([HumanMessage(content=prompt)])
comments = [response.content]
needs_revision = response.content.startswith("NEEDS_REVISION:")
return {
"review_comments": comments,
"iteration_count": state.get("iteration_count", 0) + 1,
"status": "coding" if needs_revision else "security_check"
}
def security_agent(state: CodingState) -> dict:
"""Agente sicurezza: verifica vulnerabilità nel codice."""
prompt = f"""Sei un security engineer specializzato in application security.
Analizza il seguente codice per vulnerabilità di sicurezza:
{state['generated_code']}
Cerca specificamente:
- Injection vulnerabilities (SQL, Command, Path traversal)
- Insecure deserialization
- Hardcoded secrets o credenziali
- Insecure random number generation
- Race conditions
- Input validation gaps
Lista SOLO i problemi trovati. Se nessun problema, scrivi "SECURITY_OK".
"""
response = reviewer_model.invoke([HumanMessage(content=prompt)])
issues = []
if response.content.strip() != "SECURITY_OK":
issues = [response.content]
return {
"security_issues": issues,
"final_code": state['generated_code'] if not issues else "",
"status": "done" if not issues else "coding"
}
# ============================================================
# 4. ROUTING CONDIZIONALE
# ============================================================
def route_after_review(state: CodingState) -> str:
"""Decide il prossimo nodo dopo la code review."""
if state["status"] == "coding" and state.get("iteration_count", 0) < 3:
return "developer"
elif state["status"] == "security_check":
return "security"
else:
# Max iterazioni raggiunte
return "security"
def route_after_security(state: CodingState) -> str:
"""Decide se il codice e pronto o necessità un'altra iterazione."""
if state["security_issues"] and state.get("iteration_count", 0) < 3:
return "developer"
return END
# ============================================================
# 5. COSTRUZIONE DEL GRAFO
# ============================================================
def build_coding_graph() -> StateGraph:
graph = StateGraph(CodingState)
# Aggiunta nodi
graph.add_node("planner", planner_agent)
graph.add_node("developer", developer_agent)
graph.add_node("test_writer", test_writer_agent)
graph.add_node("reviewer", code_reviewer_agent)
graph.add_node("security", security_agent)
# Entry point
graph.set_entry_point("planner")
# Edges fissi
graph.add_edge("planner", "developer")
graph.add_edge("developer", "test_writer")
graph.add_edge("test_writer", "reviewer")
# Edges condizionali
graph.add_conditional_edges(
"reviewer",
route_after_review,
{
"developer": "developer",
"security": "security"
}
)
graph.add_conditional_edges(
"security",
route_after_security,
{
"developer": "developer",
END: END
}
)
return graph
# ============================================================
# 6. ESECUZIONE CON CHECKPOINTING
# ============================================================
checkpointer = MemorySaver()
app = build_coding_graph().compile(checkpointer=checkpointer)
# Esecuzione del sistema
config = {"configurable": {"thread_id": "project-auth-001"}}
initial_state = {
"task_description": """
Implementa un sistema di autenticazione JWT in Python con:
- Login con username/password
- Generazione access token (15 min) e refresh token (7 giorni)
- Middleware per proteggere endpoint
- Revoca token (blacklist in Redis)
""",
"requirements": [],
"generated_code": "",
"test_code": "",
"review_comments": [],
"security_issues": [],
"final_code": "",
"iteration_count": 0,
"status": "planning"
}
result = app.invoke(initial_state, config=config)
print(f"Codice finale generato dopo {result['iteration_count']} iterazioni")
print(f"Problemi di sicurezza rilevati: {len(result['security_issues'])}")
Questo esempio mostra come LangGraph gestisce un workflow di sviluppo complesso con loop di revisione automatici. Il checkpointing e una feature critica per la produzione: permette di salvare lo stato del grafo e riprendere l'esecuzione in caso di errori, senza ripartire da zero.
LangGraph: Punti di Forza
- Controllo granulare: ogni aspetto del flusso e programmabile
- State persistence: checkpointing integrato per workflow lunghi
- Human-in-the-loop: possibilità di pausa per approvazione umana
- Parallel execution: nodi possono eseguire in parallelo
- Streaming: output in tempo reale da ogni nodo
- Produzione testata: usato in produzione da centinaia di aziende nel 2025
CrewAI: Role-Based Agents per Team Virtuali
CrewAI adotta un approccio fondamentalmente diverso da LangGraph: invece di pensare in termini di grafi e stati, ti chiede di pensare in termini di team e ruoli. Ogni agente e un membro della squadra con una specializzazione definita, un obiettivo chiaro e un set di strumenti a disposizione. Questo modello mentale e più intuitivo per chi ha esperienza nella gestione di team di sviluppo.
Nella versione 1.1.0 del 2025, CrewAI ha introdotto la separazione tra Crews (orchestrazione ad alto livello) e Flows (controllo granulare dei workflow), offrendo la flessibilità di LangGraph con la semplicità del modello role-based.
Architettura CrewAI
I quattro elementi fondamentali di CrewAI sono:
- Agent: un'entità AI con role, goal e backstory che definiscono la sua "personalità" e area di competenza
- Task: un'attivita specifica con description, expected_output e assegnata a un agente specifico
- Tool: capacità aggiuntive degli agenti (file I/O, web search, code execution, database access)
- Crew: il team che orchestra agenti e task con un process (sequenziale o parallelo)
from crewai import Agent, Task, Crew, Process
from crewai.tools import CodeInterpreterTool, FileReadTool, FileWriteTool
from crewai_tools import GithubSearchTool
from langchain_anthropic import ChatAnthropic
# ============================================================
# 1. CONFIGURAZIONE MODELLI
# ============================================================
opus_llm = ChatAnthropic(model="claude-opus-4-6", temperature=0.1)
sonnet_llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0.2)
# ============================================================
# 2. DEFINIZIONE DEGLI STRUMENTI
# ============================================================
code_executor = CodeInterpreterTool()
file_reader = FileReadTool()
file_writer = FileWriteTool()
# ============================================================
# 3. DEFINIZIONE DEGLI AGENTI (MEMBRI DEL TEAM)
# ============================================================
tech_lead = Agent(
role="Tech Lead e Software Architect",
goal="""Analizzare i requisiti tecnici, definire l'architettura ottimale
e decomporre il lavoro in task specifici e implementabili.""",
backstory="""Hai 15 anni di esperienza in software architecture.
Hai guidato la transizione a microservizi per 3 startup unicorn.
Sei noto per la capacità di bilanciare pragmatismo e qualità tecnica.""",
llm=opus_llm,
verbose=True,
allow_delegation=True
)
senior_developer = Agent(
role="Senior Python Developer",
goal="""Implementare codice Python di alta qualità, pulito, testabile
e conforme alle best practices SOLID e alle specifiche del tech lead.""",
backstory="""Sei un Python developer con 8 anni di esperienza.
Contribuisci a progetti open source importanti.
Scrivi codice che altri developer amano leggere e mantenere.""",
llm=sonnet_llm,
verbose=True,
tools=[code_executor, file_writer],
allow_code_execution=True
)
qa_engineer = Agent(
role="QA Engineer e Test Specialist",
goal="""Verificare la correttezza del codice attraverso test exhaustivi,
trovare edge cases e garantire la copertura dei test al 90%+.""",
backstory="""Hai un background in matematica e sei ossessionato dalla
correttezza del software. Hai trovato bug critici in sistemi che erano
in produzione da anni. Non ti fidi del codice senza prove.""",
llm=sonnet_llm,
verbose=True,
tools=[code_executor],
allow_code_execution=True
)
code_reviewer = Agent(
role="Senior Code Reviewer",
goal="""Analizzare criticamente il codice per qualità, manutenibilità,
performance e conformità agli standard del team.""",
backstory="""Hai revisionato oltre 10.000 pull request nella tua carriera.
Hai un occhio infallibile per code smells, anti-pattern e problemi
di design che si manifestano solo in produzione.""",
llm=sonnet_llm,
verbose=True,
tools=[file_reader]
)
# ============================================================
# 4. DEFINIZIONE DEI TASK
# ============================================================
architecture_task = Task(
description="""
Analizza il seguente requisito e produci un documento di architettura:
REQUISITO: {task_description}
Il documento deve includere:
1. Diagramma dei componenti (in testo/ASCII)
2. Stack tecnologico raccomandato con giustificazioni
3. Struttura delle directory del progetto
4. Interfacce principali (classi/funzioni pubbliche)
5. Considerazioni su scalabilità e manutenibilità
6. Lista prioritizzata di task di implementazione
""",
expected_output="""Un documento di architettura dettagliato in markdown,
con diagrammi ASCII, struttura del progetto e lista di task.""",
agent=tech_lead
)
implementation_task = Task(
description="""
Implementa il codice Python basandoti sul documento di architettura
fornito dal Tech Lead.
Requisiti di implementazione:
- Segui esattamente le interfacce definite nell'architettura
- Usa type hints per tutte le funzioni pubbliche
- Aggiungi docstring in formato Google style
- Gestisci tutti i casi di errore con eccezioni custom
- Salva il codice in file separati per modulo
""",
expected_output="""Codice Python completo e funzionante, organizzato in moduli,
con type hints, docstring e gestione degli errori.""",
agent=senior_developer,
context=[architecture_task] # dipende dall'output dell'architettura
)
testing_task = Task(
description="""
Scrivi test completi per il codice implementato.
Requisiti:
- Framework: pytest con pytest-cov
- Copertura target: 90%
- Include unit test, integration test e test parametrici
- Testa happy path, edge cases e scenari di errore
- Usa mocking per dipendenze esterne
- Esegui i test per verificare che passino tutti
""",
expected_output="""File di test pytest completi che passano tutti,
con report di copertura al 90%+.""",
agent=qa_engineer,
context=[implementation_task]
)
review_task = Task(
description="""
Conduci una code review approfondita del codice e dei test prodotti.
Analizza per:
1. Rispetto dei principi SOLID
2. Presenza di code smells o anti-pattern
3. Correttezza logica e completezza
4. Performance e uso della memoria
5. Sicurezza (injection, validazione input, secrets)
6. qualità della documentazione
Produci un report con: problemi CRITICI, WARNINGS e SUGGESTIONS.
Per ogni problema, includi il file/riga e la fix raccomandata.
""",
expected_output="""Report di code review strutturato con categorie CRITICAL,
WARNING e SUGGESTION, con fix raccomandate per ogni problema.""",
agent=code_reviewer,
context=[implementation_task, testing_task]
)
# ============================================================
# 5. CREW - ASSEMBLAGGIO DEL TEAM
# ============================================================
coding_crew = Crew(
agents=[tech_lead, senior_developer, qa_engineer, code_reviewer],
tasks=[architecture_task, implementation_task, testing_task, review_task],
process=Process.sequential, # o Process.hierarchical per delega automatica
verbose=True,
memory=True, # abilita memoria condivisa tra agenti
embedder={
"provider": "anthropic",
"config": {"model": "claude-3-haiku-20240307"}
}
)
# ============================================================
# 6. AVVIO DEL CREW
# ============================================================
result = coding_crew.kickoff(inputs={
"task_description": """
Implementa un sistema di rate limiting per API REST con:
- Algoritmo token bucket per controllo del flusso
- Storage su Redis per distribuzione multi-instance
- Configurazione per-endpoint e per-user
- Header HTTP standard (X-RateLimit-*)
- Dashboard di monitoring con metriche Prometheus
"""
})
print(result.raw)
Un elemento distintivo di CrewAI 2025 e l'opzione allow_code_execution=True
sugli agenti: abilita l'esecuzione del codice in una sandbox sicura, con gestione automatica
degli errori e retry intelligente. Se il codice genera un'eccezione, l'agente riceve il
messaggio di errore e tenta di correggerlo autonomamente (fino a max_retry_limit,
default 2).
CrewAI: Punti di Forza
- Curva di apprendimento bassa: il modello mentale team/ruoli e intuitivo
- Code execution integrata: sandbox sicura con retry automatico
- Memory condivisa: gli agenti ricordano il contesto di conversazioni precedenti
- MCP support: integrazione bidirezionale con MCP servers (2025)
- Enterprise features: osservabilità, audit log, control plane SaaS
- Flows: controllo granulare per workflow complessi quando necessario
AutoGen/AG2: Conversational Agents per Task Emergenti
AutoGen (rinominato AG2 dalla comunity che ha continuato lo sviluppo dopo la divergenza da Microsoft nel 2024) adotta un paradigma completamente diverso: il conversational multi-agent. Invece di definire workflow fissi o ruoli rigidi, gli agenti comunicano tra loro attraverso messaggi in linguaggio naturale, emergendo comportamenti complessi da interazioni semplici.
Questo approccio e particolarmente efficace per task dove il percorso di soluzione non e definibile a priori: problemi di debugging complessi, ricerca e implementazione di soluzioni innovative, o task che richiedono negoziazione tra vincoli contrastanti.
Architettura AG2: AssistantAgent e UserProxyAgent
Il building block fondamentale di AG2 e la coppia AssistantAgent (agente AI) e UserProxyAgent (proxy per l'esecuzione). Il UserProxyAgent può eseguire codice in una sandbox locale, fornendo feedback reale all'AssistantAgent che può quindi iterare sulla soluzione basandosi sui risultati effettivi dell'esecuzione.
import autogen
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager
from autogen.coding import LocalCommandLineCodeExecutor
import os
# ============================================================
# 1. CONFIGURAZIONE LLM
# ============================================================
config_list = [
{
"model": "claude-sonnet-4-6",
"api_key": os.environ["ANTHROPIC_API_KEY"],
"api_type": "anthropic",
}
]
llm_config = {
"config_list": config_list,
"temperature": 0.1,
"timeout": 120,
}
opus_config = {
"config_list": [{
"model": "claude-opus-4-6",
"api_key": os.environ["ANTHROPIC_API_KEY"],
"api_type": "anthropic",
}],
"temperature": 0.1,
}
# ============================================================
# 2. CODE EXECUTOR - SANDBOX LOCALE
# ============================================================
executor = LocalCommandLineCodeExecutor(
timeout=60,
work_dir="/tmp/autogen_workspace",
# Blocca comandi pericolosi
execution_policies={
"rm": False,
"curl": False,
"wget": False,
}
)
# ============================================================
# 3. DEFINIZIONE DEGLI AGENTI
# ============================================================
# Agente Software Architect - usa Opus per alta capacità di ragionamento
architect = AssistantAgent(
name="SoftwareArchitect",
system_message="""Sei un software architect senior con 15 anni di esperienza.
Il tuo ruolo e:
1. Analizzare i requisiti e proporre architetture solide
2. Definire le interfacce tra componenti
3. Identificare rischi tecnici e proporre mitigazioni
4. Guidare le decisioni tecniche del team
Quando proponi un'architettura, usa sempre diagrammi ASCII e spiega le
motivazioni delle scelte. Rispondi con "TERMINATE" quando il task e completo.""",
llm_config=opus_config,
)
# Agente Developer - implementa il codice
developer = AssistantAgent(
name="PythonDeveloper",
system_message="""Sei un senior Python developer.
Il tuo ruolo e:
1. Implementare il codice seguendo le specifiche dell'architetto
2. Scrivere codice pulito con type hints e docstring
3. Gestire gli errori in modo robusto
4. Rispettare i principi SOLID e DRY
Usa sempre blocchi di codice Python validi quando scrivi implementazioni.
Rispondi con "TERMINATE" solo quando il developer AND il reviewer approvano.""",
llm_config=llm_config,
)
# Agente Tester - scrive e verifica i test
tester = AssistantAgent(
name="QATester",
system_message="""Sei un QA engineer specializzato in Python testing.
Il tuo ruolo e:
1. Scrivere test pytest esaustivi per il codice del developer
2. Eseguire i test nella sandbox e riportare i risultati
3. Identificare edge cases non coperti
4. Garantire copertura 85%+
Produci sempre test eseguibili che posso verificare nella sandbox.""",
llm_config=llm_config,
)
# Agente Reviewer - code review critica
reviewer = AssistantAgent(
name="CodeReviewer",
system_message="""Sei un code reviewer senior con focus su qualità e sicurezza.
Il tuo ruolo e:
1. Analizzare il codice per code smells e anti-pattern
2. Verificare la sicurezza (OWASP Top 10)
3. Suggerire refactoring migliorativi
4. Approvare o richiedere modifiche
Concludi ogni review con "APPROVED" o "NEEDS_WORK: [lista problemi]".""",
llm_config=llm_config,
)
# UserProxy con esecuzione codice - fa da ponte tra agenti e sandbox
code_executor_proxy = UserProxyAgent(
name="CodeExecutor",
human_input_mode="NEVER", # completamente automatico
max_consecutive_auto_reply=5,
code_execution_config={
"executor": executor,
},
is_termination_msg=lambda msg: "TERMINATE" in msg.get("content", ""),
)
# ============================================================
# 4. GROUP CHAT - ORCHESTRAZIONE DELLA CONVERSAZIONE
# ============================================================
group_chat = GroupChat(
agents=[architect, developer, tester, reviewer, code_executor_proxy],
messages=[],
max_round=20, # massimo 20 round di conversazione
# Strategia di selezione del prossimo parlante
speaker_selection_method="auto",
# Ordine preferito per strutturare la conversazione
allowed_or_disallowed_speaker_transitions={
architect: [developer],
developer: [tester, code_executor_proxy],
tester: [code_executor_proxy, reviewer],
code_executor_proxy: [developer, tester, reviewer],
reviewer: [developer, architect],
},
speaker_transitions_type="allowed",
)
# Manager che orchestra la group chat
manager = GroupChatManager(
groupchat=group_chat,
llm_config=llm_config,
system_message="""Sei il project manager di questo team di sviluppo.
Orchestri la conversazione per garantire che il task venga completato
in modo efficiente e che ogni agente contribuisca nel momento opportuno.""",
)
# ============================================================
# 5. AVVIO DELLA CONVERSAZIONE
# ============================================================
task = """
Sviluppa un sistema di caching intelligente in Python con le seguenti caratteristiche:
- Cache LRU (Least Recently Used) con TTL configurabile per entry
- Backend multipli: in-memory, Redis, o filesystem
- Decorator @cache per applicazione trasparente alle funzioni
- Statistiche di hit/miss in tempo reale
- Thread-safe per applicazioni concorrenti
Architettura, implementazione, test e code review completi.
"""
chat_result = code_executor_proxy.initiate_chat(
manager,
message=task,
summary_method="reflection_with_llm",
)
print("\n=== SOMMARIO FINALE ===")
print(chat_result.summary)
AG2: Punti di Forza
- Code execution reale: il codice viene davvero eseguito in sandbox, gli agenti vedono gli output reali
- Flessibilità emergente: i workflow si adattano al problema, non il contrario
- AG-UI protocol: frontend dinamici con streaming in tempo reale (2025)
- OpenTelemetry: osservabilità completa dei workflow agentici
- Human-in-the-loop: facilmente configurabile con
human_input_mode - TypeScript support: versione Microsoft AutoGen 0.4 con supporto nativo
Claude Code: Sub-Agents e Esecuzione Parallela
Claude Code ha un sistema multi-agent nativo che opera in modo fondamentalmente diverso dai framework Python visti finora. Invece di orchestrare modelli LLM via API, Claude Code avvia istanze Claude separate (sub-agents) direttamente dal terminale, ciascuna con il proprio context window pulito e la capacità di operare sul filesystem in modo isolato.
Questo approccio ha vantaggi significativi: nessun overhead di framework, integrazione nativa con gli strumenti del terminale, e la possibilità di eseguire task in parallelo su sistemi con più core. Il Task tool e il meccanismo chiave: lancia un sub-agent con un prompt specifico e attende il risultato prima di procedere (o, se i task sono indipendenti, li lancia tutti in parallelo).
# Code Reviewer Agent
## Ruolo
Senior code reviewer con focus su qualità, sicurezza e manutenibilità.
## Competenze
- Principi SOLID e design patterns
- OWASP Top 10 e sicurezza applicativa
- Performance optimization
- Clean code e refactoring
## Processo di Review
1. Leggi TUTTI i file modificati prima di commentare
2. Identifica problemi per categoria: CRITICAL, WARNING, SUGGESTION
3. Per ogni problema: specifica file, riga e fix raccomandata
4. Verifica che i test coprano i casi di errore
5. Controlla presenza di hardcoded secrets o credenziali
## Output Atteso
Report strutturato in markdown con sezioni:
- Executive Summary
- CRITICAL Issues (bloccanti per il merge)
- WARNINGS (da risolvere prima della prossima release)
- SUGGESTIONS (miglioramenti opzionali)
- Security Checklist
# Security Auditor Agent
## Ruolo
Security engineer specializzato in application security e OWASP.
## Competenze
- OWASP Top 10 (2025 edition)
- Analisi di codice Python, JavaScript e SQL
- Threat modeling
- Dependency vulnerability scanning
## Checklist di Sicurezza
### Input Validation
- [ ] Tutti gli input utente sono validati e sanitizzati
- [ ] Nessuna query SQL costruita con concatenazione di stringhe
- [ ] Path traversal prevenuto con realpath/pathlib
### Authentication & Authorization
- [ ] Password hashing con bcrypt/argon2 (no MD5/SHA1)
- [ ] JWT firmati con chiavi di lunghezza adeguata
- [ ] RBAC implementato correttamente
### Secrets Management
- [ ] Nessun hardcoded secret nel codice
- [ ] Variabili d'ambiente usate per credenziali
- [ ] .gitignore include file di configurazione sensibili
### Dependencies
- [ ] Nessuna dipendenza con CVE critiche note
- [ ] requirements.txt con versioni pin-nate
## Output
- Lista vulnerabilità con CVSS score stimato
- Fix raccomandate con esempi di codice
- Priorità: CRITICAL (fix immediata), HIGH, MEDIUM, LOW
# Esempio di prompt per orchestrazione multi-agent in Claude Code
# (questo e il contenuto di un file CLAUDE.md o di un prompt interattivo)
# --- ESEMPIO 1: SEQUENTIAL MULTI-AGENT ---
# Il task tool lancia sub-agent e attende i risultati in sequenza
Implementa le seguenti feature per il modulo auth/:
1. Usa il Task tool per lanciare il sub-agent "planner":
- Input: "Analizza auth/ e crea un piano di implementazione per
aggiungere 2FA con TOTP (RFC 6238)"
- Attendi il piano completo
2. Usa il Task tool per lanciare il sub-agent "developer":
- Input: "Implementa il piano seguente: [output del planner]"
- Attendi l'implementazione completa
3. Usa il Task tool per lanciare PARALLELAMENTE:
- Sub-agent "code-reviewer": rivedi il codice in auth/
- Sub-agent "security-auditor": verifica la sicurezza di auth/
(lancia entrambi contemporaneamente, attendi entrambi)
4. Applica le fix per tutti i problemi CRITICAL trovati
---
# --- ESEMPIO 2: PARALLEL WORKTREE ISOLATION ---
# Task indipendenti su worktree separati per massima parallelizzazione
Esegui queste task IN PARALLELO su worktree separati:
Task A (worktree: feature/user-service):
- Implementa UserService con CRUD completo
- Scrivi unit test con 90% coverage
- Commit: "feat: add UserService with full CRUD"
Task B (worktree: feature/email-service):
- Implementa EmailService con template system
- Integra con SendGrid API
- Scrivi integration test
- Commit: "feat: add EmailService with SendGrid"
Task C (worktree: feature/notification-service):
- Implementa NotificationService (email + push + SMS)
- Usa UserService e EmailService come dipendenze
- Scrivi test end-to-end
- Commit: "feat: add NotificationService"
Dopo che tutti e tre i worktree sono completati:
- Mergia in ordine: A, B, C
- Risolvi eventuali conflitti
- Esegui la test suite completa
---
# --- ESEMPIO 3: AGENTE CON MEMORIA CONDIVISA ---
# Uso di file come meccanismo di comunicazione inter-agent
Prima di iniziare, crea il file /tmp/project-context.md con:
- Stack tecnologico del progetto
- Convenzioni di naming
- Pattern architetturali in uso
- Dipendenze principali
Ogni sub-agent che lanci deve:
1. Leggere /tmp/project-context.md all'inizio
2. Aggiornare /tmp/progress.md con lo stato dei propri task
3. Salvare output strutturati in /tmp/agent-outputs/[nome]/
Questo garantisce coerenza tra agenti paralleli senza conflitti di merge.
Lezione dal Replit Incident (2025)
Nel 2025, Replit ha documentato un caso in cui un agente autonomo ha eliminato
un database di produzione durante un task di "cleanup". Questo incidente ha
evidenziato la necessità di configurare esplicitamente limiti sui comandi
distruttivi. In Claude Code, questo si gestisce tramite la sezione deny
in .claude/settings.json: blocca sempre rm -rf,
DROP TABLE, git push --force e simili.
I sub-agent ereditano le stesse restrizioni dell'agente principale.
Confronto Dettagliato: Quale Framework Scegliere
La scelta del framework dipende dal contesto specifico. Non esiste un vincitore assoluto: ogni strumento eccelle in scenari diversi. La tabella seguente sintetizza le differenze chiave per aiutare la decisione.
| Criterio | LangGraph | CrewAI | AG2 (AutoGen) | Claude Code |
|---|---|---|---|---|
| Paradigma | Grafo stateful | Role-based team | Conversazionale | Sub-agent nativo |
| Curva di apprendimento | Alta (graph theory) | Bassa (intuitivo) | Media | Bassa |
| Controllo del flusso | Massimo | Medio-alto | Emergente | Medio |
| Code execution | Tramite tool | Integrata (sandbox) | Integrata (locale) | Bash nativo |
| State persistence | Checkpointing nativo | Memory integrata | Messaggi chat | Filesystem |
| Parallelismo | Nodi paralleli | Process.parallel | GroupChat async | Task tool nativo |
| Osservabilità | LangSmith | CrewAI Enterprise | OpenTelemetry | Log nativi |
| Human-in-the-loop | Interrupt nativo | Callback | human_input_mode |
Interattivo |
| Ecosistema | LangChain | Indipendente | Microsoft/AG2 | Anthropic |
| Ideale per | Workflow complessi con logica condizionale | Team virtuali con ruoli definiti | Task esplorativi con code execution | Automazione CLI e progetti esistenti |
Raccomandazioni Pratiche
- Scegli LangGraph se hai workflow complessi con molte biforcazioni condizionali, hai bisogno di state persistence affidabile tra sessioni, e hai un team che capisce i concetti di state machine e grafi diretti.
- Scegli CrewAI se vuoi iniziare rapidamente con multi-agent, il tuo team e abituato a pensare in termini di ruoli organizzativi, e hai bisogno di code execution integrata con gestione automatica degli errori.
- Scegli AG2 se il task e esplorativo e non hai un workflow predefinito, hai bisogno di vere conversazioni multi-round con esecuzione di codice reale, e sei nell'ecosistema Microsoft (Azure, TypeScript).
- Scegli Claude Code se lavori già con Claude, vuoi multi-agent senza framework aggiuntivi, e il tuo use case principale e automazione di task di sviluppo su codebase esistenti.
Architettura Production-Ready per Team Reali
Un sistema multi-agent in produzione richiede considerazioni architetturali che vanno oltre la semplice orchestrazione degli agenti. Le aziende che hanno deployato questi sistemi nel 2025 hanno imparato alcune lezioni importanti che e utile conoscere prima di iniziare.
Pattern Architetturali Consolidati
Le ricerche del 2025 mostrano che i pattern più efficaci per multi-agent coding in produzione sono tre:
- Supervisor Pattern: un agente orchestratore delega task a agenti specializzati e aggrega i risultati. Ideale per LangGraph con routing condizionale.
- Pipeline Pattern: agenti in sequenza dove l'output di uno
diventa l'input del successivo. Naturale in CrewAI con
contexte task dipendenti. - Peer-to-Peer Pattern: agenti che comunicano liberamente tra loro senza un orchestratore centrale. Naturale in AG2 con GroupChat.
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.postgres import PostgresSaver
from typing import TypedDict, Optional
import logging
import time
logger = logging.getLogger(__name__)
class ProductionCodingState(TypedDict):
task_id: str
task_description: str
generated_code: str
test_results: str
review_status: str
error_count: int
max_errors: int
last_error: Optional[str]
start_time: float
max_execution_time: float # secondi
# ============================================================
# ERROR HANDLING - Nodo dedicato alla gestione errori
# ============================================================
def error_handler(state: ProductionCodingState) -> dict:
"""Gestisce errori e decide se riprovare o terminare."""
error_count = state.get("error_count", 0) + 1
logger.error(
f"Task {state['task_id']}: errore #{error_count} - {state.get('last_error', 'unknown')}"
)
if error_count >= state["max_errors"]:
logger.critical(f"Task {state['task_id']}: max errori raggiunto, terminazione")
return {"error_count": error_count, "review_status": "FAILED"}
# Exponential backoff prima del retry
wait_time = min(2 ** error_count, 30)
logger.info(f"Retry in {wait_time}s...")
time.sleep(wait_time)
return {"error_count": error_count, "review_status": "RETRY"}
# ============================================================
# TIMEOUT MANAGEMENT
# ============================================================
def check_timeout(state: ProductionCodingState) -> str:
"""Verifica se il task ha superato il timeout massimo."""
elapsed = time.time() - state["start_time"]
if elapsed > state["max_execution_time"]:
logger.warning(
f"Task {state['task_id']}: timeout dopo {elapsed:.1f}s "
f"(max: {state['max_execution_time']}s)"
)
return "timeout"
return "continue"
# ============================================================
# COST TRACKING - Tracciamento costi in tempo reale
# ============================================================
class CostTracker:
# Prezzi Anthropic (USD per 1M token, Feb 2026)
PRICES = {
"claude-opus-4-6": {"input": 15.0, "output": 75.0},
"claude-sonnet-4-6": {"input": 3.0, "output": 15.0},
"claude-haiku-4-5": {"input": 0.25, "output": 1.25},
}
def __init__(self, budget_usd: float):
self.budget = budget_usd
self.spent = 0.0
self.calls = 0
def track(self, model: str, input_tokens: int, output_tokens: int) -> None:
prices = self.PRICES.get(model, {"input": 3.0, "output": 15.0})
cost = (input_tokens * prices["input"] + output_tokens * prices["output"]) / 1_000_000
self.spent += cost
self.calls += 1
if self.spent > self.budget * 0.8:
logger.warning(f"Costo task: 






