Share via


Verwenden von Funktionsaufrufen mit Azure OpenAI Service (Vorschau)

Die neuesten Versionen von gpt-35-turbo und gpt-4 wurden für die Arbeit mit Funktionen optimiert und können bestimmen, wann und wie eine Funktion aufgerufen werden soll. Wenn eine oder mehrere Funktionen in Ihrer Anforderung enthalten sind, bestimmt das Modell basierend auf dem Kontext des Prompts, ob eine der Funktionen aufgerufen werden soll. Wenn das Modell bestimmt, dass eine Funktion aufgerufen werden soll, antwortet es mit einem JSON-Objekt, das die Argumente für die Funktion enthält.

Die Modelle formulieren API-Aufrufe und strukturieren Datenausgaben, die alle auf den von Ihnen angegebenen Funktionen basieren. Wichtig: Die Modelle können diese Aufrufe zwar generieren, aber es liegt an Ihnen, sie auszuführen. Auf diese Weise wird sichergestellt, dass Sie die Kontrolle behalten.

Im Allgemeinen kann die Arbeit mit Funktionen in drei Schritte unterteilt werden:

  1. Aufrufen der Chat Completions-API mit Ihren Funktionen und der Benutzereingabe
  2. Verwenden der Antwort des Modells zum Aufrufen Ihrer API oder Funktion
  3. Erneutes Aufrufen der Chat Completions-API, einschließlich der Antwort Ihrer Funktion, um eine endgültige Antwort zu erhalten

Wichtig

Die Parameter functions und function_call wurden mit der Veröffentlichung der Version 2023-12-01-preview der API als veraltet markiert. Der Ersatz für functions ist der Parameter tools. Der Ersatz für function_call ist der Parameter tool_choice.

Paralleler Funktionsaufruf

Parallele Funktionsaufrufe werden unterstützt bei:

Unterstützte Modelle

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

API-Unterstützung

Die Unterstützung für parallele Funktion wurde zuerst in der API-Version 2023-12-01-preview hinzugefügt.

Parallele Funktionsaufrufe ermöglichen es Ihnen, mehrere Funktionsaufrufe zusammen auszuführen, wodurch parallele Ausführung und Abruf von Ergebnissen ermöglicht werden. Dadurch wird die Anzahl der Aufrufe an die API reduziert, die ausgeführt werden müssen, und kann die Gesamtleistung verbessert werden.

Sie möchten beispielsweise für eine einfache Wetter-App das Wetter an mehreren Orten gleichzeitig abrufen. Dies führt zu einer Chatvervollständigungsnachricht mit drei Funktionsaufrufen im tool_calls-Array und jeweils einer eindeutigen ID (id). Wenn Sie auf diese Funktionsaufrufe antworten möchten, fügen Sie der Unterhaltung drei neue Nachrichten hinzu, die jeweils das Ergebnis eines Funktionsaufrufs enthalten, wobei tool_call_id auf id von tools_calls verweist.

Nachfolgend finden Sie eine modifizierte Version des OpenAI-Beispiels get_current_weather. Dieses Beispiel soll wie das Original von OpenAI die grundlegende Struktur bereitstellen, ist aber kein vollständig funktionierendes eigenständiges Beispiel. Der Versuch, diesen Code ohne weitere Änderungen auszuführen, würde zu einem Fehler führen.

In diesem Beispiel wird eine einzelne Funktion „get_current_weather“ definiert. Das Modell ruft die Funktion mehrmals auf, und nach dem Senden der Funktionsantwort an das Modell entscheidet es über den nächsten Schritt. Es antwortet mit einer benutzerbezogenen Nachricht, die Benutzer*innen die Temperatur in San Francisco, Tokio und Paris mitteilte. Je nach Abfrage kann es sich entscheiden, eine Funktion erneut aufzurufen.

Um zu erzwingen, dass das Modell eine bestimmte Funktion aufruft, legen Sie den Parameter tool_choice mit einem bestimmten Funktionsnamen fest. Sie können auch erzwingen, dass das Modell eine Nachricht für Benutzer*innen generiert, indem Sie tool_choice: "none" festlegen.

Hinweis

Das Standardverhalten (tool_choice: "auto") dient dazu, dass das Modell selbst entscheiden kann, ob eine Funktion und welche Funktion aufgerufen werden soll.

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())

Verwenden der Funktion in der Chatvervollständigungs-API (veraltet)

Funktionsaufrufe sind in der API-Version 2023-07-01-preview verfügbar und können mit Version 0613 von gpt-35-turbo, gpt-35-turbo-16k, gpt-4 und gpt-4-32k verwendet werden.

