Menu iconMenu icon
OpenAI API Biblia Volumen 1

Capítulo 6: Llamada a funciones y uso de herramientas

6.2 Definiendo Funciones y Parámetros

Al implementar llamadas a funciones con la API de Chat Completions, los desarrolladores deben proporcionar información completa y bien estructurada sobre las funciones disponibles. Esto se logra a través de un esquema de definición de función - un objeto JSON cuidadosamente elaborado que sirve como plano detallado para cada función. Este esquema es crucial para permitir que el modelo de IA entienda y utilice correctamente las funciones a su disposición.

Analicemos en profundidad sus tres componentes críticos:

  1. El nombre de la función - Sirve como identificador único en tu código. Como un número de seguridad social para tu función, debe ser distintivo y significativo, permitiendo que el modelo lo referencie sin ambigüedad al hacer llamadas a funciones.
  2. Una descripción detallada del propósito - Esto es esencialmente la descripción del trabajo de la función. Necesita ser lo suficientemente completa para que el modelo pueda determinar con precisión cuándo esta función es la herramienta adecuada para el trabajo. La descripción debe delinear claramente las capacidades, limitaciones y casos de uso previstos de la función.
  3. Una especificación completa de parámetros - Esto incluye no solo una lista de parámetros, sino un esquema detallado que describe el tipo de cada parámetro (string, number, boolean, etc.), propósito, restricciones y si es requerido u opcional. Piensa en esto como un manual de instrucciones detallado sobre cómo "alimentar" correctamente datos a tu función.

Este enfoque estructurado crea lo que llamamos un "contrato" entre tu aplicación y el modelo de IA - un acuerdo formal sobre cómo se comunicarán y trabajarán juntos. Es similar a incorporar un nuevo miembro al equipo: así como proporcionarías documentación detallada y pautas a un nuevo empleado, necesitas dar al modelo de IA instrucciones claras y específicas sobre tus funciones.

Cuanto más precisas y completas sean tus definiciones de funciones, mejor podrá el modelo realizar estas tareas esenciales:

Emparejar las intenciones del usuario con la función apropiada

Actuando como un intérprete sofisticado, el modelo analiza la entrada en lenguaje natural y determina qué función sirve mejor a las necesidades del usuario. Este proceso involucra varios niveles de comprensión: Primero, el modelo analiza el significado semántico de la solicitud del usuario, desglosando los componentes para identificar la acción central o la información que se busca.

Luego, lo evalúa contra su biblioteca de funciones disponibles, emparejando la intención con las capacidades de la función más adecuada. Por ejemplo, cuando un usuario pregunta "¿Qué tiempo hace?", el modelo sigue este proceso: reconoce esto como una solicitud de información meteorológica actual (no histórica ni pronóstico), identifica que esto requiere acceso a datos en tiempo real, y determina que una función de API meteorológica sería la herramienta apropiada - no una función de calculadora u otras opciones disponibles. Este enrutamiento inteligente asegura que las solicitudes de los usuarios sean manejadas por las herramientas más apropiadas, haciendo las interacciones más eficientes y precisas.

Validar y formatear correctamente los parámetros de entrada

El modelo actúa como un validador de datos sofisticado, asegurando que todas las entradas se ajusten precisamente a las especificaciones requeridas antes de cualquier ejecución de función. Este proceso de validación opera en múltiples niveles:

  1. Validación de Tipo: Asegura que cada parámetro coincida con su tipo de datos declarado (texto, número, booleano, etc.)
  2. Verificación de Rango: Comprueba que los valores numéricos estén dentro de los límites y restricciones aceptables
  3. Estandarización de Formato: Convierte automáticamente los datos al formato requerido, incluyendo:
    • Conversiones de unidades (por ejemplo, Celsius a Fahrenheit, metros a pies)
    • Estandarización de fecha/hora a través de diferentes formatos y zonas horarias
    • Formateo de texto para un manejo consistente de datos
  4. Validación Contextual: Analiza si la entrada tiene sentido lógico en el contexto dado

Por ejemplo, si una función espera una lectura de temperatura en Celsius entre -50°C y 50°C, el modelo:

  • Convertirá cualquier entrada en Fahrenheit a Celsius
  • Verificará que el valor esté dentro del rango aceptable
  • Formateará el número a la precisión decimal apropiada
  • Marcará cualquier valor anómalo que pudiera indicar errores

Tomar decisiones informadas sobre cuándo activar funciones específicas

Más allá de simplemente saber cómo usar las funciones, el modelo emplea un análisis de contexto sofisticado para determinar cuándo las llamadas a funciones son apropiadas. Este proceso de toma de decisiones involucra varios niveles de evaluación:

  1. Análisis de Intención: El modelo diferencia entre varios tipos de declaraciones del usuario:
    • Solicitudes directas que requieren acción ("¿Cuál es la temperatura exterior?")
    • Preguntas retóricas ("¿No está agradable el clima?")
    • Observaciones casuales ("Se siente cálido hoy")
    • Escenarios hipotéticos ("Si estuviera lloviendo...")
  2. Evaluación de Contexto: El modelo considera:
    • Historial previo de conversación
    • Estado actual de la conversación
    • Necesidades implícitas y explícitas del usuario
    • Capacidades de funciones disponibles
  3. Evaluación de Eficiencia: El modelo determina si:
    • Una llamada a función proporcionaría valor más allá de una simple respuesta de texto
    • Múltiples funciones podrían necesitar ser coordinadas
    • La información solicitada requiere datos en tiempo real
    • Una respuesta en caché podría ser suficiente

Por ejemplo, en una conversación relacionada con el clima, el modelo puede distinguir entre "¿No está agradable el clima?" (que no requiere llamada a función) y "¿Cuál es la temperatura exterior?" (que requiere acceder a datos meteorológicos actuales a través de una función API). Esta discriminación inteligente asegura un uso óptimo de recursos y mantiene un flujo natural de conversación mientras proporciona información precisa y relevante cuando es necesario.

Evitar llamadas a funciones innecesarias o incorrectas

El modelo mantiene la eficiencia del sistema evitando llamadas a funciones redundantes o inapropiadas a través de varios mecanismos sofisticados:

  1. Conciencia de caché: Reconoce cuándo los datos han sido recuperados recientemente y evita llamadas duplicadas. Por ejemplo, no llamará a una función meteorológica múltiples veces para la misma ubicación en un corto período de tiempo.
  2. Evaluación del tipo de respuesta: El modelo evalúa si una llamada a función es verdaderamente necesaria. Para consultas simples que pueden responderse con el conocimiento existente, proporcionará una respuesta directa de texto en lugar de hacer una llamada API.
  3. Persistencia de contexto: Rastrea el contexto de la conversación para evitar solicitar información que ya se ha establecido. Esto previene llamadas API redundantes para datos que ya están disponibles.
  4. Optimización de recursos: El modelo evalúa el costo computacional de las llamadas a funciones y elige el enfoque más eficiente. Por ejemplo, si múltiples funciones podrían proporcionar una respuesta, selecciona la que requiere menos recursos.

Esta gestión inteligente de funciones asegura un rendimiento óptimo del sistema mientras mantiene la precisión de las respuestas y la calidad de la experiencia del usuario.

Al invertir tiempo en crear y mantener definiciones de funciones claras y detalladas, estableces una base robusta para tu sistema impulsado por IA. Esta base asegura interacciones confiables y consistentes que no solo satisfacen las necesidades del usuario, sino que lo hacen de una manera que mantiene la integridad del sistema y produce resultados predecibles y de alta calidad. Piensa en ello como construir un puente entre el lenguaje humano y la funcionalidad computacional - cuanto más fuerte y mejor diseñado esté este puente, más fluida será la interacción.

6.2.1 Componentes Clave de una Definición de Función

Una definición de función consiste en varios componentes cruciales que trabajan juntos para crear un contrato claro entre tu aplicación y el modelo de IA.

Exploremos cada componente:

  1. Nombre: Un identificador único para tu función que sirve como su punto de referencia principal. Piensa en él como el título oficial de la función que el modelo usará para invocarla. El nombre debe ser:
    • Descriptivo y autoexplicativo (por ejemplo, "calculate_temperature_conversion" en lugar de solo "convert")
    • Único dentro del alcance de tu aplicación para evitar confusiones
    • Seguir una convención de nomenclatura consistente (típicamente snake_case o camelCase)
  2. Descripción: Una explicación completa del propósito y capacidades de la función. Esto es crucial porque:
    • Ayuda al modelo a entender exactamente cuándo usar esta función
    • Debe especificar cualquier limitación o caso de uso específico
    • Cuanto más detallada y precisa sea la descripción, mejor podrá el modelo tomar decisiones sobre su uso
  3. Parámetros: Un esquema JSON detallado que define los requisitos de entrada de la función. Este plano integral sirve como un contrato para cómo los datos deben ser pasados a la función. Esta definición estructurada incluye:
  • Tipo: Más comúnmente un objeto, que sirve como contenedor para todos los demás parámetros. Esto permite una organización estructurada de datos y relaciones claras entre parámetros. Piensa en ello como un envoltorio que contiene todas las piezas individuales de información que tu función necesita. Por ejemplo, una función meteorológica podría tener un objeto de ubicación que contenga parámetros de ciudad, estado y país.
  • Propiedades: Un mapeo detallado de cada parámetro, incluyendo:
    • Nombre: Un identificador claro y descriptivo que transmite inmediatamente el propósito del parámetro (por ejemplo, 'temperatureCelsius' en lugar de solo 'temp')
    • Tipo de datos: Restricciones específicas de tipo (texto, número, entero, booleano, etc.) que aseguran la consistencia de datos y previenen errores relacionados con el tipo
    • Descripción: Explicación clara de lo que representa el parámetro, incluyendo cualquier requisito específico de formato o rangos de valores aceptables
    • Restricciones opcionales: Como valores mínimos/máximos, patrones o enumeraciones. Estos actúan como barandillas para prevenir el procesamiento de datos inválidos. Por ejemplo, establecer un rango de temperatura de -50 a 150 para valores en Celsius
  • Requeridos: Una lista explícita de parámetros obligatorios que deben proporcionarse para que la función funcione correctamente. Esto ayuda a asegurar que todos los datos necesarios estén disponibles antes de llamar a la función, previniendo errores en tiempo de ejecución y mejorando la confiabilidad. Por ejemplo, una función de geocodificación podría requerir tanto latitud como longitud como parámetros requeridos, mientras que un parámetro de zona horaria podría ser opcional.

Ejemplo Detallado: Definiendo una Función Calculadora

Exploremos un ejemplo práctico para demostrar la definición de función. Crearemos una función llamada calculate_sum que realiza una operación aritmética básica - sumar dos números. Este ejemplo, aunque simple, ilustra todos los componentes clave de la definición de función y nos ayudará a entender cómo estructurar funciones más complejas.

La función tomará dos entradas numéricas y devolverá su suma, demostrando la definición adecuada de parámetros, especificación de tipo y descripción del propósito de la función. Así es como puedes definirla:

Ejemplo de Esquema de Función:

function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

Explicación:

  • Nombre: "calculate_sum" identifica de manera única nuestra función.
  • Descripción: Explica que la función toma dos números y devuelve su suma.
  • Objeto de Parámetros:
    • Tipo: Establecido como "object", ya que esperamos un objeto que contenga parámetros.
    • Propiedades:
      • "a" está definido como un número con una explicación descriptiva.
      • "b" está definido de manera similar.
    • Requeridos: Tanto "a" como "b" son parámetros requeridos para la función.

Aquí hay una versión ampliada de la definición de función con más características y manejo integral de errores:

function_definitions = [
    {
        "name": "calculate_arithmetic",
        "description": "Performs basic arithmetic operations between two numbers with input validation and error handling.",
        "parameters": {
            "type": "object",
            "properties": {
                "operation": {
                    "type": "string",
                    "description": "The arithmetic operation to perform",
                    "enum": ["add", "subtract", "multiply", "divide"],
                },
                "a": {
                    "type": "number",
                    "description": "The first number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "b": {
                    "type": "number",
                    "description": "The second number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "round_to": {
                    "type": "integer",
                    "description": "Number of decimal places to round to",
                    "minimum": 0,
                    "maximum": 10,
                    "default": 2
                }
            },
            "required": ["operation", "a", "b"]
        }
    }
]

def handle_arithmetic_operation(args):
    try:
        operation = args.get("operation")
        a = float(args.get("a"))
        b = float(args.get("b"))
        round_to = int(args.get("round_to", 2))
        
        result = None
        if operation == "add":
            result = a + b
        elif operation == "subtract":
            result = a - b
        elif operation == "multiply":
            result = a * b
        elif operation == "divide":
            if b == 0:
                raise ValueError("Division by zero is not allowed")
            result = a / b
            
        return {"result": round(result, round_to)}
        
    except ValueError as e:
        return {"error": str(e)}
    except Exception as e:
        return {"error": f"Unexpected error: {str(e)}"}

