ai-agents-for-beginners

Come progettare buoni agenti AI

(Clicca sull’immagine sopra per vedere il video di questa lezione)

Pattern di Progettazione per l’Uso di Strumenti

Gli strumenti sono interessanti perché permettono agli agenti AI di avere un ampio ventaglio di capacità. Invece di avere un set limitato di azioni che un agente può eseguire, aggiungendo uno strumento, l’agente può ora compiere una vasta gamma di azioni. In questo capitolo, esamineremo il Pattern di Progettazione per l’Uso di Strumenti, che descrive come gli agenti AI possano usare specifici strumenti per raggiungere i propri obiettivi.

Introduzione

In questa lezione, cercheremo di rispondere alle seguenti domande:

Obiettivi di Apprendimento

Dopo aver completato questa lezione, sarai in grado di:

Cos’è il Pattern di Progettazione per l’Uso di Strumenti?

Il Pattern di Progettazione per l’Uso di Strumenti si concentra sul fornire ai LLM la capacità di interagire con strumenti esterni per raggiungere obiettivi specifici. Gli strumenti sono codice eseguibile da un agente per compiere azioni. Uno strumento può essere una funzione semplice come una calcolatrice, oppure una chiamata API a un servizio di terze parti come la consultazione del prezzo di un’azione o la previsione meteo. Nel contesto degli agenti AI, gli strumenti sono progettati per essere eseguiti dagli agenti in risposta a chiamate di funzione generate dal modello.

A quali casi d’uso può essere applicato?

Gli agenti AI possono sfruttare strumenti per completare compiti complessi, recuperare informazioni o prendere decisioni. Il pattern di progettazione per l’uso di strumenti viene spesso usato in scenari che richiedono un’interazione dinamica con sistemi esterni, come database, servizi web o interpreti di codice. Questa capacità è utile per diversi casi d’uso, tra cui:

Quali sono gli elementi/blocchi costitutivi necessari per implementare il pattern di utilizzo degli strumenti?

Questi blocchi costitutivi permettono all’agente AI di svolgere una vasta gamma di compiti. Esaminiamo gli elementi chiave necessari per implementare il Pattern di Progettazione per l’Uso di Strumenti:

Successivamente, esaminiamo in dettaglio le Chiamate di Funzione/Strumento.

Chiamata di Funzione/Strumento

La chiamata di funzione è il modo principale con cui permettiamo ai Modelli di Linguaggio di Grandi Dimensioni (LLM) di interagire con gli strumenti. Vedrai spesso “Funzione” e “Strumento” usati in modo intercambiabile perché le “funzioni” (blocchi di codice riutilizzabile) sono gli “strumenti” che gli agenti usano per svolgere compiti. Perché il codice di una funzione venga invocato, un LLM deve confrontare la richiesta dell’utente con la descrizione delle funzioni. Per fare ciò, uno schema contenente le descrizioni di tutte le funzioni disponibili viene inviato all’LLM. L’LLM quindi seleziona la funzione più adatta al compito e restituisce il suo nome e i relativi argomenti. La funzione selezionata viene invocata, la sua risposta viene inviata nuovamente all’LLM, che usa queste informazioni per rispondere alla richiesta dell’utente.

Per gli sviluppatori che vogliono implementare la chiamata di funzione per agenti, occorre:

  1. Un modello LLM che supporti la chiamata di funzione
  2. Uno schema contenente le descrizioni delle funzioni
  3. Il codice per ogni funzione descritta

