Tutorial: Crea un ChatBot personalizado usando la API de OpenAI.

En este tutorial te enseñamos a crear tu ChatBot personalizado con Python y Streamlit, un framework que te facilita el desarrollo de aplicaciones web. Aprenderás a usar el ChatCompletion de OpenAI, personalizar avatares para el chat y mejorar la interacción con tu asistente virtual personalizado.

Tutorial: Crea un ChatBot personalizado usando la API de OpenAI.

En un tutorial anterior te habíamos enseñamos a usar la API de OpenAI en Python. Ahora vamos a usar lo aprendido para desarrollar un ChatBot personalizado. Si no dominas el uso de la API, te sugerimos revisar el tutorial antes de continuar con este.

Para este tutorial nos apoyaremos del framework Streamlit, el cual facilita bastante el desarrollo de aplicaciones. En un par de líneas de códigos podrás desarrollar aplicaciones como un Asistente de Viaje, tal como lo muestra la siguiente figura.

¡Comencemos con el tutorial!

ℹ️
Para este tutorial necesitarás un Entorno de Desarrollo Integrado (IDE) para escribir código de Python. Te sugerimos usar Visual Studio Code, el que puedes bajar gratuitamente aquí.

El código que aquí explicamos puedes encontrarlo en el siguiente link:
> Github Repository

Preparando el ambiente de Python

Lo primero que sugerimos hacer es configurar un ambiente de Python. Para eso debes ir a la terminal y ubicarte en el directorio de tu proyecto. O también, lo puedes hacer desde Visual Studio Code, el IDE que usaremos en este tutorial. Una vez en el directorio, ejecutamos el siguiente comando:

python -m venv chatbot-env

Esto nos creará un ambiente local con el nombre de chatbot-env, así controlamos de mejor manera las librerías que necesitaremos en nuestro proyecto. Una vez creado el ambiente, debes activarlo ejecutando:

source chatbot-env/bin/activate

Ok! Ya estas listo para instalar las librerías que necesitarás. Primero, tal como lo vimos en el tutorial anterior, debes instalar el paquete de openai. También necesitarás instalar el streamlit, una librería que te facilitará la construcción de la aplicación de ChatBot. Finalmente, vas a necesitar instalar el componente streamlit-chat, el cual te permitirá desplegar la historia de mensajes de manera más amigable. Para instalar todos estos paquetes, debes ejecutar el siguiente comando en tu terminal:

pip install openai streamlit streamlit-chat

¡Listo! Tienes todo lo necesario para desarrollar tu ChatBot personalizado.

¡Manos a la obra!

Desarrollando la Interfaz

Primero, debes crear un archivo Python en la carpeta de tu proyecto y le das el nombre de chatbot.py, que es donde comenzaremos a escribir el código de nuestro proyecto. Partiremos por importar todas las librerías que usaremos. openai será usada para realizar llamados a la API de OpenAI, streamlit para desarrollar la interfaz de nuestra aplicación, y streamlit_chat para desplegar de manera amigable el historial de la conversación.

import openai
import streamlit as st
from streamlit_chat import message

Perfecto! Ahora comenzaremos a desarrollar la interfaz usando el framework de streamlit ¡Verás lo fácil que es!

Primero vamos a generar un título a nuestra página usando st.title. Este comando recibe como parámetro el texto que queremos como título. Además,  como streamlit soporta emoji shortcodes, podemos incluir los emojis en nuestro título usando los shortcodes enlistados aquí. En este tutorial, usaremos el siguiente título:

st.title("Mi Primer Chatbot :tada:")

Ahora necesitamos crear una caja donde el usuario pueda escriba los mensaje que desea entregarle al ChatBot. Para esto usaremos el componente text_input de streamlit. Este componente recibe como parámetro la llave, el label, un placeholder y el tipo de visibilidad. En nuestro caso generaremos un text_input con la siguiente configuración:

user_input = st.text_input(
    key='user_message',
    label="Your message:",
    placeholder="Escribe aquí tu mensaje",
    label_visibility='collapsed'
)

