Introduzione ad AutoGen

Introduzione ad AutoGen

9 Luglio 2024 Articoli di Intelligenza Artificiale 0

Introduzione al Framework AutoGen

In questo articolo, esploreremo le basi di AutoGen, un framework open-source multi-agente che permette di creare workflow complessi utilizzando agenti AI in stile twowaychat, sviluppato da Microsoft.
Che tu sia un neofita o abbia già esperienza con agenti AI, questo articolo ti fornirà una introduzione completa e pratica su come utilizzare AutoGen.

Cos’è AutoGen?

La risposta è chiara e coincisa e la troviamo direttamente sul sito di Microsoft:

“AutoGen is an open-source programming framework for building AI agents and facilitating cooperation among multiple agents to solve tasks. AutoGen aims to provide an easy-to-use and flexible framework for accelerating development and research on agentic AI, like PyTorch for Deep Learning. It offers features such as agents that can converse with other agents, LLM and tool use support, autonomous and human-in-the-loop workflows, and multi-agent conversation patterns.” (Microsoft).

E’ quindi un framework che permette di creare agenti AI e farli collaborare per risolvere compiti semplificando e accelerando lo sviluppo e la ricerca sull’agentic AI, come PyTorch facilita il Deep Learning.

Introduzione ad AutoGen: i Multi-Agenti

Quante volte, chiedendo a un “agente AI singolo”, come ChatGPT, di aiutarti a scrivere codice, ti è capitato di riscontrare problemi? E dovevi iterativamente copiare e incollare i messaggi di errore su ChatGPT finché finalmente, dopo n iterazioni, il tuo codice funzionava?

È in questo contesto che si inserisce AutoGen, nella sua versione più semplice, potendo dare un aiuto significativo per aumentare la rapidità.

AutoGen introduce due tipi di agenti:

  • AssistantAgent:
    E’ progettato per agire come un assistente AI, utilizzando LLM di default.
    Può scrivere codice Python (grazie al LLM, es GPT-4) quando riceve un compito da risolvere.
    Inoltre, può anche ricevere i risultati dell’esecuzione e di conseguenza suggerire correzioni o fix per i bug.
    Nel nostro esempio precedente, sarebbe ChatGPT che riceve le nostre richieste rispondendo con il codice.
  • UserProxyAgent:
    è un agente proxy per gli esseri umani con la capacità di eseguire codice e chiamare funzioni o strumenti.
    L’esecuzione del codice può essere disabilitata impostando un suo parametro interno chiamato: code_execution_config su False.
    Nel nostro esempio precedente, assume il compito di eseguire il codice e passare all’AssistantAgent i codici di errore, proprio come faremmo noi.

Di seguito un’immagine che rispecchia il “dialogo” tra lo UserProxyAgent e l’AssistantAgent:

Introduzione ad AutoGen: Come inizializzare gli LLM degli Agent?

Come abbiamo appena scoperto, per utilizzare questi agenti serve, ovviamente, utilizzare gli LLM.

Ma come inizializzare questi LLM?

Per configurare l’accesso degli agenti agli LLM, bisogna specificare un argomento llm_config nel loro costruttore.
Nel seguente frammento si mostra una configurazione della variabile llm_config che utilizza gpt-4:

import os

llm_config = {
    "config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}],
}

E ora questa llm_config può quindi essere passato al costruttore di un agente per abilitarlo ad utilizzare gli LLM.

import autogen

assistant = autogen.AssistantAgent(name="assistant", llm_config=llm_config)

Ma che variabile è questa llm_config?

E’ una lista di dizionari contenenti le configurazioni necessarie per i diversi modelli e endpoint da utilizzare.
Ogni elemento della lista può includere le seguenti chiavi:

  • model (stringa, obbligatorio):
    L’identificatore del modello da utilizzare, come ‘gpt-4’, ‘gpt-3.5-turbo’.
  • api_key (stringa, opzionale):
    La chiave API necessaria per autenticare le richieste all’endpoint API del modello.
  • base_url (stringa, opzionale):
    L’URL di base dell’endpoint API, ovvero l’indirizzo radice dove vengono indirizzate le chiamate API.
  • tags (Lista di stringhe, opzionale):
    Tag che possono essere utilizzati per il filtraggio.