Usiamo l’esempio di ottenere l’ora corrente in una città per illustrare:

  1. Inizializzare un LLM che supporti la chiamata di funzione:

    Non tutti i modelli supportano la chiamata di funzione, quindi è importante verificare che l’LLM utilizzato la supporti. Azure OpenAI supporta la chiamata di funzione. Possiamo iniziare istanziando il client Azure OpenAI.

     # Inizializza il client Azure OpenAI
     client = AzureOpenAI(
         azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
         api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
         api_version="2024-05-01-preview"
     )
    
  2. Creare uno Schema di Funzione:

    Successivamente definiamo uno schema JSON contenente il nome della funzione, la descrizione di cosa fa la funzione e i nomi e descrizioni dei parametri della funzione. Passiamo poi questo schema al client creato in precedenza, insieme alla richiesta dell’utente di trovare l’ora a San Francisco. È importante notare che ciò che viene restituito è una chiamata a strumento, non la risposta finale alla domanda. Come detto prima, l’LLM restituisce il nome della funzione selezionata per il compito e gli argomenti che le verranno passati.

     # Descrizione della funzione per il modello da leggere
     tools = [
         {
             "type": "function",
             "function": {
                 "name": "get_current_time",
                 "description": "Get the current time in a given location",
                 "parameters": {
                     "type": "object",
                     "properties": {
                         "location": {
                             "type": "string",
                             "description": "The city name, e.g. San Francisco",
                         },
                     },
                     "required": ["location"],
                 },
             }
         }
     ]
    
      
     # Messaggio iniziale dell'utente
     messages = [{"role": "user", "content": "What's the current time in San Francisco"}] 
      
     # Prima chiamata API: Chiedi al modello di usare la funzione
       response = client.chat.completions.create(
           model=deployment_name,
           messages=messages,
           tools=tools,
           tool_choice="auto",
       )
      
       # Elabora la risposta del modello
       response_message = response.choices[0].message
       messages.append(response_message)
      
       print("Model's response:")  
    
       print(response_message)
      
    
     Model's response:
     ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_pOsKdUlqvdyttYB67MOj434b', function=Function(arguments='{"location":"San Francisco"}', name='get_current_time'), type='function')])
    
  3. Il codice della funzione necessario per svolgere il compito:

    Ora che l’LLM ha scelto quale funzione deve essere eseguita, il codice che realizza il compito deve essere implementato ed eseguito. Possiamo implementare il codice per ottenere l’ora corrente in Python. Dovremo anche scrivere il codice per estrarre il nome e gli argomenti dalla response_message per ottenere il risultato finale.

       def get_current_time(location):
         """Get the current time for a given location"""
         print(f"get_current_time called with location: {location}")  
         location_lower = location.lower()
            
         for key, timezone in TIMEZONE_DATA.items():
             if key in location_lower:
                 print(f"Timezone found for {key}")  
                 current_time = datetime.now(ZoneInfo(timezone)).strftime("%I:%M %p")
                 return json.dumps({
                     "location": location,
                     "current_time": current_time
                 })
          
         print(f"No timezone data found for {location_lower}")  
         return json.dumps({"location": location, "current_time": "unknown"})
    
      # Gestire le chiamate di funzione
       if response_message.tool_calls:
           for tool_call in response_message.tool_calls:
               if tool_call.function.name == "get_current_time":
         
                   function_args = json.loads(tool_call.function.arguments)
         
                   time_response = get_current_time(
                       location=function_args.get("location")
                   )
         
                   messages.append({
                       "tool_call_id": tool_call.id,
                       "role": "tool",
                       "name": "get_current_time",
                       "content": time_response,
                   })
       else:
           print("No tool calls were made by the model.")  
      
       # Seconda chiamata API: Ottenere la risposta finale dal modello
       final_response = client.chat.completions.create(
           model=deployment_name,
           messages=messages,
       )
      
       return final_response.choices[0].message.content
    
       get_current_time called with location: San Francisco
       Timezone found for san francisco
       The current time in San Francisco is 09:24 AM.
    

La Chiamata di Funzione è al centro della maggior parte, se non di tutti, i design di utilizzo di strumenti per agenti, tuttavia implementarla da zero può essere a volte una sfida. Come abbiamo visto in Lezione 2 i framework agentici ci forniscono blocchi predefiniti per implementare l’uso di strumenti.

Esempi di Uso di Strumenti con Framework Agentici

Ecco alcuni esempi di come puoi implementare il Pattern di Progettazione per l’Uso di Strumenti usando diversi framework agentici:

Semantic Kernel

Semantic Kernel è un framework AI open-source per sviluppatori .NET, Python e Java che lavorano con Modelli di Linguaggio di Grandi Dimensioni (LLM). Semplifica il processo di uso della chiamata di funzione descrivendo automaticamente le tue funzioni e i loro parametri al modello tramite un processo chiamato serializzazione. Gestisce anche la comunicazione avanti e indietro tra modello e codice. Un altro vantaggio nell’usare un framework agentico come Semantic Kernel è che ti consente di accedere a strumenti precompilati come File Search e Code Interpreter.

Il diagramma seguente illustra il processo di chiamata di funzione con Semantic Kernel:

function calling

In Semantic Kernel le funzioni/strumenti sono chiamati Plugin. Possiamo convertire la funzione get_current_time vista prima in un plugin trasformandola in una classe che la contiene. Possiamo anche importare il decoratore kernel_function, che prende in ingresso la descrizione della funzione. Quando crei un kernel con GetCurrentTimePlugin, il kernel serializzerà automaticamente la funzione e i suoi parametri, creando lo schema da inviare all’LLM nel processo.

from semantic_kernel.functions import kernel_function

class GetCurrentTimePlugin:
    async def __init__(self, location):
        self.location = location

    @kernel_function(
        description="Get the current time for a given location"
    )
    def get_current_time(location: str = ""):
        ...

