curso
API OpenAI en tiempo real: Una guía con ejemplos
La recién presentada API en tiempo real de OpenAI nos permite integrar experiencias multimodales rápidas y de baja latencia en nuestras aplicaciones. Con esta API, podemos crear interacciones fluidas de voz a voz entre usuarios y grandes modelos lingüísticos (LLM).
Esta API elimina la necesidad de múltiples modelos para conseguir experiencias basadas en la voz, ya que ofrece la solución completa en una API integrada. No sólo pretende reducir la latencia, sino que también conserva los matices emocionales y el flujo natural de las conversaciones.
En este artículo, aprenderemos a utilizar la API OpenAI Realtime para crear asistentes de IA controlados por voz. Crearemos conexiones WebSocket persistentes utilizando Node.js y cómo se puede utilizar esto dentro del terminal para comunicarse con la API. Además, te guiaré en el despliegue de una aplicación React que utilice las capacidades de esta API.
Generar la clave API para la API en tiempo real de OpenAI
Para utilizar la OpenAI en tiempo real, primero debemos generar una clave API. Para ello, navega hasta la página de la clave API. Ten en cuenta que para ello se necesita una cuenta. En la parte superior de la página, haz clic en el botón "Crear nueva clave secreta".
Se abrirá una ventana emergente. Podemos utilizar las opciones por defecto y hacer clic en "Crear clave secreta".
Cuando se crea la clave, tenemos la oportunidad de copiarla. Asegúrate de copiarlo antes de cerrar la ventana, ya que es la única vez que se mostrará.
Si se pierde la llave, siempre podemos borrarla y crear una nueva.
Para almacenar la clave, recomendamos crear un archivo llamado .env
y guardar allí la clave con el siguiente formato:
OPENAI_API_KEY=<paste_they_key_here>
Utilizaremos este archivo a lo largo de este artículo para conectarnos a la API.
Consideraciones sobre los costes de la API en tiempo real
Antes de continuar, ten en cuenta que la API en tiempo real no es gratuita, y necesitamos añadir créditos a nuestra cuenta para utilizarla. Podemos añadir créditos en la página de facturaciónque se encuentra en nuestro perfil.
Para dar una idea de los costes, aquí tienes un resumen de lo que me ha costado experimentar con la API mientras trabajaba en este artículo:
Me gasté unos cinco dólares en total. No es una cantidad enorme, pero es mucho más cara que las otras API que proporciona OpenAI. Tenlo en cuenta mientras trabajas en este artículo.
La mayor parte del coste (primera barra) corresponde a que estoy jugando con la aplicación de consola React que exploramos al final de este artículo. El resto, alrededor de medio dólar, es lo que me costó utilizar la API mediante WebSockets. Por lo tanto, es posible seguir este artículo por menos de un dólar.
Para más detalles sobre los precios de la API, consulta la sección "API en tiempo real" en su página de precios.
Desarrollar aplicaciones de IA
Utilizar la API en tiempo real con WebSockets
A diferencia de otros componentes de la API de OpenAIla API en tiempo real utiliza WebSockets. WebSockets es un protocolo de comunicación que establece un canal de comunicación bidireccional entre un cliente y un servidor. A diferencia del modelo convencional de solicitud-respuesta utilizado por HTTP, los WebSockets admiten interacciones continuas y en tiempo real. Esto hace que los WebSockets sean especialmente adecuados para aplicaciones en tiempo real, como el chat de voz.
Este artículo explicará cómo funcionan los WebSockets e incluirá varios ejemplos de interacción con la API en tiempo real.
Vamos a utilizar Node.js, por lo que debemos asegurarnos de que está instalado en nuestro ordenador. Si no, podemos descargar e instalar Node.js desde su web oficial.
Inicializar el script
Para seguir adelante, te recomendamos que crees una carpeta con el archivo .env
creado anteriormente. Dentro de esa carpeta, ejecuta el siguiente comando para inicializar el script:
npm init -y && touch index.js
Cuando termine este comando, estos archivos deberían estar dentro de la carpeta:
Instalar las dependencias
Empieza instalando dos paquetes:
ws
: Este es el paquete WebSocket, el principal paquete necesario para interactuar con la API.dotenv
: Un paquete de utilidades que carga la clave API desde el archivo.env
.
Instálalos ejecutando el comando
npm install ws dotenv
Conectarse a la API en tiempo real
Para iniciar una conexión con la API en tiempo real, creamos un nuevo objeto WebSocket
pasándole la URL de la API y las cabeceras con la información necesaria para conectar con ella:
// Import the web socket library
const WebSocket = require("ws");
// Load the .env file into memory so the code has access to the key
const dotenv = require("dotenv");
dotenv.config();
function main() {
// Connect to the API
const url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01";
const ws = new WebSocket(url, {
headers: {
"Authorization": "Bearer " + process.env.OPENAI_API_KEY,
"OpenAI-Beta": "realtime=v1",
},
});
}
main();
Configurar una acción cuando se abre la conexión
El código anterior crea la conexión de socket web a la API, pero aún no hace nada con ella.
Los WebSockets nos permiten establecer acciones que se ejecutarán cuando se produzcan algunos eventos. Podemos utilizar el evento open
para especificar algún código que queramos ejecutar una vez establecida la conexión.
La sintaxis genérica para añadir un receptor de eventos es la siguiente:
ws.on(<event>, <function>);
Sustituye por una cadena que contenga el nombre del evento y
por una función que se ejecutará cuando se produzca el evento.
Así es como podemos mostrar el texto una vez que la conexión esté lista:
// Add inside the main() function of index.js after creating ws
async function handleOpen() {
console.log("Connection is opened");
}
ws.on("open", handleOpen);
Para ejecutar este código, utilizamos el comando
node index.js
Si la clave API está configurada correctamente, veremos el mensaje "La conexión está abierta" en el terminal. El script seguirá ejecutándose porque la conexión sigue abierta, así que tenemos que detenerlo manualmente.
Configurar una acción cuando se recibe un mensaje
Otro evento al que podemos responder cuando utilizamos WebSockets es el evento message
. Se activa cada vez que se recibe un mensaje del servidor. Añadamos una función que muestre cada mensaje recibido:
// Add inside the main() function of index.js
async function handleMessage(messageStr) {
const message = JSON.parse(messageStr);
console.log(message);
}
ws.on("message", handleMessage);
Ejecutar el script ahora también debería mostrar el session.created
evento que envía la API cuando se inicializa la sesión.
Otros eventos WebSocket
Arriba aprendimos a añadir escuchas de eventos a los eventos open
y message
. Los WebSockets admiten dos eventos adicionales que no utilizaremos en nuestros ejemplos.
El evento close
puede utilizarse para añadir una llamada de retorno cuando se cierra el socket:
async function handleClose() {
console.log(“Socket closed”);
}
ws.on(“close”, handleClose);
El evento error
se utiliza para añadir una llamada de retorno cuando se produce un error:
async function handleError(error) {
console.log(“Error”, error);
}
ws.on(“error”, handleError);
Comunicarse con la API en tiempo real
Trabajar con WebSockets requiere que programemos en un entorno impulsada por eventos eventos. Los mensajes se envían de un lado a otro del canal de comunicación, y no podemos controlar cuándo se entregarán o recibirán estos mensajes.
El código que inicia la comunicación debe añadirse dentro de handleOpen()
. O De lo contrario, se produciría un error porque ese código puede ejecutarse antes de que se cree el canal de comunicación del socket web.
Lo mismo ocurre con el código que maneja los mensajes. Toda la lógica debe ir en la función handleMessage()
.
En los próximos ejemplos, utilizaremos el siguiente código como punto de partida. La mayoría de los cambios consisten en actualizar handleOpen()
y handleMessage()
.
// Import the web socket library
const WebSocket = require("ws");
// Load the .env file into memory so the code has access to the key
const dotenv = require("dotenv");
dotenv.config();
function main() {
// Connect to the API
const url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01";
const ws = new WebSocket(url, {
headers: {
"Authorization": "Bearer " + process.env.OPENAI_API_KEY,
"OpenAI-Beta": "realtime=v1",
},
});
async function handleOpen() {
console.log("Connection is opened");
}
ws.on("open", handleOpen);
async function handleMessage(messageStr) {
const message = JSON.parse(messageStr);
console.log(message);
}
ws.on("message", handleMessage);
}
main();
Utilizar la API en tiempo real para enviar y recibir texto
La comunicación con la API en tiempo real se realiza mediante eventos. La OpenAI API de documentación en tiempo real enumera los eventos que admite. Utilizamos el conversation.item.create
evento para iniciar una conversación. Los eventos se representan como objetos JSON cuyos campos se describen en la documentación.
Aquí tienes un ejemplo de conversación.elemento.crear que envía la pregunta "Explica en una frase qué es un conector web":
const createConversationEvent = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Explain in one sentence what a web socket is"
}
]
}
};
```
This event tells the API that we want to initiate a textual conversation. This is specified in the content field, using a type of ”input_text” and providing a text prompt.
We use the ws.send() method to send a message. The web socket package expects a string as the argument so we need to convert our JSON event to a string using the JSON.stringify() function. Putting these together, here’s how we can send the above event:
```javascript
ws.send(JSON.stringify(createConversationEvent));
```
This will initiate the conversation, but it won’t trigger the API to send us a response automatically. To trigger a response, we send a response.create event. Here’s an example:
```javascript
const createResponseEvent = {
type: "response.create",
response: {
modalities: ["text"],
instructions: "Please assist the user.",
}
}
ws.send(JSON.stringify(createResponseEvent));
Este evento utiliza el parámetro de respuesta modalities
para solicitar una respuesta textual. Las instrucciones son la parte más importante, pues describen lo que queremos que haga el modelo, en este caso, un aviso genérico que pide ayuda al usuario.
Enviamos estos dos eventos en la función handleOpen()
para que se inicie una conversación en cuanto se establezca la conexión. Aquí tienes la implementación completa de la función handleOpen()
de este ejemplo:
async function handleOpen() {
// Define what happens when the connection is opened
// Create and send an event to initiate a conversation
const createConversationEvent = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Explain in one sentence what a web socket is"
}
]
}
};
// Create and send an event to initiate a response
ws.send(JSON.stringify(createConversationEvent));
const createResponseEvent = {
type: "response.create",
response: {
modalities: ["text"],
instructions: "Please assist the user.",
}
}
ws.send(JSON.stringify(createResponseEvent));
}
```
Regarding the incoming messages, there are three types of events that are worth noting for this example: the response.text.delta, response.text.done, and response.done events:
The response.text.delta events contain the response broken down into chunks in the delta field. They are important when we want to provide a real-time experience because they allow us to stream the response chunk by chunk straight away. The response.text.done event marks the end of the textual response and contains the full answer in the text field.The response.done event marks the end of the response.We can specify how we want our script to respond to these events using a switch statement in the handleMessage() function:
```javascript
async function handleMessage(messageStr) {
const message = JSON.parse(messageStr);
// Define what happens when a message is received
switch(message.type) {
case "response.text.delta":
// We got a new text chunk, print it
process.stdout.write(message.delta);
break;
case "response.text.done":
// The text is complete, print a new line
process.stdout.write("\n");
break;
case "response.done":
// Response complete, close the socket
ws.close();
break;
}
}
En este ejemplo, utilizamos el evento response.text.delta
para imprimir trozos de la respuesta en la consola a medida que la recibimos. Cuando se completa la respuesta, se activa el evento response.text.done
, e imprimimos una nueva línea para mostrar que se ha completado la salida. Por último, cerramos el socket web cuando recibimos el evento response.done
.
Para ejecutar este ejemplo, pegamos estas funciones en el código de la plantilla anterior y lo ejecutamos con el comando
node index.js
Esto generará una respuesta en el terminal a la pregunta "Explica en una frase qué es un socket web", similar a cuando utilizamos ChatGPT.
El código completo del ejemplo de texto está disponible aquí.
Utilizar la API en tiempo real para enviar y recibir audio
El ejemplo anterior mostraba cómo manejamos los datos de texto. Sin embargo, el verdadero interés de la API en tiempo real es crear un asistente de voz que responda en tiempo real.
Manejar datos de audio es algo más complicado que tratar datos textuales. Pasaremos por alto algunos detalles específicos del funcionamiento del audio, ya que nos distraerían del tema principal de este artículo.
En primer lugar, instalamos dos paquetes:
npm install node-record-lpcm16 speaker
node-record-lpcm16
graba el sonido del micrófono para que podamos enviar un aviso de voz.speaker
se utiliza para reproducir la respuesta de voz de la IA.
También tenemos que instalar SoX (Sound eXchange), una utilidad de línea de comandos para el procesamiento de audio que la biblioteca de nodos utilizará para interactuar con el micrófono y grabar audio. Utiliza brew install sox
para instalarlo en macOS o sudo apt install sox
en Linux.
Con estos paquetes instalados, los importamos y añadimos una función startRecording()
que graba las indicaciones de audio del usuario. No explicamos la función en detalle porque nos desviaría demasiado de nuestro tema principal.
Añade el siguiente código al archivo index.js
después de cargar el entorno:
// Add to index.js before the main() function
// Import the web socket library
const WebSocket = require("ws");
// Load the .env file into memory so the code has access to the key
const dotenv = require("dotenv");
dotenv.config();
const Speaker = require("speaker");
const record = require("node-record-lpcm16");
// Function to start recording audio
function startRecording() {
return new Promise((resolve, reject) => {
console.log("Speak to send a message to the assistant. Press Enter when done.");
// Create a buffer to hold the audio data
const audioData = [];
// Start recording in PCM16 format
const recordingStream = record.record({
sampleRate: 16000, // 16kHz sample rate (standard for speech recognition)
threshold: 0, // Start recording immediately
verbose: false,
recordProgram: "sox", // Specify the program
});
// Capture audio data
recordingStream.stream().on("data", (chunk) => {
audioData.push(chunk); // Store the audio chunks
});
// Handle errors in the recording stream
recordingStream.stream().on("error", (err) => {
console.error("Error in recording stream:", err);
reject(err);
});
// Set up standard input to listen for the Enter key press
process.stdin.resume(); // Start listening to stdin
process.stdin.on("data", () => {
console.log("Recording stopped.");
recordingStream.stop(); // Correctly stop the recording stream
process.stdin.pause(); // Stop listening to stdin
// Convert audio data to a single Buffer
const audioBuffer = Buffer.concat(audioData);
// Convert the Buffer to Base64
const base64Audio = audioBuffer.toString("base64");
resolve(base64Audio); // Resolve the promise with Base64 audio
});
});
};
La función startRecording()
graba el audio del micrófono y espera a que se pulse "Intro".
A continuación, actualizamos la función main()
inicializando el Speaker()
que se utiliza para reproducir la respuesta de la IA:
// Add to the main() function after ws is initialized
const speaker = new Speaker({
channels: 1, // Mono or Stereo
bitDepth: 16, // PCM16 (16-bit audio)
sampleRate: 24000, // Common sample rate (44.1kHz)
});
Con eso fuera del camino, podemos implementar el handleOpen()
y handleMessage()
para procesar el audio.
En la función handleOpen()
, sólo tenemos que llamar a la función startRecording()
para grabar la petición de audio del usuario. También tenemos que actualizar ligeramente los eventos:
- Actualiza el contenido de
createConversationEvent
para utilizar el tipo”input_audio”
en lugar de”input_text
y sustituye el campotext
poraudio: base64AudioData
. - Añade
”audio”
a la modalidad de respuesta encreateResponseEvent
.
Aquí tienes la función handleOpen()
actualizada:
async function handleOpen() {
// Define what happens when the connection is opened
const base64AudioData = await startRecording();
const createConversationEvent = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_audio",
audio: base64AudioData,
},
],
},
};
ws.send(JSON.stringify(createConversationEvent));
const createResponseEvent = {
type: "response.create",
response: {
modalities: ["text", "audio"],
instructions: "Please assist the user.",
},
};
ws.send(JSON.stringify(createResponseEvent));
}
Para implementar la función handleMessage()
, modificamos el evento ”response.audio.delta”
para actualizar el búfer de audio y reproducir el nuevo delta de sonido:
case "response.audio.delta":
// We got a new audio chunk
const base64AudioChunk = message.delta;
const audioBuffer = Buffer.from(base64AudioChunk, "base64");
speaker.write(audioBuffer);
break;
Eliminamos el evento ”response.text.done”
de la sentencia switch y actualizamos el evento ”response.done”
para detener el altavoz:
case "response.audio.done":
speaker.end();
ws.close();
break;
La implementación final de la función handleMessage()
tiene este aspecto:
function handleMessage(messageStr) {
const message = JSON.parse(messageStr);
// Define what happens when a message is received
switch (message.type) {
case "response.audio.delta":
// We got a new audio chunk
const base64AudioChunk = message.delta;
const audioBuffer = Buffer.from(base64AudioChunk, "base64");
speaker.write(audioBuffer);
break;
case "response.audio.done":
speaker.end();
ws.close();
break;
}
}
Para ejecutar este ejemplo, aplica estas modificaciones al código de la plantilla y ejecútalo con el comando
node index.js
El micrófono empezará a grabar. Podemos decir nuestra petición y pulsar "Intro" para enviarla. Entonces, la respuesta de la IA se reproducirá en los altavoces (asegúrate de que el micrófono no está silenciado y los altavoces tienen volumen).
El código completo del ejemplo de audio está disponible aquí.
Llamada a la función
Una buena característica de la API de OpenAI es la posibilidad de realizar llamadas a funciones. Podemos añadir funciones al asistente y si detecta que una de esas funciones puede ser útil para dar la respuesta, enviará un evento solicitando que se llame a una función concreta.
La documentación de OpenAI proporciona el siguiente diagrama que explica el ciclo de vida de una llamada a una función:
Fuente: OpenAI
En el diagrama vemos que el cliente debe proporcionar las definiciones de las funciones que el LLM puede llamar. Además, la ejecución de la función se producirá en el lado del cliente; la IA enviará un evento solicitando la llamada a la función y sus argumentos. Después, nos encargamos de devolver el resultado.
Vamos a dotar a nuestro asistente de una función que sume dos números. Construiremos este ejemplo ampliando el ejemplo de audio anterior.
Para especificar las funciones disponibles, debemos proporcionar al LLM una lista de herramientas. Cada herramienta es un objeto JSON que especifica la información sobre la función. Así es como podemos definir una herramienta para la función suma:
const sumTool = {
type: "function",
name: "calculate_sum",
description: "Use this function when asked to add numbers together, for example when asked 'What's 4 + 6'?.",
parameters: {
type: "object",
properties: {
"a": { "type": "number" },
"b": { "type": "number" }
},
required: ["a", "b"]
}
}
Vamos a explicar la estructura de los objetos:
- El
type
especifica que estamos definiendo una función. - La dirección
name
sirve para identificar la función. Esto es lo que utiliza la LLM para decirnos a qué función quiere llamar. - La dirección
description
se utiliza para identificar cuándo el LLM debe utilizar esta función. - Los
parameters
se utilizan para especificar los argumentos de la función. En este caso, dos números llamadosa
yb
.
El siguiente paso es definir la función en nuestro código. Utilizaremos un diccionario con la clave calculate_sum
para que sea más fácil llamar a la función adecuada cuando respondamos a un evento de llamada a función:
const functions = {
calculate_sum: (args) => args.a + args.b,
}
La API proporcionará los argumentos de la función como un diccionario con la misma estructura definida en la dirección parameters
anterior. En este caso, para añadir, digamos 3
y 5
, el diccionario sería {“a”: 3, “b”: 5}
.
Las constantes sumTool
y functions
pueden añadirse en la parte superior de index.js
, después de las importaciones y antes de la función main()
.
A continuación, actualizamos el evento response.create
para que la LLM sepa que sumTools
está disponible. Esto se hace añadiendo los campos tools
y tool_choice
a response
:
const createResponseEvent = {
type: "response.create",
response: {
modalities: ["text", "audio"],
instructions: "Please assist the user.",
tools: [sumTool], // New
tool_choice: "auto", // New
},
};
Cuando la LLM decida que quiere llamar a una función, emitirá un evento response.function_call_arguments.done
. Tenemos que responder a ello
- Obtener la información de la función y llamar a la función.
- Enviar el resultado de la llamada a la función.
- Solicitar una respuesta.
Lo haremos añadiendo el siguiente caso a la declaración switch
dentro de la función hanldeMessage()
:
case "response.function_call_arguments.done":
console.log(Using function ${message.name} with arguments ${message.arguments});
// 1. Get the function information and call the function
const function_name = message.name;
const function_arguments = JSON.parse(message.arguments);
const result = functions[function_name](function_arguments);
// 2. Send the result of the function call
const functionOutputEvent = {
type: "conversation.item.create",
item: {
type: "function_call_output",
role: "system",
output: ${result},
}
};
ws.send(JSON.stringify(functionOutputEvent));
// 3. Request a response
ws.send(JSON.stringify({type: "response.create"}));
break;
Si ahora ejecutamos el script y solicitamos el resultado de sumar dos números, el modelo debería llamar a la función y proporcionar el resultado.
Esta función es relativamente sencilla, pero como la ejecuta el cliente, podría ser cualquier cosa. En la siguiente sección, veremos dos ejemplos de funciones más complejas.
El código completo de este ejemplo está disponible aquí.
Desarrolla hoy tus habilidades MLOps
Aplicación de demostración con la API en tiempo real de OpenAI
El equipo de OpenAI proporciona una aplicación React de demostración para mostrar la API en tiempo real. Aquí aprenderemos a configurarlo y exploraremos su funcionamiento. Es un buen punto de partida para construir una aplicación más compleja.
Configuración de la aplicación
No es necesario tener conocimientos de React para ponerlo en marcha. Sin embargo, necesitarías estar familiarizado con React para modificarlo o ampliarlo.
Su aplicación está alojada en este repositorio. Para configurarlo, empieza por clonando utilizando Git siguiente:
git clone org-14957082@github.com:openai/openai-realtime-console.git
También podemos descargarlo manualmente desde la interfaz de GitHub.
Para instalar la aplicación, utilizamos el siguiente comando NPM (node package manage):
npm install
Una vez finalizada la instalación, crea un archivo llamado .env
en la carpeta raíz del proyecto y pega el siguiente contenido:
OPENAI_API_KEY=<openai_api_key>
REACT_APP_LOCAL_RELAY_SERVER_URL=http://localhost:8081
Sustituye por la clave de la API de OpenAI.
Ahora la aplicación debería estar lista para ejecutarse. Se compone de dos partes:
- Un frontend React que consiste en la interfaz web de usuario de la aplicación.
- Se utiliza un servidor de retransmisión como intermediario entre el frontend y la API de OpenAI.
El objetivo principal de implementar un servidor de retransmisión entre el frontend y la API de OpenAI es almacenar de forma segura la clave de la API. Interactuar con la API es imposible sin esta clave.
Sin embargo, si la clave se almacenara en el frontend, sería accesible para cualquier usuario. Por tanto, la solución pasa por crear un servidor que almacene la clave de forma segura y facilite el intercambio de datos entre la API y el frontend. En este caso concreto, los problemas de seguridad son mínimos, ya que la aplicación sólo se ejecutará localmente.
Para lanzar la aplicación, es necesario iniciar tanto el servidor de retransmisión como el frontend. Para iniciar el servidor de retransmisión, utiliza el siguiente comando:
npm run relay
Para iniciar el frontend React utiliza el comando:
npm start
Cuando termine de cargarse, se abrirá una pestaña en el navegador con la aplicación ejecutándose en ella.
Utilizar la app
Antes de empezar a utilizar la aplicación, asegúrate de que el ordenador no está en modo silencio y permite el acceso del micrófono a la aplicación.
Empezamos haciendo clic en el botón "Conectar". Esto enviará un mensaje "Hola" a la API en tiempo real, y recibiremos un saludo.
Una vez establecida la conexión, aparecerá un nuevo botón en el centro, que nos permitirá hablar con el asistente de IA.
Para utilizarlo, pulsa y habla sin soltar el botón. El mensaje se envía cuando se suelta el botón.
La aplicación también tiene un modo VAD (detección de actividad de voz) en el que no necesitamos pulsar ningún botón. En este modo, la aplicación escuchará continuamente permitiéndonos mantener una conversación activa con el asistente. Para utilizarlo, sólo tienes que pulsar el botón "vad" y hablar.
Funciones
Como hemos sabido, la API en tiempo real ofrece una función que permite a la IA realizar funciones específicas. Esta demostración muestra dos funciones: una para consultar la previsión meteorológica en un lugar concreto y otra para añadir elementos de memoria para personalizar el asistente.
Experimenta estas funciones haciendo preguntas como "¿Qué tiempo hará mañana en Nueva York?" y declarando preferencias como "Mi color favorito es el azul". El asistente dará respuestas verbales a estas consultas, y la información también se mostrará en la parte derecha de la aplicación.
Hubo ocasiones en las que solicité un informe meteorológico y la respuesta indicaba la imposibilidad de acceder a él en ese momento. Sin embargo, la información se mostraba sistemáticamente en el lado derecho. Al ser una aplicación de demostración, no pretende ser un producto totalmente funcional, sino que sirve para mostrar las capacidades de la API.
Comprender al cliente
Esta sección requiere una comprensión de alto nivel de React para seguir, ya que cubrimos algunos de los detalles de implementación de la aplicación.
Vamos a ver el archivo ConsolePage.tsx
. Aquí es donde se define la mayor parte de la lógica de la aplicación. La aplicación de demostración no utiliza los WebSockets en bruto como hicimos en nuestros ejemplos de aplicaciones de línea de comandos Node.js. Construyeron un cliente en tiempo real que ayuda a interactuar con la API. Esto es lo que se importa en la parte superior del archivo:
import { RealtimeClient } from '@openai/realtime-api-beta';
La integración con la API se define en esta llamada a useEffect()
. El código de este useEffect()
se ejecuta cuando se muestra inicialmente la página de la consola. Similar a nuestro script Node.js, describe cómo responder a los eventos de la API. La principal diferencia es el uso de la envoltura de cliente RealtimeClient
.
Definir las herramientas
La función RealtimeClient.addTool()
se utiliza para definir herramientas. Toma dos parámetros:
- El objeto JSON de definición de la herramienta.
- La función que se va a ejecutar.
Este enfoque simplifica la integración de herramientas, puesto que el cliente ya está equipado para gestionar eventos y automatizar la invocación de funciones. La herramienta de memoria se define aquímientras que la definición de la herramienta del tiempo se define aquí.
Por ejemplo, para añadir la herramienta suma definida anteriormente, podemos hacer lo siguiente:
client.addTool(
{
name: "calculate_sum",
description: "Use this function when asked to add numbers together, for example when asked 'What's 4 + 6'?.",
parameters: {
type: "object",
properties: {
"a": { "type": "number" },
"b": { "type": "number" }
},
required: ["a", "b"]
}
},
(a: number, b: number): number => a + b
);
Ten en cuenta que la aplicación utiliza TypeScript, por lo que requiere la especificación de tipos dentro de la definición de la función.
Escuchar los acontecimientos
Para escuchar un evento, se utiliza la función RealtimeClient.on()
. Acepta dos parámetros:
- El nombre del evento.
- La función de llamada de retorno que se va a ejecutar.
Este enfoque es similar a la función WebSocket.on()
utilizada anteriormente, excepto que implementa un conjunto diferente de eventos. Su página GitHub proporciona la lista de eventos compatibles.
En este ejemplo concreto, se utilizan los siguientes eventos:
- El evento
realtime.event
aquí se utiliza para mantener un registro de todos los eventos. - El evento
error
aquí simplemente registra los errores en la consola con fines de depuración. - El evento
conversation.interrupted
aquí se utiliza para cancelar las solicitudes cuando se interrumpe la conversión. - Por último, el evento
conversation.updated
aquí se utiliza para añadir nuevo audio al flujo de audio cuando llegan nuevos chucks desde la API.
Conclusión
En este tutorial, hemos explorado la API en tiempo real de OpenAI y cómo utiliza WebSockets para la comunicación en tiempo real. Cubrimos la configuración de un entorno Node.js para interactuar con la API, el envío y recepción de mensajes de texto y audio, y la implementación de llamadas a funciones para mejorar la funcionalidad.
También exploramos la aplicación React de demostración de OpenAI, que muestra cómo desplegar una aplicación básica de asistente de voz.
Para saber más sobre las últimas herramientas de desarrollo de OpenAI, te recomiendo estos tutoriales:
¡Aprende a desarrollar aplicaciones de IA!
programa
Developing AI Applications
curso
Fully Automated MLOps
blog
Cinco proyectos que puedes crear con modelos de IA generativa (con ejemplos)
tutorial
Guía para principiantes de la API de OpenAI: Tutorial práctico y prácticas recomendadas
tutorial
Tutorial de la API de OpenAI Assistants
tutorial
Cómo utilizar la API de conversión de texto a voz de OpenAI
tutorial
Convertir voz en texto con la API Whisper de OpenAI
tutorial