30

Contexto y memoria

¿Recuerda el modelo lo que le dijiste? ¿Aprende mientras conversas?

12 minRuta ejecutiva

Contexto y memoria

Con voz. Puedes pausar y saltar a cualquier paso.

Lección completa

Subtítulos

Le escribes a un chatbot: «me llamo Ana y trabajo en logística». Te saluda por tu nombre y platican un rato sobre envíos.

Voz

Sin voz: la lección avanza sola con subtítulos.

La voz de esta lección está en preparación: por ahora avanza sola con subtítulos.

0:00 / 0:00

Qué lograrás

  • Explicar por qué un chatbot parece recordar tu conversación aunque el modelo no guarde nada: la aplicación le reenvía todo en cada turno.
  • Reconocer qué es la ventana de contexto, qué ocupa lugar en ella y qué pasa con lo que ya no cabe.
  • Calcular cuántos tokens se procesan al reenviar una conversación, y explicar por qué lo que queda en medio de un contexto largo se aprovecha peor.

Ideas clave

  1. El modelo no tiene estado: en cada turno la aplicación le reenvía toda la conversación. Sus pesos no cambian mientras conversas.
  2. La ventana de contexto es el máximo de tokens que el modelo lee de una vez. Instrucciones del sistema, historial, tu mensaje y la respuesta comparten ese espacio.
  3. Lo que no está en la ventana no existe para el modelo. Cuando ya no cabe, la aplicación decide: algunos chats sueltan lo más viejo; otros resumen o rechazan el mensaje.
  4. Reenviar todo hace que cada turno cueste más que el anterior: diez turnos de cien tokens procesan cinco mil quinientos, no mil.
  5. Que algo quepa no garantiza que se use bien: lo del medio se aprovecha peor. Las funciones de memoria son texto guardado fuera del modelo y vuelto a enviar.

Lo que mucha gente cree

Creencia común El chatbot me conoce y aprende de mí conforme conversamos.

Lo correcto En cada turno recibe otra vez la conversación, como texto. Sus pesos no cambian: si ese texto no se reenvía o no cabe en la ventana, para el modelo no existe.

Compruébalo

Cuatro preguntas. Sin nota ni cuenta: sirven para ver si la idea quedó.

ConceptoUn chatbot responde «te llamas Ana» tres turnos después de que se lo dijiste, aunque el modelo no guarda nada entre llamadas. ¿Por qué puede hacerlo?
ConceptoEn un contexto largo con muchos documentos, ¿dónde suele aprovechar mejor un modelo el documento que tiene la respuesta?
CálculoCada turno de una conversación agrega 200 tokens, y en cada turno el modelo procesa todo lo anterior otra vez: 200 en el primero, 400 en el segundo, y así. ¿Cuántos tokens procesa en total en 5 turnos?
AplicaciónTu equipo pega un contrato largo al inicio de un chat y conversa sobre él durante horas. Al final, el asistente responde como si no conociera una cláusula clave. ¿Qué es lo más razonable?

Por dentroPara quien quiere las fórmulas y el código. No hace falta para seguir el curso.

Sin estado: lo que realmente se envía

En una API de chat, la llamada de cada turno lleva la lista completa de mensajes; el modelo devuelve solo el mensaje nuevo, y la aplicación lo agrega a su lista antes del turno siguiente:

{
  "system": "Eres un asistente de logística. ...",
  "messages": [
    {"role": "user", "content": "Me llamo Ana y trabajo en logística."},
    {"role": "assistant", "content": "Mucho gusto, Ana. ¿En qué te ayudo?"},
    {"role": "user", "content": "¿Cómo me llamo?"}
  ]
}

La documentación de Anthropic lo dice así: «la API de mensajes es sin estado: siempre se envía a la API el historial completo de la conversación» (en el original, stateless). Las instrucciones del sistema van en un campo aparte, system. Algunas plataformas guardan el historial en sus servidores y lo reinsertan por ti: el modelo sigue sin estado; solo cambia quién guarda el historial.

Qué cuenta en la ventana

Según esa misma documentación, la ventana es «todo el texto al que el modelo puede referirse al generar una respuesta, incluida la propia respuesta»: las instrucciones del sistema, cada mensaje (con documentos e imágenes), las definiciones de herramientas y lo que el modelo escribe en el turno. Si la entrada sola ya excede la ventana, la API devuelve un error («prompt is too long»); qué hacer entonces lo decide la aplicación. La documentación señala que interfaces de chat como claude.ai pueden manejar la ventana como una cola, «lo primero que entra es lo primero que sale», o resumir lo viejo («compactación»).

