Salta al contenuto principale

05 - AutoGen e AG2: Conversazione Multi-Agente Emergente

AutoGen, AG2 e Microsoft Agent Framework: quale pacchetto installare, come far conversare gli agenti e come fermarli, con listati eseguiti senza modelli.

Cosa ne pensi di questo articolo?

05 - AutoGen e AG2: Conversazione Multi-Agente Emergente
AI AgentsFoto:Pavel DanilyukPexels

In Breve

AutoGen, AG2 e Microsoft Agent Framework: quale pacchetto installare, come far conversare gli agenti e come fermarli, con listati eseguiti senza modelli.

Il problema: «AutoGen» oggi indica almeno tre cose diverse

Cerchi un tutorial su AutoGen, copi from autogen import AssistantAgent, installi un pacchetto e l'esempio non gira, oppure gira ma con un'API diversa da quella del testo. Non è sfortuna: il nome copre un articolo di ricerca del 2023, un framework mantenuto da una comunità (AG2) e una linea Microsoft ora archiviata, e il pacchetto che installi decide quale API hai. Questo articolo parte da lì, perché è la cosa che ti fa perdere più tempo, poi spiega il modello conversazionale con listati eseguiti in locale, senza modelli, con agenti che seguono un copione.

Lo stato dei pacchetti al 2026-11-12, letto sul registro PyPI e sulle pagine ufficiali, è nella tabella.

Pacchetto PyPIVersioneChe cosa è
autogen0.14.1Il framework «classico» (import autogen, ConversableAgent, GroupChat), mantenuto da AG2
ag21.3.1AG2 dalla versione 1.0: nuovo framework, si importa come ag2; non fornisce più il nome autogen
autogen-agentchat0.7.5La linea Microsoft (AgentChat); la documentazione stabile dichiara il progetto archiviato
pyautogen0.10.0Pacchetto «proxy» di autogen-agentchat, secondo la sua descrizione sul registro
agent-framework1.26.0Microsoft Agent Framework, il successore indicato da Microsoft

Il punto della tabella è che gli esempi di questo articolo (ConversableAgent, GroupChat) appartengono alla riga autogen. Il README del repository ag2ai/ag2 dice che quelle classi stanno ora in «AG2 Classic» e consiglia pip install ag2-classic, ma al 2026-11-12 quel nome non esiste su PyPI (risposta 404 dall'API JSON del registro); il README del repository ag2ai/ag2-classic indica invece pip install autogen[openai]. Abbiamo quindi installato autogen 0.14.1 in un ambiente virtuale pulito con Python 3.14.4 ed eseguito lì tutti i listati. Se leggi il README e provi ag2-classic, l'installazione fallisce: è una discrepanza della documentazione, non un tuo errore.

Il modello conversazionale

L'articolo che ha presentato il progetto (Wu e coautori, arXiv:2308.08155, agosto 2023) lo descrive come un framework open source con cui costruire applicazioni basate su modelli linguistici tramite più agenti che conversano per completare compiti; gli agenti sono personalizzabili, «conversabili» e lavorano in modi che combinano modelli, input umano e strumenti. L'idea di fondo è che la conversazione sia la primitiva di coordinamento: invece di un grafo di passi (come in LangGraph) o di una sequenza di task assegnati a ruoli (come in CrewAI), gli agenti si scambiano messaggi e ciascuno risponde in base alla storia. Il vantaggio è la flessibilità: non scrivi in anticipo tutti i percorsi. Il costo è speculare: non sai in anticipo quanti turni serviranno, né come finirà.

Il blocco usa due ConversableAgent con llm_config=False, cioè senza modello, e dà a ciascuno una funzione di risposta registrata con register_reply che segue un copione. È il modo più economico di vedere la meccanica: chi parla, quando si ferma, che cosa finisce nella storia. Il Revisore avvia la conversazione, l'Analista dà una bozza sbagliata, il Revisore la contesta, l'Analista corregge e scrive TERMINATE; is_termination_msg riconosce quella parola e chiude il giro, e max_turns è un tetto di sicurezza.

from autogen import ConversableAgent

def copione(frasi):
    """Funzione di risposta che segue un copione: nessun modello, nessuna rete."""
    it = iter(frasi)
    def _reply(recipient, messages=None, sender=None, config=None):
        return True, next(it)          # (True = risposta definitiva, testo)
    return _reply

def finito(msg):
    return "TERMINATE" in (msg.get("content") or "")

revisore = ConversableAgent("Revisore", llm_config=False, human_input_mode="NEVER",
                            is_termination_msg=finito)
analista = ConversableAgent("Analista", llm_config=False, human_input_mode="NEVER",
                            is_termination_msg=finito)
revisore.register_reply([ConversableAgent, None], copione(["Il numero non torna, ricontrolla."]))
analista.register_reply([ConversableAgent, None],
                        copione(["Bozza: 3 ordini in ritardo.", "Corretto: 2 ordini in ritardo. TERMINATE"]))

esito = revisore.initiate_chat(analista, message="Quanti ordini sono in ritardo?",
                               max_turns=6, silent=True)
for m in esito.chat_history:
    print(f'{m["name"]:9}: {m["content"]}')
print("messaggi:", len(esito.chat_history))

# Output:
# Revisore : Quanti ordini sono in ritardo?
# Analista : Bozza: 3 ordini in ritardo.
# Revisore : Il numero non torna, ricontrolla.
# Analista : Corretto: 2 ordini in ritardo. TERMINATE
# messaggi: 4

Eseguito due volte con autogen 0.14.1, con uscita identica. La storia contiene quattro messaggi, nell'ordine in cui sono stati scambiati, ed è anche la memoria condivisa: ogni agente vede ciò che è stato detto. Se togli is_termination_msg, la conversazione non si ferma sulla parola chiave e prosegue fino ai limiti (qui max_turns, e l'esaurimento del copione solleverebbe StopIteration). Con un modello vero la parola chiave è un patto debole: dipende dal fatto che il modello la scriva davvero, e per questo serve sempre anche un tetto numerico.

