Como integrar ChatGPT a tus proyectos de Ruby en 10 minutos

Si pensamos en las tecnologías que definieron una década, podemos mencionar varias. En la primera mitad de los años 2010, probablemente la palabra que más se escuchaba era "smartphone"; en la segunda mitad de la década, probablemente fueron los wearables o la realidad virtual. Ahora, claramente es la inteligencia artificial, basta con ver el boom exponencial que ha tenido los últimos años en Google Trends

Probablemente, esta noticia no sea nueva para muchos. Plataformas como ChatGPT o BingChat han sido ampliamente utilizadas y dependemos cada vez más de ellas. Su valor en distintas áreas de la sociedad es cada vez más evidente, ya sea para facilitar la escritura de informes, mejorar la calidad de las búsquedas para desarrolladores o incluso resumir textos para simplificar la comprensión.

En relación con esto, para los desarrolladores en particular, el procesamiento de lenguaje natural representa un desafío complejo. Imagina, por ejemplo, un chatbot que debe comprender la intención del usuario en su mensaje, como "hacer una reserva" u "obtener información sobre un producto". Incluso debe ser capaz de detectar dialectos locales, como en el caso de la expresión "No me tinca ir mañana en la mañana". Normalmente, este tipo de problemas implica que las aplicaciones tengan limitaciones en el uso del lenguaje natural o requieran algoritmos complejos que demandan muchas horas de desarrollo y grandes volúmenes de datos.

Sin embargo, gracias a la innovación de OpenAI, ahora es posible utilizar sus modelos ya entrenados e integrarlos en nuestras aplicaciones en menos de 10 minutos. En esta mini guía, exploraremos una implementación sencilla en Ruby que nos permitirá aprovechar estas ventajas.

1. Implementando GPT-3.5 en Ruby

Para tener algo entretenido para resolver, trataremos de implementar un símil a ChatGPT utilizando GPT-3.5 (mismo modelo) e interacción mediante consola.

Lo primero que necesitaremos es una API KEY para acceder a OpenAI, para obtener una debemos ir a https://openai.com/api/ y crear una cuenta

Posteriormente, debemos ir al perfil y generar una nueva API Key. Almacena este token en un lugar seguro, ya que no se puede volver a ver.

Ahora se debe crear una carpeta y un archivo Ruby. Además, crearemos un archivo .env (Variable de entorno) para almacenar la API Key recién generada Para esto ejecutaremos el siguiente código en una terminal

mkdir platanus-openai 
cd platanus-openai
touch main.rb
touch .env

En el archivo .env escribiremos el token que nos entregó OpenAI

# .env

OPENAI_API_KEY=your-api-key

Para facilitar la implementación en Ruby es recomendable utilizar la gema https://github.com/alexrudall/ruby-openai, porque simplifica el llamado a la API y el manejo de excepciones. Además, instalaremos dotenv para simplificar el manejo de las variables de entorno.

gem install ruby-openai
gem install dotenv

Y añadimos el require a nuestro archivo .rb

# main.rb

require 'openai'
require 'dotenv/load'

Posteriormente, debemos inicializar nuestro cliente con el token

client = OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY'])

Utilizaremos el modelogpt-3.5-turbo correspondiente a ChatGPT. Además del modelo, la API recibe como input un historial de mensajes en el siguiente formato

[{ role: ‘ROL’, content: 'message' }, {...}, ...]

Donde los roles pueden ser system, user y assistant. Que corresponden a los distintos “agentes” en la conversación y hacen referencia a instrucciones de sistema, mensajes de usuario y respuestas del asistente respectivamente. Es importante mencionar que el mensaje de sistema posee mayor control sobre como será posteriormente la conversación.

Generamos nuestros mensajes ingresando uno de sistema y luego el del usuario

# main.rb

BASE_PROMPT = 'Soy Ham, un bot inteligente que responde a preguntas generales.'.freeze
TEMPERATURE = 0.1
MAX_TOKENS = 150