¿Por qué hay un límite? GPT-1 usó «posiciones aprendidas» en lugar de las ondas del artículo original (lección 22), y GPT-3 conserva esa arquitectura con nctx=2048n_{\text{ctx}}=2048 (Brown et al., 2020): aprendió una lista por lugar y no tiene listas para más. Los modelos actuales codifican la posición de otra forma, pero el largo útil sigue dependiendo de con qué largos se entrenaron, y servir contextos largos cuesta memoria: la caché KV crece con cada token (lección 26). En Llama 3 de 8 mil millones (32 bloques, 8 cabezas de clave-valor de 128 números, 2 bytes por número) son 2⋅32⋅8⋅128⋅2=131 0722\cdot 32\cdot 8\cdot 128\cdot 2 = 131\,072 bytes por token: con 128 000 tokens, casi 17 GB.

Modelo Año Ventana (tokens) Fuente
GPT-1 2018 512 Radford et al.
GPT-2 2019 1 024 Radford et al.
GPT-3 2020 2 048 Brown et al.
Claude 2023 100 000 (≈ 75 000 palabras) Anthropic
Llama 3.1 2024 128 000 Llama Team
Gemini 1.5 2024 más de 1 millón (hasta 10 millones en sus pruebas) Gemini Team

Pesos fijos, ejemplos en el contexto

El modelo puede seguir un patrón que le muestras en el contexto (aprendizaje en contexto): Brown et al. (2020) lo evalúan «sin actualizaciones de gradiente ni ajuste fino». Es temporal: cuando esos ejemplos salen de la ventana, el comportamiento desaparece. Cambiar los pesos exige entrenar (lecciones 4, 8 y 27). El informe técnico de GPT-4 lo resume: tiene «una ventana de contexto limitada y no aprende de la experiencia».

Cuánto cuesta reenviar la conversación

Sea ss el tamaño de las instrucciones del sistema y mm los tokens que agrega cada turno (tu mensaje uu más la respuesta aa). En el turno kk el modelo procesa s+k ms + k\,m tokens (lee todo lo anterior y escribe lo nuevo); en nn turnos,

∑k=1n(s+k m)=n s+m n(n+1)2.\sum_{k=1}^{n} (s + k\,m) = n\,s + m\,\frac{n(n+1)}{2}.

Con s=0s=0 y m=100m=100: 5 500 tokens en 10 turnos y 21 000 en 20, frente a 1 000 y 2 000 de texto. El término m n2/2m\,n^2/2 domina: el total crece con el cuadrado de los turnos. Las instrucciones también se pagan en cada turno: con s=2 000s = 2\,000, u=50u = 50, a=150a = 150 y 20 turnos, la entrada suma ∑k(s+(k−1)m+u)=79 000\sum_k \big(s + (k-1)m + u\big) = 79\,000 tokens, y la mitad son las instrucciones repetidas.

Por eso existe la caché del prefijo: el proveedor guarda el trabajo hecho sobre el inicio repetido (la caché KV de la lección 26) y cobra menos por releerlo. En la documentación de Anthropic, leer de la caché cuesta 0.1×0.1\times el precio base de entrada (menos en algunos modelos) y escribir en ella, 1.25×1.25\times (caché de 5 minutos). Si acierta en cada turno, el ejemplo anterior baja a unos 14 628 tokens equivalentes, el 19 %. La caché cambia lo que pagas, no lo que ocupa la ventana.

La posición importa: «Perdido en el medio»

Liu et al. (2023) dieron a cada modelo 20 documentos (unos 4 000 tokens), uno solo con la respuesta, y cambiaron su lugar. Acierto (%), valores de la tabla 6 (apéndice G); las dos últimas columnas son de la tabla 1:

Modelo 1.º 5.º 10.º 15.º 20.º Sin documentos Solo el correcto
GPT-3.5-Turbo 75.8 57.2 53.8 55.4 63.2 56.1 88.3
Claude-1.3 59.9 55.9 56.8 57.2 60.1 48.3 76.1
LongChat-13B (16K) 68.6 57.4 55.3 52.5 55.0 35.0 83.4
MPT-30B-Instruct 53.7 51.8 52.2 52.7 56.3 31.5 81.9

Son modelos de 2023 y una sola tarea: la forma exacta cambia con el modelo y con la prueba, y los autores no encuentran que una ventana mayor, por sí sola, use mejor el contexto. RULER (Hsieh et al., 2024) amplía el hallazgo: entre los modelos que declaraban 32 000 tokens o más, solo la mitad mantenía un rendimiento satisfactorio a esa longitud. La documentación de Anthropic lo llama context rot: al crecer el número de tokens, la exactitud y el recuerdo se degradan.

Memoria fuera del modelo

