Share via


Cómo utilizar la llamada a funciones con Azure OpenAI Service (versión preliminar)

Las versiones más recientes de gpt-35-turbo y gpt-4 se han ajustado para trabajar con funciones y pueden determinar cuándo y cómo se debe llamar a una función. Si una o varias funciones se incluyen en la solicitud, el modelo determina si se debe llamar a alguna de las funciones en función del contexto del símbolo del sistema. Cuando el modelo determina que se debe llamar a una función, responde con un objeto JSON que incluye los argumentos de la función.

Los modelos formulan llamadas API y salidas de datos de estructura, todas basadas en las funciones que especifique. Es importante señalar que, aunque los modelos pueden generar estas llamadas, depende de usted ejecutarlas, lo que le garantiza que mantiene el control.

En general, el trabajo con funciones puede dividirse en tres pasos:

  1. Llamar a la API de finalización de chat con sus funciones y la entrada del usuario
  2. Uso de la respuesta del modelo para llamar a la API o función
  3. Vuelva a llamar a la API de finalizaciones de chat, incluida la respuesta de la función para obtener una respuesta final

Importante

Los parámetros functions y function_call han quedado en desuso con la versión de 2023-12-01-preview de la API. El reemplazo de functions es el parámetro tools. El reemplazo de function_call es el parámetro tool_choice.

Llamada a funciones paralelas

Las llamadas de función paralelas se admiten con:

Modelos admitidos

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

Compatibilidad con API

La compatibilidad con la función en paralelo se agregó por primera vez en la versión de API 2023-12-01-preview

Las llamadas de función paralelas permiten realizar varias llamadas de función a la vez, lo que permite la ejecución en paralelo y la recuperación de resultados. Esto reduce el número de llamadas a la API que se deben realizar y puede mejorar el rendimiento general.

Por ejemplo, en el caso de una aplicación del tiempo sencilla, es posible que desee recuperar el tiempo en varias ubicaciones al mismo tiempo. Esto dará lugar a un mensaje de finalización del chat con tres llamadas de función en la matriz tool_calls, cada una con un idúnico. Si desea responder a estas llamadas de función, debe agregar tres nuevos mensajes a la conversación, cada uno con el resultado de una llamada de función, con un tool_call_id que haga referencia al id de tools_calls.

A continuación se proporciona una versión modificada del ejemplo de get_current_weather de OpenAI. Este ejemplo, al igual que el original de OpenAI, tiene como propósito proporcionar la estructura básica, pero no es un ejemplo independiente totalmente funcional. Si se intenta ejecutar este código sin realizar ninguna modificación, se producirá un error.

En este ejemplo, se define una sola función get_current_weather. El modelo llama a la función varias veces y, tras devolverse la respuesta de la función al modelo, decide el paso siguiente. Responde con un mensaje orientado al usuario que indicaba a este la temperatura en San Francisco, Tokio y París. En función de la consulta, puede optar por llamar de nuevo a una función.

Para hacer que el modelo llame a una función específica, establezca el parámetro tool_choice con un nombre de función específico. También puede hacer que el modelo genere un mensaje orientado al usuario mediante el establecimiento de tool_choice: "none".

Nota:

El comportamiento predeterminado (tool_choice: "auto") es para que el modelo decida por sí mismo si va a llamar a una función y, en caso afirmativo, a qué función va a llamar.

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

Uso de la función en la API de finalizaciones de chat (en desuso)

La llamada a funciones está disponible en la versión de API 2023-07-01-preview y funciona con la versión 0613 de gpt-35-turbo, gpt-35-turbo-16k, gpt-4 y gpt-4-32k.

Para utilizar la llamada a funciones con la API de finalización de chat, debe incluir dos nuevas propiedades en su solicitud:functions y function_call. Puede incluir una o más functions en la solicitud y obtener más información sobre cómo definir funciones en la sección de definición de funciones a continuación. Tenga en cuenta que las funciones se insertan en el mensaje del sistema en segundo plano, por lo que las funciones cuentan con respecto al uso del token.

Cuando se proporcionan funciones, de forma predeterminada function_call se establece en "auto" y el modelo decide si se debe llamar o no a una función. Alternativamente, puede establecer el parámetro function_call en {"name": "<insert-function-name>"} para forzar a la API a llamar a una función específica o puede establecer el parámetro en "none" para evitar que el modelo llame a cualquier función.

Nota:

La versión 0.28.1 de la biblioteca de Python de OpenAI está en desuso. Recomendamos utilizar 1.x. Consulte nuestra guía de migración para obtener información sobre cómo pasar de 0.28.1 a 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}"
  }
}

La respuesta de la API incluye una propiedad function_call si el modelo determina que se debe llamar a una función. La propiedad function_call incluye el nombre de la función a llamar y los argumentos a pasar a la función. Los argumentos son una cadena JSON que puede analizar y usar para llamar a la función.

En algunos casos, el modelo puede generar content y function_call. Por ejemplo, para la consulta anterior, el contenido podría decir algo como "Claro, puedo ayudarle a encontrar algunos hoteles en San Diego que coincidan con sus criterios" junto con function_call.

Trabajar con llamadas a funciones

En la sección siguiente se ofrecen más detalles sobre cómo usar eficazmente las funciones con la API de finalizaciones de chat.

Definición de funciones

