RAG: leer antes de responder
¿Cómo se le da conocimiento propio a un modelo sin reentrenarlo?
Lección completa
Si quieres:
Un cliente le pregunta al chatbot de una tienda de bicicletas eléctricas: «¿cuánto dura la garantía de la batería?». La respuesta está en el manual de la tienda.
Sin voz: la lección avanza sola con subtítulos.
La voz de esta lección está en preparación: por ahora avanza sola con subtítulos.
Modo revisiónComentar este pasoTerminé la lección
Qué lograrás
- Explicar qué hace un sistema RAG: preparar una vez la biblioteca (fragmentar e indexar) y, con cada pregunta, recuperar, armar el prompt con fuentes y responder citando.
- Predecir qué fragmentos recupera una pregunta y si el correcto llega al prompt según el tamaño del fragmento y el valor de k.
- Reconocer las tres fallas típicas (no encontrar, contradicciones, instrucciones escondidas) y medir la recuperación con preguntas propias.
Ideas clave
- RAG no reentrena el modelo: con cada pregunta busca los fragmentos de tus documentos que más se parecen y los pega en el prompt, junto con la pregunta.
- Se prepara una vez (fragmentar, convertir en vectores, indexar) y, con cada pregunta, se recupera, se arma el prompt con fuentes numeradas y se responde citando.
- El tamaño del fragmento y el número k de fragmentos que se pegan son decisiones con costo: se eligen probando, no por intuición. Un reordenador puede mejorar el orden, pero no siempre.
- La búsqueda por palabras falla con las paráfrasis; los embeddings comparan significados y rescatan muchas, no todas.
- Fallas típicas: no encontrar lo correcto, documentos que se contradicen e instrucciones escondidas en el texto. La calidad depende sobre todo de la recuperación: mídela con preguntas propias.
Lo que mucha gente cree
Creencia común RAG «entrena» al modelo con tus documentos: después de leerlos, el modelo los sabe.
Lo correcto El modelo no aprende nada: sus pesos no cambian. Con cada pregunta se buscan los fragmentos y se pegan en el prompt; si la búsqueda no los trae, el modelo no los ve.
Compruébalo
Cuatro preguntas. Sin nota ni cuenta: sirven para ver si la idea quedó.
Por dentroPara quien quiere las fórmulas y el código. No hace falta para seguir el curso.
TF-IDF y similitud coseno
Así se calcula la búsqueda por palabras de la escena. Primero se quitan las palabras muy comunes («de», «la», «que»…) y se recorta cada palabra a su raíz, de forma sencilla («baterías» y «batería» cuentan igual). Con eso, el manual de la lección tiene 228 términos distintos: cada fragmento es un vector de 228 números.
Dados fragmentos, es cuántas veces aparece el término en el fragmento , y en cuántos fragmentos aparece. Con las variantes de la lección:
Con : «ciclos» aparece en 1 fragmento () y «garantía» en 6 (); por eso «ciclos» pesa más. Cada vector se normaliza, , y la pregunta recibe el mismo tratamiento con los del índice. La similitud coseno queda como un producto punto:
La idea de pesar por rareza es de Karen Spärck Jones (1972): un término que aparece en pocos documentos los distingue mejor.
BM25, la otra puntuación por palabras
BM25 (Robertson y Zaragoza, 2009) puntúa cada fragmento con una frecuencia que se satura y una corrección por longitud:
con y como valores que, según sus autores, suelen funcionar bien. Es un punto de partida difícil de superar solo con palabras, y muchos sistemas lo combinan con embeddings (búsqueda híbrida).
Embeddings y búsqueda aproximada
Con embeddings (lección 14) el vector tiene unos cientos de números densos y no hace falta que coincidan las palabras: la similitud coseno compara significados. Comparar la pregunta con los vectores cuesta ; con millones de fragmentos se usan índices aproximados como HNSW (Malkov y Yashunin), que cambian un poco de exactitud por mucha velocidad. En el artículo que dio nombre a RAG (Lewis et al., 2020), Wikipedia se cortó en trozos de 100 palabras, 21 millones en total, que se indexaron con FAISS y HNSW; al entrenar se recuperaban o por pregunta.
Reordenar
Un recuperador denso (bi-encoder), como DPR (Karpukhin et al., 2020), el que usa RAG, codifica la pregunta y el fragmento por separado, así que los fragmentos se convierten en vectores una sola vez. Un reordenador (cross-encoder) lee la pregunta y el fragmento juntos en una pasada del modelo y devuelve una puntuación: es más fino, pero cuesta una pasada por candidato, así que solo se aplica a los primeros de la lista. Nogueira y Cho (2019) reordenaban con BERT los 1 000 primeros pasajes que traía BM25.
En la lección, las cifras de embeddings y de reordenador se midieron aparte (ver build-dense.py) con dos modelos abiertos: intfloat/multilingual-e5-small (Wang et al., 2024) y cross-encoder/mmarco-mMiniLMv2-L12-H384-v1, entrenado con mMARCO (Bonifacio et al., 2021). El reordenador se aplica a los 5 primeros candidatos.
Cómo se mide la recuperación
Para cada pregunta de prueba se anota el fragmento correcto y su puesto en la lista ordenada ( si no aparece). Con preguntas:
En las 10 preguntas de la escena, con : por palabras, 7 de 10; con embeddings, 9 de 10; con embeddings y reordenador, 8 de 10 (en ambos idiomas). Diez preguntas son muy pocas para sacar conclusiones generales: el punto es el método.
Esto mide solo la primera mitad. La segunda, si la respuesta se apoya en lo recuperado, se evalúa con casos de prueba y con revisión humana o con un modelo como juez (lección 34). Ragas (Es et al., 2023) la separa en tres medidas: fidelidad (lo que dice la respuesta se deduce del contexto), pertinencia de la respuesta y pertinencia del contexto.
Actualizar sin reentrenar
En el artículo de RAG se probó cambiar solo el índice: con 82 gobernantes que cambiaron entre 2016 y 2018, el modelo acertó el 70 % con el índice de Wikipedia de 2016 para los de 2016, y el 68 % con el de 2018 para los de 2018; con el índice que no correspondía, solo el 12 % y el 4 %. Para actualizar lo que sabe, se cambian documentos, no pesos.
En código
Un buscador TF-IDF completo, sin librerías. Es la misma receta de la lección, sin recortar las palabras a su raíz:
import math, re
from collections import Counter
STOP = {"la", "el", "de", "los", "a", "en", "tu", "o", "es", "cuanto", "dura"}
def terms(text):
return [w for w in re.findall(r"[a-z0-9]+", text.lower()) if w not in STOP]
chunks = [
"la bateria tiene 18 meses de garantia o 500 ciclos de carga",
"puedes cancelar tu compra durante los 30 dias posteriores a la entrega",
"el envio es gratis en compras mayores a 1500",
"carga la bateria a temperatura ambiente",
]
N = len(chunks)
bags = [Counter(terms(c)) for c in chunks]
df = Counter(t for b in bags for t in b)
idf = {t: math.log((N + 1) / (df[t] + 1)) + 1 for t in df}
def vector(bag):
v = {t: (1 + math.log(c)) * idf[t] for t, c in bag.items() if t in idf}
norm = math.sqrt(sum(x * x for x in v.values())) or 1.0
return {t: x / norm for t, x in v.items()}
index = [vector(b) for b in bags]
def search(question, k=2):
q = vector(Counter(terms(question)))
scores = [sum(w * d.get(t, 0.0) for t, w in q.items()) for d in index]
return sorted(enumerate(scores), key=lambda p: -p[1])[:k]
for i, s in search("cuanto dura la garantia de la bateria"):
print(f"{s:.2f} {chunks[i]}")
# 0.47 la bateria tiene 18 meses de garantia o 500 ciclos de carga
# 0.27 carga la bateria a temperatura ambiente
Lo que viene
Aquí el modelo solo lee. En la lección 32 puede además usar herramientas: buscar, calcular, escribir. La inyección de instrucciones se retoma en la lección 35, y la evaluación completa de sistemas, en la 34.
Lente de decisión
Qué significa esta idea si tu trabajo es decidir, no programar.
- RAG da respuestas con tus datos y con fuentes que se pueden revisar, sin reentrenar: para actualizar lo que sabe se cambian documentos, no el modelo. En el artículo original, cambiar el índice de Wikipedia de 2016 por el de 2018 bastó para responder sobre los gobernantes nuevos. Pregunta cómo se actualiza el índice y cada cuánto.
- La calidad se decide en la recuperación: si el fragmento correcto no llega al prompt, ningún modelo puede citarlo. Pide la tasa de acierto de la recuperación medida con tus propias preguntas, no con una demostración.
- Gobierna los documentos: versión, fecha, dueño y permisos. Lo que el sistema puede recuperar, lo puede decir: versiones viejas que contradicen a las nuevas, y datos que no todas las personas deberían ver.
- Todo texto recuperado es de confianza limitada: una reseña, una página web o un correo pueden traer instrucciones escondidas. Limita lo que el sistema puede hacer con lo que lee y registra qué fuentes usó en cada respuesta.
Términos de la lección
- RAG (generación aumentada por recuperación) Técnica que, antes de generar, busca en una colección de documentos los fragmentos que más se parecen a la pregunta y los incluye en el prompt, de modo que el modelo responde con esas fuentes sin reentrenarse.
- Fragmentación (chunking) Cortar los documentos en trozos, o fragmentos, que se indexan y se recuperan por separado. Si son muy pequeños, un dato puede quedar sin su contexto; si son muy grandes, la respuesta se diluye.
- Base de datos vectorial Programa que guarda vectores junto con su texto y sus datos (fuente, fecha), y encuentra muy rápido los más parecidos a un vector de consulta, normalmente con búsqueda aproximada de vecinos.
- Recuperación (retrieval) Paso que convierte la pregunta en un vector, lo compara con el índice y trae los k fragmentos más parecidos.
- Anclaje (grounding) Hacer que cada afirmación de la respuesta se apoye en una fuente concreta y comprobable, citada junto a ella.
- Reordenamiento (reranking) Segundo paso que reordena los primeros candidatos de la búsqueda con un modelo más fino y más lento, que lee juntos la pregunta y cada fragmento.
- Vector Lista ordenada de números. En aprendizaje automático, cada ejemplo llega al modelo como un vector.
- Embedding Vector de números, aprendido de los datos, que representa algo (una palabra, una frase, una imagen) de modo que lo parecido quede cerca. Cada palabra tiene una fila en una tabla de embeddings.
- Similitud coseno Coseno del ángulo entre dos vectores: 1 si apuntan igual, 0 si son perpendiculares, −1 si se oponen. Ignora la longitud y mide solo la dirección.
- Búsqueda semántica Búsqueda por significado: la consulta y los documentos se convierten en vectores y se devuelven los documentos más cercanos, aunque no compartan palabras.
- Ventana de contexto Número máximo de tokens que el modelo puede leer de una vez. Cuentan las instrucciones del sistema, la conversación, los documentos y la respuesta que va escribiendo. Lo que no cabe no existe para el modelo.
- Instrucciones del sistema (system prompt) Instrucciones fijas que quien construye la aplicación pone al frente del contexto: rol, tono y reglas. Quien conversa no las ve, pero viajan y ocupan lugar en cada turno.
- Perdido en el medio Efecto medido en varios modelos: aprovechan mejor la información al principio o al final de un contexto largo que la que queda a la mitad.
- Token Pieza de texto (una palabra, un trozo de palabra, un signo o un código básico) tomada del vocabulario. Es la unidad que el modelo lee y genera, y por la que se cobra.
- Peso (w) En un modelo lineal, el número que multiplica a la entrada: la pendiente de la recta. Indica cuánto sube la predicción por cada unidad que aumenta la entrada.
- Prompt (instrucción) El texto con el que se le pide algo a un modelo generativo; en un generador de imágenes, la descripción de la imagen que se quiere.
Transcripción completa
Fuentes
- Lewis, P., Perez, E., Piktus, A., Petroni, F., Karpukhin, V., Goyal, N., Küttler, H., Lewis, M., Yih, W., Rocktäschel, T., Riedel, S., Kiela, D. (2020). Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
- Karpukhin, V., Oğuz, B., Min, S., Lewis, P., Wu, L., Edunov, S., Chen, D., Yih, W. (2020). Dense Passage Retrieval for Open-Domain Question Answering
- Spärck Jones, K. (1972). A statistical interpretation of term specificity and its application in retrieval
- Robertson, S., Zaragoza, H. (2009). The Probabilistic Relevance Framework: BM25 and Beyond
- Malkov, Y. A., Yashunin, D. A. (2016). Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs
- Nogueira, R., Cho, K. (2019). Passage Re-ranking with BERT
- Wang, L., Yang, N., Huang, X., Yang, L., Majumder, R., Wei, F. (2024). Multilingual E5 Text Embeddings: A Technical Report
- Bonifacio, L., Jeronymo, V., Abonizio, H. Q., Campiotti, I., Fadaee, M., Lotufo, R., Nogueira, R. (2021). mMARCO: A Multilingual Version of the MS MARCO Passage Ranking Dataset
- Greshake, K., Abdelnabi, S., Mishra, S., Endres, C., Holz, T., Fritz, M. (2023). Not what you’ve signed up for: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection
- Es, S., James, J., Espinosa-Anke, L., Schockaert, S. (2023). Ragas: Automated Evaluation of Retrieval Augmented Generation
ComentariosConversación pública de esta lección: dudas, datos por revisar, ideas.
Los comentarios viven en GitHub Discussions, en el repositorio público del curso; para escribir se necesita una cuenta de GitHub. Se cargan desde giscus.app al abrir esta sección. Las personas que revisan el curso los leen, y si un cambio se acepta, se aplica en los dos idiomas. Abrir el hilo en GitHub