Jak używać wywoływania funkcji z usługą Azure OpenAI Service (wersja zapoznawcza)

Najnowsze wersje gpt-35-turbo i gpt-4 są dostrojone do pracy z funkcjami i są w stanie określić, kiedy i jak należy wywołać funkcję. Jeśli co najmniej jedna funkcja jest uwzględniona w żądaniu, model określa, czy którakolwiek z funkcji powinna być wywoływana na podstawie kontekstu monitu. Gdy model ustali, że należy wywołać funkcję, odpowiada za pomocą obiektu JSON, w tym argumentów funkcji.

Modele formułują wywołania interfejsu API i strukturę danych wyjściowych, wszystkie na podstawie podanych funkcji. Należy pamiętać, że chociaż modele mogą generować te wywołania, wykonanie tych wywołań jest konieczne, dzięki czemu będziesz mieć kontrolę.

Na wysokim poziomie można podzielić pracę z funkcjami na trzy kroki:

  1. Wywoływanie interfejsu API uzupełniania czatów przy użyciu funkcji i danych wejściowych użytkownika
  2. Użyj odpowiedzi modelu, aby wywołać interfejs API lub funkcję
  3. Ponownie wywołaj interfejs API uzupełniania czatu, w tym odpowiedź z funkcji, aby uzyskać ostateczną odpowiedź

Ważne

Parametry functions i function_call zostały przestarzałe wraz z wydaniem 2023-12-01-preview wersji interfejsu API. Zamiana parametru functionstools to parametr . Zamiana parametru function_calltool_choice to parametr .

Wywoływanie funkcji równoległej

Równoległe wywołania funkcji są obsługiwane za pomocą następujących funkcji:

Obsługiwane modele

  • gpt-35-turbo (1106)
  • gpt-35-turbo (0125)
  • gpt-4 (1106-Preview)
  • gpt-4 (0125-Preview)

Obsługa interfejsu API

Obsługa funkcji równoległej została po raz pierwszy dodana w wersji interfejsu API 2023-12-01-preview

Wywołania funkcji równoległych umożliwiają jednoczesne wykonywanie wielu wywołań funkcji, co umożliwia równoległe wykonywanie i pobieranie wyników. Zmniejsza to liczbę wywołań interfejsu API, które należy wykonać i może poprawić ogólną wydajność.

Na przykład w przypadku prostej aplikacji pogodowej możesz chcieć pobrać pogodę w wielu lokalizacjach jednocześnie. Spowoduje to wyświetlenie komunikatu ukończenia czatu z trzema wywołaniami funkcji w tool_calls tablicy, z których każda ma unikatową wartość id. Jeśli chcesz odpowiedzieć na te wywołania funkcji, należy dodać do konwersacji 3 nowe wiadomości, z których każdy zawiera wynik jednego wywołania funkcji, z odwołaniem tool_call_idid z .tools_calls

Poniżej podano zmodyfikowaną wersję przykładu get_current_weather openAI. W tym przykładzie, podobnie jak w przypadku oryginalnego interfejsu OpenAI, należy podać podstawową strukturę, ale nie jest to w pełni funkcjonalny przykład autonomiczny. Próba wykonania tego kodu bez dalszej modyfikacji spowoduje wystąpienie błędu.

W tym przykładzie zdefiniowano pojedynczą funkcję get_current_weather. Model wywołuje funkcję wiele razy, a po wysłaniu odpowiedzi funkcji z powrotem do modelu decyduje o następnym kroku. Odpowiada za pomocą komunikatu skierowanego do użytkownika, który mówił użytkownikowi o temperaturze w San Francisco, Tokio i Paryżu. W zależności od zapytania może wybrać ponowne wywołanie funkcji.

Aby wymusić wywołanie określonej funkcji, ustaw tool_choice parametr z określoną nazwą funkcji. Możesz również wymusić wygenerowanie komunikatu użytkownika przez model, ustawiając wartość tool_choice: "none".

Uwaga