Che cosa vale se non scrivi il parametro

Il comportamento di un agente dipende da pochi parametri, e i tutorial tacciono i valori predefiniti. Il blocco li legge dalla firma di ConversableAgent e di GroupChat nella versione installata, e prova un valore inventato di human_input_mode per vedere se viene respinto.

import inspect
from autogen import ConversableAgent, GroupChat

# Che cosa vale se non scrivi il parametro (letto dalla firma, autogen 0.14.1)
firma = inspect.signature(ConversableAgent.__init__).parameters
for nome in ("human_input_mode", "max_consecutive_auto_reply", "code_execution_config", "llm_config"):
    print(f"{nome:28} = {firma[nome].default!r}")
print("MAX_CONSECUTIVE_AUTO_REPLY   =", ConversableAgent.MAX_CONSECUTIVE_AUTO_REPLY)
firma = inspect.signature(GroupChat).parameters
for nome in ("max_round", "speaker_selection_method"):
    print(f"GroupChat.{nome:19} = {firma[nome].default!r}")

# Un valore inventato NON viene respinto alla creazione
a = ConversableAgent("x", llm_config=False, human_input_mode="FUNCTION_CALL")
print("human_input_mode inventato accettato:", a.human_input_mode)

# Output:
# human_input_mode             = 'TERMINATE'
# max_consecutive_auto_reply   = None
# code_execution_config        = False
# llm_config                   = None
# MAX_CONSECUTIVE_AUTO_REPLY   = 100
# GroupChat.max_round           = 10
# GroupChat.speaker_selection_method = 'auto'
# human_input_mode inventato accettato: FUNCTION_CALL

Tre cose si vedono nell'uscita. human_input_mode vale 'TERMINATE': un agente che non lo specifica si ferma a chiedere input umano quando riceve un messaggio di terminazione o raggiunge il limite di risposte automatiche. In uno script non interattivo significa rimanere bloccato in attesa di tastiera; per questo i listati usano "NEVER". Il limite max_consecutive_auto_reply vale None e il tetto di classe è 100: un agente che non lo scrive può rispondere fino a cento volte di fila. Infine il valore inventato FUNCTION_CALL viene accettato senza errore: la docstring elenca solo tre modalità, ALWAYS (chiede a ogni messaggio), TERMINATE e NEVER. Un tutorial che parla di una quarta modalità, o un refuso nel nome, non produce un errore ma un comportamento che non è quello che credi.

