ai-agents-for-beginners

Hvordan designe gode AI-agenter

(Klikk på bildet over for å se videoen til denne leksjonen)

Designmønster for verktøybruk

Verktøy er interessante fordi de gir AI-agenter et bredere spekter av muligheter. I stedet for at agenten har et begrenset sett med handlinger den kan utføre, kan den ved å legge til et verktøy utføre et bredt spekter av handlinger. I dette kapittelet skal vi se på designmønsteret for verktøybruk, som beskriver hvordan AI-agenter kan bruke spesifikke verktøy for å nå sine mål.

Introduksjon

I denne leksjonen ønsker vi å svare på følgende spørsmål:

Læringsmål

Etter å ha fullført denne leksjonen vil du kunne:

Hva er designmønsteret for verktøybruk?

Designmønsteret for verktøybruk fokuserer på å gi LLM-er (Large Language Models) evnen til å samhandle med eksterne verktøy for å oppnå spesifikke mål. Verktøy er kode som kan utføres av en agent for å utføre handlinger. Et verktøy kan være en enkel funksjon som en kalkulator, eller et API-kall til en tredjepartstjeneste som aksjekursoppslag eller værmelding. I sammenheng med AI-agenter er verktøy designet for å bli utført av agenter som svar på modellgenererte funksjonskall.

Hvilke bruksområder kan det anvendes på?

AI-agenter kan bruke verktøy for å fullføre komplekse oppgaver, hente informasjon eller ta beslutninger. Designmønsteret for verktøybruk brukes ofte i scenarier som krever dynamisk interaksjon med eksterne systemer, som databaser, webtjenester eller kodefortolkere. Denne evnen er nyttig for en rekke bruksområder, inkludert:

Hvilke elementer/byggesteiner trengs for å implementere designmønsteret for verktøybruk?

Disse byggesteinene gjør det mulig for AI-agenten å utføre et bredt spekter av oppgaver. La oss se på nøkkelingrediensene som trengs for å implementere designmønsteret for verktøybruk:

La oss nå se nærmere på funksjons-/verktøykall.

Funksjons-/verktøykall

Funksjonskall er den primære måten vi gjør det mulig for store språkmodeller (LLM-er) å samhandle med verktøy. Ofte brukes begrepene “funksjon” og “verktøy” om hverandre fordi “funksjoner” (gjenbrukbare kodeblokker) er “verktøyene” agenter bruker for å utføre oppgaver. For at koden til en funksjon skal kunne utføres, må en LLM sammenligne brukerens forespørsel med funksjonsbeskrivelsen. For å gjøre dette sendes et skjema som inneholder beskrivelsene av alle tilgjengelige funksjoner til LLM-en. LLM-en velger deretter den mest passende funksjonen for oppgaven og returnerer navnet og argumentene. Den valgte funksjonen utføres, og responsen sendes tilbake til LLM-en, som bruker informasjonen til å svare på brukerens forespørsel.

For at utviklere skal kunne implementere funksjonskall for agenter, trenger du:

  1. En LLM-modell som støtter funksjonskall
  2. Et skjema som inneholder funksjonsbeskrivelser
  3. Koden for hver funksjon som er beskrevet

La oss bruke eksempelet med å finne gjeldende tid i en by for å illustrere:

  1. Initialiser en LLM som støtter funksjonskall:

    Ikke alle modeller støtter funksjonskall, så det er viktig å sjekke at LLM-en du bruker gjør det. Azure OpenAI støtter funksjonskall. Vi kan starte med å initiere Azure OpenAI-klienten.

     # Initialize the Azure OpenAI client
     client = AzureOpenAI(
         azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
         api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
         api_version="2024-05-01-preview"
     )
    
  2. Opprett et funksjonsskjema:

    Deretter definerer vi et JSON-skjema som inneholder funksjonsnavnet, en beskrivelse av hva funksjonen gjør, og navnene og beskrivelsene av funksjonsparametrene. Vi sender så dette skjemaet til klienten som ble opprettet tidligere, sammen med brukerens forespørsel om å finne tiden i San Francisco. Det som er viktig å merke seg, er at et verktøykall er det som returneres, ikke det endelige svaret på spørsmålet. Som nevnt tidligere, returnerer LLM navnet på funksjonen den valgte for oppgaven, og argumentene som skal sendes til den.

     # Function description for the model to read
     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"],
                 },
             }
         }
     ]
    
      
     # Initial user message
     messages = [{"role": "user", "content": "What's the current time in San Francisco"}] 
      
     # First API call: Ask the model to use the function
       response = client.chat.completions.create(
           model=deployment_name,
           messages=messages,
           tools=tools,
           tool_choice="auto",
       )
      
       # Process the model's response
       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. Koden som kreves for å utføre oppgaven:

    Nå som LLM-en har valgt hvilken funksjon som skal kjøres, må koden som utfører oppgaven implementeres og kjøres. Vi kan implementere koden for å finne gjeldende tid i Python. Vi må også skrive koden for å hente ut navnet og argumentene fra response_message for å få det endelige resultatet.

       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"})
    
      # Handle function calls
       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.")  
      
       # Second API call: Get the final response from the model
       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.
    

Funksjonskall er kjernen i de fleste, om ikke alle, design for verktøybruk hos agenter, men å implementere det fra bunnen av kan noen ganger være utfordrende. Som vi lærte i Leksjon 2, gir agentiske rammeverk oss forhåndsbygde byggesteiner for å implementere verktøybruk.

Eksempler på verktøybruk med agentiske rammeverk

Her er noen eksempler på hvordan du kan implementere designmønsteret for verktøybruk ved hjelp av ulike agentiske rammeverk:

Semantic Kernel

Semantic Kernel er et åpen kildekode AI-rammeverk for .NET-, Python- og Java-utviklere som jobber med store språkmodeller (LLM-er). Det forenkler prosessen med å bruke funksjonskall ved automatisk å beskrive funksjonene dine og deres parametere til modellen gjennom en prosess kalt serialisering. Det håndterer også kommunikasjonen frem og tilbake mellom modellen og koden din. En annen fordel med å bruke et agentisk rammeverk som Semantic Kernel, er at det gir tilgang til forhåndsbygde verktøy som Fil-søk og Kodefortolker.

Følgende diagram illustrerer prosessen med funksjonskall med Semantic Kernel:

funksjonskall

I Semantic Kernel kalles funksjoner/verktøy Plugins. Vi kan konvertere get_current_time-funksjonen vi så tidligere til en plugin ved å gjøre den om til en klasse med funksjonen i seg. Vi kan også importere kernel_function-dekoratoren, som tar inn beskrivelsen av funksjonen. Når du deretter oppretter en kernel med GetCurrentTimePlugin, vil kernelen automatisk serialisere funksjonen og dens parametere, og opprette skjemaet som sendes til LLM i prosessen.

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

# Create the kernel
kernel = Kernel()

# Create the plugin
get_current_time_plugin = GetCurrentTimePlugin(location)

# Add the plugin to the kernel
kernel.add_plugin(get_current_time_plugin)

Azure AI Agent Service

Azure AI Agent Service er et nyere agentisk rammeverk som er designet for å gjøre det mulig for utviklere å bygge, distribuere og skalere høykvalitets og utvidbare AI-agenter sikkert, uten å måtte administrere de underliggende ressursene for databehandling og lagring. Det er spesielt nyttig for bedriftsapplikasjoner siden det er en fullt administrert tjeneste med sikkerhet på bedriftsnivå.

Sammenlignet med å utvikle direkte med LLM-API-et, gir Azure AI Agent Service noen fordeler, inkludert:

Verktøyene som er tilgjengelige i Azure AI Agent Service kan deles inn i to kategorier:

  1. Kunnskapsverktøy:
  2. Handlingsverktøy:

Agent Service lar oss bruke disse verktøyene sammen som et toolset. Den bruker også threads, som holder oversikt over historikken til meldinger fra en bestemt samtale.

Tenk deg at du er en salgsagent i et selskap som heter Contoso. Du ønsker å utvikle en samtaleagent som kan svare på spørsmål om salgsdataene dine.

Følgende bilde illustrerer hvordan du kan bruke Azure AI Agent Service til å analysere salgsdataene dine:

Agentisk tjeneste i aksjon

For å bruke noen av disse verktøyene med tjenesten kan vi opprette en klient og definere et verktøy eller et verktøysett. For å implementere dette praktisk kan vi bruke følgende Python-kode. LLM-en vil kunne se på verktøysettet og avgjøre om den skal bruke den brukerskapte funksjonen, fetch_sales_data_using_sqlite_query, eller den forhåndsbygde kodefortolkeren, avhengig av brukerens forespørsel.

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 # fetch_sales_data_using_sqlite_query function which can be found in a fetch_sales_data_functions.py file.
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool

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

# Initialize function calling agent with the fetch_sales_data_using_sqlite_query function and adding it to the toolset
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset = ToolSet()
toolset.add(fetch_data_function)

# Initialize Code Interpreter tool and adding it to the toolset. 
code_interpreter = code_interpreter = CodeInterpreterTool()
toolset = ToolSet()
toolset.add(code_interpreter)

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

Hvilke spesielle hensyn må tas for å bruke designmønsteret for verktøybruk til å bygge pålitelige AI-agenter?

En vanlig bekymring med SQL som dynamisk genereres av LLM-er, er sikkerhet, spesielt risikoen for SQL-injeksjon eller ondsinnede handlinger, som å slette eller manipulere databasen. Selv om disse bekymringene er gyldige, kan de effektivt reduseres ved å riktig konfigurere databaseadgangstillatelser. For de fleste databaser innebærer dette å konfigurere databasen som skrivebeskyttet. For databasesystemer som PostgreSQL eller Azure SQL, bør appen tildeles en skrivebeskyttet (SELECT) rolle.

Å kjøre appen i et sikkert miljø gir ytterligere beskyttelse. I bedriftsmiljøer blir data vanligvis hentet og transformert fra operasjonelle systemer til en skrivebeskyttet database eller et datavarehus med et brukervennlig skjema. Denne tilnærmingen sikrer at dataene er sikre, optimalisert for ytelse og tilgjengelighet, og at appen har begrenset, skrivebeskyttet tilgang.

Har du flere spørsmål om designmønsteret for verktøybruk?

Bli med i Azure AI Foundry Discord for å møte andre lærende, delta på kontortid og få svar på spørsmål om AI-agenter.

Tilleggsressurser

Forrige leksjon

Forstå agentiske designmønstre

Neste leksjon

Agentisk RAG


Ansvarsfraskrivelse:
Dette dokumentet er oversatt ved hjelp av AI-oversettelsestjenesten Co-op Translator. Selv om vi streber etter nøyaktighet, vær oppmerksom på at automatiske oversettelser kan inneholde feil eller unøyaktigheter. Det originale dokumentet på sitt opprinnelige språk bør anses som den autoritative kilden. For kritisk informasjon anbefales profesjonell menneskelig oversettelse. Vi er ikke ansvarlige for misforståelser eller feiltolkninger som oppstår ved bruk av denne oversettelsen.