Módulo 08 · Arquitectura

Fine-tuning vs RAG

Esta es la decisión de diseño que separa un proyecto que funciona de uno que alucina. La regla es simple: entrena el método de enseñar y responder con fine-tuning; recupera el contenido — hechos, currículo, definiciones, todo lo que se actualiza — con RAG. No metas los libros dentro del LoRA. El fine-tuning cambia $p_\theta$ (comportamiento, formato, estilo; no garantiza hechos nuevos); RAG condiciona la generación en el pasaje recuperado $z$ (hechos frescos y con fundamento). Este módulo fija cuándo gana cada uno y la matemática de recuperación que sostiene un RAG serio.

Al terminar sabrás

  • Decidir si un requisito toca pesos (fine-tuning) o contexto (RAG) — y por qué mezclarlos es un error caro.
  • Explicar por qué FT cambia $p_\theta$ pero no "inyecta" hechos, mientras RAG los aporta vía $z$.
  • Construir el circuito de recuperación: embeddings → chunking → vector search → reranking → context assembly.
  • Leer la similitud coseno, el dual-encoder (DPR) con negativos en-batch y la marginalización RAG.
  • Presupuestar el contexto y medir grounding (fundamento) frente a alucinación.

1La frontera: método vs contenido

Un modelo entrenado es una función de probabilidad $p_\theta(y \mid x)$ sobre secuencias. El fine-tuning mueve $\theta$ para cambiar cómo responde: tono, formato, la secuencia con que explica, cuándo pide una pista, qué queda fuera de alcance. Eso es el método. Lo que el fine-tuning no hace de forma fiable es memorizar hechos concretos y mantenerlos actualizados: aunque un dato aparezca en el set de entrenamiento, el modelo puede no recuperarlo verbatim y no hay forma barata de corregirlo el día que cambia. Los hechos son el contenido.

RAG (Retrieval-Augmented Generation) no toca $\theta$. Antes de generar, busca en un corpus externo los pasajes relevantes a la pregunta y los pega en el contexto. El modelo responde condicionado en ese pasaje $z$. Cambiar un hecho es cambiar un documento del índice, no reentrenar nada.

fine-tuning · el CÓMOΔθ
forma de respondertono / voz formato de salidasecuencia de razonamiento cómo corregirfuera de alcance estilo estable
RAG · el QUÉz
hechos frescosdefiniciones currículo / políticasdocumentos citables actualizable en calientecon fundamento

La confusión más cara del oficio es querer "enseñarle los datos al modelo" con fine-tuning. Termina en un LoRA que suena seguro y se equivoca, imposible de auditar. Si el requisito es qué sabe, es RAG. Si es cómo se comporta, es fine-tuning.

2Cuándo gana cada uno

RequisitoHerramientaPor qué
Voz, formato o comportamiento consistenteFine-tuningEs una propiedad de $p_\theta$; se aprende, no se recupera.
Hechos que cambian (precios, currículo, normativa)RAGActualizas el índice, no los pesos; queda citable.
Dominio cerrado grande que no cabe en el promptRAGSe recupera solo lo relevante por consulta.
Cumplir un esquema de salida estrictoFine-tuningEl formato es método, se interioriza en los pesos.
Respuestas verificables / con fuenteRAGEl pasaje $z$ da fundamento (grounding) y trazabilidad.
Estilo estable + base de hechos que cambiaAmbosFT para el cómo, RAG para el qué. No se sustituyen.
Nota · no son excluyentes

El caso de producción más común es FT + RAG: se fine-tunea un modelo para que adopte la voz y sepa usar pasajes recuperados (citarlos, decir "no lo sé" cuando el contexto no lo cubre), y RAG le entrega los hechos en tiempo de inferencia. Fine-tuning fija el comportamiento de leer el contexto; RAG llena el contexto.

3Recuperación por embedding denso

RAG empieza por buscar. La búsqueda densa representa consulta $q$ y documento $d$ como vectores $e_q, e_d \in \mathbb{R}^n$ producidos por un sentence encoder, y mide su parecido con similitud coseno — el ángulo entre vectores, insensible a la magnitud:

$$ \mathrm{sim}(q,d) = \frac{e_q\cdot e_d}{\lVert e_q\rVert\,\lVert e_d\rVert}, \qquad \operatorname*{top\text{-}k}_{d}\; \mathrm{sim}(q,d) $$

Se indexan todos los $e_d$ del corpus en una base vectorial y, dada una consulta, se recuperan los $k$ documentos de mayor similitud (vector search). A escala esto no se hace por fuerza bruta sino con índices aproximados (ANN, p. ej. HNSW), que cambian un poco de recall por mucha velocidad.

Chunking: el tamaño importa

No se embeben documentos enteros sino chunks (fragmentos). El tamaño $c$ es un compromiso: chunks grandes traen más contexto pero diluyen la señal — el embedding promedia demasiadas ideas y baja el recall del pasaje exacto; chunks pequeños son precisos pero parten frases y pierden el hilo. Un solape $o$ entre chunks contiguos evita cortar justo donde estaba la respuesta:

$$ \text{n.º de chunks} \approx \left\lceil \frac{L - o}{c - o} \right\rceil, \qquad 0 \le o < c $$

donde $L$ es la longitud del documento en tokens. Regla práctica: elegir $c$ por la unidad semántica natural del corpus (un párrafo, una definición, una sección) y fijar $o$ en torno al 10–20 % de $c$.

Metadata filtering

Cada chunk lleva metadatos (fuente, fecha, grado, idioma, permisos). Filtrar por metadatos antes del vector search reduce el espacio de búsqueda y previene fugas: si la consulta es de "grado 3", no recuperas pasajes de "grado 7" por muy parecido que sea el embedding. Es la diferencia entre un RAG de demo y uno gobernable.

4Dual-encoder (DPR) y cómo se entrena

Un dual-encoder (o bi-encoder, DPR — Dense Passage Retrieval) usa dos codificadores, uno para preguntas y otro para pasajes, y puntúa con el producto interno:

$$ s(q,d) = e_q(q)^\top e_d(d) $$

Como cada lado se codifica por separado, los $e_d$ se precomputan una vez y se indexan; en consulta solo se codifica $q$. Se entrena con negativos en-batch: para cada par correcto $(q, d^+)$, los pasajes positivos de otras preguntas del batch hacen de negativos $d^-$. El objetivo es contrastivo (InfoNCE), que empuja al positivo por encima de todos los negativos:

$$ \mathcal{L} = -\log\frac{e^{s(q,d^+)}}{e^{s(q,d^+)} + \sum_{d^-} e^{s(q,d^-)}} $$

Los negativos en-batch son casi gratis (ya están codificados) y escalan con el tamaño del batch, por eso DPR entrena con batches grandes. Añadir hard negatives — distractores parecidos pero incorrectos — es lo que separa un buen retriever de uno mediocre.

5Reranking: barato primero, caro después

El bi-encoder es rápido pero mira $q$ y $d$ por separado, así que pierde interacciones finas. El reranking con un cross-encoder corrige eso: toma cada uno de los $k$ candidatos del bi-encoder y los re-puntúa procesando la concatenación $[q;d]$ en un solo forward, dejando que la atención cruce ambas entradas:

$$ r(q,d) = f\big([q;d]\big) $$

El cross-encoder es más preciso pero más caro: no se puede precomputar (necesita $q$ y $d$ juntos), así que no sirve para buscar sobre millones de documentos. La arquitectura ganadora es en dos etapas: bi-encoder recupera barato un top-$k$ amplio (p. ej. 100), cross-encoder re-ordena caro ese puñado y se queda con el top-$m$ (p. ej. 5).

6Generación: marginalizar sobre los pasajes

Con los pasajes recuperados se genera. La formulación RAG trata al pasaje $z$ como una variable latente y marginaliza sobre el top-$k$: la probabilidad de la respuesta $y$ es la mezcla de generar $y$ condicionado en cada pasaje, ponderada por lo relevante que es ese pasaje según el retriever $p_\eta(z\mid x)$:

$$ p(y\mid x) = \sum_{z\in\text{top-}k} p_\eta(z\mid x)\,p_\theta(y\mid x,z) $$

Es el patrón recuperar-luego-generar: $\eta$ decide qué leer, $\theta$ decide qué decir dado lo leído. En la práctica industrial se simplifica a concatenar los mejores pasajes en el prompt (context assembly) y generar una vez, pero la ecuación explica por qué RAG puede fundamentar: la respuesta está anclada a un $z$ concreto y citable, no a la memoria difusa de los pesos.

Context budgeting y grounding

La ventana de contexto es finita, así que hay un presupuesto: cuántos chunks entran, en qué orden, cuánto deja para la respuesta. Meter demasiados pasajes ruidosos empeora la salida ("lost in the middle"). El objetivo es el grounding: cada afirmación de la respuesta debe poder rastrearse a un pasaje recuperado. Cuando no hay pasaje que la sostenga y el modelo la dice igual, eso es una alucinación — y medir esa tasa es cómo se evalúa un RAG en serio.

Cuidado

RAG no arregla un modelo que ignora el contexto. Si el modelo base no aprendió a preferir el pasaje sobre su memoria, recuperará bien y responderá con lo que "creía saber". Ese comportamiento — leer el contexto, citarlo, admitir cuando no está — es método, y se afina con fine-tuning. Otra vez: el qué es RAG, el cómo es FT.

En el teléfono

RAG on-device es totalmente viable y a menudo lo correcto: el índice vectorial vive en el teléfono (una base pequeña local), se recupera sin red y el LLM responde fundamentado. Reparte bien: fine-tuning para el método (tono, formato), RAG local para el contenido actualizable — sin re-entrenar para cambiar un dato.

Cómo practicar
  1. Chunkea un corpus de dominio.
  2. Embebe los chunks con un sentence-encoder.
  3. Recupera top-k por coseno (+ rerank opcional con cross-encoder).
  4. Responde con Qwen3-0.6B fundamentado en los chunks recuperados.
  5. Compara RAG vs base sin retrieval vs LoRA-sobre-los-hechos, midiendo exactitud y tasa de alucinación.
Herramientas: Python · sentence-transformers · faiss / qdrant
Rust · on-device

RAG encaja muy bien en Rust para móvil: candle calcula los embeddings, y para el índice hay motores en Rust como qdrant (servidor) o usearch/HNSW embebible en el propio dispositivo.

Es la pieza que permite un asistente offline con datos frescos, sin depender de la nube.

Lecturas y recursos

Ejercicios

De menor a mayor complejidad. Los cuatro primeros construyen las piezas del quinto, que es el que hace un practicante de verdad.

Ejercicio 1 · calentamiento

Similitud coseno a mano

Embebe cinco frases con un sentence encoder y calcula la matriz de similitud coseno entre todas. Verifica que la diagonal es 1 y que las frases parecidas puntúan más alto.

Entrega: matriz $5\times5$ + las 3 parejas más similares.   Pista: normaliza los vectores y usa el producto punto; $\mathrm{sim} = \frac{e_q\cdot e_d}{\lVert e_q\rVert\lVert e_d\rVert}$.

Ejercicio 2

Chunking con solape

Toma un documento y chunkéalo con dos configuraciones: $(c=128, o=0)$ y $(c=128, o=32)$. Cuenta los chunks resultantes y compáralos con la fórmula $\lceil (L-o)/(c-o) \rceil$.

Entrega: nº de chunks medido vs predicho para ambas configuraciones.   Pista: cuenta en tokens, no en caracteres.

Ejercicio 3

Recuperación top-k

Indexa los chunks del ejercicio 2, embébelos y, para tres consultas, recupera el top-5 por coseno. Inspecciona a mano si los pasajes recuperados contienen la respuesta.

Entrega: por consulta, los 5 chunks recuperados marcando cuáles son relevantes.   Pista: guarda el texto original junto al vector para poder leer lo recuperado.

Ejercicio 4

Añade un reranker

Sobre el top-20 del bi-encoder, aplica un cross-encoder para re-puntuar y quédate con el top-5. Compara el orden antes y después: ¿sube algún pasaje relevante que estaba enterrado?

Entrega: tabla top-5 bi-encoder vs top-5 tras rerank, con la posición original.   Pista: el cross-encoder puntúa $r(q,d)=f([q;d])$; pásale los pares, no vectores precomputados.

Ejercicio 5 · ejercicio top

RAG vs FT vs base, medido

Sobre un set de QA de dominio cerrado, construye el pipeline RAG end-to-end — chunkea el corpus, embebe con un sentence encoder, recuperación top-k por coseno, rerank opcional con cross-encoder, luego responde con Qwen3-0.6B fundamentado en los chunks recuperados — y compáralo contra el mismo modelo base sin retrieval y contra una variante LoRA fine-tuned sobre los hechos; reporta exactitud de respuesta y una tasa de alucinación/fundamento para las tres.

Entrega: tabla comparativa a 3 vías (RAG vs FT vs base) + una regla corta escrita de cuándo elegir cada uno.   Pista: mantén fijo el prompt de respuesta; para la tasa de fundamento, marca cada respuesta como sostenida / no sostenida por los pasajes recuperados.

Puntos clave
  • Entrena el método con fine-tuning; recupera el contenido con RAG. No metas los libros en el LoRA.
  • FT cambia $p_\theta$ (comportamiento/formato/estilo) pero no garantiza hechos nuevos; RAG condiciona en el pasaje $z$.
  • El circuito de recuperación: embeddings → chunking (c vs o) → metadata → vector search → reranking → context assembly.
  • Bi-encoder recupera barato; el cross-encoder rerankea caro y más preciso. Dos etapas.
  • RAG fundamenta porque marginaliza sobre $z$ citable; medir grounding vs alucinación es cómo se evalúa.