Compartir vía


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.

Compatibilidad con la llamada de funciones

Llamada a funciones paralelas

  • gpt-35-turbo (1106)
  • gpt-35-turbo (0125)
  • gpt-4 (1106-Preview)
  • gpt-4 (0125-Preview)
  • gpt-4 (vision-preview)
  • gpt-4 (2024-04-09)
  • gpt-4o (13/05/2024)
  • gpt-4o-mini (18-07-2024)

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

Llamada a funciones básicas con herramientas

  • Todos los modelos que admiten llamadas a funciones paralelas
  • gpt-4 (0613)
  • gpt-4-32k (0613)
  • gpt-35-turbo-16k (0613)
  • gpt-35-turbo (0613)

Ejemplo de llamada a una sola herramienta o función

En primer lugar, mostraremos una llamada de función de prueba sencilla que puede comprobar la hora en tres ubicaciones codificadas de forma codificada con una sola herramienta o función definida. Hemos agregado instrucciones de impresión para ayudar a que la ejecución del código sea más fácil de seguir:

import os
import json
from openai import AzureOpenAI
from datetime import datetime
from zoneinfo import ZoneInfo

# 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"
)

# Define the deployment you want to use for your chat completions API calls

deployment_name = "<YOUR_DEPLOYMENT_NAME_HERE>"

# Simplified timezone data
TIMEZONE_DATA = {
    "tokyo": "Asia/Tokyo",
    "san francisco": "America/Los_Angeles",
    "paris": "Europe/Paris"
}

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

def run_conversation():
    # Initial user message
    messages = [{"role": "user", "content": "What's the current time in San Francisco"}] # Single function call
    #messages = [{"role": "user", "content": "What's the current time in San Francisco, Tokyo, and Paris?"}] # Parallel function call with a single tool/function defined

    # Define the function for the model
    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"],
                },
            }
        }
    ]

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

    # 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)
                print(f"Function arguments: {function_args}")  
                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

# Run the conversation and print the result
print(run_conversation())

Salida:

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')])
Function arguments: {'location': 'San Francisco'}
get_current_time called with location: San Francisco
Timezone found for san francisco
The current time in San Francisco is 09:24 AM.

Si usamos una implementación de modelos que admite llamadas a funciones paralelas, podríamos convertirla en un ejemplo de llamada de función paralela cambiando la matriz de mensajes para solicitar la hora en varias ubicaciones en lugar de una.

Para ello, cambie los comentarios de estas dos líneas:

    messages = [{"role": "user", "content": "What's the current time in San Francisco"}] # Single function call
    #messages = [{"role": "user", "content": "What's the current time in San Francisco, Tokyo, and Paris?"}] # Parallel function call with a single tool/function defined

Para que tenga este aspecto, y vuelva a ejecutar el código:

    #messages = [{"role": "user", "content": "What's the current time in San Francisco"}] # Single function call
    messages = [{"role": "user", "content": "What's the current time in San Francisco, Tokyo, and Paris?"}] # Parallel function call with a single tool/function defined

Esto genera la siguiente salida:

Salida:

