Los modelos de lenguaje grandes (LLMs) pueden generar respuestas sorprendentemente inteligentes. Pero detrás de cada respuesta hay un desafío de ingeniería fascinante:
Los LLMs generan texto un token a la vez. Para predecir cada nuevo token, un modelo transformer procesa toda la secuencia de tokens vista hasta ahora y usa su mecanismo de atención para determinar cuáles tokens anteriores son más relevantes para la siguiente predicción. Ingenuamente, esto significa que al generar el token número 1,000, el modelo tendría que recomputar repetidamente las representaciones de los 999 tokens anteriores — aunque esos tokens no hayan cambiado.
¿Cómo se genera el token número 1,000 sin recomputar información de los 999 tokens anteriores una y otra vez?
Si los modelos tuvieran que recalcular todo desde cero por cada token generado, los tiempos de respuesta serían insoportablemente lentos y los costos de inferencia se dispararían.
La solución es una de las optimizaciones más importantes en la infraestructura moderna de servidores de LLMs:
KV Cache.
Si alguna vez has trabajado con transformers, construido productos de IA, o te has preguntado por qué la longitud del prompt afecta la latencia y la memoria, entender KV Cache es esencial.
Mientras que ChatGPT es el ejemplo más conocido, KV Cache no es específico de ChatGPT. Se usa en la mayoría de modelos autoregresivos basados en transformers, incluyendo GPT, Llama, Mistral, Claude, Gemini y muchos LLMs open-source.
El Problema: La Generación Autoregresiva es Repetitiva
Los LLMs generan texto un token a la vez.
Imagina que el modelo recibe:
La capital de Francia es
El modelo predice:
París
Ahora la entrada se convierte en:
La capital de Francia es París
Para generar el siguiente token, el modelo ejecuta otra pasada forward.
Luego:
La capital de Francia es París.
Y otra pasada forward.
Y otra.
Y otra.
La observación clave es que la mayor parte de la secuencia permanece sin cambios entre pasos.
La capital de Francia es
ya fue procesado.
Recomputar representaciones para esos tokens viejos en cada paso de generación sería un desperdicio.
Esto es exactamente lo que KV Cache evita.
Entendiendo la Atención Primero
Para entender KV Cache, necesitamos un repaso rápido de self-attention.
Para cada token, el transformer computa tres vectores:
- Query (Q)
- Key (K)
- Value (V)
Un cálculo simplificado de atención luce así:
Attention(Q, K, V) = softmax(QKᵀ)V
Cada token crea sus propios vectores K y V.
Durante la generación, cuando llega un nuevo token, necesita atender a todos los tokens anteriores.
Por ejemplo:
Token 1 → K₁, V₁
Token 2 → K₂, V₂
Token 3 → K₃, V₃
...
Al generar el token 1000, el modelo necesita acceso a:
K₁ ... K₉₉₉
V₁ ... V₉₉₉
La pregunta es:
¿Por qué recalcularlos si nunca cambiaron?
La Idea Central de KV Cache
En lugar de recalcular Keys y Values para tokens anteriores, simplemente los almacenamos.
Cuando se genera el token N:
- Se computa K y V para el nuevo token.
- Se agregan al caché.
- Se reutilizan todos los tensores K y V almacenados previamente.
Visualmente:
Paso 1
Token A
↓
Computa K₁, V₁
↓
Almacena en caché
Caché:
[K₁]
[V₁]
Paso 2
Token B
↓
Computa K₂, V₂
Caché:
[K₁ K₂]
[V₁ V₂]
Paso 3
Token C
↓
Computa K₃, V₃
Caché:
[K₁ K₂ K₃]
[V₁ V₂ V₃]
Ahora la atención solo requiere computar la Query para el token más nuevo y usar las Keys y Values cacheadas de los tokens anteriores.
Esto reduce drásticamente el cómputo.
¿Qué se Guarda Exactamente?
Muchos desarrolladores asumen inicialmente que el caché almacena los estados ocultos (hidden states).
No es así.
El caché almacena:
Keys
Values
para cada capa de atención.
Supongamos un modelo con:
32 capas
32 cabezas de atención
Cada capa mantiene su propio KV cache.
Conceptualmente:
Capa 1
├── Keys
└── Values
Capa 2
├── Keys
└── Values
...
Capa 32
├── Keys
└── Values
Esto significa que la memoria del caché crece con:
- Número de capas
- Número de cabezas
- Dimensión de cada cabeza
- Longitud de la secuencia
Por eso la inferencia con contextos largos puede consumir tanta memoria en GPU.
“Sin KV Cache, los productos conversacionales de IA que usamos a diario seríandrásticamente más lentos y costarían mucho más de operar.”
Por Qué KV Cache Hace la Inferencia Más Rápida
Sin caché:
Paso de generación 1000
Recomputar tokens:
1...999
Luego computar token 1000
Con caché:
Paso de generación 1000
Reutilizar:
1...999
Computar solo:
1000
La mejora en complejidad es sustancial.
Ingenuamente:
O(n³)
surge al considerar pasos repetidos de generación.
Con KV Cache:
O(n²)
costo total de generación.
En sistemas productivos, esta diferencia es enorme. Sin KV Cache, los chatbots modernos serían mucho más lentos y significativamente más caros de operar — algo que cualquier startup latinoamericana que despliegue modelos de lenguaje debe tener en cuenta al dimensionar su infraestructura.
El Tradeoff Oculto: Memoria
KV Cache acelera el cómputo, pero el uso de memoria aumenta.
Una intuición aproximada:
Conversación más larga
↓
Más tokens
↓
KV Cache más grande
↓
Más memoria de GPU consumida
Esto crea uno de los cuellos de botella más grandes en el servicio de LLMs.
Por ejemplo:
1 usuario = caché pequeño
10,000 usuarios = 10,000 cachés
La infraestructura de inferencia debe asignar memoria de GPU para cada sesión activa.
Por eso las plataformas de inferencia invierten esfuerzo significativo en:
- Compresión de caché
- Compartición de caché
- Paged Attention (atención paginada)
- Prefix caching (caché de prefijos)
- KV caches cuantizados
En despliegues grandes, la memoria suele convertirse en el factor limitante antes que el cómputo en bruto.
Optimización Avanzada: Prefix Caching
Supongamos que muchos usuarios comparten el mismo system prompt:
Eres un asistente de programación útil...
Sin optimización:
Usuario A → Construye KV cache
Usuario B → Construye KV cache
Usuario C → Construye KV cache
El mismo trabajo se repite.
Los motores de inferencia modernos soportan prefix caching.
Prompt Compartido
↓
KV Cache Compartido
↓
Reutilizado entre solicitudes
Frameworks como vLLM y otros sistemas de servidores de alto rendimiento explotan esta idea agresivamente. Para cargas de trabajo con prompts compartidos grandes, el ahorro puede ser dramático.
Cómo se Ve KV Cache en Código
En Hugging Face Transformers, KV Cache se expone típicamente como:
past_key_values
Un loop simplificado de generación luce así:
outputs = model(
input_ids=input_ids,
past_key_values=cache,
use_cache=True
)
cache = outputs.past_key_values
La primera pasada crea el caché.
Las pasadas subsiguientes lo reutilizan.
Bajo el capó, el modelo solo computa el estado de atención para los tokens recién generados mientras aprovecha las Keys y Values cacheadas de tokens anteriores.
La mayoría de los desarrolladores nunca necesitan implementar KV Cache manualmente, pero entenderlo ayuda a explicar el comportamiento del rendimiento.
Por Qué Todo Ingeniero de LLMs Debería Entender KV Cache
Cuando los desarrolladores se encuentran con:
- Respuestas lentas en prompts largos
- Explosiones de memoria en GPU
- Limitaciones de longitud de contexto
- Cuellos de botella de throughput
- Desafíos de escalamiento de inferencia
KV Cache es a menudo parte de la explicación.
Es una de esas optimizaciones raras que cambiaron fundamentalmente la economía del servicio de LLMs.
La arquitectura transformer hizo posibles los grandes modelos de lenguaje.
KV Cache los hizo prácticos.
Sin ella, los productos de IA conversacional que usamos todos los días — desde ChatGPT hasta Claude y Gemini — se sentirían dramáticamente más lentos y costarían mucho más de operar.
¿Qué otra optimización de inferencia de LLMs te gustaría que explicáramos a continuación? Paged Attention, Speculative Decoding, Continuous Batching o FlashAttention — todas son igualmente fascinantes y esenciales para entender cómo funciona la IA que está transformando Latinoamérica y el mundo.