Preparar documentos: procesar datos para RAG
Qué cubre este artículo sobre preparación de documentos
- Qué formatos funcionan bien para RAG y qué problemas trae cada uno
- Cómo extraer texto de PDF, DOCX, HTML y otras fuentes
- Qué pasos de limpieza necesitas para que RAG entregue respuestas útiles
- Cómo los metadatos y la normalización mejoran la calidad de tu pipeline de RAG
- Un ejemplo práctico completo con código que puedes reproducir directamente
Introducción: preparar documentos explicado de forma sencilla
Ya te has familiarizado con RAG local y quieres empezar. Los modelos están instalados, Ollama funciona, la base de datos vectorial está lista. Pero antes de comenzar hay una tarea que muchos subestiman: debes preparar tus documentos.
Este artículo te muestra paso a paso cómo preparar documentos para que tu aplicación RAG entregue después respuestas limpias y relevantes. Cubrimos todos los formatos, tratamos la limpieza y normalización y trabajamos un ejemplo completo. Si aún no conoces los fundamentos de RAG, lee primero ese artículo.
¿Por qué necesito preparar documentos?
Imagina que alimentas una documentación PDF sin procesar a tu sistema RAG. El PDF contiene números de página, encabezados repetidos, índices y notas al pie. Haces la pregunta: “¿Cómo configuro la conexión a la base de datos?”
El sistema busca fragmentos de texto similares y encuentra, entre otros, una página con el encabezado “Capítulo 4, página 47, versión 2.1”. La respuesta es algo así: “En la página 47 encontrará información sobre la configuración.” No es una respuesta útil, sino un artefacto de los encabezados y números de página que no fueron limpiados.
Aquí es donde comienza la preparación de documentos. Sin ella, RAG produce basura, sin importar cuán bueno sea tu modelo de embedding. La calidad de tus respuestas depende directamente de la calidad de tus datos. No es una exageración, sino la lección más importante que aprendes al construir un pipeline de RAG.
Preparación de documentos explicada brevemente
La mejor analogía: preparar documentos es como preparar ingredientes al cocinar. Antes de cocinar, lavas las verduras, las cortas, quitas las cáscaras y mezclas las especias. Nadie tira ingredientes crudos sin lavar a la olla esperando un buen resultado.
Con RAG es igual. Tomas documentos sin procesar, eliminas todo lo que distrae, reestructuras el texto y añades metadatos. Solo entonces los textos van a la base de datos vectorial. La construcción del pipeline, desde el documento sin procesar hasta el vector buscable, ocurre en varios pasos que cubriremos en este artículo.
¿Para quién es este artículo?
Este artículo va dirigido a principiantes que construyen su primer sistema RAG local. No necesitas conocimientos previos en preparación de datos, pero debes entender básicamente qué es la IA local y cómo funciona RAG. Si ya tienes experiencia con Python, puedes adoptar los ejemplos de código directamente. Si no, explicamos cada paso de forma clara.
Términos importantes relacionados con preparación de documentos
| Término | Explicación |
|---|---|
| Formato | El formato de archivo de tu documento, por ejemplo PDF, DOCX o Markdown. Cada formato requiere herramientas diferentes para la extracción. |
| Parsing | Leer y convertir un documento en texto plano. |
| Limpieza | Eliminar texto que distrae como encabezados, pies de página, números de página y contenido repetitivo. |
| Normalización | Unificar formato, codificación y espacios en blanco para datos consistentes. |
| Deduplicación | Eliminar contenido duplicado que causa resultados de búsqueda sesgados. |
| Metadatos | Información adicional como fuente, fecha, autor y títulos de sección que ayudan a RAG a contextualizar las respuestas. |
| Markdown | Un formato de texto simple con estructura mediante caracteres como # para encabezados. |
| Un formato de documento ampliamente usado que dificulta la extracción de texto porque el diseño y el texto a menudo están mezclados. | |
| HTML | El formato de las páginas web que mezcla texto con etiquetas de marcado. |
| OCR | Optical Character Recognition, el reconocimiento de texto de imágenes o PDF escaneados. |
Formatos soportados
No todos los formatos son igualmente adecuados para RAG. Aquí encontrarás una descripción general de los formatos más comunes con sus ventajas y desventajas.
PDF es el formato más usado para documentaciones, manuales e informes. La desventaja: PDF almacena diseño y texto mezclados, lo que dificulta la extracción. Diseños multicolumna, imágenes incrustadas y páginas escaneadas hacen el parsing propenso a errores. Para PDF escaneados necesitas OCR, que cubrimos en el artículo sobre PDF y OCR.
DOCX
Los archivos DOCX de Word o LibreOffice se parsean bien porque internamente están estructurados como XML. Párrafos, encabezados y listas se preservan. La desventaja: los formatos pueden ser complejos y no todas las estructuras se convierten limpiamente a texto.
TXT
Los archivos de texto plano son el formato más simple. Sin parsing necesario, lees el texto directamente. La desventaja: TXT no proporciona estructura, tienes que identificar manualmente encabezados y secciones.
Markdown
Markdown es el mejor formato para RAG porque es estructurado y a la vez simple. Encabezados, listas y bloques de código están marcados directamente en el texto. Si creas documentos tú mismo, usa Markdown.
HTML
El HTML de páginas web contiene mucho contenido innecesario: navegación, pie de página, publicidad, scripts. Debes eliminar el marcado y extraer solo el contenido principal. Con herramientas como BeautifulSoup funciona bien, pero requiere esfuerzo.
CSV
CSV es adecuado para datos tabulares. Cada fila se convierte en un fragmento de texto, los encabezados de columna se usan como metadatos. La desventaja: las tablas son difíciles de representar como texto continuo y los modelos de RAG tienen dificultades con datos estructurados.
Paso 1: recopilar y estructurar documentos
Antes de parsear un solo documento, reúne todos los archivos en un solo lugar y asígnales una estructura consistente. Suena trivial pero ahorra mucho tiempo después.
Coloca todos los documentos fuente en una carpeta y nómbralos de forma uniforme. Un buen esquema de nombre es fecha_tema_version.pdf, por ejemplo 2026-01-15_doku-api_v2.pdf. Así ves de un vistazo qué versión tienes y cuándo se creó.
Además, para cada documento define qué metadatos quieres capturar. Un esquema simple se ve así:
metadata_schema = {
"source": "dateiname.pdf",
"title": "Título del documento",
"author": "Autor u organización",
"date": "2026-01-15",
"section": "Sección o capítulo",
"page": 42
}
Usarás este esquema más adelante para cada fragmento de texto extraído. La consistencia aquí se paga cuando tu aplicación RAG proporciona citas de fuentes, como describimos en el artículo sobre citas de fuentes.
Paso 2: Extraer texto
Cada formato requiere herramientas específicas. Aquí te muestro las principales con ejemplos de código.
PDF con PyPDF2
from pypdf import PdfReader
reader = PdfReader("dokument.pdf")
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
PyPDF2 extrae el texto puro, pero ignora la información de diseño. En PDFs multicolumna, el orden de los fragmentos de texto puede desordenarse. Herramientas más complejas como pdfplumber o unstructured ofrecen mejores resultados.
DOCX con python-docx
from docx import Document
doc = Document("dokument.docx")
text = ""
for paragraph in doc.paragraphs:
text += paragraph.text + "\n"
python-docx lee párrafos secuencialmente y mantiene el orden. Identificas encabezados por el atributo Style, por ejemplo paragraph.style.name == "Heading 1".
HTML con BeautifulSoup
from bs4 import BeautifulSoup
with open("seite.html", "r", encoding="utf-8") as f:
soup = BeautifulSoup(f, "html.parser")
# Entferne Skripte y Styles
for tag in soup(["script", "style", "nav", "footer"]):
tag.decompose()
text = soup.get_text(separator="\n")
BeautifulSoup elimina etiquetas innecesarias y extrae el texto visible. Ajusta la lista de etiquetas a eliminar según tus fuentes.
Markdown y TXT
with open("dokument.md", "r", encoding="utf-8") as f:
text = f.read()
Markdown y TXT se leen directamente sin herramientas especiales.
Paso 3: Limpiar texto
Después de la extracción, tu texto contiene muchos elementos que confunden a RAG. La limpieza es el paso más importante para obtener datos de calidad.
Eliminar encabezados y pies de página
Los PDFs repiten encabezados y pies de página en cada hoja, como el título del documento o el número de versión. Elimina estos patrones con expresiones regulares:
import re
# Elimina números de página como "Seite 12" o "- 12 -"
text = re.sub(r"(Seite|Page)\s*\d+", "", text)
text = re.sub(r"-\s*\d+\s*-", "", text)
# Elimina encabezados repetidos (adapta a tus documentos)
text = re.sub(r"Kapitel \d+", "", text)
Debes adaptar estos patrones a tus documentos específicos. No existe una solución universal porque cada documento tiene encabezados diferentes.
Corregir problemas de codificación
La codificación incorrecta genera caracteres como ü en lugar de ü. Convierte todo a UTF-8:
text = text.encode("utf-8", errors="ignore").decode("utf-8")
Eliminar contenido repetitivo
Los índices de contenidos, avisos de copyright y disclaimers se repiten en cada página y ocupan espacio en tu base de datos vectorial. Elimínalos:
boilerplate_patterns = [
r"Inhaltsverzeichnis.*?(?=\n\n)",
r"© \d{4}.*",
r"Alle Rechte vorbehalten.*",
]
for pattern in boilerplate_patterns:
text = re.sub(pattern, "", text, flags=re.DOTALL)
Normalizar espacios en blanco y líneas vacías
# Combina múltiples espacios en uno
text = re.sub(r"[ \t]+", " ", text)
# Combina múltiples líneas vacías en una
text = re.sub(r"\n{3,}", "\n\n", text)
# Elimina espacios en blanco al inicio y final
text = text.strip()
Paso 4: Añadir metadatos
Los metadatos son más importantes para RAG de lo que muchos creen. Sin ellos, tu sistema no sabe de dónde procede un fragmento de texto ni cuán actual es. Esto genera respuestas que proporcionan información obsoleta de versiones antiguas.
Para cada fragmento de texto debes capturar al menos estos campos:
- source: Nombre de archivo o URL del documento original
- title: Título del documento o sección
- date: Fecha de creación o modificación
- author: Autor u organización responsable
- section: Capítulo o encabezado de sección
- page: Número de página en PDFs
En Python almacenas metadatos como un diccionario junto al texto:
chunk = {
"text": "El fragmento de texto extraído...",
"metadata": {
"source": "handbuch_v2.pdf",
"title": "Datenbankverbindung konfigurieren",
"date": "2026-01-15",
"author": "BotServ",
"section": "Kapitel 4",
"page": 47
}
}
Cuando busques después, puedes filtrar por metadatos, por ejemplo solo versiones recientes o capítulos específicos. Esto hace tu aplicación RAG más precisa y las respuestas verificables.
Paso 5: Normalización
La normalización asegura que todos tus documentos tengan el mismo formato, sin importar su origen. Esto simplifica el chunking y la búsqueda posterior.
Los pasos de normalización más importantes:
- Unificar codificación: Convertir todo a UTF-8
- Unificar finales de línea: Transformar
\r\ny\ra\n - Normalizar espacios en blanco: Reducir espacios y líneas múltiples
- Limpiar caracteres especiales: Eliminar caracteres invisibles como espacios de ancho cero
def normalize_text(text):
# Unificar finales de línea
text = text.replace("\r\n", "\n").replace("\r", "\n")
# Eliminar espacios de ancho cero y caracteres similares invisibles
text = re.sub(r"[\u200b\u200c\u200d\ufeff]", "", text)
# Normalizar espacios en blanco
text = re.sub(r"[ \t]+", " ", text)
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip()
Aplica esta función a cada texto extraído antes de procesarlo. La consistencia en este punto se justifica cuando luego combines diferentes tipos de documentos en una base de datos vectorial.
Mejores prácticas para datos limpios en RAG
Aquí tienes los consejos más importantes que te ayudarán en la práctica:
- Empieza poco a poco: Comienza con 5 a 10 documentos, no con 500. Así detectas problemas temprano.
- Revisa manualmente el texto extraído: Lee los primeros textos extraídos. Si son ilegibles para ti, también lo serán para RAG.
- Usa Markdown como formato intermedio: Convierte todo a Markdown antes de cargarlo en la base de datos vectorial. Así preservas la estructura y puedes rastrear cambios.
- Documenta tu pipeline: Anota qué pasos ejecutas y en qué orden. Si algo falla, puedes aislar el error.
- Versionea tus datos: Cuando actualices documentos, conserva versiones antiguas. Así puedes comparar si los datos nuevos generan mejores respuestas.
- Elimina duplicados: El contenido idéntico en múltiples documentos distorsiona los resultados de búsqueda. Verifica duplicados antes de cargar datos.
- Prueba con preguntas reales: Haz a tu aplicación RAG preguntas que formularían usuarios reales. Así sabrás si la preparación es suficiente.
Ejemplo: Preparar una documentación en PDF
Ahora recorremos un ejemplo completo. Tienes una documentación en PDF de 50 páginas y quieres prepararla para RAG.
Paso 1: Cargar PDF y extraer texto
from pypdf import PdfReader
import re
reader = PdfReader("handbuch.pdf")
pages = []
for i, page in enumerate(reader.pages):
text = page.extract_text()
pages.append({"text": text, "page": i + 1})
Paso 2: Limpiar texto
def clean_page(text):
# Elimina números de página
text = re.sub(r"-\s*\d+\s*-", "", text)
text = re.sub(r"Seite\s*\d+", "", text)
# Elimina encabezados repetidos (adapta según sea necesario)
text = re.sub(r"Handbuch v\d\.\d", "", text)
# Normaliza espacios en blanco
text = re.sub(r"[ \t]+", " ", text)
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip()
for page in pages:
page["text"] = clean_page(page["text"])
Paso 3: Añadir metadatos
for page in pages:
page["metadata"] = {
"source": "handbuch.pdf",
"title": "Systemhandbuch",
"date": "2026-01-15",
"author": "BotServ",
"page": page["page"]
}
Paso 4: Dividir en chunks
Aquí usamos una división simple por párrafos. Para sistemas en producción, debes usar estrategias de chunking dedicadas, como se describe en el artículo sobre chunking.
chunks = []
for page in pages:
paragraphs = page["text"].split("\n\n")
for para in paragraphs:
if len(para.strip()) > 50: # Ignora párrafos demasiado cortos
chunks.append({
"text": para.strip(),
"metadata": page["metadata"]
})
print(f"{len(chunks)} Chunks erstellt")
Paso 5: Generar embeddings
Ahora conviertes los chunks limpios en vectores. Qué modelos son adecuados lo aprendes en el artículo sobre modelos de embedding.
# Pseudocódigo, detalles en el artículo sobre modelos de embedding
for chunk in chunks:
chunk["embedding"] = embed(chunk["text"])
Este ejemplo muestra el flujo completo. En la práctica, deberás adaptar los patrones de limpieza a tus documentos específicos, pero la estructura permanece igual.
Errores comunes en la preparación de documentos
- Ignorar encabezados y pies de página: Muchos principiantes pasan por alto que los encabezados repetidos distorsionan la búsqueda. Elimínalos de manera sistemática.
- No verificar la codificación: Si no pruebas caracteres especiales, terminarás con caracteres rotos en la base de datos que degradarán la calidad del embedding.
- Chunks demasiado grandes: Cuando un chunk es muy largo, el modelo de embedding pierde el enfoque. Mantén los chunks entre 200 y 800 palabras.
- Omitir metadatos: Sin metadatos no puedes citar fuentes ni aplicar filtros. Esto hace que RAG sea inútil para aplicaciones serias.
- No eliminar duplicados: Si el mismo texto aparece múltiples veces en la base de datos, saldrá en todas las respuestas. Eso no es útil.
- Cargar tablas de contenidos: Solo contienen referencias de páginas, no contenido real. Desperdician espacio y distorsionan búsquedas.
- Tablas como texto corrido: Las tablas pierden su estructura al analizarse. Considera si las tratas como datos estructurados en lugar de como texto.
- No probar con preguntas reales: Quien solo construye la pipeline sin probar con preguntas reales nunca descubre dónde faltan datos.
Hardware, costos y seguridad en la preparación de documentos
La preparación de documentos en sí es poco exigente computacionalmente. Extracción y limpieza de texto funcionan sin problemas en una laptop normal. No necesitas GPU para estos pasos.
Los costos provienen principalmente del almacenamiento para los datos procesados y del cálculo de embeddings, que ocurre en el siguiente paso. Si usas modelos de embedding locales, no hay costos de API.
La seguridad es particularmente importante en la preparación de documentos cuando trabajas con documentos sensibles. Como todo se ejecuta localmente en tu máquina, tus datos permanecen contigo. Este es uno de los mayores beneficios de la IA local. Aún así, asegúrate de que los archivos intermedios procesados no queden sin protección, especialmente si almacenas metadatos con datos personales.
Enlaces y recursos adicionales sobre preparación de documentos
- RAG Local - Descripción general de todos los artículos sobre RAG
- Fundamentos de RAG - Cómo funciona RAG
- Chunking - Dividir textos correctamente
- PDF y OCR - Procesar PDFs escaneados
- Modelos de embedding - Generar vectores a partir de texto
- Citas de fuentes - Hacer respuestas trazables
- Ollama - Ejecutar modelos locales
FAQ: Preparación de documentos, preguntas frecuentes
¿Debo limpiar manualmente cada documento?
No, automatizas la limpieza con scripts. Una vez escrito, se ejecutan sobre todos los documentos. Solo verificas muestras para controlar la calidad.
¿Qué formato es mejor para RAG?
Markdown es el mejor formato porque combina estructura y simplicidad. Si creas documentos tú mismo, usa Markdown. Con documentos existentes, conviértelos a Markdown como formato intermedio.
¿Necesito OCR para todos los PDFs?
No, solo para PDFs escaneados que contengan texto como imagen. Los PDFs digitales exportados desde programas de procesamiento de texto contienen texto seleccionable y no necesitan OCR.
¿Cuál debería ser el tamaño de mis chunks?
Una buena directriz es 200 a 800 palabras por chunk. Chunks demasiado pequeños pierden contexto, chunks demasiado grandes pierden enfoque. Prueba diferentes tamaños con preguntas reales y compara resultados.
¿Qué hago con las tablas en documentos?
Las tablas no funcionan bien como texto corrido. Puedes mantenerlas como tablas Markdown o almacenarlas como metadatos estructurados. Algunos sistemas RAG pueden manejar datos estructurados, otros no. Prueba qué funciona mejor para tu caso de uso.
¿Cómo manejo múltiples idiomas?
Si tus documentos son multilingües, usa un modelo de embedding que soporte varios idiomas. Limpia los textos de manera específica para cada idioma, por ejemplo con patrones diferentes para encabezados y pies de página. También puedes usar detección de idioma y separar documentos por idioma.
¿Puedo actualizar documentos después?
Sí, y deberías hacerlo. Cuando un documento cambia, elimina los chunks antiguos de la base de datos de vectores y carga los nuevos. Los metadatos te ayudan a encontrar y reemplazar versiones antiguas de manera específica.
¿Cuántos documentos necesito para un sistema RAG funcional?
Depende del caso de uso. Para documentación específica, entre 10 y 50 documentos es suficiente. Para una base de conocimiento amplia, necesitas más. Lo más importante es la calidad de los datos procesados, no la cantidad.
¿Cuál es la diferencia entre limpieza y normalización?
La limpieza elimina contenido no deseado como encabezados y boilerplate. La normalización uniformiza el formato, por ejemplo codificación y espacios en blanco. Ambos pasos son necesarios y se complementan.
¿Necesito conocimientos de programación para la preparación de documentos?
Para pipelines automatizadas sí, necesitas conocimientos básicos de Python. Para pequeños volúmenes puedes limpiar documentos manualmente, pero eso no escala. Los ejemplos de código en este artículo están escritos de manera que puedas entenderlos incluso como principiante.
Fuentes y lecturas adicionales
- Documentación de PyPDF2: github.com/py-pdf/pypdf
- Documentación de python-docx: python-docx.readthedocs.io
- Documentación de BeautifulSoup: crummy.com/software/BeautifulSoup
- Documentación de LangChain sobre procesamiento de documentos: python.langchain.com
- Librería Unstructured: github.com/Unstructured-IO/unstructured
- Artículo sobre Fundamentos de RAG en esta serie
- Artículo sobre Chunking en esta serie