Um Funktionsaufrufe mit der Chat Completions-API verwenden zu können, müssen Sie zwei neue Eigenschaften in Ihre Anforderung einschließen: functions und function_call. Sie können mehrere functions-Elemente in Ihre Anforderung einschließen. Weitere Informationen zum Definieren von Funktionen finden Sie im Abschnitt Definieren von Funktionen. Beachten Sie, dass Funktionen unter der Haube in die Systemmeldung eingefügt werden, sodass diese auf Ihre Tokennutzung angerechnet werden.

Wenn Funktionen bereitgestellt werden, wird function_call standardmäßig auf "auto" festgelegt, und das Modell entscheidet, ob eine Funktion aufgerufen werden soll. Alternativ können Sie den function_call-Parameter auf {"name": "<insert-function-name>"} festlegen, um zu erzwingen, dass die API eine bestimmte Funktion aufruft, oder Sie können den Parameter auf "none" festlegen, um zu verhindern, dass das Modell Funktionen aufruft.

Hinweis

Die OpenAI Python-Bibliotheksversion 0.28.1 ist veraltet. Wir empfehlen die Verwendung der 1.x. Informationen zur Migration von 0.28.1 zu 1.x finden Sie in unserem Migrationsleitfaden.


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}"
  }
}

Die Antwort der API enthält eine function_call-Eigenschaft, wenn das Modell bestimmt, dass eine Funktion aufgerufen werden soll. Die function_call-Eigenschaft enthält den Namen der aufzurufenden Funktion und die Argumente, die an die Funktion übergeben werden sollen. Die Argumente sind eine JSON-Zeichenfolge, die Sie analysieren und verwenden können, um Ihre Funktion aufzurufen.

In einigen Fällen kann das Modell sowohl content als auch function_call generieren. Für den Prompt über dem Inhalt könnte z. B. etwas wie „Sicher, ich kann Ihnen helfen, einige Hotels in San Diego zu finden, die Ihren Kriterien entsprechen“ zusammen mit function_call zurückgegeben werden.

Arbeiten mit Funktionsaufrufen

Im folgenden Abschnitt wird ausführlicher erläutert, wie Sie Funktionen effektiv mit der Chatvervollständigungs-API verwenden.

Definieren von Funktionen

Eine Funktion verfügt über drei Hauptparameter: name, description und parameters. Der description-Parameter wird vom Modell verwendet, um zu bestimmen, wann und wie die Funktion aufgerufen werden soll. Daher ist es wichtig, eine aussagekräftige Beschreibung des Zwecks der Funktion anzugeben.

parameters ist ein JSON-Schemaobjekt, das die Parameter beschreibt, die die Funktion akzeptiert. Weitere Informationen zu JSON-Schemaobjekten finden Sie in der JSON-Schemareferenz.

Wenn Sie eine Funktion beschreiben möchten, die keine Parameter akzeptiert, verwenden Sie {"type": "object", "properties": {}} als Wert für die parameters-Eigenschaft.

Verwalten des Flows mit Funktionen

Beispiel für 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"])

Beispiel für 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
}

Beispielausgabe

{
  "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."
}

In den Beispielen führen wir keine Validierung oder Fehlerbehandlung durch, daher sollten Sie sichergehen, dass Sie diese Ihrem Code hinzufügen.

Ein vollständiges Beispiel für die Arbeit mit Funktionen finden Sie im Beispielnotebook für Funktionsaufrufe. Sie können auch komplexere Logik anwenden, um mehrere Funktionsaufrufe miteinander zu verketten, was auch im Beispiel behandelt wird.

Prompt Engineering mit Funktionen

Wenn Sie eine Funktion als Teil Ihrer Anforderung definieren, werden die Details mithilfe der spezifischen Syntax, für die das Modell trainiert wurde, in die Systemmeldung eingefügt. Das bedeutet, dass Funktionen Token in Ihrem Prompt nutzen und Sie Prompt-Engineering-Techniken anwenden können, um die Leistung Ihrer Funktionsaufrufe zu optimieren. Das Modell verwendet den vollständigen Kontext des Prompts, um zu bestimmen, ob eine Funktion aufgerufen werden soll, einschließlich der Funktionsdefinition, der Systemmeldung und der Benutzermeldungen.

Verbesserung von Qualität und Zuverlässigkeit

Wenn das Modell Ihre Funktion nicht wann oder wie erwartet aufruft, können Sie versuchen, die Qualität zu verbessern.

Angeben weiterer Details in der Funktionsdefinition

Es ist wichtig, einen aussagekräftigen description-Wert für die Funktion und Beschreibungen für jeden Parameter anzugeben, der für das Modell möglicherweise nicht offensichtlich ist. Beispielsweise können Sie in der Beschreibung für den location-Parameter zusätzliche Details und Beispiele für das Format des Speicherorts angeben.