Ad esempio:

[
  {
    "model": "gpt-4",
    "api_key": os.environ['OPENAI_API_KEY']
  }
]

Common Use: Definire un file JSON che la contenga

Un modo comune e utile consiste nel definire questa lista nominata config_list tramite JSON (specificato come file o come variabile d’ambiente impostata su una stringa formattata in JSON) e successivamente utilizzare la funzione ausiliaria config_list_from_json per caricarlo:

config_list = autogen.config_list_from_json(
    env_or_file="OAI_CONFIG_LIST",
)

Questa funzione interpreta l’argomento env_or_file come segue:

  • Se env_or_file è una variabile d’ambiente:
    • Proverà prima a caricare il file dal percorso specificato nella variabile d’ambiente.
    • Se non esiste alcun file, proverà a interpretare la variabile d’ambiente come una stringa JSON.
  • In caso contrario, proverà ad aprire il file nel percorso specificato da env_or_file.

Questa modalità di configurazione è utile perché mantiene tutta la configurazione in un unico posto, facilitando la gestione attraverso diversi progetti o notebook.

Una volta caricato il file di configurazione, possiamo creare l’agente assistente con il config list:

assistant = autogen.AssistantAgent(name="assistant", llm_config={"config_list": config_list})

Introduzione ad AutoGen: Perché usare una lista?

Utilizzare una lista per la configurazione permette di definire più modelli che possono essere utilizzati dall’agente. Questo è vantaggioso per diversi motivi:

  • Se un modello si esaurisce o fallisce, l’agente può provare un altro modello.
  • Avere un’unica lista globale di modelli e filtrarla in base a certe chiavi (es. nome, tag) per passare modelli selezionati a un certo agente (es. usare GPT-3.5 meno costoso per agenti che risolvono compiti più semplici).
  • Mentre gli agenti principali (es. conversable o assistant) non hanno logiche speciali per la selezione delle configurazioni, alcuni agenti specializzati potrebbero avere logiche per selezionare il miglior modello in base al compito da svolgere.

Introduzione ad AutoGen: Come decide un agente quale modello scegliere dalla lista?

Un agente utilizza il primo modello disponibile nella “config_list” e fa chiamate LLM contro questo modello.
Se il modello fallisce (es. throttling dell’API), l’agente riproverà la richiesta contro il secondo modello e così via fino a quando la richiesta non viene completata (o genera un errore se nessuno dei modelli completa con successo la richiesta).
In generale, non c’è una logica implicita/nascosta negli agenti per scegliere “il miglior modello per il compito”. Tuttavia, alcuni agenti specializzati possono tentare di scegliere “il miglior modello per il compito”. È responsabilità degli sviluppatori scegliere i modelli giusti e usarli con gli agenti.

Inizializziamo gli Agent

Di seguito, il codice per inizializzare finalmente i nostri Agent.

Partiamo inizialmente inizializzando il nostro AssistantAgent.

assistant = autogen.AssistantAgent(
    name="Assistant",
    llm_config={
        "config_list": config_list  
    }
)

Nello specifico, il metodo __init__ del nostro agente contiene i seguenti parametri:

def __init__(name: str,
             system_message: Optional[str] = DEFAULT_SYSTEM_MESSAGE,
             llm_config: Optional[Union[Dict, Literal[False]]] = None,
             is_termination_msg: Optional[Callable[[Dict], bool]] = None,
             max_consecutive_auto_reply: Optional[int] = None,
             human_input_mode: Literal["ALWAYS", "NEVER",
                                       "TERMINATE"] = "NEVER",
             description: Optional[str] = None,
             **kwargs)

