Crea un ChatBot usando IA, ChatGPT y PHP

Camilo Herrera
winkhostingla
Published in
9 min readOct 12, 2023
Photo by Phillip Glickman on Unsplash

Hola!, bienvenido a esta nueva guía con contenido fresco y variado para nuestro elegante, simpático y siempre bien vestido público.

Debido a todo el ruido causado por la IA en los últimos meses, decidimos explorar la posibilidad de implementar un medio de soporte y servicio al cliente a partir de estas nueva herramienta, específicamente ChatGPT creada por OpenAI.

En esta publicación vamos a crear una prueba de concepto para determinar que tan lejos podemos llegar.

A continuación los requisitos para este proyecto:

Requisitos

  • Abrir una cuenta en openai.com, por defecto te entregarán $5 USD de crédito para que hagas pruebas, después de esto te recomiendo preparar tu tarjeta de crédito o la de tus padres… (será una linda sorpresa descubrir que su hijo está invirtiendo su crédito en el futuro de la inteligencia artificial)
  • Crear un API Key desde tu cuenta en OpenAI
  • Contar con un entorno de desarrollo para PHP en tu PC, usaremos PHP 8.2, incluyendo un servidor web.
  • Mantener una buena postura al sentarte en tu silla de escritorio, esto es muy importante!

Abriendo nuestra cuenta en OpenAI y creando un API Key

Vamos a iniciar con la apertura de nuestra cuenta, ingresa a https://openai.com y sigue los pasos en el portal para abrir tu cuenta, será necesario un paso adicional de autenticación enviando un mensaje de texto a tu teléfono, tenlo presente.

Una vez finalices el proceso de apertura de tu cuenta, ve a la opción en el menú superior, y dale clic al nombre de tu cuenta en la esquina derecha, a continuación, dale clic a la opción “View API Keys”.

Menú para crear API Keys

Será mostrada la sección con título “API Keys” allí dale clic al boton “+ Create new secret key”, asigna un nombre opcional a la nueva llave secreta y dale clic a “Create secret key”. La nueva llave será mostrada en pantalla, copia el texto (que inicia con sk-…) y guárdalo en un lugar seguro, vas a necesitar esta llave para autenticar tus peticiones y usar la API de OpenAI.

Para finalizar dale clic al botón “Done”.

Preparando nuestro entorno de desarrollo y archivos

En nuestro entorno de desarollo de PHP, vamos a crear un directorio, en este caso lo llamaremos “ai-chatbot” y en él crearemos los siguientes archivos:

  • index.html: Este será el punto de entrada a nuestro chat y se encargará de la GUI
  • ChatBot.php: Nuestra clase encargada de las rutinas de interacción con la API de ChatGPT
  • requestmanager.php: Este archivo recibirá las peticiones desde index.htmly retornará los resultados de la interacción con ChatGPT
  • test-data.json: Este archivo contendrá información simulada asociada al servicio que prestará nuestro ChatBot, puede ser cualquier cosa, en este caso usaremos información sobre componentes para computadora.

La estructura de archivos sería la siguiente:

Nuestros archivos.

Iniciaremos por crear nuestra GUI, será un formulario básico de chat con una sección para visualizar la conversación y una caja de texto con su respectivo botón para enviar mensajes.

Vamos a construir la interfaz con mi querida Bulma CSS y se verá de la siguiente forma:

GUI de nuestro chat

El contenido del archivo index.html a continuación:

index.html

<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello A.I. Bot!</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<script type="module">
window.addEventListener('load', (event) => {

document.querySelector(".sendMessage").addEventListener('click', (event) => {

event.currentTarget.classList.add('is-loading');
event.currentTarget.disabled = true;

document.querySelector(".result").parentElement.classList.add("is-hidden");
document.querySelector(".error").parentElement.classList.add("is-hidden");

let currHour = new Date();

const userMsgTemplate = `<div class="columns">
<div class="column is-one-third"></div>
<div class="column">
<div class="notification is-success">
<h6 class="subtitle is-6">${currHour.getHours() + ":" + currHour.getMinutes()}</h6>
${document.querySelector(".message").value}
</div>
</div>
</div>`


let chatBox = document.querySelector(".messageHistory");

chatBox.innerHTML += userMsgTemplate;
chatBox.scrollIntoView(false);

const payload = JSON.stringify({
"message": document.querySelector(".message").value
});

document.querySelector(".message").value = "";

fetch('requestmanager.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: payload,
}).then(response => response.json())
.then(data => {

let currHour = new Date();

data.responseMessage = data.responseMessage.replace("\n", "<br>");

let aiMsgTemplate = `<div class="columns">
<div class="column">
<div class="notification is-info">
<h6 class="subtitle is-6">${currHour.getHours() + ":" + currHour.getMinutes()}</h6>
${data.responseMessage}
</div>
</div>
<div class="column is-one-third"></div>
</div>`

chatBox.innerHTML += aiMsgTemplate;
chatBox.scrollIntoView(false);

})
.catch((error) => {
console.error('Error:', error);
}).finally(() => {
document.querySelector(".sendMessage").classList.remove('is-loading');
document.querySelector(".sendMessage").disabled = false;
});
});

});
</script>
</head>