Gruppi di agenti: GroupChat e chi parla

Con più di due agenti serve qualcuno che decida chi parla. GroupChat definisce i partecipanti e le regole, GroupChatManager conduce la conversazione. Il parametro speaker_selection_method accetta, nella versione installata, quattro valori:

  • "auto" (il predefinito): un modello legge la conversazione e sceglie chi parla, il che aggiunge almeno una chiamata al modello a ogni turno (non misurato qui).
  • "round_robin": parlano a turno, in ordine; nessuna chiamata di selezione, comportamento prevedibile.
  • "random": sceglie a caso tra i partecipanti.
  • "manual": chiede a una persona chi deve parlare.

L'elenco ha un ordine di costo e di controllo: auto è il più flessibile e il più oneroso, manual il più controllato, round_robin il più semplice da collaudare. Per provare un gruppo senza modello, il blocco usa round_robin e tre agenti con copione; il manager ha una condizione di terminazione sulla parola TERMINATE e max_round a 6 come tetto. Il manager stampa un resoconto: lo zittiamo con redirect_stdout per mostrare solo la storia dei messaggi.

import contextlib
import io
from autogen import ConversableAgent, GroupChat, GroupChatManager

def copione(frasi):
    it = iter(frasi)
    def _reply(recipient, messages=None, sender=None, config=None):
        return True, next(it)
    return _reply

def agente(nome, frasi):
    a = ConversableAgent(nome, llm_config=False, human_input_mode="NEVER")
    a.register_reply([ConversableAgent, None], copione(frasi))
    return a

autore = agente("Autore", ["Funzione scritta: ordina(lista).", "Aggiunto il caso lista vuota."])
revisore = agente("Revisore", ["Manca il caso lista vuota.", "Ora va bene. TERMINATE"])
collaudatore = agente("Collaudatore", ["Test scritti: 3 casi."])
utente = ConversableAgent("Utente", llm_config=False, human_input_mode="NEVER")

gruppo = GroupChat(agents=[autore, revisore, collaudatore], messages=[], max_round=6,
                   speaker_selection_method="round_robin")    # tocca a turno, nessun modello sceglie
manager = GroupChatManager(groupchat=gruppo, llm_config=False,
                           is_termination_msg=lambda m: "TERMINATE" in (m.get("content") or ""))
with contextlib.redirect_stdout(io.StringIO()):          # il manager stampa il resoconto: lo zittiamo
    utente.initiate_chat(manager, message="Scrivete una funzione che ordina una lista.", silent=True)

for m in gruppo.messages:
    print(f'{m["name"]:12}: {m["content"]}')

# Output:
# Utente      : Scrivete una funzione che ordina una lista.
# Autore      : Funzione scritta: ordina(lista).
# Revisore    : Manca il caso lista vuota.
# Collaudatore: Test scritti: 3 casi.
# Autore      : Aggiunto il caso lista vuota.
# Revisore    : Ora va bene. TERMINATE

Eseguito due volte, con uscita identica. L'ordine Autore, Revisore, Collaudatore, Autore, Revisore è quello di round_robin, e la conversazione si ferma al sesto messaggio perché il Revisore scrive TERMINATE. Con auto l'ordine dipenderebbe da un modello e potrebbe cambiare a ogni esecuzione: non lo abbiamo eseguito. Nota che il Collaudatore parla una volta sola prima della chiusura: in un gruppo reale un agente può ritrovarsi a non parlare mai, o a parlare troppo, e va controllato.

Il ciclo scrivi-esegui-correggi

Il pattern per cui AutoGen è più noto fa lavorare insieme due agenti: uno scrive codice, l'altro lo esegue e restituisce l'esito, errori compresi; chi ha scritto legge il traceback e riprova. Nel blocco l'assistente è a copione (un primo tentativo che divide per zero, un secondo corretto) e l'esecutore è un ConversableAgent con code_execution_config che usa LocalCommandLineCodeExecutor in una cartella temporanea, che sparisce alla fine. Il codice viene davvero eseguito, in un sottoprocesso locale: è l'unico listato dell'articolo che lo fa.

