Módulo 2: OpenAI API - Introducción

Conversaciones con Contexto

Descripción de la cápsula

Hasta ahora, cada request es independiente: GPT no recuerda mensajes previos. Para crear un chatbot real, necesitas contexto conversacional.

En esta cápsula:

  • Implementarás historial de mensajes
  • GPT recordará la conversación completa
  • Crearás chatbot interactivo (CLI)

Tiempo: 30 minutos
Dificultad: Media


🎯 Objetivos

  • ✅ Entender formato de mensajes (system, user, assistant)
  • ✅ Mantener historial conversacional
  • ✅ Crear chatbot con memoria
  • ✅ Gestionar context window

📚 Conceptos: Roles de Mensajes

Formato de messages array:

messages = [
    {"role": "system", "content": "..."},      # Instrucciones iniciales
    {"role": "user", "content": "..."},        # Usuario
    {"role": "assistant", "content": "..."},   # GPT
    {"role": "user", "content": "..."},        # Usuario responde
    {"role": "assistant", "content": "..."},   # GPT responde
]

Rol 1: system (Instrucciones)

Define comportamiento y personalidad de GPT:

{"role": "system", "content": "Eres un asistente técnico especializado en Python"}

Características:

  • Opcional (pero muy recomendado)
  • Va al inicio del array
  • GPT seguirá estas instrucciones siempre

Ejemplos:

# Soporte técnico
{"role": "system", "content": "Eres un agente de soporte. Responde de forma concisa y amigable."}

# Tutor educativo
{"role": "system", "content": "Eres un tutor de programación. Explica conceptos paso a paso."}

# Asistente formal
{"role": "system", "content": "Eres un asistente corporativo. Usa lenguaje formal."}

Rol 2: user (Usuario)

Mensajes del usuario humano:

{"role": "user", "content": "¿Qué es una lista en Python?"}

Rol 3: assistant (GPT)

Respuestas generadas por GPT:

{"role": "assistant", "content": "Una lista en Python es una colección ordenada..."}

Importante: Debes incluir respuestas previas de GPT en el historial.


💻 Implementación: Chatbot con Memoria

Código completo (chatbot_context.py):

from dotenv import load_dotenv
import os
from openai import OpenAI

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# Historial conversacional
messages = [
    {"role": "system", "content": "Eres un asistente útil y amigable."}
]

def chat(user_message: str) -> str:
    """Envía mensaje y retorna respuesta, manteniendo contexto."""
    
    # 1. Añadir mensaje del usuario al historial
    messages.append({"role": "user", "content": user_message})
    
    # 2. Enviar TODO el historial a GPT
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages  # Incluye system + todos los mensajes previos
    )
    
    # 3. Extraer respuesta
    assistant_message = response.choices[0].message.content
    
    # 4. Añadir respuesta de GPT al historial
    messages.append({"role": "assistant", "content": assistant_message})
    
    return assistant_message

# Interactivo CLI
print("Chatbot con contexto (escribe 'salir' para terminar)\n")

while True:
    user_input = input("Tú: ")
    
    if user_input.lower() in ["salir", "exit", "quit"]:
        print("¡Adiós!")
        break
    
    response = chat(user_input)
    print(f"Bot: {response}\n")

Ejecución:

python chatbot_context.py

Conversación ejemplo:

Chatbot con contexto (escribe 'salir' para terminar)

Tú: Hola, me llamo Juan
Bot: ¡Hola Juan! ¿En qué puedo ayudarte hoy?

Tú: ¿Cuál es mi nombre?
Bot: Tu nombre es Juan.

Tú: ¿Qué es Python?
Bot: Python es un lenguaje de programación...

Tú: Dame un ejemplo
Bot: Claro, aquí un ejemplo en Python:
     print("Hola mundo")

Tú: salir
¡Adiós!

Observa: GPT recuerda tu nombre y el contexto de preguntas anteriores.


🔍 Desglose: Cómo Funciona

Estado inicial:

messages = [
    {"role": "system", "content": "Eres un asistente útil y amigable."}
]

Primera interacción:

User: "Hola, me llamo Juan"

Después de messages.append():

messages = [
    {"role": "system", "content": "Eres un asistente útil y amigable."},
    {"role": "user", "content": "Hola, me llamo Juan"}
]

GPT recibe: System + User message → Responde

Respuesta: "¡Hola Juan! ¿En qué puedo ayudarte hoy?"

Después de añadir respuesta:

messages = [
    {"role": "system", "content": "Eres un asistente útil y amigable."},
    {"role": "user", "content": "Hola, me llamo Juan"},
    {"role": "assistant", "content": "¡Hola Juan! ¿En qué puedo ayudarte hoy?"}
]

Segunda interacción:

User: "¿Cuál es mi nombre?"

Después de append:

messages = [
    {"role": "system", "content": "Eres un asistente útil y amigable."},
    {"role": "user", "content": "Hola, me llamo Juan"},
    {"role": "assistant", "content": "¡Hola Juan! ¿En qué puedo ayudarte hoy?"},
    {"role": "user", "content": "¿Cuál es mi nombre?"}
]

GPT ve TODO el historial: Sabe que tu nombre es Juan → Responde correctamente


📊 Visualización del Historial

Añade debugging para ver historial:

def chat(user_message: str) -> str:
    messages.append({"role": "user", "content": user_message})
    
    # DEBUG: Mostrar historial
    print("\n--- HISTORIAL ENVIADO A GPT ---")
    for i, msg in enumerate(messages):
        print(f"{i+1}. [{msg['role']}]: {msg['content'][:50]}...")
    print("--- FIN HISTORIAL ---\n")
    
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages
    )
    
    assistant_message = response.choices[0].message.content
    messages.append({"role": "assistant", "content": assistant_message})
    
    return assistant_message

Output:

--- HISTORIAL ENVIADO A GPT ---
1. [system]: Eres un asistente útil y amigable.
2. [user]: Hola, me llamo Juan
3. [assistant]: ¡Hola Juan! ¿En qué puedo ayudarte hoy?
4. [user]: ¿Cuál es mi nombre?
--- FIN HISTORIAL ---

⚙️ Parameters: System Message Avanzado

Personalidad específica:

{"role": "system", "content": """
Eres un asistente de soporte técnico para una app de e-commerce.

Reglas:
1. Responde en español
2. Máximo 3 frases por respuesta
3. Si no sabes algo, di "Déjame escalarlo a un agente humano"
4. Nunca inventes datos (números de orden, precios, etc.)
"""}

Resultado: GPT seguirá estas reglas consistentemente.


Context injection (avanzado):

{"role": "system", "content": f"""
Eres un asistente para {user_name}.

Información del usuario:
- Nombre: {user_name}
- Plan: Premium
- Última compra: {last_purchase_date}

Usa esta info cuando sea relevante.
"""}

Útil para: Personalización con datos de base de datos.


🧪 Experimentos

Experimento 1: Sin system message

Comenta la línea:

messages = [
    # {"role": "system", "content": "Eres un asistente útil y amigable."}
]

Observa: GPT sigue funcionando, pero comportamiento menos predecible.


Experimento 2: System message específico

messages = [
    {"role": "system", "content": "Eres un pirata. Habla como pirata siempre."}
]

Output:

Tú: Hola
Bot: ¡Ahoy, marinero! ¿Qué puedo hacer por ti hoy?

Experimento 3: Token count con historial

def chat(user_message: str) -> str:
    messages.append({"role": "user", "content": user_message})
    
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages
    )
    
    assistant_message = response.choices[0].message.content
    messages.append({"role": "assistant", "content": assistant_message})
    
    # Mostrar tokens
    print(f"[Tokens usados: {response.usage.total_tokens}]")
    
    return assistant_message

Observa: Tokens aumentan con cada mensaje (historial crece).


⚠️ Problema: Context Window Overflow

Límites de context:

  • GPT-3.5-turbo: 16,385 tokens max
  • GPT-4-turbo: 128,000 tokens max

Problema: Si conversación es muy larga, historial excede límite → Error


Solución 1: Sliding window (simple)

Solo mantener últimos N mensajes:

MAX_HISTORY = 10  # Últimos 10 mensajes (5 intercambios)

def chat(user_message: str) -> str:
    messages.append({"role": "user", "content": user_message})
    
    # Mantener solo últimos MAX_HISTORY (+ system message siempre)
    if len(messages) > MAX_HISTORY + 1:  # +1 por system
        # Mantener system + últimos MAX_HISTORY
        messages[:] = [messages[0]] + messages[-(MAX_HISTORY):]
    
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=messages
    )
    
    assistant_message = response.choices[0].message.content
    messages.append({"role": "assistant", "content": assistant_message})
    
    return assistant_message

Ventaja: Nunca excedes context limit
Desventaja: GPT olvida mensajes viejos


Solución 2: Summarization (avanzado)

Cada 20 mensajes, resume historial:

def summarize_history():
    """Resume historial viejo y reemplaza con resumen."""
    if len(messages) > 20:
        # Crear prompt de resumen
        summary_prompt = "Resume esta conversación en 3 frases:\n\n"
        for msg in messages[1:-5]:  # Excluye system y últimos 5
            summary_prompt += f"{msg['role']}: {msg['content']}\n"
        
        # Pedir resumen a GPT
        summary_response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": summary_prompt}]
        )
        
        summary = summary_response.choices[0].message.content
        
        # Reemplazar historial: system + summary + últimos 5
        messages[:] = [
            messages[0],  # system
            {"role": "assistant", "content": f"[Resumen previo: {summary}]"},
            *messages[-5:]  # Últimos 5 mensajes
        ]

Ventaja: Mantiene contexto importante
Desventaja: Costo extra (request de resumen)


📊 Resumen

Conceptos clave:

  1. Tres roles de mensajes:

    • system: Instrucciones/comportamiento
    • user: Mensajes del usuario
    • assistant: Respuestas de GPT
  2. Historial conversacional:

    messages = []
    messages.append({"role": "user", "content": "..."})
    response = client.chat.completions.create(messages=messages)
    messages.append({"role": "assistant", "content": response...})
  3. Context window management:

    • Sliding window (últimos N mensajes)
    • Summarization (resume historial viejo)

Checklist:

  • Chatbot con contexto funcionando
  • GPT recuerda mensajes previos
  • System message personalizado
  • Sliding window implementado
  • Experimento con diferentes personalidades

🔗 Recursos adicionales

  1. Chat Completions Guide - Oficial
  2. Best Practices for Prompting - System messages
  3. Token Limits - Por modelo

➡️ Próximo paso

Siguiente cápsula: 05-parameters-avanzados.md

Aprenderás a controlar respuestas de GPT con:

  • temperature (creatividad vs determinismo)
  • max_tokens (longitud)
  • top_p, frequency_penalty, etc.

Tiempo: 25 minutos


Tiempo estimado: 30 minutos
Siguiente: 05-parameters-avanzados.md