Domyślne zachowanie (tool_choice: "auto") dotyczy modelu samodzielnego decydowania, czy wywołać funkcję, a jeśli tak, która funkcja ma być wywoływana.

import os
from openai import AzureOpenAI
import json

client = AzureOpenAI(
  azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
  api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
  api_version="2024-03-01-preview"
)


# Example function hard coded to return the same weather
# In production, this could be your backend API or an external API
def get_current_weather(location, unit="fahrenheit"):
    """Get the current weather in a given location"""
    if "tokyo" in location.lower():
        return json.dumps({"location": "Tokyo", "temperature": "10", "unit": unit})
    elif "san francisco" in location.lower():
        return json.dumps({"location": "San Francisco", "temperature": "72", "unit": unit})
    elif "paris" in location.lower():
        return json.dumps({"location": "Paris", "temperature": "22", "unit": unit})
    else:
        return json.dumps({"location": location, "temperature": "unknown"})

def run_conversation():
    # Step 1: send the conversation and available functions to the model
    messages = [{"role": "user", "content": "What's the weather like in San Francisco, Tokyo, and Paris?"}]
    tools = [
        {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "Get the current weather in a given location",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "The city and state, e.g. San Francisco, CA",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                    },
                    "required": ["location"],
                },
            },
        }
    ]
    response = client.chat.completions.create(
        model="<REPLACE_WITH_YOUR_MODEL_DEPLOYMENT_NAME>",
        messages=messages,
        tools=tools,
        tool_choice="auto",  # auto is default, but we'll be explicit
    )
    response_message = response.choices[0].message
    tool_calls = response_message.tool_calls
    # Step 2: check if the model wanted to call a function
    if tool_calls:
        # Step 3: call the function
        # Note: the JSON response may not always be valid; be sure to handle errors
        available_functions = {
            "get_current_weather": get_current_weather,
        }  # only one function in this example, but you can have multiple
        messages.append(response_message)  # extend conversation with assistant's reply
        # Step 4: send the info for each function call and function response to the model
        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_to_call = available_functions[function_name]
            function_args = json.loads(tool_call.function.arguments)
            function_response = function_to_call(
                location=function_args.get("location"),
                unit=function_args.get("unit"),
            )
            messages.append(
                {
                    "tool_call_id": tool_call.id,
                    "role": "tool",
                    "name": function_name,
                    "content": function_response,
                }
            )  # extend conversation with function response
        second_response = client.chat.completions.create(
            model="<REPLACE_WITH_YOUR_1106_MODEL_DEPLOYMENT_NAME>",
            messages=messages,
        )  # get a new response from the model where it can see the function response
        return second_response
print(run_conversation())

Korzystanie z funkcji w interfejsie API uzupełniania czatów (przestarzałe)

Wywoływanie funkcji jest dostępne w wersji interfejsu 2023-07-01-preview API i działa z wersją 0613 gpt-35-turbo, gpt-35-turbo-16k, gpt-4 i gpt-4-32k.

Aby użyć funkcji wywołującej przy użyciu interfejsu API uzupełniania czatów, musisz uwzględnić dwie nowe właściwości w żądaniu: functions i function_call. Możesz uwzględnić co najmniej jeden functions w żądaniu i dowiedzieć się więcej o sposobie definiowania funkcji w sekcji definiującej funkcje . Należy pamiętać, że funkcje są wstrzykiwane do komunikatu systemowego pod maską, dzięki czemu funkcje są liczone względem użycia tokenu.

Gdy funkcje są udostępniane, domyślnie function_call parametr jest ustawiony na "auto" , a model decyduje, czy należy wywołać funkcję. Możesz też ustawić parametr tak function_call , aby {"name": "<insert-function-name>"} wymusił wywołanie określonej funkcji przez interfejs API lub ustawić parametr w taki sposób, aby "none" uniemożliwić wywoływanie dowolnych funkcji przez model.

Uwaga

