Cuando se habla de optimización de inferencia de modelos de lenguaje, la mayoría de guías están escritas para equipos que gestionan clústeres de decenas de H100. Pero la realidad de muchas empresas — startups, departamentos internos, proyectos mid-market — es muy distinta: un solo servidor con una GPU, una instancia modesta en AWS o Azure, o incluso inferencia sobre CPU para modelos más pequeños. En ese contexto, frases como 'añade más GPUs' no son una opción. En Q2BSTUDIO, como empresa de desarrollo de aplicaciones a medida, trabajamos cada día con clientes que necesitan sacar el máximo rendimiento de recursos limitados. Este artículo desglosa las tres palancas que realmente mueven la aguja en entornos de pequeña y mediana escala: gestión de la caché KV, cuantización y los compromisos de latencia asociados. Ni suposiciones de clúster, ni soluciones milagrosas.
Antes de que el coste de inferencia se convierta en una crisis, conviene entenderlo. Normalmente, el gasto en latencia y memoria no aparece como una partida hasta que un proyecto paralelo o un MVP recibe tráfico real. En ese momento tienes tres opciones: gastar más en hardware, pagar más por APIs externas, o comprender qué está consumiendo tu presupuesto de memoria y tiempo. La tercera es la única que escala junto con tu conocimiento y tu infraestructura. En Q2BSTUDIO hemos ayudado a numerosos equipos a aplicar este enfoque, combinando nuestro expertise en IA, ciberseguridad, cloud AWS/Azure y BI/Power BI para diseñar soluciones eficientes y personalizadas.
La primera palanca es la caché KV. Cada token que genera un transformer necesita atender a todos los tokens anteriores. De forma ingenua, esto obligaría a recomputar la atención sobre toda la secuencia en cada paso, un despilfarro enorme. La caché KV almacena los tensores de clave y valor de los tokens previos, de modo que cada nuevo token solo necesita un pase hacia adelante. El problema: esa caché crece linealmente con la longitud de la secuencia, y su huella de memoria suele ser el verdadero cuello de botella — no los pesos del modelo — cuando atiendes múltiples peticiones concurrentes con contextos largos. La fórmula aproximada por petición es: kv_cache_bytes = 2 * num_layers * num_heads * head_dim * seq_len * bytes_per_element. Para un modelo de 7B con 32 capas, 32 cabezas, head_dim 128, en fp16 (2 bytes) y un contexto de 4096 tokens, ya son varios cientos de MB por petición concurrente. Multiplícalo por tu concurrencia objetivo y entenderás por qué la caché KV, y no los pesos, determina cuántos usuarios simultáneos puede atender una sola GPU.
Para reducir el tamaño de la caché KV, hay varias estrategias prácticas. La atención multi-consulta o grouped-query attention (GQA) comparte cabezas K/V entre múltiples cabezas de consulta, recortando el tamaño de la caché de forma significativa con una pérdida mínima de calidad. La mayoría de modelos abiertos modernos ya usan GQA, pero al elegir un modelo conviene verificarlo. Otra técnica es la ventana deslizante (sliding window attention), que limita hasta dónde debe alcanzar la caché en tareas de contexto local. No sirve si necesitas recuperar información de larga distancia, pero es una verdadera ventaja en cargas de trabajo conversacionales con contexto corto. Además, puedes implementar políticas de expulsión de caché: en aplicaciones de chat multiturno, eliminar o resumir turnos antiguos en lugar de mantener la caché completa indefinidamente. Esto es una decisión a nivel de aplicación, no solo del motor de inferencia. Por último, la gestión de memoria tipo PagedAttention — usada en vLLM y otros frameworks — trata la caché KV como páginas de memoria virtual en lugar de requerir asignación contigua, lo que reduce drásticamente la fragmentación cuando tienes muchas peticiones concurrentes con longitudes de secuencia diferentes. Si implementas tu propia capa de servido en lugar de usar vLLM o TGI, tomar prestada esta idea es la palanca de mayor impacto.
La segunda palanca es la cuantización. Consiste en reducir la precisión numérica de los pesos del modelo — y a veces de las activaciones —, lo que reduce tanto la huella de memoria como, a menudo, la latencia. La decisión práctica no es 'si cuantizar' (para cualquier presupuesto fuera de un hiperescalador, casi siempre conviene), sino qué esquema usar y en función de tu cuello de botella. Si estás limitado por memoria (el modelo apenas cabe o quieres más concurrencia), la cuantización int4 o GGUF te da el mayor margen. Si estás limitado por latencia en CPU, GGUF mediante llama.cpp con un nivel como Q4_K_M es un punto de partida razonable. Si tu tarea es sensible a la calidad (salida estructurada, generación de código, instrucciones precisas), quédate en int8 o prueba cuidadosamente antes de bajar a int4; la degradación no es lineal y algunas tareas son mucho más sensibles. Un patrón que recomendamos desde Q2BSTUDIO: no adivines — prueba tu tarea real con dos o tres niveles de cuantización antes de comprometerte. Un modelo que cuantiza bien en chat general puede degradarse notablemente en extracción estructurada o llamadas a herramientas. Este mismo instinto se aplica al enrutamiento de tareas: igual que un gateway multi-proveedor elige el modelo según la carga, puedes seleccionar la variante cuantizada más barata que cumpla el umbral de calidad para cada petición.
La tercera palanca son los compromisos de latencia. Una vez gestionadas la caché y la cuantización, el presupuesto de latencia restante se divide en partes que responden a diferentes soluciones. El tiempo hasta el primer token (TTFT) está dominado por el procesamiento del prompt inicial. Si tu sistema utiliza prompts largos o contexto extenso de RAG, eso infla directamente el TTFT. Para aplicaciones interactivas, recortar el prompt suele ser un mayor beneficio que cualquier ajuste del motor de inferencia. La latencia entre tokens, en cambio, está dominada por el ancho de banda de memoria durante la decodificación, que es exactamente donde la cuantización y el tamaño de la caché KV tienen influencia directa. El batching continuo — en lugar del batching estático — permite que nuevas peticiones se unan a un lote a medio vuelo, en lugar de esperar a que termine el lote completo. Esto importa mucho bajo patrones de tráfico reales y desiguales. Es una de las razones por las que frameworks como vLLM superan a bucles de servido ingenuos, incluso antes de tocar la cuantización. Un compromiso concreto: el batching mejora el rendimiento global pero puede aumentar la latencia por petición bajo carga, ya que una petición puede esperar brevemente por un espacio en el lote. Para un asistente interactivo individual, apenas se nota; para un endpoint compartido con concurrencia real, es la diferencia entre la latencia p50 y p95. Vale la pena decidir de antemano cuál de las dos importa más para tu caso de uso.
Poniendo todo en orden: para un despliegue de pequeña a mediana escala, la secuencia que proporciona más beneficio por hora invertida es: elegir un modelo con arquitectura GQA si tienes opción (es gratis y estructural); cuantizar y probar contra tu tarea real (int8 como seguro, int4/GGUF si estás limitado por memoria); adoptar batching continuo mediante un framework existente (vLLM, TGI) en lugar de escribir tu propio bucle; recortar el prompt si el TTFT importa para tu experiencia de usuario (es el ajuste más barato y a menudo el más ignorado); y solo entonces considerar cambios de hardware. En ese punto sabrás si estás limitado por memoria o por cómputo, y qué mejora merece realmente la pena. En Q2BSTUDIO aplicamos esta metodología en cada proyecto de software a medida, integrando además servicios de ciberseguridad, cloud AWS/Azure, BI/Power BI y agentes IA para ofrecer soluciones completas que realmente resuelven problemas de negocio. Porque la optimización de inferencia no es solo técnica: es una decisión estratégica que, bien ejecutada, transforma un MVP en un producto escalable sin necesidad de un clúster de H100.