import contextlib
import io
import tempfile
from autogen import ConversableAgent
from autogen.coding import LocalCommandLineCodeExecutor

def copione(frasi):
    it = iter(frasi)
    def _reply(recipient, messages=None, sender=None, config=None):
        return True, next(it)
    return _reply

FENCE = "`" * 3
tentativo_1 = f"{FENCE}python\nprint(sum([1, 2, 3]) / len([]))\n{FENCE}"            # divide per zero
tentativo_2 = f"{FENCE}python\nvalori = [1, 2, 3]\nprint(sum(valori) / len(valori))\n{FENCE}"

assistente = ConversableAgent("Assistente", llm_config=False, human_input_mode="NEVER",
                              is_termination_msg=lambda m: "TERMINATE" in (m.get("content") or ""))
assistente.register_reply([ConversableAgent, None],
                          copione([tentativo_1, tentativo_2, "La media è 2.0. TERMINATE"]))

with tempfile.TemporaryDirectory() as cartella:           # il codice gira qui, poi la cartella sparisce
    esecutore = LocalCommandLineCodeExecutor(work_dir=cartella, timeout=10)
    proxy = ConversableAgent("Esecutore", llm_config=False, human_input_mode="NEVER",
                             code_execution_config={"executor": esecutore},
                             is_termination_msg=lambda m: "TERMINATE" in (m.get("content") or ""))
    with contextlib.redirect_stdout(io.StringIO()):
        proxy.initiate_chat(assistente, message="Calcola la media di 1, 2, 3.", max_turns=8, silent=True)
    messaggi = proxy.chat_messages[assistente]

for m in messaggi:
    righe = [r for r in m["content"].splitlines() if r.strip() and "`" * 3 not in r]
    if m["name"] == "Esecutore" and righe[0].startswith("exitcode"):
        righe = [righe[0], righe[-1]]       # esito e ultima riga (il traceback contiene percorsi locali)
    print(f'{m["name"]:10}:', " / ".join(righe))

# Output:
# Esecutore : Calcola la media di 1, 2, 3.
# Assistente: print(sum([1, 2, 3]) / len([]))
# Esecutore : exitcode: 1 (execution failed) / ZeroDivisionError: division by zero
# Assistente: valori = [1, 2, 3] / print(sum(valori) / len(valori))
# Esecutore : exitcode: 0 (execution succeeded) / Code output: 2.0
# Assistente: La media è 2.0. TERMINATE

Eseguito due volte con uscita identica. Il primo tentativo termina con exitcode: 1 e ZeroDivisionError; il secondo restituisce 2.0 con exitcode: 0, e l'assistente chiude. Due avvertenze misurate. La prima: l'esecutore chiama il comando python; su una macchina che ha solo python3 nel PATH l'esecuzione fallisce con FileNotFoundError: [Errno 2] No such file or directory: 'python' (provato con PATH=/usr/bin:/bin), e noi abbiamo eseguito con l'ambiente virtuale attivato. La seconda riguarda la sicurezza: LocalCommandLineCodeExecutor esegue sulla tua macchina, con i tuoi permessi, codice scritto da un modello. Chiamarlo «ambiente sicuro» sarebbe sbagliato; per qualsiasi uso diverso da un esperimento serve un isolamento (un contenitore, una macchina dedicata, permessi minimi), come discusso in Security e Safety. In questo articolo non è stato usato Docker.

Che cosa significa «emergente», e dove fa male

Il titolo parla di conversazione emergente: il flusso nasce dai messaggi, non è scritto. È un vantaggio quando il compito è aperto (una revisione che richiede più passaggi), ma ha tre costi che puoi prevedere dal funzionamento. Primo, il numero di turni non è noto: servono sempre un tetto (max_turns, max_round, max_consecutive_auto_reply) e una condizione di arresto. Secondo, ogni agente vede l'intera storia, quindi ogni chiamata a un modello porta con sé un contesto che cresce a ogni turno: il costo cresce più in fretta del numero di turni (non misurato qui, perché non abbiamo chiamato modelli). Terzo, la riproducibilità è bassa: due esecuzioni possono dare conversazioni diverse, il che rende difficile il collaudo; per questo le prove di questo articolo usano copioni, e un agente vero si valuta su molti casi, come in Testing e Evaluation.