# Example API implementation
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=[
        {"role": "system", "content": "You are a helpful assistant that performs calculations."},
        {"role": "user", "content": "What is 15.7 divided by 2.5?"}
    ],
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the response
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Execute the function and get result
    result = handle_arithmetic_operation(json.loads(function_call.arguments))
    
    # Send the result back to the model for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": "You are a helpful assistant that performs calculations."},
            {"role": "user", "content": "What is 15.7 divided by 2.5?"},
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "calculate_arithmetic", "content": json.dumps(result)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)

Mejoras y adiciones clave en esta versión ampliada:

  • Parámetros Mejorados:
    • Adición de selección de tipo de operación (suma, resta, multiplicación, división)
    • Inclusión de validación de entrada con límites mínimos/máximos
    • Adición de parámetro opcional de precisión de redondeo
  • Manejo de Errores:
    • Valida tipos y rangos de entrada
    • Gestiona división por cero
    • Maneja errores inesperados de manera elegante
  • Implementación de Funciones:
    • Implementación completa de la función de manejo
    • Conversión y validación adecuada de tipos
    • Funcionalidad de redondeo de resultados
  • Integración de API:
    • Flujo de conversación en dos pasos
    • Maneja resultados de funciones y genera respuestas en lenguaje natural
    • Mantiene el contexto de la conversación

Esta versión ampliada demuestra un enfoque más preparado para producción con manejo de errores adecuado, validación de entrada y flujo de conversación completo.

6.2.2 Integrando Definiciones de Funciones en una Llamada API

Una vez que has definido tu función, el siguiente paso crucial es integrarla en tu llamada API. Este proceso de integración es fundamental ya que crea un puente entre tu aplicación y el modelo de IA, permitiéndole entender y utilizar tus funciones personalizadas de manera efectiva. El modelo emplea un sistema de análisis sofisticado que examina cuidadosamente el esquema de tu función, considerando varios factores:

  1. Análisis de Esquema: El modelo realiza un examen exhaustivo de la estructura de tu función. Esto incluye analizar el nombre de la función para entender su propósito, revisar la descripción detallada para comprender sus capacidades, y evaluar los requisitos de parámetros para asegurar un manejo adecuado de entrada. Por ejemplo, al examinar una función meteorológica, comprenderá tanto los requisitos básicos (como ubicación) como cualquier parámetro opcional (como unidades de temperatura).
  2. Evaluación de Contexto: El modelo realiza un análisis exhaustivo del flujo de conversación actual y el contexto histórico. Examina mensajes previos, consultas del usuario y el diálogo en curso para construir una comprensión completa de las necesidades actuales del usuario. Esto ayuda a determinar si una llamada a función es apropiada para la situación actual y cómo debe ejecutarse.
  3. Coincidencia de Intención: A través de algoritmos sofisticados de comparación, el modelo evalúa la intención del usuario contra la biblioteca de funciones disponibles. Busca coincidencias semánticas entre la solicitud del usuario y las capacidades de la función, considerando variaciones en cómo los usuarios podrían expresar sus necesidades. Por ejemplo, "¿Cuál es la temperatura afuera?" y "¿Qué calor hace?" coincidirían ambas con una función meteorológica.
  4. Lógica de Decisión: El modelo emplea un proceso de decisión de múltiples pasos basado en toda la información recopilada. Pondera factores como la certeza de la coincidencia, la completitud de los parámetros disponibles, y si una llamada a función proporcionaría mejores resultados que una respuesta directa. Esto asegura que las funciones solo se llamen cuando realmente mejorarán la experiencia del usuario y proporcionarán información más precisa o útil.

Examinemos este proceso de toma de decisiones en detalle a través de dos ejemplos contrastantes que destacan cuándo las llamadas a funciones son necesarias versus cuándo las respuestas directas son más apropiadas:

Ejemplo 1 - Llamada a Función Necesaria:
Usuario: "¿Qué tiempo hace en París?"

  • El modelo reconoce que esto requiere datos externos porque la información meteorológica actual no puede conocerse sin acceder a datos en tiempo real
  • Identifica la función get_weather como relevante al hacer coincidir la intención del usuario de obtener información meteorológica con el propósito de la función
  • Confirma que los parámetros (location="Paris") están disponibles y correctamente formateados para la llamada API
  • Se prepara para ejecutar la llamada a función formateando la solicitud según el esquema de la función

El código:

import openai
import json
from datetime import datetime

# Define the weather function
function_definitions = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a specified location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City name for weather information"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "default": "celsius",
                    "description": "Temperature unit preference"
                }
            },
            "required": ["location"]
        }
    }
]

# Mock weather API function
def get_weather_data(args):
    # In a real implementation, this would call an actual weather API
    location = args.get("location")
    units = args.get("units", "celsius")
    
    # Mock response
    return {
        "location": location,
        "temperature": 22 if units == "celsius" else 71.6,
        "conditions": "partly cloudy",
        "humidity": 65,
        "timestamp": datetime.now().isoformat()
    }

# Example conversation
messages = [
    {"role": "system", "content": "You are a helpful assistant that can check weather conditions."},
    {"role": "user", "content": "What's the weather in Paris?"}
]

# Initial API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the function call
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Get weather data
    weather_data = get_weather_data(json.loads(function_call.arguments))
    
    # Send the result back for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            *messages,
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "get_weather", "content": json.dumps(weather_data)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)
    # Output might be: "Currently in Paris, it's 22°C (71.6°F) and partly cloudy with 65% humidity."

Aquí está el desglose de sus componentes principales:

1. Definición de Función

  • Define una función meteorológica que acepta dos parámetros:
    • "ubicación" (obligatorio): Una cadena para el nombre de la ciudad
    • "unidades" (opcional): Unidades de temperatura, con celsius por defecto

2. Implementación Simulada del Clima

  • Crea una función simulada del clima que imita una respuesta de API
    • Devuelve ubicación, temperatura, condiciones, humedad y marca temporal
    • Maneja la conversión de unidades entre celsius y fahrenheit

3. Flujo de Conversación

  • Establece una conversación básica con:
    • Mensaje del sistema que define el rol del asistente
    • Mensaje del usuario preguntando sobre el clima en París

4. Integración de API

  • Realiza dos llamadas API a OpenAI:
    1. Llamada inicial para procesar la solicitud del usuario y determinar si se necesita una llamada a función
    2. Llamada de seguimiento para generar una respuesta en lenguaje natural basada en los datos meteorológicos

5. Manejo de Respuesta

  • Procesa la respuesta de la API mediante:
    • Verificación si se necesita una llamada a función
    • Ejecución de la función meteorológica con los argumentos proporcionados
    • Formateo de la respuesta en lenguaje natural

Este ejemplo demuestra el ciclo de vida completo de una llamada a función, desde la entrada inicial del usuario hasta la salida final en lenguaje natural.

Ejemplo 2 - Respuesta Directa Apropiada:
Usuario: "¿Cuál es la capital de Francia?"

  • El modelo reconoce esto como conocimiento general que existe dentro de sus datos de entrenamiento
  • Determina que no se necesitan datos externos ya que es un hecho estático y bien establecido
  • Omite la llamada a función para optimizar el tiempo de respuesta y reducir el uso innecesario de API
  • Proporciona una respuesta directa desde su base de conocimientos existente

El código:

import openai
import json

# Define conversation with a factual query
messages = [
    {"role": "system", "content": "You are a helpful assistant with broad knowledge."},
    {"role": "user", "content": "What is the capital of France?"}
]

# Example API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    # Note: No functions parameter needed since this is a direct knowledge query
    temperature=0
)

# Print the response
print(response.choices[0].message.content)
# Output would be: "The capital of France is Paris."

# The model provides a direct response without function calling because:
# 1. The information is part of its knowledge base
# 2. No external data or computation is required
# 3. The response is immediate and accurate

Este ejemplo demuestra una llamada simple a la API de OpenAI que no utiliza llamadas a funciones. Aquí está el desglose de sus componentes principales:

  • Importaciones y Configuración: El código importa las bibliotecas necesarias de OpenAI y JSON.
  • Definición de Conversación: Crea un array de mensajes con dos elementos: 
    • Un mensaje del sistema que define el rol del asistente como conocedor
    • Un mensaje del usuario preguntando sobre la capital de Francia
  • Llamada a la API: Realiza una llamada directa al endpoint ChatCompletion de OpenAI con: 
    • El modelo GPT-4
    • El array de mensajes
    • Temperatura establecida en 0 para obtener respuestas consistentes

El código está específicamente diseñado para demostrar un caso donde no es necesaria la llamada a funciones, ya que trata con conocimiento general que no requiere datos externos ni cálculos. Esto hace que la respuesta sea inmediata y eficiente.

Este sofisticado proceso de toma de decisiones consciente del contexto asegura que las funciones solo se llamen cuando sea necesario, optimizando el rendimiento y manteniendo la fluidez de la conversación. La capacidad del modelo para distinguir entre escenarios que requieren llamadas a funciones y los que no ayuda a crear interacciones más eficientes y naturales.

Otro Ejemplo de Llamada a la API con Definición de Función:

import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

# Define the function schema as shown above.
function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

# Create a conversation that leads to the function call.
messages = [
    {"role": "system", "content": "You are a helpful assistant that can perform calculations."},
    {"role": "user", "content": "What is the sum of 12 and 30?"}
]

response = openai.ChatCompletion.create(
    model="gpt-4o",
    messages=messages,
    functions=function_definitions,
    function_call="auto",  # Let the model decide if it should call the function.
    max_tokens=100,
    temperature=0.5
)

# Check if the model decided to call the function.
if response["choices"][0].get("finish_reason") == "function_call":
    function_call_info = response["choices"][0]["message"]["function_call"]
    print("Function to be called:")
    print(f"Name: {function_call_info['name']}")
    print(f"Arguments: {function_call_info['arguments']}")
else:
    print("Response:")
    print(response["choices"][0]["message"]["content"])

Desglose del código:

1. Configuración e Importaciones

  • El código importa las bibliotecas necesarias: openai para acceso a la API, os para variables de entorno, y dotenv para cargar variables de entorno
  • Configura la clave API de OpenAI desde las variables de entorno para la seguridad

2. Definición de Función

  • Define una función calculate_sum que toma dos parámetros (a y b)
  • El esquema de la función incluye:
    • Nombre y descripción de la función
    • Especificaciones de parámetros con tipos y descripciones
    • Ambos parámetros están marcados como obligatorios

3. Configuración de la Conversación

  • Crea un array inicial de conversación con dos mensajes:
  • Un mensaje del sistema que define el rol del asistente como ayudante de cálculos
  • Un mensaje del usuario pidiendo la suma de 12 y 30

4. Llamada a la API

  • Realiza una llamada al endpoint ChatCompletion de OpenAI con estos parámetros:
  • Utiliza el modelo GPT-4o
  • Incluye el array de mensajes
  • Pasa las definiciones de funciones
  • Establece function_call en "auto" permitiendo que el modelo decida si usar la función
  • Configura max_tokens y temperature para control de respuesta

5. Manejo de Respuesta

  • Verifica si el modelo decidió hacer una llamada a función
  • Si es así: Imprime el nombre de la función y los argumentos que planea usar
  • Si no: Imprime la respuesta directa del modelo

Este ejemplo demuestra una implementación básica de la capacidad de llamada a funciones de OpenAI, permitiendo que el modelo decida dinámicamente si usar una función personalizada basada en la entrada del usuario.

La definición precisa de funciones y parámetros es esencial para aprovechar todo el potencial de la llamada a funciones. Veamos por qué esto es crucial:

Primero, un esquema de función bien definido actúa como un contrato claro entre tu aplicación y el modelo de IA. Especifica exactamente qué hace la función, qué parámetros acepta y qué restricciones existen en esos parámetros. Esta claridad ayuda al modelo a tomar mejores decisiones sobre cuándo y cómo usar la función.

Segundo, la validación de parámetros se vuelve mucho más confiable cuando tienes definiciones precisas. Por ejemplo, si especificas que un parámetro debe ser un número dentro de cierto rango, o que una cadena debe coincidir con un patrón específico, creas una red de seguridad que detecta entradas inválidas antes de que puedan causar problemas en tu aplicación.

Tercero, las definiciones adecuadas de funciones permiten que el modelo comprenda el contexto y propósito de cada parámetro. Al incluir descripciones detalladas y ejemplos, ayudas al modelo a tomar decisiones más inteligentes sobre los valores de los parámetros y aseguras que la función se use según lo previsto.

Con esta base robusta establecida, puedes integrar con confianza operaciones dinámicas en tus aplicaciones de IA. Estas operaciones pueden incluir:

  • Realizar cálculos complejos con entradas validadas
  • Recuperar datos de fuentes externas manteniendo la integridad de los datos
  • Ejecutar lógica de negocio personalizada con manejo apropiado de errores
  • Gestionar llamadas a API con formato apropiado de parámetros

Esta atención al detalle en la definición de funciones finalmente conduce a aplicaciones de IA más confiables, mantenibles y potentes.

6.2 Definiendo Funciones y Parámetros

Al implementar llamadas a funciones con la API de Chat Completions, los desarrolladores deben proporcionar información completa y bien estructurada sobre las funciones disponibles. Esto se logra a través de un esquema de definición de función - un objeto JSON cuidadosamente elaborado que sirve como plano detallado para cada función. Este esquema es crucial para permitir que el modelo de IA entienda y utilice correctamente las funciones a su disposición.

Analicemos en profundidad sus tres componentes críticos:

  1. El nombre de la función - Sirve como identificador único en tu código. Como un número de seguridad social para tu función, debe ser distintivo y significativo, permitiendo que el modelo lo referencie sin ambigüedad al hacer llamadas a funciones.
  2. Una descripción detallada del propósito - Esto es esencialmente la descripción del trabajo de la función. Necesita ser lo suficientemente completa para que el modelo pueda determinar con precisión cuándo esta función es la herramienta adecuada para el trabajo. La descripción debe delinear claramente las capacidades, limitaciones y casos de uso previstos de la función.
  3. Una especificación completa de parámetros - Esto incluye no solo una lista de parámetros, sino un esquema detallado que describe el tipo de cada parámetro (string, number, boolean, etc.), propósito, restricciones y si es requerido u opcional. Piensa en esto como un manual de instrucciones detallado sobre cómo "alimentar" correctamente datos a tu función.

Este enfoque estructurado crea lo que llamamos un "contrato" entre tu aplicación y el modelo de IA - un acuerdo formal sobre cómo se comunicarán y trabajarán juntos. Es similar a incorporar un nuevo miembro al equipo: así como proporcionarías documentación detallada y pautas a un nuevo empleado, necesitas dar al modelo de IA instrucciones claras y específicas sobre tus funciones.

Cuanto más precisas y completas sean tus definiciones de funciones, mejor podrá el modelo realizar estas tareas esenciales:

Emparejar las intenciones del usuario con la función apropiada

Actuando como un intérprete sofisticado, el modelo analiza la entrada en lenguaje natural y determina qué función sirve mejor a las necesidades del usuario. Este proceso involucra varios niveles de comprensión: Primero, el modelo analiza el significado semántico de la solicitud del usuario, desglosando los componentes para identificar la acción central o la información que se busca.

Luego, lo evalúa contra su biblioteca de funciones disponibles, emparejando la intención con las capacidades de la función más adecuada. Por ejemplo, cuando un usuario pregunta "¿Qué tiempo hace?", el modelo sigue este proceso: reconoce esto como una solicitud de información meteorológica actual (no histórica ni pronóstico), identifica que esto requiere acceso a datos en tiempo real, y determina que una función de API meteorológica sería la herramienta apropiada - no una función de calculadora u otras opciones disponibles. Este enrutamiento inteligente asegura que las solicitudes de los usuarios sean manejadas por las herramientas más apropiadas, haciendo las interacciones más eficientes y precisas.

Validar y formatear correctamente los parámetros de entrada

El modelo actúa como un validador de datos sofisticado, asegurando que todas las entradas se ajusten precisamente a las especificaciones requeridas antes de cualquier ejecución de función. Este proceso de validación opera en múltiples niveles:

  1. Validación de Tipo: Asegura que cada parámetro coincida con su tipo de datos declarado (texto, número, booleano, etc.)
  2. Verificación de Rango: Comprueba que los valores numéricos estén dentro de los límites y restricciones aceptables
  3. Estandarización de Formato: Convierte automáticamente los datos al formato requerido, incluyendo:
    • Conversiones de unidades (por ejemplo, Celsius a Fahrenheit, metros a pies)
    • Estandarización de fecha/hora a través de diferentes formatos y zonas horarias
    • Formateo de texto para un manejo consistente de datos
  4. Validación Contextual: Analiza si la entrada tiene sentido lógico en el contexto dado

Por ejemplo, si una función espera una lectura de temperatura en Celsius entre -50°C y 50°C, el modelo:

  • Convertirá cualquier entrada en Fahrenheit a Celsius
  • Verificará que el valor esté dentro del rango aceptable
  • Formateará el número a la precisión decimal apropiada
  • Marcará cualquier valor anómalo que pudiera indicar errores

Tomar decisiones informadas sobre cuándo activar funciones específicas

Más allá de simplemente saber cómo usar las funciones, el modelo emplea un análisis de contexto sofisticado para determinar cuándo las llamadas a funciones son apropiadas. Este proceso de toma de decisiones involucra varios niveles de evaluación:

  1. Análisis de Intención: El modelo diferencia entre varios tipos de declaraciones del usuario:
    • Solicitudes directas que requieren acción ("¿Cuál es la temperatura exterior?")
    • Preguntas retóricas ("¿No está agradable el clima?")
    • Observaciones casuales ("Se siente cálido hoy")
    • Escenarios hipotéticos ("Si estuviera lloviendo...")
  2. Evaluación de Contexto: El modelo considera:
    • Historial previo de conversación
    • Estado actual de la conversación
    • Necesidades implícitas y explícitas del usuario
    • Capacidades de funciones disponibles
  3. Evaluación de Eficiencia: El modelo determina si:
    • Una llamada a función proporcionaría valor más allá de una simple respuesta de texto
    • Múltiples funciones podrían necesitar ser coordinadas
    • La información solicitada requiere datos en tiempo real
    • Una respuesta en caché podría ser suficiente

Por ejemplo, en una conversación relacionada con el clima, el modelo puede distinguir entre "¿No está agradable el clima?" (que no requiere llamada a función) y "¿Cuál es la temperatura exterior?" (que requiere acceder a datos meteorológicos actuales a través de una función API). Esta discriminación inteligente asegura un uso óptimo de recursos y mantiene un flujo natural de conversación mientras proporciona información precisa y relevante cuando es necesario.

Evitar llamadas a funciones innecesarias o incorrectas

El modelo mantiene la eficiencia del sistema evitando llamadas a funciones redundantes o inapropiadas a través de varios mecanismos sofisticados:

  1. Conciencia de caché: Reconoce cuándo los datos han sido recuperados recientemente y evita llamadas duplicadas. Por ejemplo, no llamará a una función meteorológica múltiples veces para la misma ubicación en un corto período de tiempo.
  2. Evaluación del tipo de respuesta: El modelo evalúa si una llamada a función es verdaderamente necesaria. Para consultas simples que pueden responderse con el conocimiento existente, proporcionará una respuesta directa de texto en lugar de hacer una llamada API.
  3. Persistencia de contexto: Rastrea el contexto de la conversación para evitar solicitar información que ya se ha establecido. Esto previene llamadas API redundantes para datos que ya están disponibles.
  4. Optimización de recursos: El modelo evalúa el costo computacional de las llamadas a funciones y elige el enfoque más eficiente. Por ejemplo, si múltiples funciones podrían proporcionar una respuesta, selecciona la que requiere menos recursos.

Esta gestión inteligente de funciones asegura un rendimiento óptimo del sistema mientras mantiene la precisión de las respuestas y la calidad de la experiencia del usuario.

Al invertir tiempo en crear y mantener definiciones de funciones claras y detalladas, estableces una base robusta para tu sistema impulsado por IA. Esta base asegura interacciones confiables y consistentes que no solo satisfacen las necesidades del usuario, sino que lo hacen de una manera que mantiene la integridad del sistema y produce resultados predecibles y de alta calidad. Piensa en ello como construir un puente entre el lenguaje humano y la funcionalidad computacional - cuanto más fuerte y mejor diseñado esté este puente, más fluida será la interacción.

6.2.1 Componentes Clave de una Definición de Función

Una definición de función consiste en varios componentes cruciales que trabajan juntos para crear un contrato claro entre tu aplicación y el modelo de IA.

Exploremos cada componente:

  1. Nombre: Un identificador único para tu función que sirve como su punto de referencia principal. Piensa en él como el título oficial de la función que el modelo usará para invocarla. El nombre debe ser:
    • Descriptivo y autoexplicativo (por ejemplo, "calculate_temperature_conversion" en lugar de solo "convert")
    • Único dentro del alcance de tu aplicación para evitar confusiones
    • Seguir una convención de nomenclatura consistente (típicamente snake_case o camelCase)
  2. Descripción: Una explicación completa del propósito y capacidades de la función. Esto es crucial porque:
    • Ayuda al modelo a entender exactamente cuándo usar esta función
    • Debe especificar cualquier limitación o caso de uso específico
    • Cuanto más detallada y precisa sea la descripción, mejor podrá el modelo tomar decisiones sobre su uso
  3. Parámetros: Un esquema JSON detallado que define los requisitos de entrada de la función. Este plano integral sirve como un contrato para cómo los datos deben ser pasados a la función. Esta definición estructurada incluye:
  • Tipo: Más comúnmente un objeto, que sirve como contenedor para todos los demás parámetros. Esto permite una organización estructurada de datos y relaciones claras entre parámetros. Piensa en ello como un envoltorio que contiene todas las piezas individuales de información que tu función necesita. Por ejemplo, una función meteorológica podría tener un objeto de ubicación que contenga parámetros de ciudad, estado y país.
  • Propiedades: Un mapeo detallado de cada parámetro, incluyendo:
    • Nombre: Un identificador claro y descriptivo que transmite inmediatamente el propósito del parámetro (por ejemplo, 'temperatureCelsius' en lugar de solo 'temp')
    • Tipo de datos: Restricciones específicas de tipo (texto, número, entero, booleano, etc.) que aseguran la consistencia de datos y previenen errores relacionados con el tipo
    • Descripción: Explicación clara de lo que representa el parámetro, incluyendo cualquier requisito específico de formato o rangos de valores aceptables
    • Restricciones opcionales: Como valores mínimos/máximos, patrones o enumeraciones. Estos actúan como barandillas para prevenir el procesamiento de datos inválidos. Por ejemplo, establecer un rango de temperatura de -50 a 150 para valores en Celsius
  • Requeridos: Una lista explícita de parámetros obligatorios que deben proporcionarse para que la función funcione correctamente. Esto ayuda a asegurar que todos los datos necesarios estén disponibles antes de llamar a la función, previniendo errores en tiempo de ejecución y mejorando la confiabilidad. Por ejemplo, una función de geocodificación podría requerir tanto latitud como longitud como parámetros requeridos, mientras que un parámetro de zona horaria podría ser opcional.

Ejemplo Detallado: Definiendo una Función Calculadora

Exploremos un ejemplo práctico para demostrar la definición de función. Crearemos una función llamada calculate_sum que realiza una operación aritmética básica - sumar dos números. Este ejemplo, aunque simple, ilustra todos los componentes clave de la definición de función y nos ayudará a entender cómo estructurar funciones más complejas.

La función tomará dos entradas numéricas y devolverá su suma, demostrando la definición adecuada de parámetros, especificación de tipo y descripción del propósito de la función. Así es como puedes definirla:

Ejemplo de Esquema de Función:

function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

Explicación:

  • Nombre: "calculate_sum" identifica de manera única nuestra función.
  • Descripción: Explica que la función toma dos números y devuelve su suma.
  • Objeto de Parámetros:
    • Tipo: Establecido como "object", ya que esperamos un objeto que contenga parámetros.
    • Propiedades:
      • "a" está definido como un número con una explicación descriptiva.
      • "b" está definido de manera similar.
    • Requeridos: Tanto "a" como "b" son parámetros requeridos para la función.

Aquí hay una versión ampliada de la definición de función con más características y manejo integral de errores:

function_definitions = [
    {
        "name": "calculate_arithmetic",
        "description": "Performs basic arithmetic operations between two numbers with input validation and error handling.",
        "parameters": {
            "type": "object",
            "properties": {
                "operation": {
                    "type": "string",
                    "description": "The arithmetic operation to perform",
                    "enum": ["add", "subtract", "multiply", "divide"],
                },
                "a": {
                    "type": "number",
                    "description": "The first number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "b": {
                    "type": "number",
                    "description": "The second number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "round_to": {
                    "type": "integer",
                    "description": "Number of decimal places to round to",
                    "minimum": 0,
                    "maximum": 10,
                    "default": 2
                }
            },
            "required": ["operation", "a", "b"]
        }
    }
]

def handle_arithmetic_operation(args):
    try:
        operation = args.get("operation")
        a = float(args.get("a"))
        b = float(args.get("b"))
        round_to = int(args.get("round_to", 2))
        
        result = None
        if operation == "add":
            result = a + b
        elif operation == "subtract":
            result = a - b
        elif operation == "multiply":
            result = a * b
        elif operation == "divide":
            if b == 0:
                raise ValueError("Division by zero is not allowed")
            result = a / b
            
        return {"result": round(result, round_to)}
        
    except ValueError as e:
        return {"error": str(e)}
    except Exception as e:
        return {"error": f"Unexpected error: {str(e)}"}

# Example API implementation
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=[
        {"role": "system", "content": "You are a helpful assistant that performs calculations."},
        {"role": "user", "content": "What is 15.7 divided by 2.5?"}
    ],
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the response
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Execute the function and get result
    result = handle_arithmetic_operation(json.loads(function_call.arguments))
    
    # Send the result back to the model for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": "You are a helpful assistant that performs calculations."},
            {"role": "user", "content": "What is 15.7 divided by 2.5?"},
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "calculate_arithmetic", "content": json.dumps(result)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)

Mejoras y adiciones clave en esta versión ampliada:

  • Parámetros Mejorados:
    • Adición de selección de tipo de operación (suma, resta, multiplicación, división)
    • Inclusión de validación de entrada con límites mínimos/máximos
    • Adición de parámetro opcional de precisión de redondeo
  • Manejo de Errores:
    • Valida tipos y rangos de entrada
    • Gestiona división por cero
    • Maneja errores inesperados de manera elegante
  • Implementación de Funciones:
    • Implementación completa de la función de manejo
    • Conversión y validación adecuada de tipos
    • Funcionalidad de redondeo de resultados
  • Integración de API:
    • Flujo de conversación en dos pasos
    • Maneja resultados de funciones y genera respuestas en lenguaje natural
    • Mantiene el contexto de la conversación

Esta versión ampliada demuestra un enfoque más preparado para producción con manejo de errores adecuado, validación de entrada y flujo de conversación completo.

6.2.2 Integrando Definiciones de Funciones en una Llamada API

Una vez que has definido tu función, el siguiente paso crucial es integrarla en tu llamada API. Este proceso de integración es fundamental ya que crea un puente entre tu aplicación y el modelo de IA, permitiéndole entender y utilizar tus funciones personalizadas de manera efectiva. El modelo emplea un sistema de análisis sofisticado que examina cuidadosamente el esquema de tu función, considerando varios factores:

  1. Análisis de Esquema: El modelo realiza un examen exhaustivo de la estructura de tu función. Esto incluye analizar el nombre de la función para entender su propósito, revisar la descripción detallada para comprender sus capacidades, y evaluar los requisitos de parámetros para asegurar un manejo adecuado de entrada. Por ejemplo, al examinar una función meteorológica, comprenderá tanto los requisitos básicos (como ubicación) como cualquier parámetro opcional (como unidades de temperatura).
  2. Evaluación de Contexto: El modelo realiza un análisis exhaustivo del flujo de conversación actual y el contexto histórico. Examina mensajes previos, consultas del usuario y el diálogo en curso para construir una comprensión completa de las necesidades actuales del usuario. Esto ayuda a determinar si una llamada a función es apropiada para la situación actual y cómo debe ejecutarse.
  3. Coincidencia de Intención: A través de algoritmos sofisticados de comparación, el modelo evalúa la intención del usuario contra la biblioteca de funciones disponibles. Busca coincidencias semánticas entre la solicitud del usuario y las capacidades de la función, considerando variaciones en cómo los usuarios podrían expresar sus necesidades. Por ejemplo, "¿Cuál es la temperatura afuera?" y "¿Qué calor hace?" coincidirían ambas con una función meteorológica.
  4. Lógica de Decisión: El modelo emplea un proceso de decisión de múltiples pasos basado en toda la información recopilada. Pondera factores como la certeza de la coincidencia, la completitud de los parámetros disponibles, y si una llamada a función proporcionaría mejores resultados que una respuesta directa. Esto asegura que las funciones solo se llamen cuando realmente mejorarán la experiencia del usuario y proporcionarán información más precisa o útil.

Examinemos este proceso de toma de decisiones en detalle a través de dos ejemplos contrastantes que destacan cuándo las llamadas a funciones son necesarias versus cuándo las respuestas directas son más apropiadas:

Ejemplo 1 - Llamada a Función Necesaria:
Usuario: "¿Qué tiempo hace en París?"

  • El modelo reconoce que esto requiere datos externos porque la información meteorológica actual no puede conocerse sin acceder a datos en tiempo real
  • Identifica la función get_weather como relevante al hacer coincidir la intención del usuario de obtener información meteorológica con el propósito de la función
  • Confirma que los parámetros (location="Paris") están disponibles y correctamente formateados para la llamada API
  • Se prepara para ejecutar la llamada a función formateando la solicitud según el esquema de la función

El código:

import openai
import json
from datetime import datetime

# Define the weather function
function_definitions = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a specified location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City name for weather information"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "default": "celsius",
                    "description": "Temperature unit preference"
                }
            },
            "required": ["location"]
        }
    }
]

# Mock weather API function
def get_weather_data(args):
    # In a real implementation, this would call an actual weather API
    location = args.get("location")
    units = args.get("units", "celsius")
    
    # Mock response
    return {
        "location": location,
        "temperature": 22 if units == "celsius" else 71.6,
        "conditions": "partly cloudy",
        "humidity": 65,
        "timestamp": datetime.now().isoformat()
    }

# Example conversation
messages = [
    {"role": "system", "content": "You are a helpful assistant that can check weather conditions."},
    {"role": "user", "content": "What's the weather in Paris?"}
]

# Initial API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the function call
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Get weather data
    weather_data = get_weather_data(json.loads(function_call.arguments))
    
    # Send the result back for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            *messages,
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "get_weather", "content": json.dumps(weather_data)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)
    # Output might be: "Currently in Paris, it's 22°C (71.6°F) and partly cloudy with 65% humidity."

Aquí está el desglose de sus componentes principales:

1. Definición de Función

  • Define una función meteorológica que acepta dos parámetros:
    • "ubicación" (obligatorio): Una cadena para el nombre de la ciudad
    • "unidades" (opcional): Unidades de temperatura, con celsius por defecto

2. Implementación Simulada del Clima

  • Crea una función simulada del clima que imita una respuesta de API
    • Devuelve ubicación, temperatura, condiciones, humedad y marca temporal
    • Maneja la conversión de unidades entre celsius y fahrenheit

3. Flujo de Conversación

  • Establece una conversación básica con:
    • Mensaje del sistema que define el rol del asistente
    • Mensaje del usuario preguntando sobre el clima en París

4. Integración de API

  • Realiza dos llamadas API a OpenAI:
    1. Llamada inicial para procesar la solicitud del usuario y determinar si se necesita una llamada a función
    2. Llamada de seguimiento para generar una respuesta en lenguaje natural basada en los datos meteorológicos

5. Manejo de Respuesta

  • Procesa la respuesta de la API mediante:
    • Verificación si se necesita una llamada a función
    • Ejecución de la función meteorológica con los argumentos proporcionados
    • Formateo de la respuesta en lenguaje natural

Este ejemplo demuestra el ciclo de vida completo de una llamada a función, desde la entrada inicial del usuario hasta la salida final en lenguaje natural.

Ejemplo 2 - Respuesta Directa Apropiada:
Usuario: "¿Cuál es la capital de Francia?"

  • El modelo reconoce esto como conocimiento general que existe dentro de sus datos de entrenamiento
  • Determina que no se necesitan datos externos ya que es un hecho estático y bien establecido
  • Omite la llamada a función para optimizar el tiempo de respuesta y reducir el uso innecesario de API
  • Proporciona una respuesta directa desde su base de conocimientos existente

El código:

import openai
import json

# Define conversation with a factual query
messages = [
    {"role": "system", "content": "You are a helpful assistant with broad knowledge."},
    {"role": "user", "content": "What is the capital of France?"}
]

# Example API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    # Note: No functions parameter needed since this is a direct knowledge query
    temperature=0
)

# Print the response
print(response.choices[0].message.content)
# Output would be: "The capital of France is Paris."

# The model provides a direct response without function calling because:
# 1. The information is part of its knowledge base
# 2. No external data or computation is required
# 3. The response is immediate and accurate

Este ejemplo demuestra una llamada simple a la API de OpenAI que no utiliza llamadas a funciones. Aquí está el desglose de sus componentes principales:

  • Importaciones y Configuración: El código importa las bibliotecas necesarias de OpenAI y JSON.
  • Definición de Conversación: Crea un array de mensajes con dos elementos: 
    • Un mensaje del sistema que define el rol del asistente como conocedor
    • Un mensaje del usuario preguntando sobre la capital de Francia
  • Llamada a la API: Realiza una llamada directa al endpoint ChatCompletion de OpenAI con: 
    • El modelo GPT-4
    • El array de mensajes
    • Temperatura establecida en 0 para obtener respuestas consistentes

El código está específicamente diseñado para demostrar un caso donde no es necesaria la llamada a funciones, ya que trata con conocimiento general que no requiere datos externos ni cálculos. Esto hace que la respuesta sea inmediata y eficiente.

Este sofisticado proceso de toma de decisiones consciente del contexto asegura que las funciones solo se llamen cuando sea necesario, optimizando el rendimiento y manteniendo la fluidez de la conversación. La capacidad del modelo para distinguir entre escenarios que requieren llamadas a funciones y los que no ayuda a crear interacciones más eficientes y naturales.

Otro Ejemplo de Llamada a la API con Definición de Función:

import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

# Define the function schema as shown above.
function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

# Create a conversation that leads to the function call.
messages = [
    {"role": "system", "content": "You are a helpful assistant that can perform calculations."},
    {"role": "user", "content": "What is the sum of 12 and 30?"}
]

response = openai.ChatCompletion.create(
    model="gpt-4o",
    messages=messages,
    functions=function_definitions,
    function_call="auto",  # Let the model decide if it should call the function.
    max_tokens=100,
    temperature=0.5
)

# Check if the model decided to call the function.
if response["choices"][0].get("finish_reason") == "function_call":
    function_call_info = response["choices"][0]["message"]["function_call"]
    print("Function to be called:")
    print(f"Name: {function_call_info['name']}")
    print(f"Arguments: {function_call_info['arguments']}")
else:
    print("Response:")
    print(response["choices"][0]["message"]["content"])

Desglose del código:

1. Configuración e Importaciones

  • El código importa las bibliotecas necesarias: openai para acceso a la API, os para variables de entorno, y dotenv para cargar variables de entorno
  • Configura la clave API de OpenAI desde las variables de entorno para la seguridad

2. Definición de Función

  • Define una función calculate_sum que toma dos parámetros (a y b)
  • El esquema de la función incluye:
    • Nombre y descripción de la función
    • Especificaciones de parámetros con tipos y descripciones
    • Ambos parámetros están marcados como obligatorios

3. Configuración de la Conversación

  • Crea un array inicial de conversación con dos mensajes:
  • Un mensaje del sistema que define el rol del asistente como ayudante de cálculos
  • Un mensaje del usuario pidiendo la suma de 12 y 30

4. Llamada a la API

  • Realiza una llamada al endpoint ChatCompletion de OpenAI con estos parámetros:
  • Utiliza el modelo GPT-4o
  • Incluye el array de mensajes
  • Pasa las definiciones de funciones
  • Establece function_call en "auto" permitiendo que el modelo decida si usar la función
  • Configura max_tokens y temperature para control de respuesta

5. Manejo de Respuesta

  • Verifica si el modelo decidió hacer una llamada a función
  • Si es así: Imprime el nombre de la función y los argumentos que planea usar
  • Si no: Imprime la respuesta directa del modelo

Este ejemplo demuestra una implementación básica de la capacidad de llamada a funciones de OpenAI, permitiendo que el modelo decida dinámicamente si usar una función personalizada basada en la entrada del usuario.

La definición precisa de funciones y parámetros es esencial para aprovechar todo el potencial de la llamada a funciones. Veamos por qué esto es crucial:

Primero, un esquema de función bien definido actúa como un contrato claro entre tu aplicación y el modelo de IA. Especifica exactamente qué hace la función, qué parámetros acepta y qué restricciones existen en esos parámetros. Esta claridad ayuda al modelo a tomar mejores decisiones sobre cuándo y cómo usar la función.

Segundo, la validación de parámetros se vuelve mucho más confiable cuando tienes definiciones precisas. Por ejemplo, si especificas que un parámetro debe ser un número dentro de cierto rango, o que una cadena debe coincidir con un patrón específico, creas una red de seguridad que detecta entradas inválidas antes de que puedan causar problemas en tu aplicación.

Tercero, las definiciones adecuadas de funciones permiten que el modelo comprenda el contexto y propósito de cada parámetro. Al incluir descripciones detalladas y ejemplos, ayudas al modelo a tomar decisiones más inteligentes sobre los valores de los parámetros y aseguras que la función se use según lo previsto.

Con esta base robusta establecida, puedes integrar con confianza operaciones dinámicas en tus aplicaciones de IA. Estas operaciones pueden incluir:

  • Realizar cálculos complejos con entradas validadas
  • Recuperar datos de fuentes externas manteniendo la integridad de los datos
  • Ejecutar lógica de negocio personalizada con manejo apropiado de errores
  • Gestionar llamadas a API con formato apropiado de parámetros

Esta atención al detalle en la definición de funciones finalmente conduce a aplicaciones de IA más confiables, mantenibles y potentes.

6.2 Definiendo Funciones y Parámetros

Al implementar llamadas a funciones con la API de Chat Completions, los desarrolladores deben proporcionar información completa y bien estructurada sobre las funciones disponibles. Esto se logra a través de un esquema de definición de función - un objeto JSON cuidadosamente elaborado que sirve como plano detallado para cada función. Este esquema es crucial para permitir que el modelo de IA entienda y utilice correctamente las funciones a su disposición.