<body>
<section class="section">
<div class="container">
<div class="columns box">
<div class="column">
<div class="columns">
<div class="column has-text-centered">
<h1 class="title">
Hello A.I. Bot!
</h1>
</div>
</div>
<div class="columns">
<div class="column">
<div class="card-content" style="height:600px;overflow:auto;flex-grow: 1;flex-shrink: 1;">
<div class="content messageHistory">


</div>
</div>
</div>
</div>


<div class="columns">
<div class="column">
<input class="input message" type="text" placeholder="Type your message">
</div>
<div class="column is-narrow">
<button class="button is-info sendMessage">
Send
</button>
</div>
</div>

</div>
</div>
<div class="columns box is-hidden">
<div class="column result"></div>
</div>
<div class="columns box is-hidden">
<div class="column notification is-danger error has-text-centered">
</div>
</div>
</div>
</section>
</body>

</html>

Con JavaScript crearemos un manejador de eventos para el botón de envío identificado con la clase “sendMessage” y dentro de él creamos la rutina para enviar el mensaje usando POST a través de la API Fetch a nuestro archivo requestmanager.php. Esperaremos la respuesta y la usaremos para mostrar el mensaje de recibido en la caja de chat identificada con la clase “messageHistory”.

Ahora vamos a crear nuestro archivo JSON que simulará una fuente de datos personalizada, para usarla en caso tal que el visitante realice una pregunta asociada a los productos que vende nuestro bot.

El contenido del archivo será el siguiente:

test-data.json

{
"cpu": [
{
"brand": "AMD",
"model": "AMD Ryzen 5 5600X",
"price": "USD 158"
},
{
"brand": "AMD",
"model": "AMD Ryzen 9 5950X",
"price": "USD 799"
},
{
"brand": "Intel",
"model": "Core i5-12600K",
"price": "USD 179"
}
],
"memory": [
{
"brand": "Corsair",
"model": "VENGEANCE DDR5 1x32GB",
"price": "USD 159"
},
{
"brand": "G.SKILL",
"model": "Trident Z5 RGB Series DDR5 1x16GB",
"price": "USD 104"
}
]
}

Usaremos algunos modelos de CPU y RAM para las pruebas.

Ahora vamos con la clase encargada de la interacción con la API de OpenAI, aquí es donde la diversión comienza.

Así deberías verte en este punto.

ChatBot.php

<?php

/**
* Clase ChatBot
*/
class ChatBot
{
/**
* @var string El token de autorización para la API
*/
private $authorization;

/**
* @var string La URL del endpoint para la API de OpenAI.com
*/
private $endpoint;

/**
* Constructor de ChatBot.
*/
public function __construct()
{
// Este token no es real, por si estabas pensando lo que yo estoy pensando...
$this->authorization = 'sk-bkd04ADr2TXXDGugb2NEWuH3BlbkFJFavL1XJbMZcFIegY5qY';
$this->endpoint = 'https://api.openai.com/v1/chat/completions';
}

/**
* Envía un mensaje a la API y devuelve la respuesta.
*
* @param string $message El mensaje a enviar
* @return string El mensaje de respuesta
* @throws Exception Si hay un error al enviar el mensaje a través de cURL
*/
public function sendMessage(string $message): string
{
// Leer datos de ejemplo de nuestro archivo JSON
$jsonSampleData = file_get_contents("test-data.json");

// Preparamos los datos para enviar
$data = [
'messages' => [
[
'role' => 'system',
'content' => 'Eres un amable y servicial miembro del servicio al cliente en una tienda de componentes de PC.
Si el usuario pregunta cómo comprar, remítelo a nuestro sitio https://medium.com/winkhosting.
Si el usuario pregunta algo sobre CPUs o RAM utiliza exclusivamente la entrada de cpu o ram en la siguiente cadena JSON para sugerir opciones:' . $jsonSampleData
],
[
'role' => 'user',
'content' => $message
],
],
'model' => 'gpt-3.5-turbo'
];

// Establecemos los encabezados para la solicitud de la API
$headers = [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->authorization,
];

// Envía la solicitud a la API usando cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->endpoint);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);

// Verifica si hay errores en la respuesta de la API
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception('Error al enviar el mensaje: ' . $error);
}

curl_close($ch);

// Analiza la respuesta de la API
$arrResult = json_decode($response, true);
$resultMessage = $arrResult["choices"][0]["message"]["content"];

// Devuelve el mensaje de respuesta
return $resultMessage;
}
}