Las funciones de «memoria» guardan texto fuera de la ventana y lo vuelven a meter: un resumen que reemplaza los turnos viejos (la compactación de Anthropic), o archivos de notas que el modelo lee y escribe entre conversaciones (la herramienta de memoria de Anthropic; MemGPT, de Packer et al., 2023, se inspira en la jerarquía de memoria de un sistema operativo). Lo guardado puede quedar equivocado u obsoleto, y es información que sale de tu organización.

En código

def recortar(mensajes, limite, contar):
    """Deja las instrucciones y los turnos más recientes que quepan: lo primero que entró sale primero."""
    sistema, resto = mensajes[0], mensajes[1:]
    usado, dentro = contar(sistema), []
    for m in reversed(resto):                       # de lo más nuevo a lo más viejo
        if usado + contar(m) > limite:
            break
        dentro.append(m)
        usado += contar(m)
    return [sistema] + dentro[::-1]

def procesados(turnos, s, m):
    """Tokens que procesa el modelo: en el turno k, las instrucciones s más k turnos de m tokens."""
    return sum(s + k * m for k in range(1, turnos + 1))

def entrada_con_cache(turnos, s, u, a, lectura=0.1, escritura=1.25):
    """Tokens de entrada equivalentes con caché del prefijo (supuesto: acierta en cada turno)."""
    m = u + a
    total = escritura * (s + u)                     # turno 1: se escribe todo
    for k in range(2, turnos + 1):
        prefijo = s + (k - 2) * m + u               # lo que se envió en el turno anterior
        total += lectura * prefijo + escritura * m  # se lee el prefijo y se escribe lo nuevo
    return total

contar = lambda m: len(m["texto"].split())         # aproximación: una palabra ≈ un token
historial = [
    {"rol": "sistema",   "texto": "Eres un asistente de logística."},
    {"rol": "usuario",   "texto": "Me llamo Ana y trabajo en logística."},
    {"rol": "asistente", "texto": "Mucho gusto, Ana. ¿En qué te ayudo?"},
    {"rol": "usuario",   "texto": "Dame una idea para ahorrar en envíos."},
    {"rol": "asistente", "texto": "Junta pedidos pequeños en un solo camión."},
    {"rol": "usuario",   "texto": "¿Cómo me llamo?"},
]
dentro = recortar(historial, limite=25, contar=contar)
print([m["rol"] for m in dentro])
print("¿Sigue el nombre en la ventana?", any("Ana" in m["texto"] for m in dentro))
print(procesados(10, s=0, m=100), procesados(20, s=0, m=100))
sin = sum(2000 + (k - 1) * 200 + 50 for k in range(1, 21))
con = entrada_con_cache(20, s=2000, u=50, a=150)
print(sin, round(con), f"{con / sin:.0%}")
# ['sistema', 'usuario', 'asistente', 'usuario']
# ¿Sigue el nombre en la ventana? False
# 5500 21000
# 79000 14628 19%

Lo que viene

En la lección 31 (RAG) el almacén externo se vuelve un sistema completo: buscar los fragmentos relevantes de tus documentos y ponerlos en la ventana. En la 32, los agentes usan esa misma idea, con memoria en archivos, para trabajos que duran más que una ventana.

Lente de decisión

Qué significa esta idea si tu trabajo es decidir, no programar.

  • Lo que no está en el contexto no existe para el modelo: si necesita una política, un contrato o un dato de cliente, hay que ponérselo en la ventana en cada consulta. Pregunta qué se envía exactamente en cada llamada: instrucciones, historial y documentos.
  • El costo crece con cada turno porque se reenvía todo, incluidas las instrucciones del sistema. Mide el costo por conversación, no por mensaje; acorta o resume las conversaciones largas, y pregunta si hay caché del prefijo y cuánto descuenta.
  • La ventana anunciada no es la ventana útil: prueba con tus documentos y con el dato clave en distintos lugares. Pon lo importante al principio o al final, y no rellenes: más contexto puede rendir peor y casi siempre cuesta más.
  • Privacidad: todo lo que se pega se envía al proveedor en cada turno, y las funciones de memoria guardan notas sobre personas. Revisa cuánto tiempo se conserva, si se usa para entrenar y cómo ver, borrar o apagar esa memoria.

Términos de la lección

Transcripción completa
ComentariosConversación pública de esta lección: dudas, datos por revisar, ideas.

Los comentarios viven en GitHub Discussions, en el repositorio público del curso; para escribir se necesita una cuenta de GitHub. Se cargan desde giscus.app al abrir esta sección. Las personas que revisan el curso los leen, y si un cambio se acepta, se aplica en los dos idiomas. Abrir el hilo en GitHub

Fuentes