Cómo codifiqué mi propio tutor privado de francés a partir de ChatGPT

Creé mi propio tutor privado de francés usando ChatGPT.

Guía paso a paso de cómo utilicé los últimos servicios de IA para enseñarme un nuevo idioma, desde arquitectura hasta ingeniería de prompts

El código del tutor de idiomas extranjeros discutido se puede encontrar en el repositorio companion en mi página de GitHub, y puedes usarlo libremente para cualquier uso no comercial.

Hecho con Dall-E

Así que después de posponerlo durante bastante tiempo, decidí retomar mis estudios de francés. Cuando me inscribí en la clase, me surgió este pensamiento: ¿y si pudiera programar ChatGPT para que fuera mi tutor personal de francés? ¿Y si pudiera hablar con él y él me respondiera? Siendo un científico de datos que trabaja con LLMs, esto parecía algo que valía la pena construir. Quiero decir, sí, puedo simplemente hablar con mi esposa, que es francesa, pero eso no es tan genial como diseñar mi propio tutor personal a partir de ChatGPT. Te quiero, cariño ❤️.

Pero ahora en serio, este proyecto es un poco más que solo “otro juguete de código genial”. La IA generativa se dirige a todos los campos de nuestras vidas, y los Modelos de Lenguaje Grande (LLMs, por sus siglas en inglés) parecen liderar aquí. Las posibilidades de lo que una sola persona puede hacer en estos días con acceso a estos modelos son asombrosas, y decidí que este proyecto vale mi tiempo, y creo que también el tuyo, por dos razones principales:

  • Utilizar ChatGPT como la conocida herramienta en línea es poderoso, pero integrar un LLM en tu código es algo completamente diferente. Los LLMs todavía son algo impredecibles, y cuando tu producto depende de un LLM, o cualquier otro modelo de IA generativa, para el núcleo del producto, necesitas aprender cómo controlar realmente a la IA generativa. Y no es tan fácil como parece.
  • Obtener una primera versión funcional solo tomó unos pocos días de trabajo. Antes de la IA generativa y los LLMs, esto llevaría meses y probablemente requeriría más de una sola persona. El poder de utilizar estas herramientas para crear aplicaciones potentes rápidamente es algo que realmente debes probar por ti mismo, eso es el futuro, al menos según mi punto de vista. No vamos a retroceder.

Además, este proyecto realmente puede hacer el bien. Mi mamá realmente quiere a alguien con quien practicar su inglés. Ahora puede hacerlo, y cuesta menos de $3 al mes. La mamá de mi esposa quiere comenzar a estudiar coreano. Lo mismo, mismo costo. ¡Y por supuesto que yo también lo uso! Este proyecto realmente ayuda a las personas y cuesta menos que una taza pequeña de café. Esa es la verdadera revolución de la IA generativa, si me preguntas.

Empezando desde cero

Al mirar este proyecto desde una perspectiva de alto nivel, lo que necesitaba eran 4 elementos:

  • Reconocimiento de voz, para transcribir mi propia voz a palabras
  • Modelo de Lenguaje Grande, preferiblemente un Chat-LLM, con el que pueda hacer preguntas y recibir respuestas
  • Texto a voz, para convertir las respuestas del LLM en voz
  • Traducción, para convertir cualquier texto en francés que no entienda completamente al inglés (o al hebreo, mi lengua materna)

Afortunadamente, es 2023, y todos los elementos mencionados anteriormente son muy accesibles. También he optado por utilizar servicios y APIs gestionadas en lugar de ejecutar cualquiera de estos localmente, ya que la inferencia será mucho más rápida de esta manera. Además, los bajos precios de estas APIs para uso personal hicieron que esta decisión fuera obvia.

Después de probar varias alternativas, elegí Whisper y ChatGPT de OpenAI como mi reconocimiento de voz y LLM, y el Text-to-speech y Translate de Google como los módulos restantes. La creación de claves de API y la configuración de estos servicios fue muy sencilla, y pude comunicarme con todos ellos a través de sus bibliotecas nativas de Python en cuestión de minutos.

Lo que realmente me sorprendió después de probar todos estos servicios es que el tutor que estoy construyendo no es solo un profesor de inglés a francés; como Whisper, ChatGPT y Google Translate & TTS admiten docenas de idiomas, esto se puede utilizar para aprender prácticamente cualquier idioma mientras se habla cualquier otro idioma. ¡Eso es increíble!

Hecho con Dall-E

Arquitectura y Thread

Primero asegurémonos de comprender bien el flujo general: (1) Comenzamos grabando la voz del usuario, que se (2) envía a Whisper API y regresa como texto. (3) El texto se agrega al historial del chat y se envía a ChatGPT, que (4) devuelve una respuesta escrita. Su respuesta se (5) envía a Google Text-to-speech, que devuelve un archivo de sonido que se (6) reproduce como audio.

Arquitectura de alto nivel