Como puedes ver, nuestra clase solo tiene un método que recibe un enunciado/pregunta y se encarga de enviar la información al endpoint correspondiente en openai.com para recibir una respuesta (https://api.openai.com/v1/chat/completions).

En la configuración de nuestra solicitud a la API (arreglo $data) usaremos el modelo “gpt-3.5-turbo” este es el recomendado en cuanto a relación costo/beneficio, pero si tienes suficiente dinero y valentía, puedes usar el modelo de gpt-4 que es el más reciente con mayores prestaciones y capacidad de respuesta.

Para conocer más sobre los modelos disponibles ingresa a este enlace https://platform.openai.com/docs/models

También en la misma configuración le indicaremos a nuestra IA el rol (rol “system”) que debe desempeñar, “ser un amable y servicial miembro del servicio al cliente” y otras indicaciones sobre la información que debe brindar al momento de una consulta de componentes como RAM y CPU.

Importante: dar las indicaciones en un lenguaje claro tendrá como resultado menos errores en la interacción con un visitante.

Si estás poniendo atención también notarás que cargamos el contenido de nuestro archivo dummy en una variable llamada $jsonSampleData y lo agregamos al contenido del rol que tendrá nuestar IA, esto para que tenga acceso a la información y entregue respuestas a partir del listado de productos en ella.

Rol System

[
'role' => 'system',
'content' => 'Eres un amable y servicial miembro del servicio al cliente en una tienda de componentes de PC.
Si el usuario pregunta cómo comprar, remítelo a nuestro sitio https://medium.com/winkhosting.
Si el usuario pregunta algo sobre CPUs o RAM utiliza exclusivamente la entrada de cpu o ram en
la siguiente cadena JSON para sugerir opciones:' .
$jsonSampleData
]

El mensaje del usuario será agregado al arreglo $data asociado al rol “user

Rol User

[
'role' => 'user',
'content' => $message
]

Una vez contamos con nuestro payload de la petición a enviar, vamos a realizarlo usando cURL. Si todo sale bien recibiremos la respuesta de nuestra IA en formato JSON, la transformamos a un arreglo asociativo (gracias a la mágia de PHP) y nuestra respuesta estará en la entrada $arrResult[“choices”][0][“message”][“content”] a continuación extraemos el texto y lo retornamos.

Con nuestra clase lista, vamos a continuar con el archivo encargado de recibir las peticiones desde nuestra interfaz y retornar la respuesta al usuario.

La responsabilidad de este archivo será recibir una cadena de texto (el mensaje) a través de una petición POST desde index.html, entregar el mensaje a nuestra IA y retornar la respuesta así:

requestmanager.php

<?php

//Se incluye la definición de la clase ChatBot a usar para nuestra consulta.
require("ChatBot.php");

//Decodificamos los parámetros recibidos desde el archivo index.html y los almacenamos en el arreglo $paramsFetch
$paramsFetch = json_decode(
file_get_contents("php://input"),
true
);

//instanciamos nuestra clase
$ChatBot = new ChatBot();
//Enviamos el mensaje a nuestra IA
$resMessage = $ChatBot->sendMessage($paramsFetch["message"]);

//A continuación retornamos la respuesta en formato JSON y finalizamos la ejecución.
$jsonResponse = json_encode(array("responseMessage" => $resMessage));
echo $jsonResponse;
exit;

Este script incluye la clase ChatBot, recibe el mensaje y hace el llamado a sendMessage(), la respuesta retornada es convertida a JSON y se imprime para retornarla a index.html

Y es todo, si todo funciona como debe y no hay errores puedes empezar a interactuar con tu IA:

Nuestro bot!

Ingresa en tu entorno de desarrollo al URL de tu proyecto, en mi caso http://localhost/ai-chatbot/

El resultado.

Escribe alguna pregunta o mensaje para la IA y empieza a interactuar con ella.

Gracias por llegar hasta este punto y recuerda que en Winkhosting.co somos mucho más que hosting!

P.D. El código PHP usado para esta guía fue creado con la ayuda de IA. ;)

--

--