RAG agresivo en modelos pequeños: compensar parámetros con recuperación

Este post pertenece a la serie sobre rendimiento de inferencia en modelos pequeños. Su pieza hermana, El roofline se invierte en modelos pequeños, explica por qué el prefill compute-bound es el cuello de botella que aquí da forma a toda la discusión. Conviene leerlo antes: aquí asumimos que meter más contexto no es gratis.

TL;DR

Un SLM (digamos 1B–8B de parámetros) sabe menos hechos que un modelo de 70B–700B, simplemente porque tiene menos pesos donde memorizarlos. Pero su capacidad de razonar sobre texto que tiene delante —seguir instrucciones, extraer, sintetizar, comparar— se degrada mucho menos con el tamaño que su conocimiento enciclopédico. La consecuencia operacional es directa: usa el SLM como motor de razonamiento sobre contexto curado, no como base de datos. Mueve el conocimiento de los pesos al contexto vía recuperación. El problema es que “recuperación agresiva” se interpreta a menudo como “meter muchos chunks”, y eso choca de frente con tres hechos sobre los SLM: ventanas de contexto más cortas, peor aprovechamiento del contexto largo (el efecto lost in the middle es más severo cuanto más pequeño el modelo) y un prefill compute-bound cuyo coste crece con la longitud del contexto $C$ —lineal en las proyecciones, cuadrático en la atención—. No puedes simplemente añadir tokens. La salida no es recuperar menos, sino recuperar mejor: reranking de precisión sobre recall, compresión de contexto antes de inyectarlo, prefix caching de los documentos estables, caché semántico de respuestas y structured output con herramientas externas que sustituyen al conocimiento interno. Este post trabaja las matemáticas y da un número de TTFT antes y después de comprimir un contexto de 4000 a 1000 tokens en una RTX 4090.

La analogía: el examen a libro abierto

Dos estudiantes se presentan al mismo examen. El primero tiene una memoria prodigiosa: ha memorizado el temario entero, párrafo a párrafo. El segundo tiene una memoria normal —olvida fechas, confunde nombres— pero le permiten entrar con una chuleta.

Si la chuleta del segundo estudiante es un caos de fotocopias amontonadas, pierde: tarda en encontrar lo que busca, se distrae con páginas irrelevantes y se le acaba el tiempo. Pero si su chuleta es excelente —recortada a lo esencial, reordenada por relevancia, con lo importante arriba y sin paja—, entonces no solo no pierde: a menudo gana, porque razona igual de bien que el primero y además trabaja sobre material verificado en lugar de sobre recuerdos borrosos que puede estar inventando.

La moraleja tiene tres capas, y cada una mapea a una decisión de ingeniería:

  • Memorizarlo todo es caro. El primer estudiante invirtió meses. Un modelo grande invierte parámetros —y VRAM, y FLOPs de inferencia— en memorizar hechos.
  • La chuleta importa más que su tamaño. Una chuleta de una página bien hecha bate a diez páginas mal organizadas. Más contexto recuperado no es mejor contexto: la precisión del material gana al volumen.
  • Saber buscar y sintetizar es una habilidad distinta de saber. Es la que el SLM conserva. La estrategia entera consiste en apoyarse en esa habilidad y subcontratar la memoria.

El resto del post es, esencialmente, cómo construir una chuleta excelente bajo la restricción de que el estudiante (el SLM) lee despacio y se cansa con los textos largos.

El argumento de capacidad: cuántos hechos caben en los pesos

Empecemos por justificar la tesis con orden de magnitud, no con fe. ¿Cuánto conocimiento factual cabe realmente en los pesos de un modelo?

Hay una estimación empírica recurrente en la literatura de interpretabilidad y memorización: un modelo denso es capaz de almacenar del orden de 2 bits de información memorizada por parámetro antes de saturar (la cifra exacta varía según el estudio y el régimen de entrenamiento; tómese como orden de magnitud, no como ley). Un modelo de 8B parámetros tiene entonces un techo de almacenamiento de información del orden de:

$$8 \times 10^9 \text{ params} \times 2 \text{ bits/param} = 1.6 \times 10^{10} \text{ bits} \approx 2 \text{ GB de información}$$

Y ese presupuesto no es solo para hechos: la inmensa mayoría se gasta en gramática, sintaxis, capacidad de razonamiento, código, formato, y solo una fracción queda para conocimiento enciclopédico. Compáralo con el otro lado: un corpus recuperable de varios millones de documentos —una wiki corporativa, un repositorio documental, una base de conocimiento técnica— ocupa fácilmente cientos de GB a terabytes de texto, indexado y consultable con latencia de milisegundos. La asimetría es de dos o tres órdenes de magnitud a favor del corpus externo.

La conclusión no es que los pesos sean inútiles —son donde vive el razonamiento, que es lo caro de replicar— sino que competir con un índice externo por capacidad de hechos es perder por construcción. Un modelo de 70B tiene ~9× más presupuesto de memorización que uno de 8B, pero sigue siendo despreciable frente al corpus. Por eso el modelo grande también hace RAG en producción. La diferencia es que el SLM lo necesita: sin recuperación, su conocimiento factual es demasiado escaso y, peor, propenso a alucinar justo en los huecos que no memorizó.

Dónde vive el conocimiento

En los pesos (memorizado)~2 GB de info útil en 8Bfijo, caro de actualizar, alucina en huecos

En el contexto (recuperado)cientos de GB – TB indexadosfresco, citable, verificable, sin reentrenar

El SLM como motor de razonamientorazona sobre el contexto curadono es la base de datos: es quien la lee y sintetiza

La tensión central: recuperar más no es meter más

Aquí es donde la mayoría de los diseños ingenuos se rompen. “Recuperación agresiva” suena a top-k grande: si recuperar ayuda, recupera 20 chunks en vez de 5. Pero en un SLM eso falla por dos razones independientes, una de calidad y otra de coste.

(a) Los SLM usan peor el contexto largo

El efecto lost in the middle (Liu et al., 2023) es bien conocido: los LLM recuperan mejor la información situada al principio y al final del contexto, y peor la del medio. Lo que se enfatiza menos es que el efecto es más severo cuanto más pequeño el modelo. Un SLM tiene menos cabezas de atención, menos capas y representaciones internas más pobres para “rastrear” un hecho relevante enterrado en la posición 14 de 20 chunks. Además, su ventana de contexto nominal suele ser más corta (4K–32K frente a los 128K+ de los grandes), y la ventana efectiva —la longitud a partir de la cual la calidad se desploma— es todavía menor. Meter 20 chunks no significa que el modelo los lea los 20: significa que probablemente ignore o malinterprete los del medio, mientras paga el coste de todos.

(b) El prefill crece con el contexto y es compute-bound

Este es el golpe que la gente subestima. El prefill —procesar el prompt completo antes de emitir el primer token— es la fase compute-bound de la inferencia (a diferencia del decode, memory-bound; el detalle vive en El roofline se invierte). Su coste crece con la longitud del contexto $C$, y determina el TTFT (time to first token). Más chunks → más tokens de prefill → más TTFT y más coste de cómputo por petición. En un SLM, donde el prefill es proporcionalmente más caro respecto al modelo, esto duele especialmente.

La conclusión operacional es incómoda pero clara: no puedes compensar menos parámetros simplemente metiendo más contexto. Cada token recuperado se paga dos veces —en calidad degradada y en TTFT— y el SLM es el peor situado para absorber ambos costes. La salida es recuperar menos pero mejor, y comprimir lo que recuperas.

Las matemáticas del prefill

Pongamos números a “el prefill crece con el contexto”. Para un contexto de $C$ tokens, una capa transformer hace dos clases de trabajo:

  1. Proyecciones lineales (QKV, salida de atención, FFN): cada token se multiplica por matrices de pesos de tamaño fijo. El coste es $O(C)$ en FLOPs —lineal en el número de tokens.
  2. Atención ($QK^\top$ y la multiplicación por $V$): cada token atiende a todos los demás. El coste es $O(C^2)$ —cuadrático en el número de tokens.

