Una aplicación LLM en producción rara vez recibe una pregunta que nadie haya hecho antes. Los asistentes de soporte y las tuberías de recuperación atienden la misma intención miles de veces al día, con otras palabras cada vez.

La mayoría de las pilas tratan esas repeticiones como peticiones nuevas y facturadas por completo. Pero el coste se acumula en dos sitios distintos, y esos dos sitios tienen arreglos distintos.

Esta guía separa las dos capas de caché: una abarata la llamada, la otra la elimina del todo. Cuál ayuda en cada carga, cuánto ahorran apiladas en el orden correcto y dónde detenerse viene a continuación.

Dos cachés, dos tipos de ahorro

La diferencia cabe en una frase: un acierto de caché de prefijo es una llamada de generación más barata, no una llamada evitada. Un acierto de caché semántica significa que no se llega al modelo en absoluto.

Esa distinción decide todo el cálculo de coste. La primera capa reduce la latencia y el número de tokens procesados; la segunda elimina la petición misma.

  • Caché de prefijo: guarda la forma calculada del inicio fijo del prompt; el modelo se ejecuta igual.
  • Enrutado por prefijo: manda las peticiones que comparten inicio a la misma máquina para que esa caché se caliente.
  • Caché semántica: devuelve la respuesta guardada cuando ya se respondió una pregunta que coincide en significado.
  • Las tres juntas: las capas no se sustituyen entre sí, funcionan en secuencia.
  • El orden importa: si la capa semántica falla, la petición cae igualmente en la caché de prefijo.

Qué hace la caché de prefijo

El prompt enviado al modelo en una aplicación LLM suele tener dos partes: una sección fija que monta el contexto y una variable que lleva la entrada real del usuario.

ParteEjemploTamaño típico
Sección fijaInstrucciones, textos de políticas, historial de conversación3.000 tokens
Sección variableLa pregunta que escribió el usuario50 tokens
ResultadoLos mismos 3.000 tokens procesados otra vez en cada peticiónpor petición

Es fácil concretarlo con un ejemplo. En un bot de atención al cliente cada petición empieza con el mismo bloque de texto: "Eres un agente de soporte de AnyCompany. Estas son nuestras políticas…" y después lo que escribió el cliente. Si esas instrucciones ocupan 3.000 tokens, a lo largo de cientos de peticiones el modelo procesa una y otra vez esos mismos 3.000 tokens.

Los marcos de servicio como vLLM y TensorRT-LLM tienen una solución. Guardan los pares clave-valor calculados para los prefijos de prompt ya vistos. Cuando el mismo inicio aparece en una petición nueva, el modelo reutiliza ese cálculo y solo procesa los tokens nuevos del final.

A esto se le llama caché de prefijo y puede reducir mucho el tiempo hasta el primer token. En una sola máquina funciona como se espera.

El problema de escala: la flota dispersa la caché

En cuanto se pasa de una máquina a una flota detrás de un endpoint, el mecanismo se rompe. Las peticiones se reparten entre todas.

  • El mismo prefijo de 3.000 tokens cae en la instancia A en una petición, en la B en la siguiente y luego en la C.
  • Ninguna instancia ve ese prefijo con la frecuencia suficiente para construir una caché fiable.
  • La función de caché está activada, pero el enrutado dispersa tanto las peticiones que no sirve de nada.

El problema no está en la caché sino en cómo se reparten las peticiones. La función funciona; simplemente nunca tiene ocasión de calentarse. Y empeora conforme crece la flota: cuantas más instancias, menor la probabilidad de atrapar el mismo prefijo en la misma máquina.

Enrutado consciente del prefijo

Amazon SageMaker Inference añadió una estrategia para justo este punto, llamada enrutado consciente del prefijo. Mira el inicio de cada petición que llega al endpoint y manda de forma consistente las que comparten inicio a la misma instancia.

  • Si diez peticiones comparten prefijo, las diez van a la misma máquina y su caché se mantiene caliente.
  • Los inicios distintos se reparten entre instancias distintas, así que el equilibrio de carga se conserva.
  • No hace falta etiquetar peticiones ni gestionar la afinidad a mano.
  • En una prueba con Llama 3.1 70B, los aciertos de caché KV pasaron de cerca del 25% a más del 80%.

En esa misma prueba el P50 del tiempo hasta el primer token bajó hasta un 77% y el rendimiento subió hasta un 16%. Son cifras notables, pero seguimos hablando de abaratar una llamada de generación. Para un equipo que corre vLLM en su propia infraestructura la lección es idéntica: activar la caché no basta, hay que montar el enrutado en consecuencia.

La ganancia aquí es real pero su techo está claro. La petición sigue llegando al modelo, los tokens nuevos siguen procesándose y la respuesta completa sigue decodificándose. Un acierto de prefijo encoge la factura, no la pone a cero.

Caché semántica: no hacer la llamada

