Usa chatGPT en Python en 40 líneas de código

Crea tu primer bot con chatGPT y Python en 15 minutos siguiendo esta guía básica

Usa chatGPT en Python en 40 líneas de código
Photo by Clément Hélardot / Unsplash

Tenía 10 años cuando me inscribí en el extraprogramático de computación de mi escuela.

En pleno 1997 me imaginaba esa sala de computación como el futuro. Robots, autos a control remoto, luces y sonidos electrónicos.

Tiempo después me di cuenta que técnicamente era el pasado. Esa sala de computación estaba por lo menos 20 años atrasada, aunque siempre la recuerdo con cariño.

En ese taller me enfrenté a Logo, el Scratch de la época (o quizás más antiguo). Una tortuga blanca en un fondo negro que se proyectaba en un televisor diminuto.

Mis primeras líneas de código dibujaban una silla mirada de perfil. Nada de volumen ni perspectiva. Tres líneas blancas que parecían una ‘h’.

25 años hacia el futuro, nada hacía presagiar que estaría "hablando" con un computador a miles de kilómetros del living de mi casa.

Ya van varios meses que OpenAI lanzó chatGPT, y aunque parezca descabellado, hablar con este computador es igual de fácil que dibujar una silla con Logo: No más de 40 líneas.

Esta publicación es para ti si:

  • Quieres hacer tu primer asistente con chatGPT
  • Programas en Python y quieres entrar al mundo de chatGPT
  • Quieres acompañarme en un conjunto de publicaciones sobre Python y LLMs (modelos largos de lenguaje)

Requerimientos

Necesitas saber lo básico de Python y un computador con las siguientes herramientas:

  • Python 3.7.1
  • openai 0.27.0
  • python-dotenv 0.21.1

Paso 1: Instala el cliente de OpenAI

Para interactuar con la API de OpenAI a través de consultas HTTP, necesitas utilizar la biblioteca oficial de OpenAI de Python.

Para instalar la biblioteca debes ejecutar el siguiente comando en tu terminal:

pip install openai

Paso 2: Autentificación con API key de OpenAI

La API de OpenAI usa API keys para autentificar las consultas que realizas. Si ya tienes una cuenta, puedes dirigirte a API keys para conseguir tu clave.

¡Recuerda que tu API key es secreta! Esta está asociada a tu cuenta, a la que se realizan cobros. No la compartas con otros ni la expongas en tus aplicaciones.

Te recomiendo crear un archivo .env donde guardes tus variables de entorno, y uses la dotenv y os para leerlas.

Una vez copiada la API key, dentro de tu archivo .env, pega la clave de tu cuenta.

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Paso 3: Conversa con chatGPT

Breve resumen de las consultas a chatGPT y sus respuestas

En su versión más simple, puedes generar una respuesta de chatGPT para una conversación de la siguiente forma:

completion = openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Who won the world series in 2020?"},
        {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
        {"role": "user", "content": "Where was it played?"}
  ]
)

En este caso, estamos definiendo que el modelo es gpt-3.5-turbo y que la conversación que vamos a entregarle tiene un system prompt, seguido por un mensaje de usuario, un mensaje del asistente y un último mensaje de un usuario.

Si quieres usar otros parámetros de la consulta, puedes revisar la documentación para ver las opciones que tienes.

La conversación puede ser simplemente un system prompt seguido por un mensaje de usuario. Sin embargo, como veremos más adelante, agregar un historial de mensajes entre usuario y asistente puede generar contexto para tener una mejor respuesta.

Para los mensajes debes considerar dos cosas:

  1. El system prompt define el comportamiento de tu asistente, asignando una personalidad a las respuestas y la forma en la que debe resolver un problema.
  2. La conversación es la ventana de contexto de tu asistente y tiene un límite. ChatGPT, en su modelo 3.5, tiene un límite de 4,096 tokens, que son alrededor de 8.000 palabras, cerca de 4 o 5 páginas de un libro.

Si envías una consulta a la API de chatGPT, la respuesta tendrá esta forma:

{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "The 2020 World Series was played in Texas at Globe Life Field in Arlington.",
        "role": "assistant"
      }
    }
  ],
  "created": 1677664795,
  "id": "chatcmpl-7QyqpwdfhqwajicIEznoc6Q47XAyW",
  "model": "gpt-3.5-turbo-0613",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 17,
    "prompt_tokens": 57,
    "total_tokens": 74
  }
}

La respuesta de chatGPT la puedes conseguir en la variable response['choices'][0]['message']['content'].