from semantic_kernel import Kernel

# Crea il kernel
kernel = Kernel()

# Crea il plugin
get_current_time_plugin = GetCurrentTimePlugin(location)

# Aggiungi il plugin al kernel
kernel.add_plugin(get_current_time_plugin)

Azure AI Agent Service

Azure AI Agent Service è un framework agentico più recente progettato per permettere agli sviluppatori di costruire, distribuire e scalare agenti AI di alta qualità, estensibili e sicuri senza dover gestire le risorse di calcolo e storage sottostanti. È particolarmente utile per applicazioni aziendali, in quanto è un servizio completamente gestito con sicurezza da livello enterprise.

Rispetto allo sviluppo diretto con l’API LLM, Azure AI Agent Service offre alcuni vantaggi, tra cui:

Gli strumenti disponibili in Azure AI Agent Service possono essere divisi in due categorie:

  1. Strumenti di Conoscenza:
  2. Strumenti di Azione:

Il servizio Agent ci permette di usare questi strumenti insieme come un toolset. Utilizza anche i threads che tengono traccia della cronologia dei messaggi di una particolare conversazione.

Immagina di essere un agente di vendita in un’azienda chiamata Contoso. Vuoi sviluppare un agente conversazionale che possa rispondere a domande sui tuoi dati di vendita.

L’immagine seguente illustra come potresti usare Azure AI Agent Service per analizzare i tuoi dati di vendita:

Agentic Service In Action

Per usare uno qualsiasi di questi strumenti con il servizio, possiamo creare un client e definire uno strumento o un set di strumenti. Per implementarlo praticamente possiamo usare il seguente codice Python. L’LLM potrà esaminare il toolset e decidere se usare la funzione creata dall’utente, fetch_sales_data_using_sqlite_query, o l’interprete di codice predefinito a seconda della richiesta dell’utente.

import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from fetch_sales_data_functions import fetch_sales_data_using_sqlite_query # funzione fetch_sales_data_using_sqlite_query che si può trovare in un file fetch_sales_data_functions.py.
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool

project_client = AIProjectClient.from_connection_string(
    credential=DefaultAzureCredential(),
    conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)

# Inizializzare il set di strumenti
toolset = ToolSet()

# Inizializzare un agente di chiamata di funzione con la funzione fetch_sales_data_using_sqlite_query e aggiungerlo al set di strumenti
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset.add(fetch_data_function)

# Inizializzare lo strumento Code Interpreter e aggiungerlo al set di strumenti.
code_interpreter = code_interpreter = CodeInterpreterTool()
toolset.add(code_interpreter)

agent = project_client.agents.create_agent(
    model="gpt-4o-mini", name="my-agent", instructions="You are helpful agent", 
    toolset=toolset
)

Quali sono le considerazioni speciali per usare il Pattern di Progettazione per l’Uso di Strumenti per costruire agenti AI affidabili?

Una preoccupazione comune con l’SQL generato dinamicamente dagli LLM è la sicurezza, in particolare il rischio di SQL injection o azioni malevole, come il drop o la manomissione del database. Sebbene queste preoccupazioni siano valide, possono essere efficacemente mitigate configurando correttamente i permessi di accesso al database. Per la maggior parte dei database questo comporta la configurazione in sola lettura. Per servizi database come PostgreSQL o Azure SQL, l’applicazione dovrebbe essere assegnata a un ruolo di sola lettura (SELECT). Eseguire l’app in un ambiente sicuro aumenta ulteriormente la protezione. Negli scenari aziendali, i dati vengono solitamente estratti e trasformati dai sistemi operativi in un database o data warehouse a sola lettura con uno schema intuitivo. Questo approccio garantisce che i dati siano sicuri, ottimizzati per prestazioni e accessibilità, e che l’app abbia un accesso ristretto e di sola lettura.

Sample Codes

Hai altre domande sull’uso dei design pattern dello strumento?

Unisciti a Azure AI Foundry Discord per incontrare altri studenti, partecipare agli office hours e ottenere risposte alle tue domande sugli AI Agents.

Risorse aggiuntive

Lezione precedente

Understanding Agentic Design Patterns

Lezione successiva

Agentic RAG


Avvertenza: Questo documento è stato tradotto utilizzando il servizio di traduzione automatica Co-op Translator. Pur cercando di garantire la massima accuratezza, si prega di notare che le traduzioni automatiche possono contenere errori o inesattezze. Il documento originale nella sua lingua originale deve essere considerato la fonte autorevole. Per informazioni critiche, si raccomanda una traduzione professionale effettuata da un esperto umano. Non siamo responsabili per eventuali incomprensioni o interpretazioni errate derivanti dall’uso di questa traduzione.