Model's response:
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_IjcAVz9JOv5BXwUx1jd076C1', function=Function(arguments='{"location": "San Francisco"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_XIPQYTCtKIaNCCPTdvwjkaSN', function=Function(arguments='{"location": "Tokyo"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_OHIB5aJzO8HGqanmsdzfytvp', function=Function(arguments='{"location": "Paris"}', name='get_current_time'), type='function')])
Function arguments: {'location': 'San Francisco'}
get_current_time called with location: San Francisco
Timezone found for san francisco
Function arguments: {'location': 'Tokyo'}
get_current_time called with location: Tokyo
Timezone found for tokyo
Function arguments: {'location': 'Paris'}
get_current_time called with location: Paris
Timezone found for paris
As of now, the current times are:

- **San Francisco:** 11:15 AM
- **Tokyo:** 03:15 AM (next day)
- **Paris:** 08:15 PM

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 nuestra aplicación de período simple recuperamos varias veces al mismo tiempo. Esto dio 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.

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.

Llamada a funciones paralelas con varias funciones

Ahora mostraremos otro ejemplo de función de prueba que llama a este tiempo con dos herramientas o funciones diferentes definidas.

import os
import json
from openai import AzureOpenAI
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

# 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"
)

# Provide the model deployment name you want to use for this example

deployment_name = "YOUR_DEPLOYMENT_NAME_HERE" 

# Simplified weather data
WEATHER_DATA = {
    "tokyo": {"temperature": "10", "unit": "celsius"},
    "san francisco": {"temperature": "72", "unit": "fahrenheit"},
    "paris": {"temperature": "22", "unit": "celsius"}
}

# Simplified timezone data
TIMEZONE_DATA = {
    "tokyo": "Asia/Tokyo",
    "san francisco": "America/Los_Angeles",
    "paris": "Europe/Paris"
}

def get_current_weather(location, unit=None):
    """Get the current weather for a given location"""
    print(f"get_current_weather called with location: {location}, unit: {unit}")  
    
    for key in WEATHER_DATA:
        if key in location_lower:
            print(f"Weather data found for {key}")  
            weather = WEATHER_DATA[key]
            return json.dumps({
                "location": location,
                "temperature": weather["temperature"],
                "unit": unit if unit else weather["unit"]
            })
    
    print(f"No weather data found for {location_lower}")  
    return json.dumps({"location": location, "temperature": "unknown"})

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

def run_conversation():
    # Initial user message
    messages = [{"role": "user", "content": "What's the weather and current time in San Francisco, Tokyo, and Paris?"}]

    # Define the functions for the model
    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 name, e.g. San Francisco",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                    },
                    "required": ["location"],
                },
            }
        },
        {
            "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"],
                },
            }
        }
    ]

    # First API call: Ask the model to use the functions
    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)  

    # Handle function calls
    if response_message.tool_calls:
        for tool_call in response_message.tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)
            print(f"Function call: {function_name}")  
            print(f"Function arguments: {function_args}")  
            
            if function_name == "get_current_weather":
                function_response = get_current_weather(
                    location=function_args.get("location"),
                    unit=function_args.get("unit")
                )
            elif function_name == "get_current_time":
                function_response = get_current_time(
                    location=function_args.get("location")
                )
            else:
                function_response = json.dumps({"error": "Unknown function"})
            
            messages.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": function_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

# Run the conversation and print the result
print(run_conversation())

Salida

Model's response:
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_djHAeQP0DFEVZ2qptrO0CYC4', function=Function(arguments='{"location": "San Francisco", "unit": "celsius"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='call_q2f1HPKKUUj81yUa3ITLOZFs', function=Function(arguments='{"location": "Tokyo", "unit": "celsius"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='call_6TEY5Imtr17PaB4UhWDaPxiX', function=Function(arguments='{"location": "Paris", "unit": "celsius"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='call_vpzJ3jElpKZXA9abdbVMoauu', function=Function(arguments='{"location": "San Francisco"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_1ag0MCIsEjlwbpAqIXJbZcQj', function=Function(arguments='{"location": "Tokyo"}', name='get_current_time'), type='function'), ChatCompletionMessageToolCall(id='call_ukOu3kfYOZR8lpxGRpdkhhdD', function=Function(arguments='{"location": "Paris"}', name='get_current_time'), type='function')])
Function call: get_current_weather
Function arguments: {'location': 'San Francisco', 'unit': 'celsius'}
get_current_weather called with location: San Francisco, unit: celsius
Weather data found for san francisco
Function call: get_current_weather
Function arguments: {'location': 'Tokyo', 'unit': 'celsius'}
get_current_weather called with location: Tokyo, unit: celsius
Weather data found for tokyo
Function call: get_current_weather
Function arguments: {'location': 'Paris', 'unit': 'celsius'}
get_current_weather called with location: Paris, unit: celsius
Weather data found for paris
Function call: get_current_time
Function arguments: {'location': 'San Francisco'}
get_current_time called with location: San Francisco
Timezone found for san francisco
Function call: get_current_time
Function arguments: {'location': 'Tokyo'}
get_current_time called with location: Tokyo
Timezone found for tokyo
Function call: get_current_time
Function arguments: {'location': 'Paris'}
get_current_time called with location: Paris
Timezone found for paris
Here's the current information for the three cities:

### San Francisco
- **Time:** 09:13 AM
- **Weather:** 72°C (quite warm!)

### Tokyo
- **Time:** 01:13 AM (next day)
- **Weather:** 10°C

### Paris
- **Time:** 06:13 PM
- **Weather:** 22°C

Is there anything else you need?

Importante

Es posible que la respuesta JSON no siempre sea válida, por lo que debe agregar lógica adicional al código para poder controlar los errores. En algunos casos de uso, es posible que tenga que usar el ajuste preciso para mejorar el rendimiento de las llamadas a funciones.

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