Migrare, e scegliere oggi

La documentazione stabile di AutoGen avverte che il progetto è archiviato e non più mantenuto da Microsoft (nessuna correzione di bug o di sicurezza), sconsiglia l'uso in produzione e indirizza i nuovi utenti al Microsoft Agent Framework, con una guida di migrazione. La pagina Microsoft Learn descrive l'Agent Framework come la combinazione delle astrazioni semplici di AutoGen con le funzioni enterprise di Semantic Kernel (stato di sessione, tipi, middleware, telemetria), più flussi di lavoro basati su grafi per l'orchestrazione esplicita; la stessa pagina ricorda, in modo utile per chi sta per costruire un agente, che se puoi scrivere una funzione che risolve il compito conviene usare la funzione. Dal lato AG2, il README dice che la versione 1.0 non è un aggiornamento compatibile con la classica (modello degli agenti, orchestrazione e importazioni sono cambiati) e rimanda a una guida di migrazione per i gruppi.

La tua situazioneChe cosa conviene
Hai codice con import autogen che funzionaFissa la versione di autogen (0.14.1 al 2026-11-12) e non installare ag2 1.x pensando a un aggiornamento
Hai codice su AutoGen 0.4 / autogen-agentchatPrevedi la migrazione: il progetto è archiviato, la guida Microsoft porta ad Agent Framework
Parti da zero e il flusso è notoConsidera prima un workflow o un grafo, come negli articoli precedenti: la conversazione libera si paga
Parti da zero e vuoi la conversazione tra agentiValuta Agent Framework o AG2 1.x, leggendo la loro documentazione attuale; qui non li abbiamo eseguiti

La tabella non dice «usa X»: dice di leggere prima quale riga ti riguarda, perché le righe hanno costi di migrazione diversi. Non abbiamo installato né eseguito agent-framework o ag2 1.x, quindi non possiamo dire come si scrive oggi lo stesso esempio con loro; sappiamo solo, dalle fonti, che l'API è cambiata. Il coordinamento di più agenti in produzione è il tema dell'articolo successivo, Orchestrazione Multi-Agente: Architetture Production, e il quadro dei framework è nel primo articolo.

Domande frequenti

AutoGen è ancora mantenuto?

Dipende da quale: la documentazione stabile di Microsoft dichiara il progetto archiviato e non più mantenuto; il framework classico continua nel repository AG2 (pacchetto autogen, 0.14.1 al 2026-11-12). Per il codice nuovo Microsoft indica Agent Framework.

Perché pip install ag2-classic non funziona?

Al 2026-11-12 il pacchetto ag2-classic non esiste su PyPI, anche se un README lo cita; l'altro README indica autogen[openai]. Abbiamo installato autogen 0.14.1 e i listati hanno girato.

Come impedisco a una conversazione tra agenti di non finire mai?

Con più limiti insieme: max_turns o max_round, max_consecutive_auto_reply e una condizione di terminazione con is_termination_msg. Il predefinito di max_consecutive_auto_reply è nessun limite per istanza e un tetto di classe di 100, troppo alto per un agente che chiama un modello a pagamento.

Che cosa sai fare adesso

Sai riconoscere quale AutoGen ha scritto un tutorial dal pacchetto e dall'import, e sai installare quello giusto. Sai far conversare due o più agenti, fermarli con una condizione e un tetto, scegliere chi parla in un gruppo, e far scrivere, eseguire e correggere codice sapendo dove l'esecuzione locale è pericolosa e dove si rompe. Sai inoltre provare tutto con agenti a copione prima di collegare un modello. E sai che la decisione di oggi riguarda il futuro del progetto, non solo la sintassi.

Fonti

AI Agents Complete Guide

La guida completa agli AI Agents: architetture, pattern, implementazione e casi d'uso reali. 10 capitoli, 172 pagine, esempi pratici in Python.

Prova Questi Strumenti

Strumenti gratuiti correlati a questo argomento.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti