Cómo estructurar las respuestas JSON en ChatGPT con llamadas a funciones

Cómo Estructurar Respuestas JSON en ChatGPT con Llamadas de Funciones

El Problema de ChatGPT con JSON

Abre la Interfaz de ChatGPT y pídele algo de JSON. Lo más probable es que obtengas una respuesta como la que puedes ver en la foto de portada: se te presenta un objeto JSON en formato markdown, con un poco de texto a cada lado que explica lo que muestra el JSON.  

Si pruebas esta misma indicación en el OpenAI Playground, verás que el JSON está encerrado entre tres acentos graves (sintaxis de markdown).

Interfaz del Open AI Playground que muestra una indicación del usuario solicitando algún JSON
https://platform.openai.com/playground

Esto es genial. ChatGPT ha ido más allá al explicar la respuesta en términos fáciles de entender, fáciles de entender… para un humano. No para una máquina. Las máquinas necesitan datos en un formato que cumpla con un esquema confiable, consistente y predecible.

Idealmente, te gustaría analizar la respuesta de ChatGPT en tu código y hacer algo útil con ella, como esto:

// Usa el paquete openai de npm para llamar a ChatGPTimport OpenAI from "openai";// Crea una nueva instancia del cliente openai con nuestra clave APIconst openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });// Llama al endpoint de completados de ChatGPT y solicita algo de JSONconst gptResponse = await openai.chat.completions.create({    model: "gpt-3.5-turbo",    temperature: 1,    messages: [        {            role: "user",            content: "Dame el JSON de un objeto que represente a un gato."        }    ],});// Intenta leer la respuesta como JSON,// Lo más probable es que falle con un SyntaxError...const json = JSON.parse(gptResponse.choices[0].message.content);

Pero esto solo funcionará si gptResponse.choices[0].message.content es un JSON válido cada vez.

También sería bueno tener un JSON que cumpla de forma confiable con un esquema:

type Cat = {    name: string,    colour: "brown" | "grey" | "black",    age: number}// Lee el JSON de respuesta y tipifícalo con nuestro esquema de objeto Catconst json = <Cat>JSON.parse(gptResponse.choices[0].message.content);

No poder confiar en que ChatGPT devuelva un JSON válido en un formato predecible puede introducir errores en tu aplicación, especialmente cuando la consistencia es clave. Esto se convierte en un problema real cuando estás escribiendo código que depende de respuestas en tiempo real de ChatGPT para activar acciones o actualizaciones específicas, por lo que necesitamos encontrar una solución. Hay un par de formas de abordar esto…

Cómo Puede Ayudar la Ingeniería de Indicaciones

Un enfoque para resolver este problema es a través de la ingeniería de indicaciones.

Imagen del Open AI Playground que muestra una indicación del sistema controlando el formato de respuesta
https://platform.openai.com/playground

Aquí, hemos agregado algunas instrucciones tanto a la indicación del usuario como al mensaje del sistema. Las instrucciones intentan forzar al modelo a devolver solo el JSON que queremos, con el formato que deseamos.

Y para muchos casos de uso, esto funciona aceptablemente. Puedes ver en la captura de pantalla de arriba que la respuesta del “Asistente” es nada más que JSON, y el JSON se adhiere al esquema que describimos para él.

Pero esto no funciona al 100% del tiempo.

Aquí tienes el mismo par exacto de indicaciones con la temperatura del modelo establecida por encima de 1. Observa cómo el campo color en el JSON devuelto ahora no cumple con uno de los valores permitidos que especificamos en la indicación del usuario:

Interfaz del Open AI Playground que muestra cómo el parámetro de Temperatura causa respuestas inválidas con valores más altos

La Llamada a Funciones al Rescate

La llamada a funciones es una nueva forma de usar la API de ChatGPT. En lugar de recibir un mensaje del modelo de lenguaje, recibes una solicitud para llamar a una función.

Si alguna vez has utilizado complementos en la interfaz de ChatGPT, la Función de Llamada es la característica detrás de escena que permite la integración de los complementos con las respuestas de LLM.