El coste total de prefill por capa es de la forma:

$$\text{FLOPs}{\text{prefill}} \approx \underbrace{a \cdot C}{\text{proyecciones}} + \underbrace{b \cdot C^2}_{\text{atención}}$$

con $a$ y $b$ constantes que dependen de la dimensión del modelo. Para contextos moderados (unos pocos miles de tokens) en un SLM, el término lineal aún domina o es comparable al cuadrático; el término cuadrático se vuelve dominante a contextos largos. Lo relevante: si comprimes el contexto $C \to C/k$, el término lineal cae $\times k$ y el cuadrático cae $\times k^2$. Comprimir es la única palanca que ataca ambos términos a la vez, y ataca el peor de forma desproporcionada.

Ejemplo numérico: TTFT antes y después de comprimir, RTX 4090

Modelemos el TTFT como el tiempo de procesar los tokens de prefill a un throughput de prefill dado. Tomemos una RTX 4090 (24 GB, Ada Lovelace) sirviendo un SLM cuantizado, con un throughput de prefill de ~5000 tok/s (cifra ilustrativa; el valor real depende del modelo, la cuantización y el batch —mídelo, no lo asumas).

Sea un contexto recuperado de 4000 tokens (8 chunks de ~500 tokens). Aproximando el TTFT como dominado por el prefill del contexto:

$$\text{TTFT}_{\text{antes}} \approx \frac{4000 \text{ tok}}{5000 \text{ tok/s}} = 0.80 \text{ s}$$

Ahora comprimimos ese contexto a 1000 tokens ($k = 4$). El throughput de prefill no es constante con $C$ —baja un poco a contextos largos por el término cuadrático— pero, tomando la aproximación lineal conservadora de tokens/throughput:

$$\text{TTFT}_{\text{después}} \approx \frac{1000 \text{ tok}}{5000 \text{ tok/s}} = 0.20 \text{ s}$$

El TTFT cae de 0.80 s a 0.20 s, una reducción de $4\times$ en la parte lineal. Pero la cuenta de FLOPs es más favorable todavía en la componente de atención: esa parte del trabajo cae $\sim k^2 = 16\times$. En la práctica el TTFT total no cae 16× porque el coste no es puramente cuadrático a esta escala, pero la reducción real está entre 4× y un valor mayor según cuánto pesara la atención, y el ahorro de cómputo agregado (lo que paga la factura eléctrica y libera la GPU para otra petición) es sustancialmente mayor que el simple 4× del recuento de tokens.

El argumento se generaliza: comprimir el contexto un factor $k$ reduce el TTFT al menos $\sim k\times$ y el coste de atención $\sim k^2\times$. Para un SLM, donde el TTFT es a menudo el SLA que importa, esto es la diferencia entre un asistente que responde al instante y uno que se siente lento.

Las cinco palancas para resolver la tensión

La estrategia no es “recuperar menos y conformarse”. Es recuperar agresivamente del índice y luego destilar agresivamente lo recuperado antes de que llegue al SLM. Cinco palancas, en orden de aplicación dentro del pipeline.

1. Reranking agresivo: precisión sobre recall

El retriever inicial (denso, sparse o híbrido) optimiza recall: trae 50–100 candidatos para no dejarse nada fuera. El reranker —un cross-encoder que ve la query y el documento juntos— optimiza precisión: reordena esos candidatos y te quedas con los 3–5 mejores. Para un SLM esto no es un lujo, es estructural: como el modelo usa mal el contexto largo, cada chunk que entra debe ganarse su sitio. Mejor 4 chunks de altísima relevancia que 15 mediocres. El detalle de retrieval híbrido y reranking está en Reranking e hybrid retrieval; aquí basta con la regla: maximiza recall en el retriever, maximiza precisión en el reranker, e inyecta pocos.

2. Compresión de contexto: destilar la chuleta

