RAG en español de verdad: arquitectura, chunking, embeddings con FastEmbed/ONNX, reranking, evaluación de recuperación y cómo frenar alucinaciones.
Casi todos los tutoriales de RAG asumen inglés, corpus limpio y preguntas de juguete. Luego lo montas sobre documentación real en español —pliegos técnicos, normativa, fichas de producto con acentos, eñes y frases de tres líneas— y la recuperación devuelve fragmentos que no vienen a cuento. El modelo, obediente, alucina sobre ellos. El problema casi nunca es el LLM: es todo lo que pasa antes de llamarlo.
Este artículo es lo que he aprendido construyendo sistemas de RAG en español que tienen que funcionar fuera del notebook. Incluye el que mueve la propia terminal IA de este portfolio: un RAG ligero que recupera contexto de mis proyectos y experiencia para responder con datos reales, no inventados.
Qué es RAG y por qué el español lo complica
RAG (Retrieval-Augmented Generation) es simple de enunciar: en vez de fiarlo todo a lo que el modelo memorizó, recuperas los fragmentos relevantes de tu corpus y se los pasas como contexto para que genere la respuesta anclada en ellos. Reduces alucinaciones, citas fuentes y actualizas conocimiento sin reentrenar nada.
La dificultad está en la primera R. Recuperar bien en español tiene fricciones propias que el ecosistema, optimizado para inglés, suele ignorar:
- Morfología rica. “Instalación”, “instalaciones”, “instalar”, “instalado” comparten raíz pero rompen cualquier matching léxico ingenuo. Un buen embedding multilingüe las acerca; un tokenizador pensado para inglés las fragmenta de forma subóptima.
- Diacríticos y normalización. “Cámara” y “camara” deberían colisionar en búsqueda léxica. Si tu pipeline no normaliza Unicode (NFC) de forma consistente entre indexado y consulta, pierdes coincidencias silenciosamente.
- Frases largas y subordinadas. El español encadena oraciones más que el inglés. Un chunking por número fijo de caracteres parte ideas a la mitad con más frecuencia.
Ignorar esto es la causa número uno de un RAG en español que “casi va”.
Arquitectura: las cuatro decisiones que importan
Un RAG de producción son cuatro piezas, y cada una es una decisión de ingeniería, no una librería que importas y ya:
- Ingesta y chunking — cómo trocear el corpus.
- Embeddings — con qué modelo vectorizas.
- Recuperación + reranking — cómo encuentras y reordenas candidatos.
- Generación con guardarraíles — cómo evitas que el modelo se invente lo que no recuperaste.
El error clásico es invertir todo el esfuerzo en la 4 (prompt engineering infinito) cuando el 80% de la calidad se decide en la 1 y la 3.
1. Chunking: el trabajo invisible que decide todo
El chunking es donde se gana o se pierde la partida. Reglas que aplico:
- Trocea por estructura, no por longitud. Respeta encabezados, párrafos y listas. Un fragmento que empieza a mitad de una tabla o de una cláusula es ruido. Markdown y HTML te dan esa estructura gratis; un PDF mal extraído, no, así que la extracción limpia es parte del chunking.
- Tamaño moderado con solape. Entre 300 y 600 tokens por fragmento, con un solape de 10-15%. El solape evita que la respuesta caiga justo en la frontera entre dos chunks y se pierda.
- Adjunta metadatos. Fuente, sección, fecha, idioma. Sirven para filtrar antes de buscar y para citar después. Filtrar por metadatos es la optimización más barata que existe.
En la terminal IA de este portfolio el corpus es pequeño y muy estructurado (mis content collections de proyectos y experiencia), así que cada entrada ya es su propio chunk semántico con un límite de caracteres por cuerpo. No necesito un splitter sofisticado: necesito que el contexto total quepa holgado en la ventana del modelo más pequeño de la cadena de fallback. KISS gana cuando el corpus lo permite; reserva la artillería para cuando de verdad haga falta.
2. Embeddings: FastEmbed y ONNX, sin pagar por token
El embedding es lo que convierte texto en un vector donde “cercanía” significa “significado parecido”. Para español, dos criterios mandan: que el modelo sea genuinamente multilingüe (no inglés con un barniz) y que puedas ejecutarlo local y barato.
Aquí es donde FastEmbed sobre ONNX Runtime brilla. En vez de depender de una API de embeddings que cobra por token y manda tu corpus fuera, ejecutas modelos cuantizados en local:
- Coste cero por inferencia y latencia predecible: nada de rate limits ajenos.
- Privacidad por diseño: los documentos no salen de tu infraestructura. En proyectos con datos sensibles —pliegos, datos de personas— esto no es un lujo, es un requisito.
- Portabilidad: ONNX corre en CPU decente sin GPU, y el mismo modelo cuantizado va de servidor a edge.
Es la misma filosofía que llevé al extremo en RecruitSecure AI, donde los embeddings de los CVs se calculan dentro del navegador con Transformers.js y Web Workers: cero datos enviados fuera, cumplimiento GDPR por construcción. FastEmbed/ONNX es esa idea en el lado servidor.
Para almacenar y buscar esos vectores uso Qdrant cuando el volumen lo justifica: filtrado por payload (los metadatos) combinado con búsqueda vectorial en una sola consulta. Para corpus pequeños, un índice en memoria sobra.
3. Recuperación híbrida y reranking
La búsqueda puramente vectorial falla en un caso muy concreto y muy común en español técnico: términos exactos. Un código de norma (“UNE-EN 1090”), una referencia, un nombre propio. El embedding los entiende “parecidos” a otras cosas y los diluye. La solución es recuperación híbrida: combinar búsqueda densa (vectores) con búsqueda léxica (BM25) y fundir los resultados.
Y luego, reranking. La recuperación inicial prioriza recall: trae 20-50 candidatos amplios. Un cross-encoder de reranking los reordena por relevancia real frente a la pregunta, y te quedas con los 3-5 mejores. Es la diferencia entre meterle al LLM un cajón desastre y pasarle exactamente lo que necesita. Recuperar mucho y luego afinar siempre bate a recuperar poco y rezar.
Evaluación de la recuperación: si no lo mides, no lo tienes
“Funciona” no es una métrica. Antes de tocar el prompt, mide la recuperación, que es donde de verdad se gana:
- Recall@k — ¿está el fragmento correcto entre los k recuperados? Si el contexto bueno no llega, ningún prompt lo salva.
- MRR / nDCG — ¿llega además bien posicionado? Lo más relevante debe ir arriba.
- Faithfulness — ¿la respuesta se apoya solo en lo recuperado, o el modelo rellenó huecos por su cuenta?
Construye un set de evaluación pequeño pero real: 30-50 preguntas en español con la respuesta y el fragmento esperado. No hace falta más para detectar regresiones cada vez que cambias el chunking o el modelo de embeddings. Sin esto, optimizas a ciegas y “mejorar” el sistema es una sensación, no un hecho.
Alucinaciones: por qué pasan y cómo frenarlas
Una alucinación en RAG casi siempre es un fallo de recuperación disfrazado: el modelo no recibió el dato, así que lo inventó con total aplomo. Mis defensas, por orden de impacto:
- Arregla la recuperación primero. El 70% de las alucinaciones desaparecen cuando el contexto correcto llega de verdad. Empieza por ahí, no por el prompt.
- Instruye a citar y a abstenerse. El system prompt debe ordenar responder solo con el contexto y decir “no lo sé” cuando no esté. En la terminal de este portfolio la regla es explícita: “Responde SOLO con la información de abajo. Si no la sabes, dilo con honestidad. NUNCA inventes datos, motivos ni fechas.”
- Pon un guardarraíl determinista de salida. El prompt es necesario pero no suficiente: un LLM se lo puede saltar. Por eso la terminal lleva además un filtro programático posterior a la respuesta, independiente del modelo. Cinturón y tirantes: el modelo propone, pero una capa de código tiene la última palabra.
- Degrada con elegancia. Si la recuperación viene vacía o el proveedor falla, responde “no tengo ese dato” en vez de improvisar. Mi endpoint encadena varios proveedores y, si todos caen, devuelve un contexto reducido en lugar de un error opaco.
Esa combinación —prompt + filtro determinista + fallback honesto— es lo que separa una demo de algo que puedes poner delante de un cliente.
El patrón completo, en una frase
Trocea con cabeza, vectoriza local y barato con FastEmbed/ONNX, recupera en híbrido y reordena con un reranker, mide la recuperación con un set real en español, y pon guardarraíles deterministas sobre la generación. El LLM es la última pieza, no la primera.
Esta misma disciplina —ingeniería de contexto sobre fuentes reales— es la que aplico en mis proyectos de IA aplicada, de la terminal de este portfolio a DevFlow AI y su cadena de proveedores con fallback. El modelo de moda cambia cada trimestre; la arquitectura de recuperación que lo rodea es lo que aguanta.
¿Montando un RAG en español y se te tuerce la recuperación?
Es justo el tipo de problema en el que disfruto: corpus en español, datos sensibles que no pueden salir de tu infraestructura, y una recuperación que tiene que ser fiable, no solo demostrable. Si quieres una segunda opinión sobre tu arquitectura o construir el sistema desde cero, hablemos.
Artículos relacionados
Agentes LLM en producción: del prototipo al sistema fiable
Guía técnica para llevar agentes LLM a producción: orquestación, function-calling, memoria, guardarraíles, evaluación y costes. Qué falla al salir de la demo.
Leer artículoGEO: que ChatGPT y Perplexity citen tu web
GEO (generative engine optimization): llms.txt, schema.org (Person, FAQPage, hasCredential), contenido extraíble y E-E-A-T para que la IA cite tu web.
Leer artículoIA resiliente y gratis: fallback entre LLMs sin caer
Cómo monté un fallback entre LLMs gratis (Groq, OpenRouter, Pollinations) con degradación elegante: capas free encadenadas que no caen por cuota ni timeout.
Leer artículo