Una función tiene tres parámetros principales: name, description y parameters. El parámetro description es utilizado por el modelo para determinar cuándo y cómo llamar a la función, por lo que es importante dar una descripción significativa de lo que hace la función.

parameters es un objeto de esquema JSON que describe los parámetros que acepta la función. Puede obtener más información sobre los objetos del esquema JSON en la referencia del esquema JSON.

Si desea describir una función que no acepta ningún parámetro, utilice {"type": "object", "properties": {}} como valor para la propiedad parameters.

Administración del flujo con funciones

Ejemplo en 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"])

Ejemplo en 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
}

Salida de ejemplo.

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

En estos ejemplos, no realizamos ningún control de errores ni validación, por lo que le recomendamos agregarlos al código.

Para ver un ejemplo completo de cómo trabajar con funciones, consulte el cuaderno de ejemplos sobre la llamada a funciones. También puede aplicar lógica más compleja para encadenar varias llamadas de función, que también se tratan en el ejemplo.

Ingeniería de mensajes con funciones

Cuando se define una función como parte de la solicitud, los detalles se insertan en el mensaje del sistema mediante una sintaxis específica para la que se ha entrenado el modelo. Esto significa que las funciones consumen tokens en su solicitud y que puede aplicar técnicas de ingeniería de solicitud para optimizar el rendimiento de sus llamadas a funciones. El modelo usa el contexto completo de la solicitud para determinar si se debe llamar a una función, incluida la definición de función, el mensaje del sistema y los mensajes de usuario.

Mejora de la calidad y confiabilidad

Si el modelo no llama a su función cuando o como usted espera, hay algunas cosas que puede intentar para mejorar la calidad.

Proporcione más detalles en la definición de la función

Es importante que proporcione un significado description de la función y proporcione descripciones para cualquier parámetro que no sea obvio para el modelo. Por ejemplo, en la descripción del parámetro location, podría incluir detalles adicionales y ejemplos sobre el formato de la ubicación.

"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)"
},
Proporcionar más contexto en el mensaje del sistema

El mensaje del sistema también se puede usar para proporcionar más contexto al modelo. Por ejemplo, si tiene una función llamada search_hotels podría incluir un mensaje de sistema como el siguiente para indicar al modelo que llame a la función cuando un usuario pida ayuda para encontrar un hotel.

{"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."}
Indicar al modelo que haga preguntas aclarando

En algunos casos, conviene indicar al modelo que haga preguntas aclaratorias y evitar que se hagan suposiciones sobre qué valores usar con las funciones. Por ejemplo, con usted search_hotels querría que el modelo pidiera aclaraciones si la solicitud del usuario no incluyera detalles sobre location. Para indicar al modelo que formule una pregunta aclaratoria, puede incluir en el mensaje del sistema un contenido como el siguiente.

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

Reducción de errores

Otra área en la que la ingeniería de solicitudes puede ser valiosa es reducir los errores en las llamadas de función. Se ha entrenado a los modelos para generar llamadas a funciones que coincidan con el esquema que usted defina, pero los modelos pueden producir una llamada a una función que no coincida con el esquema que usted definió o pueden intentar llamar a una función que usted no incluyó.

Si encuentra que el modelo está generando llamadas de función que no se proporcionaron, intente incluir una oración en el mensaje del sistema que indica "Only use the functions you have been provided with.".

Uso de llamadas de función de forma responsable

Al igual que cualquier sistema de inteligencia artificial, el uso de llamadas de funciones para integrar modelos de lenguaje con otras herramientas y sistemas presenta posibles riesgos. Es importante comprender los riesgos que podrían presentar las llamadas a funciones y tomar medidas para asegurarse de que usa las funcionalidades de forma responsable.

Aquí tiene algunos consejos que le ayudarán a utilizar las funciones de forma segura:

  • Validar llamadas de función: compruebe siempre las llamadas de función generadas por el modelo. Esto incluye comprobar los parámetros, la función a la que se llama y asegurarse de que la llamada se alinea con la acción prevista.
  • Usar herramientas y datos de confianza: use solo datos de orígenes de confianza y comprobados. Los datos que no son de confianza en la salida de una función se pueden usar para indicar al modelo que escriba llamadas de función de una manera distinta a la prevista.
  • Siga el principio de privilegios mínimos: conceda solo el acceso mínimo necesario para que la función realice su trabajo. Esto reduce el impacto potencial si una función se usa o se aprovecha de forma incorrecta. Por ejemplo, si usa llamadas de función para consultar una base de datos, solo debe conceder a la aplicación acceso de solo lectura a la base de datos. Tampoco debe depender únicamente de la exclusión de funcionalidades en la definición de función como control de seguridad.
  • Considere el impacto en el mundo real: sea consciente del impacto en el mundo real de las llamadas a funciones que planea ejecutar, especialmente aquellas que desencadenan acciones como la ejecución de código, la actualización de bases de datos o el envío de notificaciones.
  • Implementar pasos de confirmación de usuario: especialmente para las funciones que realizan acciones, se recomienda incluir un paso en el que el usuario confirme la acción antes de que se ejecute.

Para obtener más información acerca de nuestras recomendaciones sobre cómo utilizar los modelos de Azure OpenAI de forma responsable, consulte la Descripción general de las prácticas de IA responsable para los modelos de Azure OpenAI.

Pasos siguientes