Una vez tienes los mejores chunks, todavía contienen paja —frases de relleno, redundancia, contexto irrelevante a la query concreta. La compresión de contexto los recorta antes de inyectarlos:

  • Compresión extractiva (estilo LLMLingua / LongLLMLingua, Jiang et al. 2023): un modelo pequeño puntúa la perplejidad o relevancia de cada token/frase respecto a la query y elimina los de baja información, quedándose con el subconjunto extractivo más denso. Reduce tokens sin un segundo modelo generativo grande de por medio. LongLLMLingua añade reordenación consciente de la posición para mitigar lost in the middle.
  • Compresión abstractiva: un modelo resume los chunks recuperados en un texto más corto. Más agresiva en reducción de tokens, pero introduce un paso generativo (coste y posible pérdida de fidelidad).
  • Soft prompts / context distillation: comprimir el contexto recuperado no a texto, sino a un puñado de embeddings/soft tokens que el modelo consume directamente. Reduce el número de tokens de prefill al mínimo, a costa de un componente entrenado y específico del modelo.

El punto clave conecta con las matemáticas de arriba: comprimir lo recuperado un factor $k$ reduce los tokens de prefill $\times k$, y por tanto el TTFT $\sim\times k$ y el coste de atención $\sim\times k^2$. Es la palanca con mejor retorno cuando el contexto largo es el cuello de botella.

3. Prefix caching del contexto estable

No todo el contexto cambia entre peticiones. Instrucciones de sistema, definiciones, documentos de referencia recurrentes, esquemas: son prefijos estables. El prefix caching guarda el KV cache ya computado de esos prefijos y lo reutiliza, de modo que el prefill solo procesa la parte nueva (la query y los chunks específicos). Si el 60 % de tu contexto es estable, te ahorras el 60 % del prefill de ese segmento en cada hit. Para que funcione, el contexto estable debe ir al principio del prompt (el KV cache es prefijo-dependiente) y conviene maximizar el hit rate; el detalle de ingeniería de hit rate está en Prefix cache hit rate. Combina especialmente bien con RAG: documentos recuperados que se repiten entre sesiones se cachean una vez.

4. Caché semántico de respuestas

Una capa por delante del modelo: si una query es semánticamente equivalente a una respondida antes (similitud de embeddings por encima de un umbral), devuelve la respuesta cacheada y sáltate el modelo entero —retrieval, prefill y decode incluidos. En cargas reales con colas largas de preguntas repetidas o casi-repetidas (FAQ, soporte), el ahorro es enorme porque elimina el coste completo, no solo el de prefill. La trampa es el umbral: demasiado laxo y sirves respuestas equivocadas a preguntas parecidas-pero-distintas. El diseño está en Caché semántico para RAG.

5. Structured output y function calling: apoyarse en herramientas, no en memoria

La última palanca cambia de qué depende el SLM. En lugar de pedirle que sepa un dato (su punto débil), haz que llame a una herramienta que lo sabe: una consulta a base de datos, una API, una calculadora, un validador. El structured output (forzar JSON conforme a un esquema) y el function calling convierten al SLM en un orquestador que extrae argumentos del contexto y delega el cálculo o la consulta. Un SLM razonablemente capaz emite un tool call bien formado mucho más fiablemente de lo que recuerda un hecho concreto. Esto reduce la presión sobre el conocimiento paramétrico y sobre la recuperación: para datos estructurados y frescos (precios, inventario, estados), consultar bate a recuperar texto y a memorizar. Los fundamentos están en Structured output y Function calling.

El pipeline completo

Las cinco palancas no son alternativas: se encadenan. El flujo, con el contador de tokens cayendo en cada paso:

Recuperar agresivo, destilar agresivo, razonar barato

Retrieverhíbrido, recall~80 chunksRerankerprecisión5 chunks · 4000 tokCompresiónextractiva k=41000 tokPrefix cache+ cachésemánticoprefill mínimoSLMrazona +tool callsrespuesta

