Tutorial: Chat con ChatGPT - Parte 2
Crearemos una webapp desde la cual permitiremos a los usuarios comunicarse con un chat de OpenAI que podremos ajustar con nuestros parámetros.
Si han realizado la parte 1 a esta altura tienen una webapp basada en React que puede comunicarse con un servidor WebSocket y envÃa y recibe mensajes de chat.
TecnologÃas que utilizaremos en este tutorial:
- Node
- React
- LibrerÃa de OpenAI
- WebSocket
Vamos a checkear que estemos utilizando versiones similares de Node:
node -v
Yo estoy usando la v20.16.0
Parte 2: Conectar nuestro chat a los servicios de OpenAI
OpenAI
Vamos a agregar la interfaz con los servicios de OpenAI. En la carpeta donde esté el servidor instalaremos la librerÃa oficial de OpenAI
npm install openai --save
La librerÃa de openai leerá por default la variable de entorno OPENAI_API_KEY como su key. Para definirla puede usarse la librerÃa dotenv para leerla de un archivo .env o la opción --env-file=.env al correr node (en versiones superiores a Node v20). En ese caso recuerde agregar .env al .gitignore para evitar subir la key a un repositorio. Para simplificar la definiremos en la terminal.
En Mac o Linux
OPENAI_API_KEY=mi-key
En Windows:
set OPENAI_API_KEY=mi-key
Luego en chat-ws-server.js, al comienzo:
const OpenAI = require('openai');
const openai = new OpenAI();
OpenAI provee diferentes servicios, por ejemplo, generación de imágenes, generación de text to speech y otros, pero el que utilizaremos especÃficamente para chat se llama chat.completions. La documentación completa está en https://platform.openai.com/docs/guides/chat-completions
El parámetro principal de este método se llama messages y es un array de objetos, cada uno tendrá las props role y content. Role puede ser system, assistant y user.
System es un mensaje invisible al usuario, es para darle parámetros de contexto e instrucciones al asistente. Por ejemplo podrÃa ser "Eres un asistente de una tienda que ofrece estos productos: ..." o "No respondas preguntas relacionadas con polÃtica". No necesariamente debe ser breve. Luego los de de role assistant y user se irán intercalando en la medida que la conversación se desarrolle, siendo assistant los que responda OpenAI. En este caso el historial de mensajes lo guardaremos de manera local en el server asà que los incluiré en un array.
Dentro del listener on connection:
...
myWsServer.on('connection', (theConnection) => {
...
// Creamos un array que va a contener el historial
const chatHistory = [];
theConnection.on('message', async (receivedBufferData) => {
// El WebSocket recibe la información como Buffer data
const dataAsStringifiedObject = receivedBufferData.toString();
// En realidad sabemos que es un json stringificado con la propiedad text porque nosotros lo hemos armado asà en el cliente
const dataAsObject = JSON.parse(dataAsStringifiedObject);
// Agregamos el nuevo mensaje del usuario al historial de chat
chatHistory.push({
role: 'user',
content: dataAsObject.text,
});
// chat.completions es el nombre de este servicio de generación de texto de OpenAI, buscar opciones en la documentación
const aiConversation = await openai.chat.completions.create({
messages: [
{
role: 'system',
content: `Eres un asistente especializado en información sobre cocina y recetas.`
},
// Enviamos el historial cada vez. PodrÃamos limitarlo a n cantidad de los últimos elementos de extenderse mucho.
...chatHistory,
],
// Otros modelos podrÃan ser 'gpt-4-vision-preview', 'gpt-3.5-turbo' o 'gpt-3.5-turbo-1106' etc.
model: 'gpt-4o',
max_tokens: 500,
//
temperature: 0.5,
})
// Obtenemos únicamente el texto de la respuesta
const answer = aiConversation.choices[0].message.content.trim();
// Agregamos también la respuesta al historial.
chatHistory.push({
role: 'assistant',
content: answer,
});
theConnection.send(JSON.stringify({ text: answer }));
});
});