Analicemos en profundidad sus tres componentes críticos:

  1. El nombre de la función - Sirve como identificador único en tu código. Como un número de seguridad social para tu función, debe ser distintivo y significativo, permitiendo que el modelo lo referencie sin ambigüedad al hacer llamadas a funciones.
  2. Una descripción detallada del propósito - Esto es esencialmente la descripción del trabajo de la función. Necesita ser lo suficientemente completa para que el modelo pueda determinar con precisión cuándo esta función es la herramienta adecuada para el trabajo. La descripción debe delinear claramente las capacidades, limitaciones y casos de uso previstos de la función.
  3. Una especificación completa de parámetros - Esto incluye no solo una lista de parámetros, sino un esquema detallado que describe el tipo de cada parámetro (string, number, boolean, etc.), propósito, restricciones y si es requerido u opcional. Piensa en esto como un manual de instrucciones detallado sobre cómo "alimentar" correctamente datos a tu función.

Este enfoque estructurado crea lo que llamamos un "contrato" entre tu aplicación y el modelo de IA - un acuerdo formal sobre cómo se comunicarán y trabajarán juntos. Es similar a incorporar un nuevo miembro al equipo: así como proporcionarías documentación detallada y pautas a un nuevo empleado, necesitas dar al modelo de IA instrucciones claras y específicas sobre tus funciones.

Cuanto más precisas y completas sean tus definiciones de funciones, mejor podrá el modelo realizar estas tareas esenciales:

Emparejar las intenciones del usuario con la función apropiada

Actuando como un intérprete sofisticado, el modelo analiza la entrada en lenguaje natural y determina qué función sirve mejor a las necesidades del usuario. Este proceso involucra varios niveles de comprensión: Primero, el modelo analiza el significado semántico de la solicitud del usuario, desglosando los componentes para identificar la acción central o la información que se busca.

Luego, lo evalúa contra su biblioteca de funciones disponibles, emparejando la intención con las capacidades de la función más adecuada. Por ejemplo, cuando un usuario pregunta "¿Qué tiempo hace?", el modelo sigue este proceso: reconoce esto como una solicitud de información meteorológica actual (no histórica ni pronóstico), identifica que esto requiere acceso a datos en tiempo real, y determina que una función de API meteorológica sería la herramienta apropiada - no una función de calculadora u otras opciones disponibles. Este enrutamiento inteligente asegura que las solicitudes de los usuarios sean manejadas por las herramientas más apropiadas, haciendo las interacciones más eficientes y precisas.

Validar y formatear correctamente los parámetros de entrada

El modelo actúa como un validador de datos sofisticado, asegurando que todas las entradas se ajusten precisamente a las especificaciones requeridas antes de cualquier ejecución de función. Este proceso de validación opera en múltiples niveles:

  1. Validación de Tipo: Asegura que cada parámetro coincida con su tipo de datos declarado (texto, número, booleano, etc.)
  2. Verificación de Rango: Comprueba que los valores numéricos estén dentro de los límites y restricciones aceptables
  3. Estandarización de Formato: Convierte automáticamente los datos al formato requerido, incluyendo:
    • Conversiones de unidades (por ejemplo, Celsius a Fahrenheit, metros a pies)
    • Estandarización de fecha/hora a través de diferentes formatos y zonas horarias
    • Formateo de texto para un manejo consistente de datos
  4. Validación Contextual: Analiza si la entrada tiene sentido lógico en el contexto dado

Por ejemplo, si una función espera una lectura de temperatura en Celsius entre -50°C y 50°C, el modelo:

  • Convertirá cualquier entrada en Fahrenheit a Celsius
  • Verificará que el valor esté dentro del rango aceptable
  • Formateará el número a la precisión decimal apropiada
  • Marcará cualquier valor anómalo que pudiera indicar errores

Tomar decisiones informadas sobre cuándo activar funciones específicas

Más allá de simplemente saber cómo usar las funciones, el modelo emplea un análisis de contexto sofisticado para determinar cuándo las llamadas a funciones son apropiadas. Este proceso de toma de decisiones involucra varios niveles de evaluación:

  1. Análisis de Intención: El modelo diferencia entre varios tipos de declaraciones del usuario:
    • Solicitudes directas que requieren acción ("¿Cuál es la temperatura exterior?")
    • Preguntas retóricas ("¿No está agradable el clima?")
    • Observaciones casuales ("Se siente cálido hoy")
    • Escenarios hipotéticos ("Si estuviera lloviendo...")
  2. Evaluación de Contexto: El modelo considera:
    • Historial previo de conversación
    • Estado actual de la conversación
    • Necesidades implícitas y explícitas del usuario
    • Capacidades de funciones disponibles
  3. Evaluación de Eficiencia: El modelo determina si:
    • Una llamada a función proporcionaría valor más allá de una simple respuesta de texto
    • Múltiples funciones podrían necesitar ser coordinadas
    • La información solicitada requiere datos en tiempo real
    • Una respuesta en caché podría ser suficiente

Por ejemplo, en una conversación relacionada con el clima, el modelo puede distinguir entre "¿No está agradable el clima?" (que no requiere llamada a función) y "¿Cuál es la temperatura exterior?" (que requiere acceder a datos meteorológicos actuales a través de una función API). Esta discriminación inteligente asegura un uso óptimo de recursos y mantiene un flujo natural de conversación mientras proporciona información precisa y relevante cuando es necesario.

Evitar llamadas a funciones innecesarias o incorrectas

El modelo mantiene la eficiencia del sistema evitando llamadas a funciones redundantes o inapropiadas a través de varios mecanismos sofisticados:

  1. Conciencia de caché: Reconoce cuándo los datos han sido recuperados recientemente y evita llamadas duplicadas. Por ejemplo, no llamará a una función meteorológica múltiples veces para la misma ubicación en un corto período de tiempo.
  2. Evaluación del tipo de respuesta: El modelo evalúa si una llamada a función es verdaderamente necesaria. Para consultas simples que pueden responderse con el conocimiento existente, proporcionará una respuesta directa de texto en lugar de hacer una llamada API.
  3. Persistencia de contexto: Rastrea el contexto de la conversación para evitar solicitar información que ya se ha establecido. Esto previene llamadas API redundantes para datos que ya están disponibles.
  4. Optimización de recursos: El modelo evalúa el costo computacional de las llamadas a funciones y elige el enfoque más eficiente. Por ejemplo, si múltiples funciones podrían proporcionar una respuesta, selecciona la que requiere menos recursos.

Esta gestión inteligente de funciones asegura un rendimiento óptimo del sistema mientras mantiene la precisión de las respuestas y la calidad de la experiencia del usuario.

Al invertir tiempo en crear y mantener definiciones de funciones claras y detalladas, estableces una base robusta para tu sistema impulsado por IA. Esta base asegura interacciones confiables y consistentes que no solo satisfacen las necesidades del usuario, sino que lo hacen de una manera que mantiene la integridad del sistema y produce resultados predecibles y de alta calidad. Piensa en ello como construir un puente entre el lenguaje humano y la funcionalidad computacional - cuanto más fuerte y mejor diseñado esté este puente, más fluida será la interacción.

6.2.1 Componentes Clave de una Definición de Función

Una definición de función consiste en varios componentes cruciales que trabajan juntos para crear un contrato claro entre tu aplicación y el modelo de IA.

Exploremos cada componente:

  1. Nombre: Un identificador único para tu función que sirve como su punto de referencia principal. Piensa en él como el título oficial de la función que el modelo usará para invocarla. El nombre debe ser:
    • Descriptivo y autoexplicativo (por ejemplo, "calculate_temperature_conversion" en lugar de solo "convert")
    • Único dentro del alcance de tu aplicación para evitar confusiones
    • Seguir una convención de nomenclatura consistente (típicamente snake_case o camelCase)
  2. Descripción: Una explicación completa del propósito y capacidades de la función. Esto es crucial porque:
    • Ayuda al modelo a entender exactamente cuándo usar esta función
    • Debe especificar cualquier limitación o caso de uso específico
    • Cuanto más detallada y precisa sea la descripción, mejor podrá el modelo tomar decisiones sobre su uso
  3. Parámetros: Un esquema JSON detallado que define los requisitos de entrada de la función. Este plano integral sirve como un contrato para cómo los datos deben ser pasados a la función. Esta definición estructurada incluye:
  • Tipo: Más comúnmente un objeto, que sirve como contenedor para todos los demás parámetros. Esto permite una organización estructurada de datos y relaciones claras entre parámetros. Piensa en ello como un envoltorio que contiene todas las piezas individuales de información que tu función necesita. Por ejemplo, una función meteorológica podría tener un objeto de ubicación que contenga parámetros de ciudad, estado y país.
  • Propiedades: Un mapeo detallado de cada parámetro, incluyendo:
    • Nombre: Un identificador claro y descriptivo que transmite inmediatamente el propósito del parámetro (por ejemplo, 'temperatureCelsius' en lugar de solo 'temp')
    • Tipo de datos: Restricciones específicas de tipo (texto, número, entero, booleano, etc.) que aseguran la consistencia de datos y previenen errores relacionados con el tipo
    • Descripción: Explicación clara de lo que representa el parámetro, incluyendo cualquier requisito específico de formato o rangos de valores aceptables
    • Restricciones opcionales: Como valores mínimos/máximos, patrones o enumeraciones. Estos actúan como barandillas para prevenir el procesamiento de datos inválidos. Por ejemplo, establecer un rango de temperatura de -50 a 150 para valores en Celsius
  • Requeridos: Una lista explícita de parámetros obligatorios que deben proporcionarse para que la función funcione correctamente. Esto ayuda a asegurar que todos los datos necesarios estén disponibles antes de llamar a la función, previniendo errores en tiempo de ejecución y mejorando la confiabilidad. Por ejemplo, una función de geocodificación podría requerir tanto latitud como longitud como parámetros requeridos, mientras que un parámetro de zona horaria podría ser opcional.

Ejemplo Detallado: Definiendo una Función Calculadora

Exploremos un ejemplo práctico para demostrar la definición de función. Crearemos una función llamada calculate_sum que realiza una operación aritmética básica - sumar dos números. Este ejemplo, aunque simple, ilustra todos los componentes clave de la definición de función y nos ayudará a entender cómo estructurar funciones más complejas.

La función tomará dos entradas numéricas y devolverá su suma, demostrando la definición adecuada de parámetros, especificación de tipo y descripción del propósito de la función. Así es como puedes definirla:

Ejemplo de Esquema de Función:

function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

Explicación:

  • Nombre: "calculate_sum" identifica de manera única nuestra función.
  • Descripción: Explica que la función toma dos números y devuelve su suma.
  • Objeto de Parámetros:
    • Tipo: Establecido como "object", ya que esperamos un objeto que contenga parámetros.
    • Propiedades:
      • "a" está definido como un número con una explicación descriptiva.
      • "b" está definido de manera similar.
    • Requeridos: Tanto "a" como "b" son parámetros requeridos para la función.

Aquí hay una versión ampliada de la definición de función con más características y manejo integral de errores:

function_definitions = [
    {
        "name": "calculate_arithmetic",
        "description": "Performs basic arithmetic operations between two numbers with input validation and error handling.",
        "parameters": {
            "type": "object",
            "properties": {
                "operation": {
                    "type": "string",
                    "description": "The arithmetic operation to perform",
                    "enum": ["add", "subtract", "multiply", "divide"],
                },
                "a": {
                    "type": "number",
                    "description": "The first number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "b": {
                    "type": "number",
                    "description": "The second number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "round_to": {
                    "type": "integer",
                    "description": "Number of decimal places to round to",
                    "minimum": 0,
                    "maximum": 10,
                    "default": 2
                }
            },
            "required": ["operation", "a", "b"]
        }
    }
]

def handle_arithmetic_operation(args):
    try:
        operation = args.get("operation")
        a = float(args.get("a"))
        b = float(args.get("b"))
        round_to = int(args.get("round_to", 2))
        
        result = None
        if operation == "add":
            result = a + b
        elif operation == "subtract":
            result = a - b
        elif operation == "multiply":
            result = a * b
        elif operation == "divide":
            if b == 0:
                raise ValueError("Division by zero is not allowed")
            result = a / b
            
        return {"result": round(result, round_to)}
        
    except ValueError as e:
        return {"error": str(e)}
    except Exception as e:
        return {"error": f"Unexpected error: {str(e)}"}

# Example API implementation
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=[
        {"role": "system", "content": "You are a helpful assistant that performs calculations."},
        {"role": "user", "content": "What is 15.7 divided by 2.5?"}
    ],
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the response
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Execute the function and get result
    result = handle_arithmetic_operation(json.loads(function_call.arguments))
    
    # Send the result back to the model for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": "You are a helpful assistant that performs calculations."},
            {"role": "user", "content": "What is 15.7 divided by 2.5?"},
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "calculate_arithmetic", "content": json.dumps(result)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)