Wersja 0.28.1 biblioteki języka Python openAI jest przestarzała. Zaleca się korzystanie z aplikacji 1.x. Zapoznaj się z naszym przewodnikiem migracji , aby uzyskać informacje na temat przechodzenia z 0.28.1 do programu 1.x.


import os
import openai

openai.api_key = os.getenv("AZURE_OPENAI_API_KEY")
openai.api_version = "2023-07-01-preview"
openai.api_type = "azure"
openai.api_base = os.getenv("AZURE_OPENAI_ENDPOINT")

messages= [
    {"role": "user", "content": "Find beachfront hotels in San Diego for less than $300 a month with free breakfast."}
]

functions= [  
    {
        "name": "search_hotels",
        "description": "Retrieves hotels from the search index based on the parameters provided",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The location of the hotel (i.e. Seattle, WA)"
                },
                "max_price": {
                    "type": "number",
                    "description": "The maximum price for the hotel"
                },
                "features": {
                    "type": "string",
                    "description": "A comma separated list of features (i.e. beachfront, free wifi, etc.)"
                }
            },
            "required": ["location"]
        }
    }
]  

response = openai.ChatCompletion.create(
    engine="gpt-35-turbo-0613", # engine = "deployment_name"
    messages=messages,
    functions=functions,
    function_call="auto", 
)

print(response['choices'][0]['message'])
{
  "role": "assistant",
  "function_call": {
    "name": "search_hotels",
    "arguments": "{\n  \"location\": \"San Diego\",\n  \"max_price\": 300,\n  \"features\": \"beachfront,free breakfast\"\n}"
  }
}

Odpowiedź z interfejsu API zawiera function_call właściwość, jeśli model określa, że należy wywołać funkcję. Właściwość function_call zawiera nazwę funkcji do wywołania i argumenty, które mają być przekazywane do funkcji. Argumenty są ciągiem JSON, który można przeanalizować i użyć do wywołania funkcji.

W niektórych przypadkach model generuje zarówno content , jak function_calli . Na przykład w przypadku monitu powyżej treści można powiedzieć coś takiego jak "Oczywiście, mogę pomóc znaleźć niektóre hotele w San Diego, które spełniają Twoje kryteria" wraz z function_call.

Praca z wywoływaniem funkcji

W poniższej sekcji bardziej szczegółowo opisano sposób efektywnego używania funkcji za pomocą interfejsu API uzupełniania czatów.

Definiowanie funkcji

Funkcja ma trzy główne parametry: name, descriptioni parameters. Parametr description jest używany przez model do określenia, kiedy i jak wywołać funkcję, dlatego ważne jest, aby podać znaczący opis działania funkcji.

parameters to obiekt schematu JSON, który opisuje parametry, które akceptuje funkcja. Więcej informacji na temat obiektów schematu JSON można uzyskać w dokumentacji schematu JSON.

Jeśli chcesz opisać funkcję, która nie akceptuje żadnych parametrów, użyj {"type": "object", "properties": {}} jako wartości właściwości parameters .

Zarządzanie przepływem za pomocą funkcji

Przykład w języku Python.


response = openai.ChatCompletion.create(
    deployment_id="gpt-35-turbo-0613",
    messages=messages,
    functions=functions,
    function_call="auto", 
)
response_message = response["choices"][0]["message"]

# Check if the model wants to call a function
if response_message.get("function_call"):

    # Call the function. The JSON response may not always be valid so make sure to handle errors
    function_name = response_message["function_call"]["name"]

    available_functions = {
            "search_hotels": search_hotels,
    }
    function_to_call = available_functions[function_name] 

    function_args = json.loads(response_message["function_call"]["arguments"])
    function_response = function_to_call(**function_args)

    # Add the assistant response and function response to the messages
    messages.append( # adding assistant response to messages
        {
            "role": response_message["role"],
            "function_call": {
                "name": function_name,
                "arguments": response_message["function_call"]["arguments"],
            },
            "content": None
        }
    )
    messages.append( # adding function response to messages
        {
            "role": "function",
            "name": function_name,
            "content": function_response,
        }
    ) 

    # Call the API again to get the final response from the model
    second_response = openai.ChatCompletion.create(
            messages=messages,
            deployment_id="gpt-35-turbo-0613"
            # optionally, you could provide functions in the second call as well
        )
    print(second_response["choices"][0]["message"])