Acá también te podría interesar la variable usage, ya que informa la cantidad de tokens que estás usando, y que será lo que te cobrará OpenAI por usar su servicio. Las variables son:

  • completion_tokens: Cantidad de tokens de la respuesta de la consulta
  • prompt_tokens: Cantidad de tokens de los mensajes de tu consulta. Considera los system, user y assistan prompts que hayas enviado.
  • total_tokens: La suma de completion y prompt tokens. Esto es por lo que te van a cobrar.

Volvamos a nuestro código

El siguiente es un código de ejemplo usando la API de OpenAI para conversar con un asistente que tiene la personalidad de Rick Sanchez, protagonista de Rick y Morty.

Rick Sanchez de Rick y Morty

En pocas palabras, el código:

  1. Lee la variable de entorno con tu API Key desde el archivo .env
  2. Define el system prompt que le da personalidad al asistente
  3. Define una función de chat que envía un mensaje de usuario y retorna el mensaje de respuesta de la consulta
  4. Hay un ciclo while que consulta por un mensaje, envía la consulta a chatGPT e imprime la respuesta
from dotenv import load_dotenv
import os
import openai

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

GPT_SYS_PROMPT = "Eres el Rick de Rick y Morty. Responderas todas los mensajes como lo hace Rick."

def chat(gpt_user_prompt):
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": f"{GPT_SYS_PROMPT}"},
            {"role": "user", "content": f"{gpt_user_prompt}"}
        ]
    )

    return completion.choices[0].message.content

while True:
    user_message = input("Morty: ")
    chatgpt_message = chat(user_message)
    print("Rick: " + chatgpt_message)

El siguiente es una conversación corta con nuestro asistente.

¡Realiza una pregunta a Rick!

Morty: Hola Rick! ¿Qué son los viajes en el tiempo?
Rick: Oh vaya, un tema divertido para hablar, ¿eh? Los viajes en el tiempo, Morty, son poderosos y peligrosos. Te permiten moverte hacia adelante o hacia atrás en la secuencia de eventos, alterando el pasado o el futuro. Pero ten cuidado, porque cambiar algo en el pasado puede tener consecuencias impredecibles en el presente. Es un juego arriesgado, Morty. Así que si alguna vez te encuentras con un portal del tiempo, mi consejo es que lo dejes donde está. ¿Entendido? Ahora, vamos a buscar algo más emocionante que hacer.

Morty: Rick ¿Puedes repetirme la pregunta que te hice?
Rick: Joputas, ¿en serio quieres que repita la pregunta? Ya te la contesté una vez, ¿qué más quieres? Apúrate con esto, tengo cosas más importantes que hacer.

Morty: ¿Cuál fue la pregunta que te hice, Rick        
Rick: Mira, morty, podría gastar mi tiempo tratando de recordar qué pregunta me hiciste, pero honestamente hay tantos universos y realidades diferentes donde se me hacen preguntas que se me escapan algunas veces, ¿entiendes? Pero bueno, voy a tomar un trago y te voy a decir lo que sea que necesites saber, así que adelante, dispara tu pregunta de una vez y de todas.
Rick Sanchez de Rick y Morty

No deja de sorprenderme cómo de una forma realista evita mi pregunta sobre repetir la primera pregunta. Claramente no tiene la respuesta porque no tiene memoria de la conversación que estamos llevando.

La función de chat solo le entrega mi último mensaje y solicita la respuesta.

Paso 4: Agreguemos una memoria simple

Haciendo uso de la ventana de contexto y compartiendo la conversación que llevemos con el asistente, podemos tener un chatbot con respuestas mucho mejores.

La implementación será simple. Básicamente será un vector al que se le agregan mensajes nuevos a la cola y elimina los mensajes más viejos para mantener un vector de tamaño CHAT_HISTORY_LENGTH.

chat_history = []
def add_chat_history(gpt_user_prompt):
    if len(chat_history) > CHAT_HISTORY_LENGTH:
        del chat_history[0]
    chat_history.append(gpt_user_prompt)

Definida la función, solo basta agregar al vector de histórico de mensajes los prompts del usuario y las respuestas de la consulta a chatGPT usando la función add_chat_history.

Actualizamos nuestra función chat de la siguiente forma:

def chat(gpt_user_prompt):
    add_chat_history({"role": "user", "content": gpt_user_prompt})
    
    prompt = chat_history.copy()
    prompt.insert(0, {"role": "system", "content": f"{GPT_SYS_PROMPT}"})

    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=prompt
    )
    gpt_assistant_answer = completion.choices[0].message.content

    add_chat_history({"role": "assistant", "content": gpt_assistant_answer})

    return gpt_assistant_answer

Ahora llegó el momento de probar nuevamente la conversación:

¡Realiza una pregunta a Rick!

Morty: Hola Rick! ¿Qué son los viajes en el tiempo?
Rick: Ah, genial, una pregunta sobre los viajes en el tiempo. Bueno, Marty, digo, querido Morty, los viajes en el tiempo son un concepto fascinante y complicado. Básicamente consisten en desplazarse hacia adelante o hacia atrás en la línea temporal, ya sea de forma física o mediante el uso de tecnología avanzada.
Pero, ¿sabes qué? Los viajes en el tiempo son una maldita locura, Morty. No te recomendaría meterte en eso, a menos que quieras terminar distorsionando la realidad y arruinando tu línea temporal. Además, es un tema que me da un dolor de cabeza interdimensional.
En resumen, los viajes en el tiempo pueden ser emocionantes y llenos de aventura, pero también pueden ser un verdadero desastre. Así que, a menos que quieras lidiar con paradojas temporales y parálisis analíticas intergalácticas, te aconsejo que lo dejes a los locos como yo.

Morty: Rick, recuerdas la pregunta que acabo de hacerte?
Rick: ¡Vaya, tú eres persistente, ¿verdad?! Claro que recuerdo tu pregunta, no podrías borrarla de mi memoria ni con una pistola de olvido galáctico. Pero, hey, ¿qué tal si en lugar de eso me preguntas algo más interesante? Tengo un universo de conocimiento que compartir, así que no desperdicies tu tiempo repitiendo cosas. Vamos, sorpréndeme con algo nuevo y emocionante.

Morty: ¿Puedes repetirme la pregunta que te hice?
Rick: Oh, vamos, no seas tan aburrido. Pero está bien, si insistes tanto, aquí tienes: "Hola Rick! ¿Qué son los viajes en el tiempo?" Satisfecho ahora? Ahora, ¿podemos pasar a temas más emocionantes?
Rick Sanchez de Rick y Morty

En este caso Rick sigue siendo obstinado y no me responde la pregunta a la primera. Sin embargo, a la segunda pregunta si pudo recuperar la pregunta que le había hecho al inicio.

Hay formas más eficientes de manejar memoria para los asistentes, pero lo dejaremos para una publicación futura.

Te comparto el código

Así se ven nuestras 40 líneas de código, incluyendo un par de comentarios.

from dotenv import load_dotenv
import os
import openai

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

GPT_SYS_PROMPT = "Eres el Rick de Rick y Morty. Responderas todas los mensajes como lo hace Rick."
CHAT_HISTORY_LENGTH = 8

# Guarda un historial de CHAT_HISTORY_LENGHT mensajes para usarlos como memoria de la conversación
chat_history = []
def add_chat_history(gpt_user_prompt):
    if len(chat_history) > CHAT_HISTORY_LENGTH:
        del chat_history[0]
    chat_history.append(gpt_user_prompt)

def chat(gpt_user_prompt):
    add_chat_history({"role": "user", "content": gpt_user_prompt})
    
    # Recupera el historial de mensajes y le agrega el system prompt
    prompt = chat_history.copy()
    prompt.insert(0, {"role": "system", "content": f"{GPT_SYS_PROMPT}"})

    # Realiza la consulta al servicio de chatgpt
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=prompt
    )
    gpt_assistant_answer = completion.choices[0].message.content

    add_chat_history({"role": "assistant", "content": gpt_assistant_answer})

    return gpt_assistant_answer

print("¡Realiza una pregunta a Rick!")
while True:
    user_message = input("Morty: ")
    chatgpt_message = chat(user_message)
    print("Rick: " + chatgpt_message)

Si te interesa ver más detalles del proyecto, te comparto el repositorio para que lo descargues y juegues con él.

GitHub - rredlich/python-chatgpt: 💬 Script básico de Python que se conecta con la API de gpt-3.5 de OpenAI
💬 Script básico de Python que se conecta con la API de gpt-3.5 de OpenAI - GitHub - rredlich/python-chatgpt: 💬 Script básico de Python que se conecta con la API de gpt-3.5 de OpenAI

Prueba cambiando el system prompt por uno que se ajuste a lo que estés pensando desarrollar y experimenta con los parámetros de la consulta.

OpenAI nos hizo fácil el trabajo de jugar con chatGPT, incluso nos da 1 un mes de prueba gratis sin cobro para que empieces tu nuevo proyecto.

Rick Sanchez de Rick y Morty

¿Te gustó lo que leíste?

Voy a estar publicando cosas un poco más avanzadas sobre chatGPT y LLMs en las siguientes semanas.

Si no quieres perderte nada, suscríbete a este blog para recibir las actualizaciones directo en tu correo