Mejoras y adiciones clave en esta versión ampliada:

  • Parámetros Mejorados:
    • Adición de selección de tipo de operación (suma, resta, multiplicación, división)
    • Inclusión de validación de entrada con límites mínimos/máximos
    • Adición de parámetro opcional de precisión de redondeo
  • Manejo de Errores:
    • Valida tipos y rangos de entrada
    • Gestiona división por cero
    • Maneja errores inesperados de manera elegante
  • Implementación de Funciones:
    • Implementación completa de la función de manejo
    • Conversión y validación adecuada de tipos
    • Funcionalidad de redondeo de resultados
  • Integración de API:
    • Flujo de conversación en dos pasos
    • Maneja resultados de funciones y genera respuestas en lenguaje natural
    • Mantiene el contexto de la conversación

Esta versión ampliada demuestra un enfoque más preparado para producción con manejo de errores adecuado, validación de entrada y flujo de conversación completo.

6.2.2 Integrando Definiciones de Funciones en una Llamada API

Una vez que has definido tu función, el siguiente paso crucial es integrarla en tu llamada API. Este proceso de integración es fundamental ya que crea un puente entre tu aplicación y el modelo de IA, permitiéndole entender y utilizar tus funciones personalizadas de manera efectiva. El modelo emplea un sistema de análisis sofisticado que examina cuidadosamente el esquema de tu función, considerando varios factores:

  1. Análisis de Esquema: El modelo realiza un examen exhaustivo de la estructura de tu función. Esto incluye analizar el nombre de la función para entender su propósito, revisar la descripción detallada para comprender sus capacidades, y evaluar los requisitos de parámetros para asegurar un manejo adecuado de entrada. Por ejemplo, al examinar una función meteorológica, comprenderá tanto los requisitos básicos (como ubicación) como cualquier parámetro opcional (como unidades de temperatura).
  2. Evaluación de Contexto: El modelo realiza un análisis exhaustivo del flujo de conversación actual y el contexto histórico. Examina mensajes previos, consultas del usuario y el diálogo en curso para construir una comprensión completa de las necesidades actuales del usuario. Esto ayuda a determinar si una llamada a función es apropiada para la situación actual y cómo debe ejecutarse.
  3. Coincidencia de Intención: A través de algoritmos sofisticados de comparación, el modelo evalúa la intención del usuario contra la biblioteca de funciones disponibles. Busca coincidencias semánticas entre la solicitud del usuario y las capacidades de la función, considerando variaciones en cómo los usuarios podrían expresar sus necesidades. Por ejemplo, "¿Cuál es la temperatura afuera?" y "¿Qué calor hace?" coincidirían ambas con una función meteorológica.
  4. Lógica de Decisión: El modelo emplea un proceso de decisión de múltiples pasos basado en toda la información recopilada. Pondera factores como la certeza de la coincidencia, la completitud de los parámetros disponibles, y si una llamada a función proporcionaría mejores resultados que una respuesta directa. Esto asegura que las funciones solo se llamen cuando realmente mejorarán la experiencia del usuario y proporcionarán información más precisa o útil.

Examinemos este proceso de toma de decisiones en detalle a través de dos ejemplos contrastantes que destacan cuándo las llamadas a funciones son necesarias versus cuándo las respuestas directas son más apropiadas:

Ejemplo 1 - Llamada a Función Necesaria:
Usuario: "¿Qué tiempo hace en París?"

  • El modelo reconoce que esto requiere datos externos porque la información meteorológica actual no puede conocerse sin acceder a datos en tiempo real
  • Identifica la función get_weather como relevante al hacer coincidir la intención del usuario de obtener información meteorológica con el propósito de la función
  • Confirma que los parámetros (location="Paris") están disponibles y correctamente formateados para la llamada API
  • Se prepara para ejecutar la llamada a función formateando la solicitud según el esquema de la función

El código:

import openai
import json
from datetime import datetime

# Define the weather function
function_definitions = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a specified location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City name for weather information"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "default": "celsius",
                    "description": "Temperature unit preference"
                }
            },
            "required": ["location"]
        }
    }
]

# Mock weather API function
def get_weather_data(args):
    # In a real implementation, this would call an actual weather API
    location = args.get("location")
    units = args.get("units", "celsius")
    
    # Mock response
    return {
        "location": location,
        "temperature": 22 if units == "celsius" else 71.6,
        "conditions": "partly cloudy",
        "humidity": 65,
        "timestamp": datetime.now().isoformat()
    }

# Example conversation
messages = [
    {"role": "system", "content": "You are a helpful assistant that can check weather conditions."},
    {"role": "user", "content": "What's the weather in Paris?"}
]

# Initial API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the function call
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Get weather data
    weather_data = get_weather_data(json.loads(function_call.arguments))
    
    # Send the result back for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            *messages,
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "get_weather", "content": json.dumps(weather_data)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)
    # Output might be: "Currently in Paris, it's 22°C (71.6°F) and partly cloudy with 65% humidity."

Aquí está el desglose de sus componentes principales:

1. Definición de Función

  • Define una función meteorológica que acepta dos parámetros:
    • "ubicación" (obligatorio): Una cadena para el nombre de la ciudad
    • "unidades" (opcional): Unidades de temperatura, con celsius por defecto

2. Implementación Simulada del Clima

  • Crea una función simulada del clima que imita una respuesta de API
    • Devuelve ubicación, temperatura, condiciones, humedad y marca temporal
    • Maneja la conversión de unidades entre celsius y fahrenheit

3. Flujo de Conversación

  • Establece una conversación básica con:
    • Mensaje del sistema que define el rol del asistente
    • Mensaje del usuario preguntando sobre el clima en París

4. Integración de API

  • Realiza dos llamadas API a OpenAI:
    1. Llamada inicial para procesar la solicitud del usuario y determinar si se necesita una llamada a función
    2. Llamada de seguimiento para generar una respuesta en lenguaje natural basada en los datos meteorológicos

5. Manejo de Respuesta

  • Procesa la respuesta de la API mediante:
    • Verificación si se necesita una llamada a función
    • Ejecución de la función meteorológica con los argumentos proporcionados
    • Formateo de la respuesta en lenguaje natural

Este ejemplo demuestra el ciclo de vida completo de una llamada a función, desde la entrada inicial del usuario hasta la salida final en lenguaje natural.

Ejemplo 2 - Respuesta Directa Apropiada:
Usuario: "¿Cuál es la capital de Francia?"

  • El modelo reconoce esto como conocimiento general que existe dentro de sus datos de entrenamiento
  • Determina que no se necesitan datos externos ya que es un hecho estático y bien establecido
  • Omite la llamada a función para optimizar el tiempo de respuesta y reducir el uso innecesario de API
  • Proporciona una respuesta directa desde su base de conocimientos existente

El código:

import openai
import json

# Define conversation with a factual query
messages = [
    {"role": "system", "content": "You are a helpful assistant with broad knowledge."},
    {"role": "user", "content": "What is the capital of France?"}
]

# Example API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    # Note: No functions parameter needed since this is a direct knowledge query
    temperature=0
)

# Print the response
print(response.choices[0].message.content)
# Output would be: "The capital of France is Paris."

# The model provides a direct response without function calling because:
# 1. The information is part of its knowledge base
# 2. No external data or computation is required
# 3. The response is immediate and accurate

Este ejemplo demuestra una llamada simple a la API de OpenAI que no utiliza llamadas a funciones. Aquí está el desglose de sus componentes principales:

  • Importaciones y Configuración: El código importa las bibliotecas necesarias de OpenAI y JSON.
  • Definición de Conversación: Crea un array de mensajes con dos elementos: 
    • Un mensaje del sistema que define el rol del asistente como conocedor
    • Un mensaje del usuario preguntando sobre la capital de Francia
  • Llamada a la API: Realiza una llamada directa al endpoint ChatCompletion de OpenAI con: 
    • El modelo GPT-4
    • El array de mensajes
    • Temperatura establecida en 0 para obtener respuestas consistentes

El código está específicamente diseñado para demostrar un caso donde no es necesaria la llamada a funciones, ya que trata con conocimiento general que no requiere datos externos ni cálculos. Esto hace que la respuesta sea inmediata y eficiente.

Este sofisticado proceso de toma de decisiones consciente del contexto asegura que las funciones solo se llamen cuando sea necesario, optimizando el rendimiento y manteniendo la fluidez de la conversación. La capacidad del modelo para distinguir entre escenarios que requieren llamadas a funciones y los que no ayuda a crear interacciones más eficientes y naturales.

Otro Ejemplo de Llamada a la API con Definición de Función:

import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

# Define the function schema as shown above.
function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

# Create a conversation that leads to the function call.
messages = [
    {"role": "system", "content": "You are a helpful assistant that can perform calculations."},
    {"role": "user", "content": "What is the sum of 12 and 30?"}
]

response = openai.ChatCompletion.create(
    model="gpt-4o",
    messages=messages,
    functions=function_definitions,
    function_call="auto",  # Let the model decide if it should call the function.
    max_tokens=100,
    temperature=0.5
)

# Check if the model decided to call the function.
if response["choices"][0].get("finish_reason") == "function_call":
    function_call_info = response["choices"][0]["message"]["function_call"]
    print("Function to be called:")
    print(f"Name: {function_call_info['name']}")
    print(f"Arguments: {function_call_info['arguments']}")
else:
    print("Response:")
    print(response["choices"][0]["message"]["content"])

Desglose del código:

1. Configuración e Importaciones

  • El código importa las bibliotecas necesarias: openai para acceso a la API, os para variables de entorno, y dotenv para cargar variables de entorno
  • Configura la clave API de OpenAI desde las variables de entorno para la seguridad

2. Definición de Función

  • Define una función calculate_sum que toma dos parámetros (a y b)
  • El esquema de la función incluye:
    • Nombre y descripción de la función
    • Especificaciones de parámetros con tipos y descripciones
    • Ambos parámetros están marcados como obligatorios

3. Configuración de la Conversación

  • Crea un array inicial de conversación con dos mensajes:
  • Un mensaje del sistema que define el rol del asistente como ayudante de cálculos
  • Un mensaje del usuario pidiendo la suma de 12 y 30

4. Llamada a la API

  • Realiza una llamada al endpoint ChatCompletion de OpenAI con estos parámetros:
  • Utiliza el modelo GPT-4o
  • Incluye el array de mensajes
  • Pasa las definiciones de funciones
  • Establece function_call en "auto" permitiendo que el modelo decida si usar la función
  • Configura max_tokens y temperature para control de respuesta

5. Manejo de Respuesta

  • Verifica si el modelo decidió hacer una llamada a función
  • Si es así: Imprime el nombre de la función y los argumentos que planea usar
  • Si no: Imprime la respuesta directa del modelo

Este ejemplo demuestra una implementación básica de la capacidad de llamada a funciones de OpenAI, permitiendo que el modelo decida dinámicamente si usar una función personalizada basada en la entrada del usuario.

La definición precisa de funciones y parámetros es esencial para aprovechar todo el potencial de la llamada a funciones. Veamos por qué esto es crucial:

Primero, un esquema de función bien definido actúa como un contrato claro entre tu aplicación y el modelo de IA. Especifica exactamente qué hace la función, qué parámetros acepta y qué restricciones existen en esos parámetros. Esta claridad ayuda al modelo a tomar mejores decisiones sobre cuándo y cómo usar la función.

Segundo, la validación de parámetros se vuelve mucho más confiable cuando tienes definiciones precisas. Por ejemplo, si especificas que un parámetro debe ser un número dentro de cierto rango, o que una cadena debe coincidir con un patrón específico, creas una red de seguridad que detecta entradas inválidas antes de que puedan causar problemas en tu aplicación.

Tercero, las definiciones adecuadas de funciones permiten que el modelo comprenda el contexto y propósito de cada parámetro. Al incluir descripciones detalladas y ejemplos, ayudas al modelo a tomar decisiones más inteligentes sobre los valores de los parámetros y aseguras que la función se use según lo previsto.

Con esta base robusta establecida, puedes integrar con confianza operaciones dinámicas en tus aplicaciones de IA. Estas operaciones pueden incluir:

  • Realizar cálculos complejos con entradas validadas
  • Recuperar datos de fuentes externas manteniendo la integridad de los datos
  • Ejecutar lógica de negocio personalizada con manejo apropiado de errores
  • Gestionar llamadas a API con formato apropiado de parámetros

Esta atención al detalle en la definición de funciones finalmente conduce a aplicaciones de IA más confiables, mantenibles y potentes.

6.2 Definiendo Funciones y Parámetros

Al implementar llamadas a funciones con la API de Chat Completions, los desarrolladores deben proporcionar información completa y bien estructurada sobre las funciones disponibles. Esto se logra a través de un esquema de definición de función - un objeto JSON cuidadosamente elaborado que sirve como plano detallado para cada función. Este esquema es crucial para permitir que el modelo de IA entienda y utilice correctamente las funciones a su disposición.