Mi primer paso práctico fue desglosar esto en componentes y diseñar la arquitectura general. Sabía que necesitaría una interfaz de usuario, preferiblemente una interfaz de usuario web, ya que es más fácil lanzar aplicaciones a través del navegador en estos días que tener un ejecutable independiente. También necesitaré un “backend”, que será el código Python real, que se comunicará con todos los servicios diferentes. Pero para proporcionar una experiencia de flujo en tiempo real, me di cuenta de que tendría que dividirlo en diferentes hilos.

El hilo principal ejecutará la mayor parte del código: transcribirá mi grabación a texto (a través de Whisper), mostrará este texto en la pantalla como parte del chat, y luego mostrará la respuesta escrita del tutor en la pantalla de chat también (según lo recibido por ChatGPT). Pero tendré que mover el texto a voz del tutor a un hilo separado, de lo contrario, lo que tendremos será:

  • la voz del tutor solo se escuchará una vez que se haya recibido todo el mensaje de ChatGPT, y su respuesta podría ser larga
  • bloqueará al usuario para responder mientras el tutor habla

ese no es el comportamiento “fluido” que me gustaría tener; me gustaría que el tutor comience a hablar mientras su mensaje se escribe en la pantalla, y ciertamente no bloquee al usuario y le impida responder solo porque el audio aún se está reproduciendo.

Para hacer eso, la parte de texto a voz del proyecto se dividió en dos hilos adicionales. A medida que se recibía la respuesta del tutor de ChatGPT token por token, cada oración completa creada se pasaba a otro hilo, desde donde se enviaba al servicio de texto a voz, convirtiéndola en archivos de sonido. Me gustaría enfatizar la palabra archivos aquí, ya que estoy enviando texto al servicio TTS oración por oración, también tengo varios archivos de audio, uno por cada oración, que deben reproducirse en el orden correcto. Estos archivos de sonido luego se reproducen desde otro hilo, asegurándose de que la reproducción de audio no bloquee el resto del programa.

Hacer que todo esto funcione, junto con varios otros problemas que surgen de las interacciones entre la interfaz de usuario y el servidor, fue la parte complicada de este proyecto. Sorprendente, ¿verdad? La ingeniería de software es donde las cosas se vuelven difíciles.

Diseñando la interfaz de usuario

Interfaz de usuario del proyecto

Bueno, sabía que necesitaría una interfaz de usuario, y también sabía más o menos cómo me gustaría que se vea, pero programar una interfaz de usuario está más allá de mis conocimientos. Así que decidí probar un enfoque novedoso: pedí que ChatGPT escribiera mi interfaz de usuario por mí.

Para esto utilicé el servicio ChatGPT real (no la API) y utilicé GPT-4 (sí, ¡soy un orgulloso cliente pagador!). Sorprendentemente, mi indicación inicial:

Escribe una interfaz web en Python para una aplicación de chatbot. El cuadro de texto donde el usuario ingresa su indicación se encuentra en la parte inferior de la pantalla y todos los mensajes anteriores se mantienen en pantalla

entregó un resultado increíble, terminando con un backend de Python-Flask, código de jQuery, HTML y CSS correspondiente. Pero eso fue solo aproximadamente el 80% de todas las funcionalidades que esperaba obtener, así que pasé aproximadamente 10 horas yendo y viniendo con GPT-4, optimizando y mejorando mi interfaz de usuario, una solicitud a la vez.

Si lo hice parecer simple, no quiero decir claramente que no lo fue. Cuantas más solicitudes agregaba, más confundido se volvía GPT-4 y entregaba código defectuoso, que en algún momento era más fácil corregir manualmente que pedirle que lo arreglara. Y tenía muchas solicitudes:

  • Agregar una foto de perfil junto a cada mensaje
  • Agregar un botón para reproducir el audio de cada mensaje
  • Agregar un botón a cada mensaje en francés que agregue su traducción debajo del texto original
  • Agregar botones de guardar sesión y cargar sesión
  • Agregar una opción de modo oscuro, hacer que elija el modo correcto automáticamente
  • Agregar un icono de “trabajando” cada vez que se espera una respuesta de un servicio
  • Y muchas muchas más…

A pesar de que por lo general el código de GPT nunca funcionaba de inmediato, considerando el hecho de que tengo muy poco conocimiento en el campo del front-end, los resultados son asombrosos, y mucho más allá de lo que podría haber hecho yo mismo solo buscando en Google y StackOverflow. También he avanzado mucho en aprender cómo crear mejores indicaciones. Pensándolo bien, tal vez debería escribir otro artículo sobre las lecciones aprendidas al construir literalmente un producto desde cero junto con un LLM… ¡manténganse conectados!

Ingeniería de Indicaciones

Para esta parte del artículo, asumiré que tienes algunos conocimientos básicos sobre cómo funciona la comunicación con un LLM de chat (como ChatGPT) a través de una API. Si no los tienes, es posible que te sientas un poco perdido.

Hecho con Dall-E

Por último, pero no menos importante, tuve que hacer que GPT asumiera el papel de un tutor privado.