El texto ingresado por el usuario será guardado en la variable user_input. Si eventualmente no se ha ingresado algún texto, esta variable es empty.

Para gestionar de mejor manera cada vez que el usuario envía un mensaje al asistente, podemos crear un formulario con un botón que gatille un request. Para eso, usaremos el componente form de streamlit. Dentro del formulario, vamos a crear dos columnas por medio de st.columns, donde una de ellas contendrá el text_input y otra el botón de envío que gatillará los requests. Esto lo podemos realizar con el siguiente código:

with st.form("chat_input", clear_on_submit=True):
    a, b = st.columns([10, 2])

    user_input = a.text_input(
        key='user_message',
        label="Your message:",
        placeholder="Escribe aquí tu mensaje",
        label_visibility='collapsed'
    )
    
    b.form_submit_button("\>", use_container_width=True)

En el código, st.columns inserta dos contenedores uno al lado del otro. El número indica la cantidad de columnas de igual ancho por cada contenedor. En nuestro caso lo dejamos como 10 y 2. En el primer contenedor creamos el text_input y en el segundo el botón.

Para comprobar que esto funciona, puedes correr de manera local la aplicación ejecutando el comando de más abajo en tu terminal. De ejecutarse correctamente, se abrirá la aplicación de manera automática en el navegador que tengas por defecto. En el caso que esto no suceda, puedes usar la dirección que streamlit entrega en tu terminal. La aplicación abierta en tu navegador se debería ver parecido a la Figura 1 de más abajo.

streamlit run chatbot.py
Figura 1. Interfaz chatbot usando streamlit.

Es importante destacar que de ahora en adelante no es necesario volver a ejecutar la aplicación en la terminal. Para reflejar cualquier cambio que hagas, solamente necesitarás refrescar la página abierta en tu navegador.

El siguiente paso será crear un hilo en el que se muestren los mensajes que el usuario y el asistente han generado durante la conversación. Para esto necesitaremos dos cosas: (1) guardar el historial de los mensajes, y (2) mostrar este historial de una manera amigable.

Para el primer objetivo vamos a aprovechar la capacidad que nos entrega streamlit para guardar estados de la sesión de nuestra aplicación, los cuales permiten capturar parámetros o guardar estados de nuestros componentes o variables. Primero, vamos a crear un estado de sesión llamado messages siguiendo esta estructura que nos la API de OpenAI en el parámetro message del modelo ChatCompletino, el cual era un arreglo de mensajes con las llaves role y content. Este arreglo partirá con un primer mensaje del asistente, y cada vez que el usuario ingrese un mensaje, o el asistente genere uno por medio de la API, lo iremos agregando a este arreglo.

if "messages" not in st.session_state:
    st.session_state["messages"] = [{"role": "assistant", "content": "Hola soy tu robot personal ¿En qué te puedo ayudar hoy?"}]

Para el segundo objetivo, usaremos la función message del componente streamlit_chat, el cual nos ayuda a desplegar de manera amigable los mensajes, mostrando a la izquierda los mensajes del asistente y a la derecha los del usuario. Además, este componente permite personalizar un avatar para cada uno (en este link puedes ver la lista de avatares). En este tutorial asignaremos el avatar lorelei al usuario, y pixel-art al asistente.

El siguiente crea un diccionario con los avatares del usuario y asistente, luego recorre los mensajes que están en el estado messages y los muestra en la aplicación aplicando el formato correspondiente si es un mensaje del usuario o del asistente. Sugiremos agregar el diccionario inmediatamente después de importar las librerías, y el bucle que recorre los mensajes al final del código.

avatar = {
    'user': 'lorelei',
    'assistant': 'pixel-art'
}

for i, msg in enumerate(reversed(st.session_state.messages)):
    message(msg["content"], 
            is_user= msg["role"] == "user", 
            key=str(i), 
            avatar_style=avatar[msg["role"]])

Si guardas los cambios y refrescas la aplicación en el navegador, verás que solo se muestra por el momento el mensaje inicial, algo muy parecido a la Figura 2.

