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:
-
Tres roles de mensajes:
system: Instrucciones/comportamientouser: Mensajes del usuarioassistant: Respuestas de GPT
-
Historial conversacional:
messages = [] messages.append({"role": "user", "content": "..."}) response = client.chat.completions.create(messages=messages) messages.append({"role": "assistant", "content": response...}) -
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
- Chat Completions Guide - Oficial
- Best Practices for Prompting - System messages
- 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