Para RAG, convierte los PDF a Markdown cuando quieras fragmentos que sigan las secciones del documento: cada título se convierte en un punto de corte, y los títulos que hay por encima de un fragmento viajan con él como metadatos. Revisa los títulos, las tablas y el texto OCR antes de indexar y luego prueba la recuperación con preguntas cuyas respuestas ya conoces.

Esta guía es para desarrolladores que crean búsquedas sobre documentos privados: políticas, manuales, contratos, investigación. Da por hecho que ya tienes en mente un modelo de embeddings y una base de datos vectorial.

Pilas de fichas rayadas y tiras de papel junto a un portátil, una de ellas atada con una cinta lila.
Fragmentos que siguen las secciones del documento.

Por qué los títulos dan mejores fragmentos

Un divisor de tamaño fijo corta el texto en trozos de una longitud dada, caiga donde caiga el corte. Un fragmento puede empezar en mitad de una sección y perder el título que dice de qué trata. «Las solicitudes están limitadas a 100 por minuto» significa una cosa bajo «Sandbox» y otra bajo «Production». Sin el título, ni el buscador ni el modelo pueden saber cuál.

Markdown conserva el esquema del documento en líneas que empiezan por #, ## y ###. Un divisor por títulos corta en esas líneas, de modo que cada fragmento abarca una sección, y registra los títulos que hay por encima del fragmento: su ruta de sección. La guía de LangChain para dividir Markdown describe la misma idea: mantener el texto junto a los títulos a los que pertenece.

Las secciones tienen longitudes distintas, así que una sección larga sigue necesitando una segunda división por tamaño. La ruta de sección se aplica a cada uno de sus trozos.

Convierte los PDF y revisa los títulos

En MD Factory, añade la carpeta con tus PDF. Cada archivo Markdown se guarda junto a su PDF con el mismo nombre, con las imágenes en una carpeta al lado. Los PDF escaneados pasan por OCR en tu PC. Los archivos de Word y otros formatos de la carpeta se convierten igual.

Abre algunos resultados antes de escribir código de división:

  • Comprueba que los capítulos, secciones y subsecciones salen como #, ## y ###. Un PDF suele marcar un título solo con una fuente más grande o más gruesa, así que cualquier convertidor tiene que deducir dónde empiezan las secciones y cómo se anidan. Corrige los niveles que estén mal.
  • En los archivos de Word, los títulos se conservan si el autor usó los estilos de título de Word. El texto que solo está en negrita o en grande queda como un párrafo normal.
  • Busca encabezados, pies de página y números de página en mitad del texto.
  • Haz clic en Necesita revisión junto a un archivo para ver qué hay que revisar. En un PDF, la lista puede incluir páginas en las que el OCR no pudo leer parte del texto o no encontró texto.

Corrige los problemas en el archivo Markdown antes de indexarlo. Cada fragmento los hereda.

Divide por títulos en el código

MarkdownHeaderTextSplitter de LangChain divide por los niveles de título que indiques y guarda cada título en los metadatos del fragmento. Un segundo divisor limita el tamaño dentro de cada sección:

from pathlib import Path
from langchain_text_splitters import (
    MarkdownHeaderTextSplitter,
    RecursiveCharacterTextSplitter,
)

corpus = Path("corpus")
by_heading = MarkdownHeaderTextSplitter(
    [("#", "h1"), ("##", "h2"), ("###", "h3")], strip_headers=False
)
by_size = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=150)

chunks = []
for path in sorted(corpus.rglob("*.md")):
    sections = by_heading.split_text(path.read_text(encoding="utf-8"))
    for chunk in by_size.split_documents(sections):
        chunk.metadata["source"] = path.relative_to(corpus).as_posix()
        chunk.metadata["section"] = " > ".join(
            chunk.metadata[h] for h in ("h1", "h2", "h3") if h in chunk.metadata
        )
        chunks.append(chunk)

El tamaño de fragmento y el solapamiento de este ejemplo son solo un punto de partida; ajústalos a tu modelo de embeddings. Para que las líneas de título se queden en el texto del fragmento, y así el texto que conviertes en embeddings también nombre su sección, el ejemplo usa strip_headers=False.

MarkdownNodeParser de LlamaIndex hace la misma división por títulos. Si cargas los archivos con SimpleDirectoryReader, cada nodo lleva además el nombre del archivo:

from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import MarkdownNodeParser

documents = SimpleDirectoryReader(
    "corpus", recursive=True, required_exts=[".md"]
).load_data()
nodes = MarkdownNodeParser().get_nodes_from_documents(documents)
print(nodes[0].metadata["file_name"], nodes[0].metadata["header_path"])

header_path enumera los títulos que hay por encima de un nodo, por ejemplo /Employee handbook/Expenses/; el título propio del nodo es la primera línea de su texto.

Guarda la fuente con cada fragmento

Guarda al menos tres cosas con cada fragmento:

  • El archivo. Un archivo Markdown se llama igual que su PDF, así que un fragmento de handbook.md lleva de vuelta a handbook.pdf. Guarda la ruta relativa a la carpeta del corpus, porque dos archivos llamados terms.md en carpetas distintas son documentos distintos.
  • La ruta de sección, para que una respuesta pueda nombrar la sección que usó y un lector vaya directo al pasaje.
  • La edición o la fecha. Cuando una política cambia, borras los fragmentos del archivo antiguo e indexas los nuevos, en lugar de servir ambas versiones.

Si personas con distintos permisos de acceso van a consultar el mismo índice, guarda también quién puede leer cada documento y filtra por ello al consultar. Un buscador devuelve cualquier fragmento que coincida con la pregunta.

El Markdown que genera MD Factory no tiene números de página. Si las respuestas deben citar páginas, busca el pasaje en el PDF original.

Trata con cuidado las tablas y el texto OCR

Una tabla Markdown tiene su fila de encabezado una sola vez, arriba. Cuando una tabla es más larga que tu tamaño de fragmento, el divisor por tamaño la corta y los trozos siguientes llegan sin nombres de columna. En nuestra prueba con LangChain (langchain-text-splitters 1.1.3), una tabla de 40 filas dividida en fragmentos de 300 caracteres dio cuatro trozos, y solo el primero contenía la fila de encabezado.

Hay dos formas de arreglarlo: mantener cada tabla en un solo fragmento aunque sea más largo que los demás, o repetir la fila de encabezado al principio de cada trozo.

En archivos de Word y Excel, MD Factory marca un documento como Necesita revisión cuando se simplificaron celdas combinadas o complejas. Las tablas de PDF no reciben esa marca, así que compara algunas con el original, sobre todo las que tienen encabezados combinados.

El texto OCR necesita su propia revisión. Los números, nombres y códigos mal reconocidos pasan tal cual a las respuestas, y nada en el fragmento parece incorrecto. Compara las cifras de algunos fragmentos con el escaneo y deja fuera del índice las páginas marcadas hasta corregirlas. Qué revisar se explica en nuestra guía para revisar el OCR de PDF escaneados.

Prueba la recuperación con preguntas reales

Apunta preguntas que la gente vaya a hacer de verdad y, para cada una, el archivo y la sección que contienen la respuesta. Empieza con una o dos docenas.

  1. Pasa cada pregunta por tu buscador y mira los primeros resultados.
  2. Anota si la sección correcta está entre ellos.
  3. En cada fallo, compara los fragmentos devueltos con el que debería haber salido. Causas típicas: un nivel de título incorrecto, una tabla separada de su encabezado, un error de OCR en el término clave o una sección tan larga que la respuesta queda enterrada.
  4. Cambia una sola cosa, como el tamaño de fragmento, los niveles de título o la forma de dividir las tablas, y vuelve a pasar las mismas preguntas.

Guarda las preguntas en un archivo junto al corpus y vuelve a pasarlas cada vez que cambien los documentos o el código de división.

Cuándo usar MD Factory y cuándo una biblioteca

MD Factory es una aplicación de escritorio para Windows. Sirve para una colección privada que quieres convertir en tu propio PC y revisar a ojo: añade una carpeta, mira los estados, abre los archivos que necesitan revisión, corrígelos y luego apunta tu script de indexación a la carpeta.

MD Factory mantiene los documentos en tu PC mientras los convierte. Si después generas embeddings de los fragmentos con una API alojada o los envías a un modelo alojado, su texto va a ese proveedor. Usa un modelo local si los documentos no deben salir de tu red.

Si los documentos llegan constantemente, están en un servidor o deben convertirse sin que nadie los supervise, usa en su lugar una biblioteca de conversión en tu código. Para ver opciones, consulta nuestra comparativa de conversores de PDF a Markdown.