Figura 2. Aplicación del ChatBot personal con la caja para escribir mensajes y el historial de mensajes.

¡Tenemos lista la interfaz! Ahora vamos a escribir el código que toma el mensaje enviado por el usuario y luego genera la respuesta usando la API del OpenAI, dándole personalidad y autonomía al asistente.

Programando al Asistente

Ahora vamos a usar la API según lo aprendido en el tutorial anterior. Primero, nos autenticamos usando la API Key guardada en el texto plano key.txt. Te sugerimos escribir el código de la autenticación después de la importación de la librería. Si no tienes esta llave, te sugiero seguir los pasos en la Parte 1 de este tutorial.

openai_api_key = open('key.txt').read()
openai.api_key = openai_api_key

Ahora podemos programar el asistente. La lógica que usaremos se compone de dos pasos: (1) primero vamos a asegurarnos que el usuario haya ingresado un mensaje, y (2) llamamos la API para generar la respuesta del asistente. Para el primer paso, en caso que todavía no haya enviado un mensaje, no pasaremos al paso (2). Sin embargo, si ya ingresó algún mensaje, antes de ir al paso 2, primero vamos adjuntar el mensaje del usuario al estado messages asignándole el rol de user. Luego, comenzando con el paso (2), usaremos el ChatCompletion de la API para generar la respuesta y agregaremos el texto generado al estado messages con el rol de assistant. Todo este proceso se puede ver en el siguiente código:

if user_input:
    st.session_state.messages.append({"role": "user", "content": user_input})

    response = openai.ChatCompletion.create(
                model="gpt-3.5-turbo",
                messages=st.session_state.messages,
                max_tokens = 400,
                temperature = 1.0
                )
    msg = response.choices[0].message.content

    st.session_state.messages.append({'role': 'assistant', 'content': msg})

Listo! Ya puedes comenzar a interactuar con tu ChatBot personalizado. Si deseas, puedes agregar un mensaje al estado messages con el rol system en el que configuras el comportamiento del asistente.

Mejoras de Interacción

Si guardas los cambios y pruebas la aplicación notarás que la forma en que se carga y despliegan los mensajes no es tan natural. Para mejorar un poco ese aspecto, haremos un pequeño cambio.

Nuestro objetivo será agregar un spinner el cual le indica al usuario que el ChatBot está cargando el mensaje. Para eso agregaremos un contenedor inicialmente vacío entre el formulario que contiene el input_text y el historial de mensajes. Este contenedor será reemplazado por un spinner justo antes de llamar a la API y será reemplazado nuevamente por el espacio vacío una vez que se reciba la respuesta de la API. El código que implementa este cambio se encuentra a continuación:

placeholder = st.empty()
if user_input:
    st.session_state.messages.append({"role": "user", "content": user_input})

    with placeholder.container():
        with st.spinner('Cargando...'):
            response = openai.ChatCompletion.create(
                        model="gpt-3.5-turbo",
                        messages=st.session_state.messages,
                        max_tokens = 400,
                        temperature = 1.0
                        )
            msg = response.choices[0].message.content

    st.session_state.messages.append({'role': 'assistant', 'content': msg})

El espacio vacío es generado con el código placeholder = st.empty(). Luego, durante la llamada de la API, se genera un contenedor con el spinner y el texto 'Cargando...'. Una vez que el llamado es finalizado y la respuesta es recuperada en la variable msg, este placeholder vuelve ser asignado con un contenedor vacío.

Guardando los cambios y refrescando la página de tu navegador deberías ver algo como esto:

Figura 2. Ejempo de spinner una vez que el mensaje es enviado.

¡Felicitaciones! ¡Ya has creado tu primer ChatBot personalizado!

Esperamos que te haya gustado y haznos saber en los comentarios cualquier duda que tengas. Recuerda suscribirte y coméntanos si te ha gustado este tutorial.

💡
¿Quieres recibir contenido como este?
Suscríbete a nuestras novedades aquí.