Los complementos definen las funciones que están disponibles para el modelo y le permiten llamar esas funciones en respuesta a las indicaciones del usuario.  

Cuando uses la API en tu propio código, también puedes aprovechar la llamada de funciones para tener un mejor control sobre los datos que obtienes del modelo, incluido forzarlo a devolver datos JSON en un formato predecible.

Aquí tienes una solicitud básica que utiliza la Función de Llamada para devolver un objeto message.function_call en lugar de una cadena message.content en la respuesta.  

Aquí, simplemente le pedimos a ChatGPT que llame a una función (“getName”) y una descripción de la función dentro del array functions: []. También le indicamos a ChatGPT que esperamos que llame a esta función en la respuesta estableciendo el valor de function_call como el nombre de nuestra función.

const gptResponse = await openai.chat.completions.create({    model: "gpt-3.5-turbo-0613",    messages: [        {            role: "user",            content: "Llama a la función 'getName' y dime el resultado."        }    ],    functions: [        {            name: "getName",            parameters: {                type: "object",                properties: {}            }        }    ],    function_call: { name: "getName" }});// Imprimirá "getName"...console.log(gptResponse.choices[0].message.function_call.name);

Es importante tener en cuenta aquí que la función getName() no necesita existir en ningún lugar de nuestro código. Lo único que estamos haciendo es decirle a ChatGPT que existe y está disponible para ser llamada.

Cómo agregar argumentos de función

La llamada de funciones es genial porque hace que la respuesta de ChatGPT sea predecible y estructurada.  

En el ejemplo anterior, obtendremos un objeto en gptResponse.choices[0].message.function_call que contendrá los detalles de cómo ChatGPT quiere ejecutar nuestra (imaginaria) función.

Este objeto se ve así, con el nombre de la función y una versión stringified de los argumentos de la función que debe recibir:

{  name: "nombreDeLaFuncion",  arguments: "{ \"arg1\": \"valor\" }",}

Podemos aprovechar este segundo valor de arguments describiendo la estructura de los argumentos de la función y haciendo que ChatGPT complete este valor con un JSON que se ajuste a nuestra estructura.

Aquí tienes el ejemplo original como una llamada de función. Observa cómo hemos descrito el esquema del objeto Cat a ChatGPT dentro de la definición de una función createCatObject:

    type Cat = {        name: string,        colour: "brown" | "grey" | "black",        age: number    }    const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });    const gptResponse = await openai.chat.completions.create({        model: "gpt-3.5-turbo-0613",        messages: [            {                role: "user",                content: "Crea un nuevo objeto Gato."            }        ],        functions: [            {                name: "createCatObject",                parameters: {                    type: "object",                    properties: {                        name: {                            type: "string"                        },                        colour: {                            type: "string",                            enum: ["brown", "grey", "black"]                        },                        age: {                            type: "integer"                        }                    },                    required: ["name", "colour", "age"]                }            }        ],        function_call: { name: "createCatObject" }    });    const functionCall = gptResponse.choices[0].message.function_call;    const json = <Cat>JSON.parse(functionCall.arguments);

Esta solicitud de completado instruirá a ChatGPT para crear un nuevo objeto gato y enviarlo a la función createCatObject() en un formato específico. La última línea:

const json = <Cat>JSON.parse(functionCall.arguments);

analiza los argumentos de ChatGPT en nuestro tipo Cat, que refleja la descripción que le dimos al modelo sobre la forma de nuestro objeto esperado.

Conclusión

Llamar a la función con ChatGPT no solo aporta claridad y previsibilidad a los resultados, sino que también introduce un cambio de paradigma en cómo puedes interactuar con modelos de aprendizaje automático.

Este enfoque estructurado garantiza que las respuestas de ChatGPT sean escalables, lo que permite una integración más sencilla en varias aplicaciones, ya sean aplicaciones web sencillas o tuberías de aprendizaje automático más complejas.


Leave a Reply

Your email address will not be published. Required fields are marked *