Primero, la versión derrochadora
Recuerde, de la explicación sobre autoatención, que para producir un token el modelo compara la posición actual con cada posición anterior usando tres proyecciones por token: una consulta (query), una clave (key) y un valor (value). La consulta del token actual atiende a todas las claves anteriores para decidir qué tomar de todos los valores anteriores.
Aquí está el detalle en la generación: las claves y los valores de los tokens 1 a 999 no cambian cuando se genera el token 1,000. Quedaron fijos desde el momento en que esos tokens existieron. Recalcularlos en cada paso haría que generar una respuesta de N tokens costara un trabajo del orden de N²: cuadrático, y ruinoso para salidas largas.
La caché: calcular claves y valores una sola vez
La solución es simple y es lo que hace todo motor de inferencia: después de calcular los vectores de clave y valor de un token, se guardan. Al generar el siguiente token, solo se calculan la consulta, la clave y el valor del token nuevo, se agrega su clave/valor a la caché y se atiende a toda la caché. Cada token nuevo cuesta ahora un trabajo aproximadamente constante, en lugar de rehacer todo el prefijo. Ese conjunto guardado de vectores de clave y valor, para cada token y cada capa, es la caché KV.
Esto también explica un patrón de tiempos que quizá haya notado: una respuesta transmitida suele tener una pausa antes del primer token y luego un flujo constante. La pausa es el prefill: calcular las claves y los valores de todo el prompt en una gran pasada por lotes para llenar la caché. Después viene el decode: emitir tokens uno a la vez, cada uno barato porque la caché ya está caliente.
Por qué la caché es grande, y crece
La caché guarda un vector de clave y uno de valor por token, por capa de atención y por cabeza de atención. Multiplíquelo para un modelo con decenas de capas y un contexto largo, y la caché KV se vuelve grande (a menudo gigabytes) y crece de forma lineal con cada token del contexto. Esta es la razón real y física por la que el contexto largo cuesta más: no solo más cómputo, sino más memoria de GPU de alto ancho de banda retenida durante toda la solicitud, lo que limita cuántas solicitudes puede atender una GPU a la vez.
También es la razón por la que dos solicitudes con el mismo total de tokens pueden costar distinto: una solicitud con un prompt enorme y una respuesta corta es sobre todo prefill barato; una que genera una respuesta larga paga el costo de decode sobre una caché que crece sin parar. Las funciones de caché de prompts (reutilizar la caché KV de un prefijo compartido entre llamadas) existen precisamente porque recalcular el prefill del mismo prompt de sistema cada vez es puro desperdicio.
Reducir la caché: GQA, MQA y PagedAttention
Como la caché KV domina la memoria de inferencia, buena parte del diseño moderno de modelos se trata de reducirla. Multi-Query Attention (MQA) y Grouped-Query Attention (GQA) permiten que muchas cabezas de consulta compartan un solo conjunto de cabezas de clave/valor, lo que reduce la caché en un factor grande con poca pérdida de calidad; por eso GQA hoy es estándar en modelos como Llama. Los embeddings de posición rotatorios (RoPE) y esquemas relacionados hacen que las claves en caché se extiendan limpiamente a contextos más largos.
Del lado del servicio, PagedAttention (la idea detrás de vLLM) toma prestada la memoria virtual de los sistemas operativos: en lugar de reservar un gran bloque contiguo por solicitud, guarda la caché en páginas de tamaño fijo asignadas bajo demanda. Eso elimina la fragmentación de memoria que de otro modo desperdicia buena parte de la GPU, y permite que un servidor acomode muchas más solicitudes simultáneas en el mismo hardware: una ganancia directa en rendimiento y costo.
La conclusión
La caché KV es el puente entre un detalle de arquitectura y su factura. El contexto largo no es caro porque el modelo “lea más” en algún sentido vago: es caro porque cada token en el contexto ocupa una porción de memoria rápida de GPU durante toda la solicitud, y esa memoria es el recurso escaso. Entenderlo explica la latencia de prefill frente a decode, por qué la caché de prompts ahorra dinero y por qué tanta ingeniería de inferencia (GQA, PagedAttention, el giro hacia modelos de espacio de estados que evitan por completo una caché creciente) en realidad trata de una sola cosa: administrar la caché KV.