else:
    print(response["choices"][0]["message"])

Przykład w programie PowerShell.

# continues from the previous PowerShell example

$response = Invoke-RestMethod -Uri $url -Headers $headers -Body $body -Method Post -ContentType 'application/json'
$response.choices[0].message | ConvertTo-Json

# Check if the model wants to call a function
if ($null -ne $response.choices[0].message.function_call) {

    $functionName  = $response.choices[0].message.function_call.name
    $functionArgs = $response.choices[0].message.function_call.arguments

    # Add the assistant response and function response to the messages
    $messages += @{
        role          = $response.choices[0].message.role
        function_call = @{
            name      = $functionName
            arguments = $functionArgs
        }
        content       = 'None'
    }
    $messages += @{
        role          = 'function'
        name          = $response.choices[0].message.function_call.name
        content       = "$functionName($functionArgs)"
    }

    # Call the API again to get the final response from the model
    
    # these API arguments are introduced in model version 0613
    $body = [ordered]@{
        messages      = $messages
        functions         = $functions
        function_call   = 'auto'
    } | ConvertTo-Json -depth 6

    $url = "$($openai.api_base)/openai/deployments/$($openai.name)/chat/completions?api-version=$($openai.api_version)"

    $secondResponse = Invoke-RestMethod -Uri $url -Headers $headers -Body $body -Method Post -ContentType 'application/json'
    $secondResponse.choices[0].message | ConvertTo-Json
}

Przykładowe dane wyjściowe.

{
  "role": "assistant",
  "content": "I'm sorry, but I couldn't find any beachfront hotels in San Diego for less than $300 a month with free breakfast."
}

W przykładach nie wykonujemy żadnej weryfikacji ani obsługi błędów, dlatego warto pamiętać, aby dodać ją do kodu.

Aby zapoznać się z pełnym przykładem pracy z funkcjami, zobacz przykładowy notes dotyczący wywoływania funkcji. Można również zastosować bardziej złożoną logikę, aby połączyć wiele wywołań funkcji, które zostały również omówione w przykładzie.

Monituj inżynierię za pomocą funkcji

Podczas definiowania funkcji w ramach żądania szczegóły są wstrzykiwane do komunikatu systemowego przy użyciu określonej składni, na której został wytrenowany model. Oznacza to, że funkcje używają tokenów w monicie i że można zastosować techniki inżynieryjne monitów w celu zoptymalizowania wydajności wywołań funkcji. Model używa pełnego kontekstu monitu, aby określić, czy należy wywołać funkcję, w tym definicję funkcji, komunikat systemowy i komunikaty użytkownika.

Poprawa jakości i niezawodności

Jeśli model nie wywołuje funkcji, gdy lub jak się spodziewasz, istnieje kilka rzeczy, które można spróbować poprawić jakość.

Podaj więcej szczegółów w definicji funkcji

Ważne jest, aby podać opis description funkcji i podać opisy dla dowolnego parametru, który może nie być oczywisty dla modelu. Na przykład w opisie parametru location można uwzględnić dodatkowe szczegóły i przykłady dotyczące formatu lokalizacji.

"location": {
    "type": "string",
    "description": "The location of the hotel. The location should include the city and the state's abbreviation (i.e. Seattle, WA or Miami, FL)"
},
Podaj więcej kontekstu w komunikacie systemowym

Komunikat systemowy może również służyć do zapewnienia większego kontekstu dla modelu. Jeśli na przykład masz funkcję o nazwie search_hotels , możesz dołączyć komunikat systemowy podobny do poniższego, aby poinstruować model o wywołanie funkcji, gdy użytkownik poprosi o pomoc w znalezieniu hotelu.