Analicemos en profundidad sus tres componentes críticos:

  1. El nombre de la función - Sirve como identificador único en tu código. Como un número de seguridad social para tu función, debe ser distintivo y significativo, permitiendo que el modelo lo referencie sin ambigüedad al hacer llamadas a funciones.
  2. Una descripción detallada del propósito - Esto es esencialmente la descripción del trabajo de la función. Necesita ser lo suficientemente completa para que el modelo pueda determinar con precisión cuándo esta función es la herramienta adecuada para el trabajo. La descripción debe delinear claramente las capacidades, limitaciones y casos de uso previstos de la función.
  3. Una especificación completa de parámetros - Esto incluye no solo una lista de parámetros, sino un esquema detallado que describe el tipo de cada parámetro (string, number, boolean, etc.), propósito, restricciones y si es requerido u opcional. Piensa en esto como un manual de instrucciones detallado sobre cómo "alimentar" correctamente datos a tu función.

Este enfoque estructurado crea lo que llamamos un "contrato" entre tu aplicación y el modelo de IA - un acuerdo formal sobre cómo se comunicarán y trabajarán juntos. Es similar a incorporar un nuevo miembro al equipo: así como proporcionarías documentación detallada y pautas a un nuevo empleado, necesitas dar al modelo de IA instrucciones claras y específicas sobre tus funciones.

Cuanto más precisas y completas sean tus definiciones de funciones, mejor podrá el modelo realizar estas tareas esenciales:

Emparejar las intenciones del usuario con la función apropiada

Actuando como un intérprete sofisticado, el modelo analiza la entrada en lenguaje natural y determina qué función sirve mejor a las necesidades del usuario. Este proceso involucra varios niveles de comprensión: Primero, el modelo analiza el significado semántico de la solicitud del usuario, desglosando los componentes para identificar la acción central o la información que se busca.

Luego, lo evalúa contra su biblioteca de funciones disponibles, emparejando la intención con las capacidades de la función más adecuada. Por ejemplo, cuando un usuario pregunta "¿Qué tiempo hace?", el modelo sigue este proceso: reconoce esto como una solicitud de información meteorológica actual (no histórica ni pronóstico), identifica que esto requiere acceso a datos en tiempo real, y determina que una función de API meteorológica sería la herramienta apropiada - no una función de calculadora u otras opciones disponibles. Este enrutamiento inteligente asegura que las solicitudes de los usuarios sean manejadas por las herramientas más apropiadas, haciendo las interacciones más eficientes y precisas.

Validar y formatear correctamente los parámetros de entrada

El modelo actúa como un validador de datos sofisticado, asegurando que todas las entradas se ajusten precisamente a las especificaciones requeridas antes de cualquier ejecución de función. Este proceso de validación opera en múltiples niveles:

  1. Validación de Tipo: Asegura que cada parámetro coincida con su tipo de datos declarado (texto, número, booleano, etc.)
  2. Verificación de Rango: Comprueba que los valores numéricos estén dentro de los límites y restricciones aceptables
  3. Estandarización de Formato: Convierte automáticamente los datos al formato requerido, incluyendo:
    • Conversiones de unidades (por ejemplo, Celsius a Fahrenheit, metros a pies)
    • Estandarización de fecha/hora a través de diferentes formatos y zonas horarias
    • Formateo de texto para un manejo consistente de datos
  4. Validación Contextual: Analiza si la entrada tiene sentido lógico en el contexto dado

Por ejemplo, si una función espera una lectura de temperatura en Celsius entre -50°C y 50°C, el modelo:

  • Convertirá cualquier entrada en Fahrenheit a Celsius
  • Verificará que el valor esté dentro del rango aceptable
  • Formateará el número a la precisión decimal apropiada
  • Marcará cualquier valor anómalo que pudiera indicar errores

Tomar decisiones informadas sobre cuándo activar funciones específicas

Más allá de simplemente saber cómo usar las funciones, el modelo emplea un análisis de contexto sofisticado para determinar cuándo las llamadas a funciones son apropiadas. Este proceso de toma de decisiones involucra varios niveles de evaluación:

  1. Análisis de Intención: El modelo diferencia entre varios tipos de declaraciones del usuario:
    • Solicitudes directas que requieren acción ("¿Cuál es la temperatura exterior?")
    • Preguntas retóricas ("¿No está agradable el clima?")
    • Observaciones casuales ("Se siente cálido hoy")
    • Escenarios hipotéticos ("Si estuviera lloviendo...")
  2. Evaluación de Contexto: El modelo considera:
    • Historial previo de conversación
    • Estado actual de la conversación
    • Necesidades implícitas y explícitas del usuario
    • Capacidades de funciones disponibles
  3. Evaluación de Eficiencia: El modelo determina si:
    • Una llamada a función proporcionaría valor más allá de una simple respuesta de texto
    • Múltiples funciones podrían necesitar ser coordinadas
    • La información solicitada requiere datos en tiempo real
    • Una respuesta en caché podría ser suficiente

Por ejemplo, en una conversación relacionada con el clima, el modelo puede distinguir entre "¿No está agradable el clima?" (que no requiere llamada a función) y "¿Cuál es la temperatura exterior?" (que requiere acceder a datos meteorológicos actuales a través de una función API). Esta discriminación inteligente asegura un uso óptimo de recursos y mantiene un flujo natural de conversación mientras proporciona información precisa y relevante cuando es necesario.

Evitar llamadas a funciones innecesarias o incorrectas

El modelo mantiene la eficiencia del sistema evitando llamadas a funciones redundantes o inapropiadas a través de varios mecanismos sofisticados:

  1. Conciencia de caché: Reconoce cuándo los datos han sido recuperados recientemente y evita llamadas duplicadas. Por ejemplo, no llamará a una función meteorológica múltiples veces para la misma ubicación en un corto período de tiempo.
  2. Evaluación del tipo de respuesta: El modelo evalúa si una llamada a función es verdaderamente necesaria. Para consultas simples que pueden responderse con el conocimiento existente, proporcionará una respuesta directa de texto en lugar de hacer una llamada API.
  3. Persistencia de contexto: Rastrea el contexto de la conversación para evitar solicitar información que ya se ha establecido. Esto previene llamadas API redundantes para datos que ya están disponibles.
  4. Optimización de recursos: El modelo evalúa el costo computacional de las llamadas a funciones y elige el enfoque más eficiente. Por ejemplo, si múltiples funciones podrían proporcionar una respuesta, selecciona la que requiere menos recursos.

Esta gestión inteligente de funciones asegura un rendimiento óptimo del sistema mientras mantiene la precisión de las respuestas y la calidad de la experiencia del usuario.

Al invertir tiempo en crear y mantener definiciones de funciones claras y detalladas, estableces una base robusta para tu sistema impulsado por IA. Esta base asegura interacciones confiables y consistentes que no solo satisfacen las necesidades del usuario, sino que lo hacen de una manera que mantiene la integridad del sistema y produce resultados predecibles y de alta calidad. Piensa en ello como construir un puente entre el lenguaje humano y la funcionalidad computacional - cuanto más fuerte y mejor diseñado esté este puente, más fluida será la interacción.

6.2.1 Componentes Clave de una Definición de Función

Una definición de función consiste en varios componentes cruciales que trabajan juntos para crear un contrato claro entre tu aplicación y el modelo de IA.

Exploremos cada componente:

  1. Nombre: Un identificador único para tu función que sirve como su punto de referencia principal. Piensa en él como el título oficial de la función que el modelo usará para invocarla. El nombre debe ser:
    • Descriptivo y autoexplicativo (por ejemplo, "calculate_temperature_conversion" en lugar de solo "convert")
    • Único dentro del alcance de tu aplicación para evitar confusiones
    • Seguir una convención de nomenclatura consistente (típicamente snake_case o camelCase)
  2. Descripción: Una explicación completa del propósito y capacidades de la función. Esto es crucial porque:
    • Ayuda al modelo a entender exactamente cuándo usar esta función
    • Debe especificar cualquier limitación o caso de uso específico
    • Cuanto más detallada y precisa sea la descripción, mejor podrá el modelo tomar decisiones sobre su uso
  3. Parámetros: Un esquema JSON detallado que define los requisitos de entrada de la función. Este plano integral sirve como un contrato para cómo los datos deben ser pasados a la función. Esta definición estructurada incluye:
  • Tipo: Más comúnmente un objeto, que sirve como contenedor para todos los demás parámetros. Esto permite una organización estructurada de datos y relaciones claras entre parámetros. Piensa en ello como un envoltorio que contiene todas las piezas individuales de información que tu función necesita. Por ejemplo, una función meteorológica podría tener un objeto de ubicación que contenga parámetros de ciudad, estado y país.
  • Propiedades: Un mapeo detallado de cada parámetro, incluyendo:
    • Nombre: Un identificador claro y descriptivo que transmite inmediatamente el propósito del parámetro (por ejemplo, 'temperatureCelsius' en lugar de solo 'temp')
    • Tipo de datos: Restricciones específicas de tipo (texto, número, entero, booleano, etc.) que aseguran la consistencia de datos y previenen errores relacionados con el tipo
    • Descripción: Explicación clara de lo que representa el parámetro, incluyendo cualquier requisito específico de formato o rangos de valores aceptables
    • Restricciones opcionales: Como valores mínimos/máximos, patrones o enumeraciones. Estos actúan como barandillas para prevenir el procesamiento de datos inválidos. Por ejemplo, establecer un rango de temperatura de -50 a 150 para valores en Celsius
  • Requeridos: Una lista explícita de parámetros obligatorios que deben proporcionarse para que la función funcione correctamente. Esto ayuda a asegurar que todos los datos necesarios estén disponibles antes de llamar a la función, previniendo errores en tiempo de ejecución y mejorando la confiabilidad. Por ejemplo, una función de geocodificación podría requerir tanto latitud como longitud como parámetros requeridos, mientras que un parámetro de zona horaria podría ser opcional.

Ejemplo Detallado: Definiendo una Función Calculadora

Exploremos un ejemplo práctico para demostrar la definición de función. Crearemos una función llamada calculate_sum que realiza una operación aritmética básica - sumar dos números. Este ejemplo, aunque simple, ilustra todos los componentes clave de la definición de función y nos ayudará a entender cómo estructurar funciones más complejas.

La función tomará dos entradas numéricas y devolverá su suma, demostrando la definición adecuada de parámetros, especificación de tipo y descripción del propósito de la función. Así es como puedes definirla:

Ejemplo de Esquema de Función:

function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

Explicación:

  • Nombre: "calculate_sum" identifica de manera única nuestra función.
  • Descripción: Explica que la función toma dos números y devuelve su suma.
  • Objeto de Parámetros:
    • Tipo: Establecido como "object", ya que esperamos un objeto que contenga parámetros.
    • Propiedades:
      • "a" está definido como un número con una explicación descriptiva.
      • "b" está definido de manera similar.
    • Requeridos: Tanto "a" como "b" son parámetros requeridos para la función.

Aquí hay una versión ampliada de la definición de función con más características y manejo integral de errores:

function_definitions = [
    {
        "name": "calculate_arithmetic",
        "description": "Performs basic arithmetic operations between two numbers with input validation and error handling.",
        "parameters": {
            "type": "object",
            "properties": {
                "operation": {
                    "type": "string",
                    "description": "The arithmetic operation to perform",
                    "enum": ["add", "subtract", "multiply", "divide"],
                },
                "a": {
                    "type": "number",
                    "description": "The first number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "b": {
                    "type": "number",
                    "description": "The second number for the operation",
                    "minimum": -1000000,
                    "maximum": 1000000
                },
                "round_to": {
                    "type": "integer",
                    "description": "Number of decimal places to round to",
                    "minimum": 0,
                    "maximum": 10,
                    "default": 2
                }
            },
            "required": ["operation", "a", "b"]
        }
    }
]

def handle_arithmetic_operation(args):
    try:
        operation = args.get("operation")
        a = float(args.get("a"))
        b = float(args.get("b"))
        round_to = int(args.get("round_to", 2))
        
        result = None
        if operation == "add":
            result = a + b
        elif operation == "subtract":
            result = a - b
        elif operation == "multiply":
            result = a * b
        elif operation == "divide":
            if b == 0:
                raise ValueError("Division by zero is not allowed")
            result = a / b
            
        return {"result": round(result, round_to)}
        
    except ValueError as e:
        return {"error": str(e)}
    except Exception as e:
        return {"error": f"Unexpected error: {str(e)}"}

# Example API implementation
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=[
        {"role": "system", "content": "You are a helpful assistant that performs calculations."},
        {"role": "user", "content": "What is 15.7 divided by 2.5?"}
    ],
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the response
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Execute the function and get result
    result = handle_arithmetic_operation(json.loads(function_call.arguments))
    
    # Send the result back to the model for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": "You are a helpful assistant that performs calculations."},
            {"role": "user", "content": "What is 15.7 divided by 2.5?"},
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "calculate_arithmetic", "content": json.dumps(result)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)