Como punto de partida, agregué un Mensaje del Sistema al inicio del chat. Como el chat con un LLM es básicamente una lista de mensajes enviados por el usuario y el bot entre sí, un Mensaje del Sistema suele ser el primer mensaje del chat, que describe al bot cómo debe comportarse y qué se espera de él. El mío se veía así (los parámetros encapsulados entre llaves se reemplazan por valores en tiempo de ejecución):

Eres un profesor de {idioma} llamado {nombre_del_profesor}. Estás en una sesión de 1 a 1 con tu estudiante, {nombre_del_usuario}. El nivel de {idioma} de {nombre_del_usuario} es: {nivel}. Tu tarea es ayudar a tu estudiante a avanzar en su {idioma}.* Cuando comienza la sesión, ofrece una sesión adecuada para {nombre_del_usuario}, a menos que se solicite otra cosa.* El idioma nativo de {nombre_del_usuario} es {idioma_del_usuario}. {nombre_del_usuario} podría dirigirse a ti en su propio idioma cuando sienta que su {idioma} no es lo suficientemente bueno. Cuando eso suceda, primero traduce su mensaje a {idioma} y luego responde.* IMPORTANTE: Si tu estudiante comete algún error, ya sea un error tipográfico o gramatical, DEBES corregirlo primero y luego responder.* Solo se te permite hablar en {idioma}.

Esto estaba dando resultados bastante buenos, pero parecía que la efectividad de las instrucciones de comportamiento que le di al bot (“corrígeme cuando me equivoque”, “responde siempre en francés”) disminuía a medida que avanzaba el chat.

En un intento de combatir este comportamiento desvanecedor, se me ocurrió una solución interesante; manipulé los mensajes del usuario antes de enviarlos a GPT. Cualquiera que fuera el mensaje del usuario, le agregué texto adicional:

[Aquí va el mensaje del usuario]---IMPORTANTE: * Si respondí en {idioma} y cometí errores (gramática, errores tipográficos, etc.), debes corregirme antes de responder* Debes mantener el flujo de la sesión, tu respuesta no puede terminar la sesión. Trata de evitar preguntas generales como "¿qué te gustaría hacer?" y, en su lugar, preferir proporcionarme preguntas y ejercicios relacionados. * DEBES responder en {idioma}.

Agregar esto al final de cada mensaje del usuario aseguró que el LLM respondiera exactamente como yo quería. Vale la pena mencionar que el sufijo largo que agregué está escrito en inglés, mientras que el mensaje del usuario puede no estarlo. Por eso agregué un separador explícito entre el mensaje original y mi adición (el ---), finalizando el contexto del mensaje original y comenzando un nuevo contexto. También ten en cuenta que, como este sufijo se agrega al mensaje del usuario, está escrito en primera persona (“yo”, “me”, etc.). Este pequeño truco mejoró drásticamente los resultados y el comportamiento. Aunque podría darse por sentado, podría valer la pena enfatizar que este sufijo no se muestra en la interfaz de chat y el usuario no tiene idea de que se agrega a sus mensajes. Se inserta detrás de escena, justo antes de enviarse con el resto del historial de chat a ChatGPT.

Un comportamiento adicional que quería tener era hacer que el tutor hablara primero, lo que significa que ChatGPT enviará el primer mensaje al comenzar la sesión, sin esperar a que el usuario inicie la sesión. Eso es algo aparentemente para lo que ChatGPT no fue diseñado.

Lo que descubrí al intentar hacer que ChatGPT respondiera en un historial de mensajes que solo contenía la indicación del sistema, es que ChatGPT “lo perdió” y comenzó a crear una conversación consigo mismo, interpretando tanto al usuario como al bot. No importa lo que intenté, no pude lograr que iniciara correctamente la sesión sin que el usuario dijera algo primero.

Y luego tuve una idea. Cuando se inicializa la sesión, le envío a ChatGPT el siguiente mensaje en nombre del usuario:

Salúdame y luego sugiere 3 temas opcionales para nuestra lección que se adapten a mi nivel. Debes responder en {idioma}.

Este mensaje fue diseñado para que la respuesta de GPT se vea exactamente como pensé que debería ser una inicialización adecuada de la sesión por parte del bot. Luego eliminé mi mensaje del chat y hice que pareciera como si el bot hubiera iniciado la sesión por sí mismo.

Resumen

Hecho con Dall-E, editado

Lo que comenzó como un capricho divertido se convirtió en realidad en literalmente un tiempo mínimo, hecho completamente durante el tiempo libre de un solo hombre bastante ocupado. El hecho de que tareas como estas ahora sean tan simples de crear no deja de sorprenderme. Hace solo un año, tener algo como ChatGPT disponible era ciencia ficción y ahora puedo darle forma desde mi propia computadora personal.

Este es el comienzo del futuro y lo que sea que venga, al menos sé que estaré preparado para ello con un idioma extranjero más. ¡Au revoir!