Analizziamoli insieme:

  • name (str):
    nome dell’agente.
  • system_message (Optional[str]):
    E’ il messaggio di sistema settato con un messaggio di default. In poche parole, è progettato per risolvere un compito con l’utilizzo di LLM, incluso il suggerimento di blocchi di codice Python e il debugging.

Per i più curiosi mettiamo di seguito il system_message preso direttamente dal paper “AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation” :

  • llm_config (Optional[Union[Dict, Literal[False]]]):
    configurazione del LLM.
  • is_termination_msg (Optional[int]):
    una funzione che prende un messaggio sotto forma di dizionario e restituisce un valore booleano indicante se questo messaggio ricevuto è un messaggio di terminazione. Il dizionario può contenere le seguenti chiavi: “content”, “role”, “name”, “function_call”.
  • max_consecutive_auto_reply (Optional[int]):
    il numero massimo di risposte automatiche consecutive.
    Di Default è settato a None (nessun limite fornito, l’attributo di classe MAX_CONSECUTIVE_AUTO_REPLY sarà utilizzato come limite in questo caso).
    Il limite ha effetto solo quando human_input_mode non è impostato su “ALWAYS”.
  • human_input_mode(Literal[“ALWAYS”, “NEVER”, “TERMINATE”] ):
    Impostato di default su NEVER. Il nostro Assistant non ha bisogno di nessun intervento da parte dell’utente.

Passiamo ora all’inizializzazione dell’agente UserProxyAgent:

    user_proxy = autogen.UserProxyAgent(
        name="user",
        human_input_mode="NEVER",
        code_execution_config={
            "work_dir": "coding",
            "use_docker": False
        }
    )

Anche in questo caso, il metodo __init__ del nostro agente contiene i seguenti parametri:

def __init__(name: str,
             is_termination_msg: Optional[Callable[[Dict], bool]] = None,
             max_consecutive_auto_reply: Optional[int] = None,
             human_input_mode: Literal["ALWAYS", "TERMINATE",
                                       "NEVER"] = "ALWAYS",
             function_map: Optional[Dict[str, Callable]] = None,
             code_execution_config: Union[Dict, Literal[False]] = {},
             default_auto_reply: Optional[Union[str, Dict, None]] = "",
             llm_config: Optional[Union[Dict, Literal[False]]] = False,
             system_message: Optional[Union[str, List]] = "",
             description: Optional[str] = None)