El contador de tokens de prefill cae a lo largo del pipelineretrieve: muchorerank: 4000 tok1000

TTFT en RTX 4090 a ~5000 tok/s · 4000 tok = 0.80 s → 1000 tok = 0.20 satención cae ~k² = 16× en esa parte del cómputo

El orden importa. Recuperar agresivo (recall alto) antes de filtrar garantiza que el material correcto está entre los candidatos; rerankear y comprimir después garantiza que solo lo denso y relevante paga el peaje del prefill; cachear envuelve todo para no repetir trabajo. El SLM solo ve la chuleta final, corta y ordenada.

Implicaciones para inferencia on-premise

La trampa mental a evitar: tratar el SLM como un modelo grande con menos calidad. No lo es. Es un perfil de coste distinto que premia un diseño distinto. Tres consecuencias prácticas:

  • El presupuesto de tokens es un recurso de primera clase. Con un modelo grande de 128K de ventana, “meter un poco más” es barato relativo al modelo. Con un SLM, cada token de contexto se nota en el TTFT y en la calidad. Trata el tamaño del contexto como una cantidad a minimizar bajo restricción de cubrir la respuesta, no a maximizar.
  • La inversión vale la pena precisamente porque el modelo es barato. Reranker, compresor y cachés añaden complejidad, pero el modelo que sirven es lo suficientemente económico como para correr muchas réplicas. El cuello de botella se desplaza del modelo al pipeline de datos, que es justo donde quieres que esté.
  • Recuperar no sustituye a adaptar; se combinan. Para conocimiento de dominio profundo y recurrente, adaptar el SLM con LoRA (ver el hermano QLoRA y multi-LoRA agresivo) puede meter parte del conocimiento “en los pesos” de forma barata, reduciendo lo que hay que recuperar. RAG agresivo y adaptación agresiva no compiten: la primera da frescura y citabilidad, la segunda da fluidez y formato de dominio. El diseño bueno usa ambas.

En la RTX 4090 (24 GB, Ada Lovelace)

El escenario canónico: un SLM cuantizado (4B–8B en INT4/FP8) cabe holgado, dejando VRAM para un KV cache generoso —imprescindible para el prefix caching— y para el reranker (un cross-encoder de unos cientos de MB). El compresor extractivo tipo LLMLingua corre en un modelo pequeño aparte o en CPU. El cálculo de TTFT de arriba (0.80 s → 0.20 s comprimiendo 4× a ~5000 tok/s) es representativo de esta tarjeta. La regla de pulgar: si el TTFT se va por encima de tu SLA, el primer ajuste es comprimir el contexto, no cambiar de modelo.

Con 320 GB y FP8 nativo el prefill es mucho más rápido, así que la tentación es relajar la disciplina de tokens. No conviene del todo: la palanca cambia de TTFT a throughput agregado. Comprimir el contexto no solo acelera cada petición sino que libera cómputo de prefill para servir más peticiones por GPU —el prefill compute-bound es exactamente el recurso que satura primero bajo carga. Aquí el prefix caching y el caché semántico, compartidos entre réplicas, son los que más rinden: a alto QPS, el trabajo de prefill que evitas cachear es throughput puro que ganas. El SLM sigue siendo el motor de razonamiento barato; la diferencia es que ahora corres muchos en paralelo y el pipeline de datos es lo que decide cuántas peticiones caben.

Lo que no hemos cubierto

  • Evaluación de la compresión: cómo medir que comprimir $k=4$ no tira respuestas correctas (faithfulness, answer recall sobre un set de preguntas con ground truth).
  • Compresión consciente de la query frente a agnóstica: comprimir antes o después de conocer la pregunta cambia qué se puede cachear y qué se puede tirar.
  • Chunking y granularidad: el tamaño de chunk interactúa con el reranking y la compresión; queda para el post de curación de corpus.
  • Multi-hop y agentes: cuando una pregunta requiere varias rondas de recuperación, el presupuesto de tokens se reparte entre hops y la disciplina de compresión se vuelve crítica.

Ver también

Referencias