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.
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
| Requisito | Herramienta | Por qué |
|---|---|---|
| Voz, formato o comportamiento consistente | Fine-tuning | Es una propiedad de $p_\theta$; se aprende, no se recupera. |
| Hechos que cambian (precios, currículo, normativa) | RAG | Actualizas el índice, no los pesos; queda citable. |
| Dominio cerrado grande que no cabe en el prompt | RAG | Se recupera solo lo relevante por consulta. |
| Cumplir un esquema de salida estricto | Fine-tuning | El formato es método, se interioriza en los pesos. |
| Respuestas verificables / con fuente | RAG | El pasaje $z$ da fundamento (grounding) y trazabilidad. |
| Estilo estable + base de hechos que cambia | Ambos | FT para el cómo, RAG para el qué. No se sustituyen. |
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:
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:
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:
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:
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:
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)$:
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.
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.
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.
- Chunkea un corpus de dominio.
- Embebe los chunks con un sentence-encoder.
- Recupera top-k por coseno (+ rerank opcional con cross-encoder).
- Responde con Qwen3-0.6B fundamentado en los chunks recuperados.
- Compara RAG vs base sin retrieval vs LoRA-sobre-los-hechos, midiendo exactitud y tasa de alucinación.
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.
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}$.
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.
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.
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.
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.
- 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.