"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)"
},
Angeben von mehr Kontext in der Systemmeldung

Die Systemmeldung kann auch verwendet werden, um mehr Kontext für das Modell bereitzustellen. Wenn Sie beispielsweise über eine Funktion mit dem Namen search_hotels verfügen, können Sie eine Systemmeldung wie die folgende einschließen, um das Modell anzuweisen, die Funktion aufzurufen, wenn ein*e Benutzer*in um Hilfe bei der Suche nach einem Hotel bittet.

{"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."}
Anweisen des Modells, klärende Fragen zu stellen

In einigen Fällen sollten Sie das Modell ggf. anweisen, klärende Fragen zu stellen, um zu verhindern, dass es Annahmen darüber trifft, welche Werte für Funktionen zu verwenden sind. Sie könnten das Modell bei search_hotels beispielsweise anweisen, um Klärung zu bitten, wenn die Benutzeranforderung keine Details zu locationenthält. Um das Modell anzuweisen, eine klärende Frage zu stellen, können Sie Inhalte wie im folgenden Beispiel in Ihre Systemmeldung aufnehmen.

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

Reduzieren von Fehlern

Ein weiterer Bereich, in dem das Prompt Engineering nützlich sein kann, ist die Reduzierung von Fehlern bei Funktionsaufrufen. Die Modelle werden trainiert, um Funktionsaufrufe zu generieren, die dem von Ihnen definierten Schema entsprechen. Sie erzeugen jedoch einen Funktionsaufruf, der nicht mit dem von Ihnen definierten Schema übereinstimmt, oder versuchen, eine Funktion aufzurufen, die Sie nicht eingeschlossen haben.

Wenn Sie feststellen, dass das Modell Funktionsaufrufe generiert, die nicht vorgesehen wurden, versuchen Sie, einen Satz wie "Only use the functions you have been provided with." in die Systemmeldung aufzunehmen.

Verantwortungsvolles Verwenden von Funktionsaufrufen

Wie bei jedem KI-System birgt die Verwendung von Funktionsaufrufen zur Integration von Sprachmodellen in andere Tools und Systeme potenzielle Risiken. Es ist wichtig, die Risiken zu verstehen, die Funktionsaufrufe darstellen können, und Maßnahmen zu ergreifen, um sicherzugehen, dass die Funktionen verantwortungsvoll genutzt werden.

Hier sind einige Tipps, die Ihnen dabei helfen, Funktionen sicher zu verwenden:

  • Überprüfen von Funktionsaufrufen: Überprüfen Sie immer die vom Modell generierten Funktionsaufrufe. Dazu zählt das Überprüfen der Parameter und der aufgerufenen Funktion. Außerdem müssen Sie sich vergewissern, dass der Aufruf der beabsichtigten Aktion entspricht.
  • Verwenden von vertrauenswürdigen Daten und Tools: Verwenden Sie nur Daten aus vertrauenswürdigen und verifizierten Quellen. Nicht vertrauenswürdige Daten in der Ausgabe einer Funktion können verwendet werden, um das Modell anzuweisen, Funktionsaufrufe auf nicht beabsichtigte Weise zu schreiben.
  • Anwenden des Prinzips der geringsten Rechte: Gewähren Sie nur den mindestens erforderlichen Zugriff, den die Funktion benötigt, um ihre Aufgabe zu erfüllen. Dadurch sind die potenziellen Auswirkungen weniger schwerwiegend, wenn eine Funktion missbraucht oder ausgenutzt wird. Wenn Sie beispielsweise Funktionsaufrufe zum Abfragen einer Datenbank verwenden, sollten Sie Ihrer Anwendung nur schreibgeschützten Zugriff auf die Datenbank gewähren. Funktionen in der Funktionsdefinition auszuschließen sollte nicht Ihre einzige Sicherheitsmaßnahme sein.
  • Berücksichtigen der realen Auswirkungen: Beachten Sie die realen Auswirkungen von Funktionsaufrufen, die Sie ausführen möchten, insbesondere solche, die Aktionen auslösen, z. B. das Ausführen von Code, das Aktualisieren von Datenbanken oder das Senden von Benachrichtigungen.
  • Implementieren von Benutzerbestätigungsschritten: Insbesondere für Funktionen, die Aktionen ausführen, wird empfohlen, einen Schritt aufzunehmen, in dem Benutzer*innen die Aktion bestätigen müssen, bevor sie ausgeführt wird.

Weitere Informationen zu unseren Empfehlungen zur verantwortungsvollen Verwendung von Azure OpenAI-Modellen finden Sie unter Übersicht über Methoden der verantwortungsbewussten KI-Anwendung für Azure OpenAI-Modelle.

Nächste Schritte