{"role": "system", "content": "You're an AI assistant designed to help users search for hotels. When a user asks for help finding a hotel, you should call the search_hotels function."}
Poinstruuj model, aby zadawał wyjaśnienia pytań

W niektórych przypadkach chcesz poinstruować model o zadawaniu wyjaśnień pytań, aby zapobiec założeniu, jakie wartości mają być używane z funkcjami. Na przykład jeśli chcesz, search_hotels aby model poprosił o wyjaśnienie, czy żądanie użytkownika nie zawiera szczegółów dotyczących locationelementu . Aby poinstruować model, aby zadać wyjaśnienie pytania, możesz dołączyć zawartość podobną do następnego przykładu w komunikacie systemowym.

{"role": "system", "content": "Don't make assumptions about what values to use with functions. Ask for clarification if a user request is ambiguous."}

Zmniejszanie błędów

Innym obszarem, w którym może być cenna inżynieria monitu, jest zmniejszenie błędów w wywołaniach funkcji. Modele są trenowane w celu generowania wywołań funkcji pasujących do zdefiniowanego schematu, ale modele tworzą wywołanie funkcji, które nie jest zgodne ze zdefiniowanym schematem lub spróbuje wywołać funkcję, która nie została uwzględniona.

Jeśli okaże się, że model generuje wywołania funkcji, które nie zostały podane, spróbuj zastosować zdanie w komunikacie systemowym z informacją "Only use the functions you have been provided with.".

Odpowiedzialne wywoływanie funkcji

Podobnie jak w przypadku każdego systemu sztucznej inteligencji użycie wywołania funkcji w celu zintegrowania modeli językowych z innymi narzędziami i systemami stwarza potencjalne ryzyko. Ważne jest, aby zrozumieć czynniki ryzyka, które mogą przedstawiać wywołania funkcji i podejmować działania w celu zapewnienia odpowiedzialnego korzystania z możliwości.

Poniżej przedstawiono kilka wskazówek ułatwiających bezpieczne i bezpieczne korzystanie z funkcji:

  • Weryfikowanie wywołań funkcji: zawsze weryfikuj wywołania funkcji wygenerowane przez model. Obejmuje to sprawdzenie parametrów, wywoływanie funkcji i upewnienie się, że wywołanie jest zgodne z zamierzonym działaniem.
  • Użyj zaufanych danych i narzędzi: używaj tylko danych z zaufanych i zweryfikowanych źródeł. Niezaufane dane w danych wyjściowych funkcji mogą służyć do poinstruowania modelu, aby zapisywał wywołania funkcji w inny sposób niż zamierzone.
  • Postępuj zgodnie z zasadą najniższych uprawnień: przyznaj tylko minimalny dostęp niezbędny do wykonania zadania przez funkcję. Zmniejsza to potencjalny wpływ, jeśli funkcja jest nadużywana lub wykorzystywana. Jeśli na przykład używasz wywołań funkcji w celu wykonywania zapytań dotyczących bazy danych, musisz przyznać aplikacji dostęp tylko do odczytu do bazy danych. Nie należy również zależeć wyłącznie od wykluczania możliwości w definicji funkcji jako kontroli zabezpieczeń.
  • Rozważ rzeczywisty wpływ: należy pamiętać o rzeczywistym wpływie wywołań funkcji, które planujesz wykonać, zwłaszcza tych, które wyzwalają akcje, takie jak wykonywanie kodu, aktualizowanie baz danych lub wysyłanie powiadomień.
  • Implementowanie kroków potwierdzenia użytkownika: szczególnie w przypadku funkcji, które podejmują działania, zalecamy dołączenie kroku, w którym użytkownik potwierdzi akcję przed wykonaniem.

Aby dowiedzieć się więcej na temat naszych zaleceń dotyczących odpowiedzialnego używania modeli Usługi Azure OpenAI, zobacz Omówienie praktyk dotyczących odpowiedzialnego używania sztucznej inteligencji dla modeli azure OpenAI.

Następne kroki