def ask_question(question)
  return '' if question.empty?

  client = OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY'])

  result = client.chat(
    parameters: {
      model: 'gpt-3.5-turbo',
      messages: [
					{role: 'system', content: BASE_PROMPT},
          {role: 'user', content: question}
			],
      temperature: TEMPERATURE,
			max_tokens: MAX_TOKENS
    }
  )
	result.dig("choices", 0, "message", "content")
end

El código anterior define un mensaje inicial, y dos parámetros max_tokens que indica la cantidad máxima de tokens de la respuesta (1 Token es aproximadamente 0.75 Palabra) y temperature que indica el rango de exploración de la IA, en pocas palabras esto indica que tanta prioridad el modelo le da a ‘Inventar’ por sobre simplemente responder con la información que ya posee. Por ejemplo, en este caso el 0.1 indica que la IA intentará inventar lo menos posible.

Finalmente, definimos una función para hacer el llamado a la API y construir el mensaje que le enviaremos. Algo importante que definimos es el model, esto hace referencia a qué modelo de OpenAI queremos utilizar, por ejemplo, el modelo gpt-3.5-turbo hace referencia a GPT-3.5 el cual es equivalente a ChatGPT. Mientras que existen otros modelos como Whisper, cuya utilización es para procesado de audio.

Finalmente, agregamos un While para que mantenga ciclo de preguntas y respuestas, y probamos nuestro código

# main.rb
require 'openai'
require 'dotenv/load'

BASE_PROMPT = 'Soy Ham, un bot inteligente que responde a preguntas generales.'.freeze
TEMPERATURE  = 0.1

def ask_question(question)
  return '' if question.empty?

  client = OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY'])

  result = client.chat(
    parameters: {
      model: 'gpt-3.5-turbo',
      messages: [
        { role: 'system', content: BASE_PROMPT },
        { role: 'user', content: question }
      ],
      temperature: TEMPERATURE,
    }
  )
	result.dig("choices", 0, "message", "content")
end

p 'Realiza una pregunta a Ham'

while true
  print 'Pregunta: '
  question = gets.chomp
  answer = ask_question(question)
  puts "Ham: #{answer}"
end

Lo ejecutamos con ruby main.rb Y listo! Nuestro bot ya responde preguntas

Lo logramos! Ham (el bot) ya puede responder preguntas utilizando ChatGPT mediante API. Sin embargo, una de las funcionalidades más útiles de ChatGPT es su ‘Memoria’, donde puede recordar mensajes anteriores y continuar la conversación desde ahí

2. Agregando Memoria y Persistencia a nuestro bot

# main.rb
require 'openai'
require 'dotenv/load'

BASE_PROMPT = 'Soy Ham, un bot inteligente que responde a preguntas generales.'.freeze
history = [ { role: 'system', content: BASE_PROMPT } ]

def ask_question(question, history)
  return '' if question.empty?
	history.append({role: 'user', content: question})
  client = OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY'])

  result = client.chat(
    parameters: {
      model: 'gpt-3.5-turbo',
      messages: history,
      temperature: 0.1,
    }
  )
	answer = result.dig("choices", 0, "message", "content")

	history.append({role: 'assistant', content: answer})

	answer
end

p 'Realiza una pregunta a Ham'

while true
  print 'Pregunta: '
  question = gets.chomp
  answer = ask_question(question, history)
  puts "Ham: #{answer}"
end

Nuevamente, Lo ejecutamos con ruby main.rb Y listo! Nuestro bot es capaz de recordar mensajes anteriores

Con esto nuestro bot ya posee memoria y es capaz de responder preguntas recordando el contexto y mensajes anteriores. Y con eso estamos, tenemos lista una base para hacer que nuestras APPs sean inteligentes!

Así como GPT3.5 o GPT4, existen muchos más modelos disponibles, como Whisper para reconocimiento de voz, dall-e para generar imágenes o modelos para generar embeddings para búsqueda de documentos. Además de este asociado a OpenAI, existen muchos otros modelos OpenSource disponibles en HuggingFace.