Mejoras y adiciones clave en esta versión ampliada:

  • Parámetros Mejorados:
    • Adición de selección de tipo de operación (suma, resta, multiplicación, división)
    • Inclusión de validación de entrada con límites mínimos/máximos
    • Adición de parámetro opcional de precisión de redondeo
  • Manejo de Errores:
    • Valida tipos y rangos de entrada
    • Gestiona división por cero
    • Maneja errores inesperados de manera elegante
  • Implementación de Funciones:
    • Implementación completa de la función de manejo
    • Conversión y validación adecuada de tipos
    • Funcionalidad de redondeo de resultados
  • Integración de API:
    • Flujo de conversación en dos pasos
    • Maneja resultados de funciones y genera respuestas en lenguaje natural
    • Mantiene el contexto de la conversación

Esta versión ampliada demuestra un enfoque más preparado para producción con manejo de errores adecuado, validación de entrada y flujo de conversación completo.

6.2.2 Integrando Definiciones de Funciones en una Llamada API

Una vez que has definido tu función, el siguiente paso crucial es integrarla en tu llamada API. Este proceso de integración es fundamental ya que crea un puente entre tu aplicación y el modelo de IA, permitiéndole entender y utilizar tus funciones personalizadas de manera efectiva. El modelo emplea un sistema de análisis sofisticado que examina cuidadosamente el esquema de tu función, considerando varios factores:

  1. Análisis de Esquema: El modelo realiza un examen exhaustivo de la estructura de tu función. Esto incluye analizar el nombre de la función para entender su propósito, revisar la descripción detallada para comprender sus capacidades, y evaluar los requisitos de parámetros para asegurar un manejo adecuado de entrada. Por ejemplo, al examinar una función meteorológica, comprenderá tanto los requisitos básicos (como ubicación) como cualquier parámetro opcional (como unidades de temperatura).
  2. Evaluación de Contexto: El modelo realiza un análisis exhaustivo del flujo de conversación actual y el contexto histórico. Examina mensajes previos, consultas del usuario y el diálogo en curso para construir una comprensión completa de las necesidades actuales del usuario. Esto ayuda a determinar si una llamada a función es apropiada para la situación actual y cómo debe ejecutarse.
  3. Coincidencia de Intención: A través de algoritmos sofisticados de comparación, el modelo evalúa la intención del usuario contra la biblioteca de funciones disponibles. Busca coincidencias semánticas entre la solicitud del usuario y las capacidades de la función, considerando variaciones en cómo los usuarios podrían expresar sus necesidades. Por ejemplo, "¿Cuál es la temperatura afuera?" y "¿Qué calor hace?" coincidirían ambas con una función meteorológica.
  4. Lógica de Decisión: El modelo emplea un proceso de decisión de múltiples pasos basado en toda la información recopilada. Pondera factores como la certeza de la coincidencia, la completitud de los parámetros disponibles, y si una llamada a función proporcionaría mejores resultados que una respuesta directa. Esto asegura que las funciones solo se llamen cuando realmente mejorarán la experiencia del usuario y proporcionarán información más precisa o útil.

Examinemos este proceso de toma de decisiones en detalle a través de dos ejemplos contrastantes que destacan cuándo las llamadas a funciones son necesarias versus cuándo las respuestas directas son más apropiadas:

Ejemplo 1 - Llamada a Función Necesaria:
Usuario: "¿Qué tiempo hace en París?"

  • El modelo reconoce que esto requiere datos externos porque la información meteorológica actual no puede conocerse sin acceder a datos en tiempo real
  • Identifica la función get_weather como relevante al hacer coincidir la intención del usuario de obtener información meteorológica con el propósito de la función
  • Confirma que los parámetros (location="Paris") están disponibles y correctamente formateados para la llamada API
  • Se prepara para ejecutar la llamada a función formateando la solicitud según el esquema de la función

El código:

import openai
import json
from datetime import datetime

# Define the weather function
function_definitions = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a specified location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "City name for weather information"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "default": "celsius",
                    "description": "Temperature unit preference"
                }
            },
            "required": ["location"]
        }
    }
]

# Mock weather API function
def get_weather_data(args):
    # In a real implementation, this would call an actual weather API
    location = args.get("location")
    units = args.get("units", "celsius")
    
    # Mock response
    return {
        "location": location,
        "temperature": 22 if units == "celsius" else 71.6,
        "conditions": "partly cloudy",
        "humidity": 65,
        "timestamp": datetime.now().isoformat()
    }

# Example conversation
messages = [
    {"role": "system", "content": "You are a helpful assistant that can check weather conditions."},
    {"role": "user", "content": "What's the weather in Paris?"}
]

# Initial API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    functions=function_definitions,
    function_call="auto",
    temperature=0
)

# Handle the function call
if response.choices[0].message.get("function_call"):
    function_call = response.choices[0].message.function_call
    
    # Get weather data
    weather_data = get_weather_data(json.loads(function_call.arguments))
    
    # Send the result back for natural language response
    final_response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            *messages,
            {"role": "assistant", "content": None, "function_call": function_call},
            {"role": "function", "name": "get_weather", "content": json.dumps(weather_data)}
        ],
        temperature=0
    )
    
    print(final_response.choices[0].message.content)
    # Output might be: "Currently in Paris, it's 22°C (71.6°F) and partly cloudy with 65% humidity."

Aquí está el desglose de sus componentes principales:

1. Definición de Función

  • Define una función meteorológica que acepta dos parámetros:
    • "ubicación" (obligatorio): Una cadena para el nombre de la ciudad
    • "unidades" (opcional): Unidades de temperatura, con celsius por defecto

2. Implementación Simulada del Clima

  • Crea una función simulada del clima que imita una respuesta de API
    • Devuelve ubicación, temperatura, condiciones, humedad y marca temporal
    • Maneja la conversión de unidades entre celsius y fahrenheit

3. Flujo de Conversación

  • Establece una conversación básica con:
    • Mensaje del sistema que define el rol del asistente
    • Mensaje del usuario preguntando sobre el clima en París

4. Integración de API

  • Realiza dos llamadas API a OpenAI:
    1. Llamada inicial para procesar la solicitud del usuario y determinar si se necesita una llamada a función
    2. Llamada de seguimiento para generar una respuesta en lenguaje natural basada en los datos meteorológicos

5. Manejo de Respuesta

  • Procesa la respuesta de la API mediante:
    • Verificación si se necesita una llamada a función
    • Ejecución de la función meteorológica con los argumentos proporcionados
    • Formateo de la respuesta en lenguaje natural

Este ejemplo demuestra el ciclo de vida completo de una llamada a función, desde la entrada inicial del usuario hasta la salida final en lenguaje natural.

Ejemplo 2 - Respuesta Directa Apropiada:
Usuario: "¿Cuál es la capital de Francia?"

  • El modelo reconoce esto como conocimiento general que existe dentro de sus datos de entrenamiento
  • Determina que no se necesitan datos externos ya que es un hecho estático y bien establecido
  • Omite la llamada a función para optimizar el tiempo de respuesta y reducir el uso innecesario de API
  • Proporciona una respuesta directa desde su base de conocimientos existente

El código:

import openai
import json

# Define conversation with a factual query
messages = [
    {"role": "system", "content": "You are a helpful assistant with broad knowledge."},
    {"role": "user", "content": "What is the capital of France?"}
]

# Example API call
response = openai.ChatCompletion.create(
    model="gpt-4",
    messages=messages,
    # Note: No functions parameter needed since this is a direct knowledge query
    temperature=0
)

# Print the response
print(response.choices[0].message.content)
# Output would be: "The capital of France is Paris."

# The model provides a direct response without function calling because:
# 1. The information is part of its knowledge base
# 2. No external data or computation is required
# 3. The response is immediate and accurate

Este ejemplo demuestra una llamada simple a la API de OpenAI que no utiliza llamadas a funciones. Aquí está el desglose de sus componentes principales:

  • Importaciones y Configuración: El código importa las bibliotecas necesarias de OpenAI y JSON.
  • Definición de Conversación: Crea un array de mensajes con dos elementos: 
    • Un mensaje del sistema que define el rol del asistente como conocedor
    • Un mensaje del usuario preguntando sobre la capital de Francia
  • Llamada a la API: Realiza una llamada directa al endpoint ChatCompletion de OpenAI con: 
    • El modelo GPT-4
    • El array de mensajes
    • Temperatura establecida en 0 para obtener respuestas consistentes

El código está específicamente diseñado para demostrar un caso donde no es necesaria la llamada a funciones, ya que trata con conocimiento general que no requiere datos externos ni cálculos. Esto hace que la respuesta sea inmediata y eficiente.

Este sofisticado proceso de toma de decisiones consciente del contexto asegura que las funciones solo se llamen cuando sea necesario, optimizando el rendimiento y manteniendo la fluidez de la conversación. La capacidad del modelo para distinguir entre escenarios que requieren llamadas a funciones y los que no ayuda a crear interacciones más eficientes y naturales.

Otro Ejemplo de Llamada a la API con Definición de Función:

import openai
import os
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

# Define the function schema as shown above.
function_definitions = [
    {
        "name": "calculate_sum",
        "description": "Calculates the sum of two numbers provided as parameters.",
        "parameters": {
            "type": "object",
            "properties": {
                "a": {
                    "type": "number",
                    "description": "The first number to add."
                },
                "b": {
                    "type": "number",
                    "description": "The second number to add."
                }
            },
            "required": ["a", "b"]
        }
    }
]

# Create a conversation that leads to the function call.
messages = [
    {"role": "system", "content": "You are a helpful assistant that can perform calculations."},
    {"role": "user", "content": "What is the sum of 12 and 30?"}
]

response = openai.ChatCompletion.create(
    model="gpt-4o",
    messages=messages,
    functions=function_definitions,
    function_call="auto",  # Let the model decide if it should call the function.
    max_tokens=100,
    temperature=0.5
)

# Check if the model decided to call the function.
if response["choices"][0].get("finish_reason") == "function_call":
    function_call_info = response["choices"][0]["message"]["function_call"]
    print("Function to be called:")
    print(f"Name: {function_call_info['name']}")
    print(f"Arguments: {function_call_info['arguments']}")
else:
    print("Response:")
    print(response["choices"][0]["message"]["content"])

Desglose del código:

1. Configuración e Importaciones

  • El código importa las bibliotecas necesarias: openai para acceso a la API, os para variables de entorno, y dotenv para cargar variables de entorno
  • Configura la clave API de OpenAI desde las variables de entorno para la seguridad

2. Definición de Función

  • Define una función calculate_sum que toma dos parámetros (a y b)
  • El esquema de la función incluye:
    • Nombre y descripción de la función
    • Especificaciones de parámetros con tipos y descripciones
    • Ambos parámetros están marcados como obligatorios

3. Configuración de la Conversación

  • Crea un array inicial de conversación con dos mensajes:
  • Un mensaje del sistema que define el rol del asistente como ayudante de cálculos
  • Un mensaje del usuario pidiendo la suma de 12 y 30

4. Llamada a la API

  • Realiza una llamada al endpoint ChatCompletion de OpenAI con estos parámetros:
  • Utiliza el modelo GPT-4o
  • Incluye el array de mensajes
  • Pasa las definiciones de funciones
  • Establece function_call en "auto" permitiendo que el modelo decida si usar la función
  • Configura max_tokens y temperature para control de respuesta

5. Manejo de Respuesta

  • Verifica si el modelo decidió hacer una llamada a función
  • Si es así: Imprime el nombre de la función y los argumentos que planea usar
  • Si no: Imprime la respuesta directa del modelo

Este ejemplo demuestra una implementación básica de la capacidad de llamada a funciones de OpenAI, permitiendo que el modelo decida dinámicamente si usar una función personalizada basada en la entrada del usuario.

La definición precisa de funciones y parámetros es esencial para aprovechar todo el potencial de la llamada a funciones. Veamos por qué esto es crucial:

Primero, un esquema de función bien definido actúa como un contrato claro entre tu aplicación y el modelo de IA. Especifica exactamente qué hace la función, qué parámetros acepta y qué restricciones existen en esos parámetros. Esta claridad ayuda al modelo a tomar mejores decisiones sobre cuándo y cómo usar la función.

Segundo, la validación de parámetros se vuelve mucho más confiable cuando tienes definiciones precisas. Por ejemplo, si especificas que un parámetro debe ser un número dentro de cierto rango, o que una cadena debe coincidir con un patrón específico, creas una red de seguridad que detecta entradas inválidas antes de que puedan causar problemas en tu aplicación.

Tercero, las definiciones adecuadas de funciones permiten que el modelo comprenda el contexto y propósito de cada parámetro. Al incluir descripciones detalladas y ejemplos, ayudas al modelo a tomar decisiones más inteligentes sobre los valores de los parámetros y aseguras que la función se use según lo previsto.

Con esta base robusta establecida, puedes integrar con confianza operaciones dinámicas en tus aplicaciones de IA. Estas operaciones pueden incluir:

  • Realizar cálculos complejos con entradas validadas
  • Recuperar datos de fuentes externas manteniendo la integridad de los datos
  • Ejecutar lógica de negocio personalizada con manejo apropiado de errores
  • Gestionar llamadas a API con formato apropiado de parámetros

Esta atención al detalle en la definición de funciones finalmente conduce a aplicaciones de IA más confiables, mantenibles y potentes.