La segunda capa responde a otra pregunta. Piensa en tres peticiones que llegan a un asistente de soporte: "¿Puedo pedir reembolso tras contratar el plan mensual?", "¿La suscripción mensual es reembolsable?", "¿Puedo cancelar el plan y recuperar mi dinero?"

La redacción cambia pero la pregunta y la respuesta son idénticas. Sin caché semántica cada una de esas formulaciones dispara una generación completa: tokens de entrada procesados, tokens de salida decodificados, usuario esperando.

Redis ofrece esta capa como servicio gestionado con el nombre de LangCache. Se sitúa entre la aplicación y el modelo, empareja los prompts entrantes con los ya respondidos por significado y no por texto, y devuelve la respuesta guardada cuando existe una coincidencia suficientemente cercana. La empresa declara ahorros de hasta el 90% en coste de API y respuestas hasta 15 veces más rápidas en los aciertos.

Un bucle de dos llamadas

La arquitectura es simple y se reduce a dos llamadas del lado de la aplicación.

  • Antes de invocar el modelo, el prompt va al endpoint de búsqueda; el servicio genera una incrustación y hace una búsqueda vectorial sobre las entradas guardadas.
  • Si una entrada semánticamente similar supera el umbral configurado, se devuelve la respuesta guardada y el modelo no se llama.
  • Si no hay acierto, la aplicación llama a su modelo como siempre.
  • Después el prompt y la nueva respuesta se escriben en el endpoint de entradas y entran al fondo para futuras coincidencias.

La generación de incrustaciones corre a cargo del servicio; puede usar modelos por defecto o los tuyos. El servicio está en vista previa pública sobre Redis Cloud, se accede por una API REST y cuenta con SDK de Python y JavaScript. Al ser vista previa, se advierte que el comportamiento puede cambiar.

Qué medir

Las cifras que muestran si cada capa funciona también son distintas. Juzgar una con la métrica de la otra hace que la ganancia parezca mayor o menor de lo que es.

  • Para la caché de prefijo: tasa de acierto de caché KV y P50 del primer token. Un acierto bajo indica un problema de enrutado.
  • Para la caché semántica: tasa de acierto y número de llamadas evitadas. La línea de la factura depende directamente de la segunda.
  • Métrica común: coste total por petición. Observa cómo se mueve ese único número al añadir capas.
  • La partida olvidada: el coste de generar incrustaciones. Con poca tasa de acierto puede comerse el ahorro.

Medir capa por capa también importa. Si enciendes las dos la misma semana no puedes saber de dónde vino cada ganancia, y el siguiente ajuste queda a ciegas.

Dónde fijar el umbral de similitud

El único riesgo real de una caché semántica es el falso acierto: devolver una respuesta vieja a una pregunta que solo parece parecida. El umbral de similitud es lo que gestiona ese riesgo.

Con el umbral alto baja la tasa de acierto y con ella el ahorro; con el umbral bajo sube la probabilidad de responder mal. Junto a él, el servicio ofrece TTL, políticas de expulsión y controles adaptativos que ajustan precisión y exhaustividad. En la práctica conviene empezar alto e ir bajando mientras se leen a mano los aciertos registrados.

Separar tipos de pregunta ayuda también al ajustar. Las preguntas de política o de definición toleran un umbral amplio porque la respuesta no cambia con el tiempo. Para lo que depende de una persona o de un momento — estado de cuenta, un precio — la caché semántica debe estar apagada desde el principio; ahí la respuesta correcta no es la de ayer.

Cuál para cada carga

Las dos capas rinden en patrones distintos. La caché de prefijo ayuda en cualquier aplicación con un prompt de sistema largo y fijo, aunque no haya dos preguntas parecidas.

La caché semántica solo aporta valor si las preguntas se repiten. Asistentes de soporte, preguntas frecuentes y búsqueda con recuperación encajan en esa descripción. En una herramienta de generación de código donde cada petición es única casi no habrá aciertos, y el coste añadido de las incrustaciones se convierte en pérdida.

Una nota breve para cargas que no están en inglés. El emparejamiento semántico depende de lo bien que el modelo de incrustaciones represente el idioma, y los modelos multilingües pueden no distinguir en español con la nitidez que logran en inglés. Trasladar un umbral ajustado en inglés puede provocar falsos aciertos silenciosos.

Dónde detenerse

Medir primero. Montar una caché semántica sin haber sacado antes de tus registros cuántas peticiones entrantes son repeticiones semánticas es elegir la solución antes que el problema.

El orden también cuenta: primero ordena el prompt fijo para que pueda aprovechar la caché de prefijo, luego monta el enrutado acorde si la flota es grande, y solo después añade la capa semántica. Al revés, la segunda capa tapa un problema sin resolver de la primera y la factura no baja tanto como esperabas. Una caché conserva con la misma fidelidad un prompt mal construido o un contexto innecesariamente largo; el ahorro real suele empezar por acortar ese texto.