Cerchiamo di capire meglio:

  • name (str):
    Nome dell’agente.
  • is_termination_msg (Optional[Callable[[Dict], bool]]):
    Funzione che prende un messaggio sotto forma di dizionario e restituisce un valore booleano indicante se il messaggio ricevuto è un messaggio di terminazione.
  • max_consecutive_auto_reply (Optional[int]):
    Numero massimo di risposte automatiche consecutive. Di default è None, il che significa che non c’è limite specifico fornito. In questo caso, viene utilizzato l’attributo di classe MAX_CONSECUTIVE_AUTO_REPLY come limite. Questo limite ha effetto solo quando human_input_mode non è impostato su “ALWAYS”.
  • human_input_mode (Literal[“ALWAYS”, “TERMINATE”, “NEVER”]):
    Specifica se chiedere input umano ogni volta che viene ricevuto un messaggio. I valori possibili sono:
    • “ALWAYS”: L’agente chiede input umano ogni volta che riceve un messaggio. La conversazione si interrompe quando l’input umano è “exit”, oppure quando is_termination_msg è True e non c’è input umano.
    • “TERMINATE”: L’agente chiede input umano solo quando riceve un messaggio di terminazione o quando il numero di risposte automatiche raggiunge max_consecutive_auto_reply.
    • “NEVER”: L’agente non chiede mai input umano. La conversazione si interrompe quando il numero di risposte automatiche raggiunge max_consecutive_auto_reply, o quando is_termination_msg è True.
  • function_map (Optional[Dict[str, Callable]]):
    Mappa dei nomi delle funzioni (passati a OpenAI) alle funzioni richiamabili.
  • code_execution_config (Union[Dict, Literal[False]]):
    Configurazione per l’esecuzione del codice. Per disabilitare l’esecuzione del codice, impostare a False. Altrimenti, impostare a un dizionario con le seguenti chiavi:
    • work_dir (Optional[str]):
      La directory di lavoro per l’esecuzione del codice. Se None, verrà utilizzata una directory di lavoro predefinita.
    • use_docker (Optional[Union[list, str, bool]]):
      L’immagine Docker da utilizzare per l’esecuzione del codice. Il valore predefinito è True, il che significa che il codice sarà eseguito in un container Docker. Si consiglia vivamente di utilizzare Docker per l’esecuzione del codice.
    • timeout (Optional[int]):
      Il tempo massimo di esecuzione in secondi.
    • last_n_messages (Sperimentale, Optional[int]):
      Il numero di messaggi da considerare per l’esecuzione del codice. Predefinito a 1.
  • default_auto_reply (Optional[Union[str, Dict, None]]):
    Messaggio di risposta automatica predefinito quando non viene generata una risposta basata sull’esecuzione del codice o sugli LLM.
  • llm_config (Optional[Union[Dict, Literal[False], None]]):
    Configurazione per LLM. Di default è False, che disabilita le risposte automatiche basate sugli LLM. Quando impostato su None, verrà utilizzato self.DEFAULT_CONFIG, che di default è False.
  • system_message (Optional[Union[str, List]]):
    Messaggio di sistema per l’inferenza ChatCompletion. Utilizzato solo quando llm_config non è False, per riprogrammare l’agente.
  • description (Optional[str]):
    Breve descrizione dell’agente. Questa descrizione è utilizzata da altri agenti (ad esempio, GroupChatManager) per decidere quando chiamare questo agente. Di default è system_message.

Esempio Concreto: Azioni di Tesla e Meta

Nel seguente esempio andremo a utilizzare Autogen per farci compilare e eseguire un semplice script:

“Realizza un grafico del cambiamento del prezzo delle azioni di META e TESLA.”

Sapendo già come scrivere e inizializzare correttamente i file e gli agenti, sarà un gioco da ragazzi. Basterà semplicemente utilizzare il metodo initiate_chat di user_proxy, immettendo nel suo parametro message la domanda che vogliamo fare.

Installazione di Py-Autogen

Aprire il terminale qualsiasi altro IDE e installare il pacchetto pyautogen utilizzando il comando:

pip install pyautogen
Codice
mport autogen

def main():
    config_list = autogen.config_list_from_json(
        env_or_file="OAI_CONFIG_LIST.json"
    )
    
    assistant = autogen.AssistantAgent(
        name="Assistant",
        llm_config={
            "config_list": config_list  
        }
    )
    
    user_proxy = autogen.UserProxyAgent(
        name="user",
        human_input_mode="NEVER",
        code_execution_config={
            "work_dir": "coding",
            "use_docker": False
        }
    )
    
    user_proxy.initiate_chat(assistant, message="Plot a chart of META and TESLA stock price change.")

if __name__ == '__main__':
    main()

Output Ottenuto

Compilando il codice si ottengono la serie di messaggi tra questi due agenti, proprio come se fosse la nostra conversazione con ChatGPT:

Grafico finale ottenuto:

Introduzione ad AutoGen: Conclusione

Abbiamo esplorato le basi del framework AutoGen, configurato l’ambiente di sviluppo e scritto un semplice esempio di applicazione multi-agente. Abbiamo anche approfondito il ruolo degli agenti AssistantAgent e UserProxyAgent.

Nei prossimi articoli, esploreremo funzionalità più avanzate di AutoGen, inclusi agenti multipli e modelli locali. Restate sintonizzati per ulteriori approfondimenti e progetti pratici.

Buon apprendimento!

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *