Docling para RAG
Del documento a chunks listos para retrieval: qué chunker elegir, cómo dimensionar con el tokenizer correcto, cómo sobreviven tablas y citas, qué integración encaja y qué vector store emparejar — verificado contra los conceptos oficiales de chunking y la referencia de serialización.
1. ¿Qué vía RAG te encaja?
| Situación | Vía | Por qué |
|---|---|---|
| Ya usas LangChain | langchain-docling DoclingLoader | Loader oficial; modo DOC_CHUNKS trocea nativo, modo MARKDOWN + splitter de cabeceras como fallback. |
| Ya usas LlamaIndex | DoclingReader + Docling Node Parser | El Reader carga JSON sin pérdida o Markdown con pérdida; el parser los vuelve Nodes. |
| Ya usas Haystack | Conversor docling-haystack | Docling como componente conversor de Haystack. |
| Sin framework / store propio | HybridChunker directo | Control total: trocea, contextualiza, incrusta y sube donde sea (Qdrant, Milvus, Chroma, Pinecone…). |
| Necesitas ficheros de chunks ya | CLI --to chunks | Sin Python: chunks híbridos o jerárquicos desde el terminal. |
| Ingesta masiva a escala | Data Prep Kit + Docling | Pipelines de chunk + tokenize para grandes corpus. |
2. Dónde encaja Docling en RAG
Un pipeline RAG tiene seis etapas; Docling posee las tres primeras — las que deciden la calidad antes de que exista ningún embedding. Dos filosofías: exportar a Markdown y redividir (p. ej. MarkdownHeaderTextSplitter de LangChain), o trocear nativo sobre el DoclingDocument. Lo nativo preserva tablas, cabeceras y procedencia que el redividido Markdown pierde en silencio: prefiérelo salvo motivo.
- Parseo — layout, orden de lectura, tablas, fórmulas, imágenes → un DoclingDocument estructurado.
- Serialización / chunking — chunks con estructura, cabeceras, captions y procedencia (o exportación Markdown/HTML/JSON para redividir).
- Enriquecer texto —
contextualize()antepone contexto de cabeceras para que cada chunk valga solo. - Embeddings → store → retrieval → generación — tu modelo, tu store y tu framework; agnósticos a Docling.
3. Conversión: conserva el DoclingDocument
Para un pipeline programático, convierte en Python y conserva el objeto documento — los chunkers operan sobre él, no sobre ficheros:
docling convert report.pdf --to mdfrom docling.document_converter import DocumentConverter
converter = DocumentConverter()
result = converter.convert("report.pdf")
document = result.document
4. Elige exportación: Markdown, JSON, HTML, DocTags
Las exportaciones son atajos sobre serializadores (MarkdownDocSerializer etc.). Para RAG la elección importa sobre todo en tablas. Si las celdas combinadas de cabecera deciden respuestas, prefiere HTML o JSON a Markdown — o mantén Markdown pero repite cabeceras al trocear (sección 7). Los serializadores se personalizan: subclasifica BaseTableSerializer y afines para control total (ver ejemplo avanzado de chunking y serialización).
| Exportación | Spans de tabla | Uso RAG |
|---|---|---|
| export_to_markdown() | Aplanados — sin sintaxis de spans; celdas combinadas salen vacías | Vía de texto por defecto; bien salvo que importen cabeceras combinadas. |
| export_to_html() | Preservados — rowspan/colspan nativos | Mejor exportación de texto cuando las celdas combinadas importan. |
| export_to_dict() / JSON | Preservados sin pérdida — TableData completo con spans | Vía sin pérdida (modo JSON del Reader LlamaIndex); payloads más pesados. |
| DocTags / Docling Language | Preservados — tokens de continuación OTSL | Formato compacto que preserva estructura para tooling nativo Docling. |
5. Comparativa de chunkers
Todos los chunkers nativos implementan BaseChunker (chunk() → flujo de chunks, contextualize() → texto enriquecido), así que integraciones estilo LlamaIndex aceptan cualquier chunker propio, personalizado o de terceros por la misma interfaz.
| Chunker | Estrategia | Cuándo |
|---|---|---|
| HybridChunker (defecto) | Estructura jerárquica + división consciente de tokens si sobra + fusión si falta | Defecto para RAG. Equilibrado, consciente de cabeceras y tablas. |
| HierarchicalChunker | Un chunk por elemento; fusiona ítems de lista (opt-out con merge_list_items) | Granularidad fina por elemento, con metadatos completos. |
| LineBasedTokenChunker | Respeta líneas; solo parte una línea si ella sola supera el límite | Tablas, código, logs, listas: todo lo orientado a líneas. |
| TrivialChunker | Troceado mínimo de base | Depuración y benchmarks, no retrieval en producción. |
| Redivisión Markdown | Exporta a Markdown y redivide (p. ej. MarkdownHeaderTextSplitter) | Solo cuando un componente downstream exige entrada Markdown. |
6. HybridChunker a fondo (por defecto)
Cómo funciona: parte de chunks jerárquicos; una pasada solo divide los sobredimensionados (consciente de tokens) y otra solo fusiona peers infradimensionados sucesivos con mismas cabeceras y captions. Parámetros clave:
Incrusta el texto contextualizado, no chunk.text:
Dos notas operativas: el aviso transformers «Token indices sequence length …» al trocear es una falsa alarma documentada (ver FAQ oficial), y define TOKENIZERS_PARALLELISM=false para silenciar avisos de fork en servidores.
tokenizer— alinéalo al tokenizer de tu modelo de embeddings (sección 9). Por defecto deriva límites del tokenizer.max_tokens— tope por chunk; debe caber la forma enriquecida (contextualizada).merge_peers=True— fusiona peers pequeños; pon False para divisiones estrictas.repeat_table_header=True— cada chunk de tabla empieza con la fila de cabecera (sección 7).omit_header_on_overflow=False— omite la cabecera en filas que caben sin ella pero desbordan con ella (tablas anchas, presupuestos estrictos).serializer_provider— p. ej. tablas Markdown víaChunkingSerializerProviderpara controlar la forma del texto.
from docling.chunking import HybridChunker
chunker = HybridChunker()
chunks = list(chunker.chunk(document))
print(len(chunks), chunks[0].text[:120])
for chunk in chunker.chunk(document):
enriched = chunker.contextualize(chunk) # cabeceras antepuestas — INCRUSTA ESTO
vector = embed(enriched)
7. Tablas en RAG: cabeceras, spans, formatos
Demos resueltas: chunking híbrido (incl. repetición en CSV ancho), chunking por líneas, extracción de tablas.
docling convert data.csv --to md- Repite cabeceras (
repeat_table_header=True, defecto): cada chunk de una tabla dividida empieza con la fila de cabecera, así cada chunk se autodescribe ante el modelo. - Escape de desborde (
omit_header_on_overflow=True): en tablas anchas, las filas que caben sin cabecera pero desbordan con ella la omiten: eficiencia sin romper filas. - Celdas combinadas: Markdown aplana spans; si importan, trocea desde serialización HTML/JSON o un serializador de tablas propio.
- Alternativa por líneas:
LineBasedTokenChunkercon prefijo repetido mantiene filas tipo CSV intactas, con la misma lógica de desborde víaomit_prefix_on_overflow.
8. Metadatos y citas (DocMeta)
Cada chunk trae dl_meta: consérvalo en el payload vectorial; convierte «una respuesta» en «una respuesta con fuente». La procedencia incluye páginas y bounding boxes por ítem: basta para citas «página 3» o resaltado de la región (ver visual grounding). Nunca recortes metadatos por «ahorrar»: sin ellos, citar es imposible.
| Campo | Contiene | Úsalo para |
|---|---|---|
| headings | Ruta de cabeceras, p. ej. ["3.2 AI models"] | Etiquetas de sección, prefijos de contexto, filtros. |
| origin | Mimetype, nombre, hash binario | Identidad del documento, dedup, enlaces fuente. |
| doc_items | Self refs, etiquetas, procedencia: page_no, bbox, charspan | Citas de página, resaltado bbox, visual grounding. |
9. Tokenizers: iguala tu modelo de embeddings
La regla es absoluta: mide los chunks con el tokenizer de tu modelo de embeddings. Tokenizers dispares dimensionan mal en silencio y rompen decisiones de fusión/división. A gran escala, el pipeline chunk+tokenize de Data Prep Kit aplica lo mismo en batch.
| Tokenizer | Instalación | Notas |
|---|---|---|
HuggingFace (HuggingFaceTokenizer) | pip install "docling-core[chunking]" | Vía por defecto. max_tokens opcional: derivado del tokenizer. Ejemplo: sentence-transformers/all-MiniLM-L6-v2 (también defecto CLI). |
OpenAI / tiktoken (OpenAITokenizer) | pip install "docling-core[chunking-openai]" | Requiere max_tokens explícito (ventana de contexto, p. ej. 128 * 1024 para gpt-4o). |
from transformers import AutoTokenizer
from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer
from docling.chunking import HybridChunker
EMBED_MODEL_ID = "sentence-transformers/all-MiniLM-L6-v2"
tokenizer = HuggingFaceTokenizer(
tokenizer=AutoTokenizer.from_pretrained(EMBED_MODEL_ID),
max_tokens=512,
)
chunker = HybridChunker(tokenizer=tokenizer, merge_peers=True)
10. Integraciones con frameworks
LangChain — langchain-docling: Dos modos: DOC_CHUNKS (defecto: un Document LangChain por chunk nativo, metadatos intactos) y MARKDOWN (un Document por fichero; divide después, p. ej. con MarkdownHeaderTextSplitter en #/##/###). Flujo completo en el ejemplo oficial RAG LangChain (Milvus + Mixtral) y la guía LangChain.
LlamaIndex — Reader + Node Parser: DoclingReader puebla Documents LlamaIndex (JSON sin pérdida o Markdown con pérdida); el Docling Node Parser los convierte en Nodes conociendo el formato. Cualquier BaseChunker encaja en la misma interfaz. Ver ejemplo oficial RAG LlamaIndex.
Haystack y resto: Haystack trae Docling como componente conversor (ejemplo, docs de integración). Más allá de los tres grandes: txtai, Kotaemon, DocETL, Vectara, Semantica, Hector, haiku.rag, Bee, CrewAI, Langflow, Open WebUI, spaCy, NVIDIA, RAG del cookbook Granite y Data Prep Kit integran Docling: explora el índice oficial de integraciones.
pip install langchain-docling langchain-huggingface langchain_milvuspip install llama-index-readers-docling llama-index-node-parser-doclingpip install docling-haystackfrom transformers import AutoTokenizer
from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer
from docling.chunking import HybridChunker
from langchain_docling import DoclingLoader
from langchain_docling.loader import ExportType
tokenizer = HuggingFaceTokenizer(
tokenizer=AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")
)
loader = DoclingLoader(
file_path=["https://arxiv.org/pdf/2408.09869"],
export_type=ExportType.DOC_CHUNKS, # chunks nativos (defecto)
chunker=HybridChunker(tokenizer=tokenizer),
)
docs = loader.load() # un Document LangChain por chunk, dl_meta en metadata
from llama_index.readers.docling import DoclingReader
reader = DoclingReader(export_type="json") # sin pérdida; "markdown" con pérdida
documents = reader.load_data("report.pdf")
11. Vector stores: ejemplos resueltos
El store nunca afecta a Docling: chunks + embeddings caben en cualquiera. Ejemplos oficiales para:
| Store | Ejemplo | Notas |
|---|---|---|
| Milvus | RAG con Milvus | También el store del ejemplo LangChain (docling.db local, índice FLAT). |
| Weaviate | RAG con Weaviate | Búsqueda vectorial + híbrida nativas. |
| Qdrant | Retrieval con Qdrant | Receta enfocada a retrieval. |
| OpenSearch | RAG con OpenSearch | Búsqueda + vector en un motor. |
| MongoDB + VoyageAI | RAG con MongoDB | Atlas Vector Search + embeddings VoyageAI. |
| Azure AI Search | RAG con Azure AI Search | Retrieval gestionado en Azure. |
| Chroma / Pinecone / otros | — | Sin receta oficial; mismo patrón: incrusta contextualize(chunk), sube texto + DocMeta. |
12. Chunks desde el CLI (sin Python)
--chunks-type acepta hybrid (defecto) o hierarchical; el tokenizer por defecto es sentence-transformers/all-MiniLM-L6-v2. La conversión remota (docling convert-remote / docling-serve) soporta las mismas opciones en servidor. Ver el buscador de comandos.
docling convert report.pdf --to chunks --chunks-type hybriddocling convert report.pdf --to chunks --chunks-type hybrid --chunks-max-tokens 512 --chunks-tokenizer sentence-transformers/all-MiniLM-L6-v213. Checklist de ingesta en producción
- Tokenizer == modelo de embeddings. Siempre. Revísalo en cada cambio de modelo.
- Incrusta la salida de
contextualize(); guardachunk.textaparte para mostrar. - Conserva DocMeta (headings, page_no, bbox, origin) en el payload para citas.
- Repite cabeceras de tabla; para tablas con celdas combinadas usa fuente HTML/JSON.
- Recorta coste de conversión:
--no-ocren PDF digitales, omite enrichments que no uses, convierte en batch y pre-descarga modelos (docling-tools models download --all). - Escala: chunk+tokenize de Data Prep Kit para corpus;
/v1/convert/sourcede docling-serve con--to chunkstras una cola para servicios. - PII primero: detecta y ofusca PII antes de incrustar (ver ejemplo PII): una fuga no se puede des-incrustar.
14. Solución de problemas de ingesta RAG
- Respuestas sin contexto / chunks «huérfanos» — incrustaste
chunk.text; cambia acontextualize()y confirma cabeceras. - Tablas mal respondidas — activa
repeat_table_header; revisa celdas combinadas (Markdown aplana spans → usa HTML/JSON). - Aviso transformers de longitud — falsa alarma documentada, ignorable (FAQ oficial).
- Chunks grandes/pequeños para el modelo — desajuste de tokenizers; fija el del modelo y ajusta
max_tokens. - Sin citas posibles — metadatos perdidos al subir; persiste
dl_meta(page_no, headings, origin). - Ingesta lenta — ver conversión lenta: apaga OCR/enrichments sobrantes, usa GPU, batch.
15. FAQ de RAG
¿Con qué chunker empiezo?
¿chunk.text o contextualize() — qué incrusto?
chunker.contextualize(chunk) para embeddings. Antepone la ruta de cabeceras y vuelve el chunk autocontenido. Guarda chunk.text para mostrar.¿Necesito LangChain / LlamaIndex / Haystack?
¿Exportación Markdown + splitter o chunking nativo?
¿Cómo sobrevive la estructura de tablas?
repeat_table_header), cuida el desborde (omit_header_on_overflow) y fuentea tablas desde HTML/JSON si importan celdas combinadas. LineBasedTokenChunker es el especialista en datos por filas.¿Cómo citan páginas las respuestas?
dl_meta de cada chunk (headings, page_no, bbox, origin) en el payload y devuélvelo con los hits. La procedencia sostiene citas hasta resaltado bbox.¿Qué vector store funciona con Docling?
¿Cómo escalo la ingesta?
Verificado con Docling v2.129.0 · Última comprobación 2026-09-22 · Fuente oficial