Skip to content

Ingestion DSL

A partir de la v3.5.0 tambien la ingestion documental se ha vuelto canvas-native. La pipeline que lleva un PDF subido por el usuario hasta los chunks vectorizados en Qdrant ya no es un servicio TypeScript imperativo: es un canvas con purpose = INGESTION ejecutado por el motor DSL.

Esto habilita personalizaciones avanzadas por tenant, ingestion role-aware y una migracion gradual desde las pipelines legacy.

Que cambia

AspectoLEGACY (v3.1.x)DSL V2 (v3.5.0+)
ParserPipeline PDF unicaAuto-router MIME, Docling + Unstructured como pares
Chunking14 chunkers domain-specific en codigo5 chunkers por rol + presets de dominio configurables
Roles documentoNo modeladosTRUTH / FORMAT / RULES / OPERATIONAL / EXAMPLES
CleanupRe-embedding manualRecordManager con 4 modos (UPSERT, FULL, INCREMENTAL, SCOPED)
ResumeNoPipeline snapshot + checkpoint
Multi-sinkSolo Qdrant + PostgresQdrant + Postgres + OperationalData + RecordManager como nodos
ConectoresSolo upload + scrapeV2-ready para email, cloud storage, SharePoint

Roles documento

Cada documento ingestado se clasifica en uno o mas roles, que determinan como lo trata la pipeline:

RolEjemplos tipicosTratamiento
TRUTHManuales, normativas, politicas, librosChunking semantico por parrafos/articulos
FORMATPlantillas, formularios, esquemasExtraccion de estructura + placeholders
RULESSentencias, reglamentos, decisionesChunking por articulo/clausula con citas
OPERATIONALListas de precios, tablas, datos estructuradosExtraccion por fila, sink OperationalData
EXAMPLESCasos de estudio, escenarios, FAQsChunking por par Q&A o bloque-escenario

La clasificacion es automatica (nodo role_classifier) pero puede forzarla el admin en la subida o mediante override por topic. Un documento puede tener varios roles (ej. una sentencia es TRUTH + RULES).

TRUTH

Contenido autoritativo, narrativo, de referencia. Es el rol "default" para la mayoria de los documentos empresariales.

  • Ejemplos: manuales operativos, normativas interpretadas, politicas empresariales, white paper, libros.
  • Chunker: chunker_truth -- segmentacion semantica por parrafos y secciones, alineamiento con los limites naturales, overlap 15%.
  • Metadatos extraidos: titulo, secciones, idioma, nivel de confianza, eventual resumen.
  • En retrieval: peso base alto, citado como [N] con preview del parrafo original.

FORMAT

Documentos que muestran una estructura a reproducir mas que contener informacion autoritativa.

  • Ejemplos: templates de contratos, formularios vacios, esquemas, layouts de documentos estandar.
  • Chunker: chunker_format -- extrae la estructura (campos, secciones, placeholders) y la indexa por separado del "texto de relleno".
  • Metadatos extraidos: lista de campos/placeholders, tipologia de documento, idioma del template.
  • En retrieval: usados principalmente por Generacion Documentos para poblar nuevos documentos. Citados raramente en la chat (a menos que la pregunta sea explicitamente "como se rellena el formulario X?").

RULES

Documentos prescriptivos: establecen lo que se puede o no se puede hacer, definen obligaciones, sanciones, vencimientos, regulaciones aplicables. El sistema los trata de forma especial porque la precision de la referencia normativa es critica.

  • Ejemplos: sentencias (Tribunal Supremo, Tribunales de apelacion, tribunales administrativos), decretos, regulaciones UE, articulos de ley, ordenes, decisiones administrativas, circulares de la Agencia Tributaria, regulaciones internas empresariales con efecto vinculante.
  • Chunker: chunker_rules -- reconoce la estructura articulo - apartado - letra - numero tipica de la legislacion italiana y de las regulaciones UE. Mantiene cada articulo como unidad coherente, preserva las referencias cruzadas (ej. "conforme al art. 12 ap. 3 letra b") e indexa por separado maximas y dispositivos en las sentencias.
  • Tipo de chunk asignado: ARTICLE, CLAUSE, SECTION, MASSIMA, DISPOSITIVO -- granularidad mas fina respecto a TRUTH.
  • Metadatos extraidos:
    • documentType -- ej. SENTENZA, DECRETO_LEGISLATIVO, REGOLAMENTO_UE, CIRCOLARE_AE.
    • articleNumber, paragraphNumber, letter -- identificadores estructurados para citacion precisa.
    • dateInForce, dateRepealed -- vigencia temporal (si se detecta).
    • issuingAuthority -- ej. "Agencia Tributaria", "Tribunal Supremo Seccion I", "Parlamento UE".
    • citedNorms[] -- otras normas citadas en el texto (enlace a chunks RULES correlacionados).
  • En retrieval:
    • Prioridad explicita respecto a TRUTH cuando la pregunta concierne obligaciones, sanciones, conformidad.
    • Citacion precisa: la respuesta cita art. 5 ap. 2 DLgs 231/2001 no solo el titulo del documento.
    • Filtro automatico de vigencia: reglas con dateRepealed < hoy se excluyen por defecto (el filtro puede desactivarse para busquedas historicas).
    • Compatibilidad con fuentes externas: una norma italiana ingestada internamente puede enlazarse a la version en Legal Sources (Normattiva).
  • Confidentiality: tipicamente public o internal. Las sentencias internas (ej. arbitrajes) pueden ser confidential.

Ejemplo practico

Pregunta: "Puedo despedir a un empleado en baja por enfermedad?" -> El sistema prefiere documentos RULES (Estatuto de los Trabajadores art. 2110, sentencias Cass. Sec. Lab. correlacionadas) respecto a una circular HR interna marcada TRUTH. La respuesta cita art. 2110 c.c. y Cass. n. 12345/2023 con dispositivo textual, no un resumen.

Multi-rol TRUTH + RULES

Una sentencia tiene dos aspectos: la motivacion (TRUTH -- razonamiento juridico, contexto) y el dispositivo + maxima (RULES -- regla establecida). El classifier asigna ambos roles y los dos chunkers producen chunks distintos que coexisten en el mismo documento.

OPERATIONAL

Documentos tabulares o estructurados con datos que tienen sentido "por fila" mas que "por texto libre".

  • Ejemplos: listas de precios, datos maestros proveedores/clientes, fichas de producto, balances en formato tabular, conciliaciones, time sheet, KPI dashboard.
  • Chunker: chunker_operational -- una fila = un chunk. Mantiene la clave-valor de la fila (ej. producto=Alpha, precio=120.00, disponibilidad=en stock).
  • Sink dedicado: sink_operational escribe las filas en OperationalDataRow (Postgres) habilitando consultas estructuradas (filtra, suma, agrupa) junto al retrieval textual.
  • En retrieval: el planner reconoce intents agregativos ("cual es la facturacion total?", "cuantos productos bajo 100 euros?") y los enruta hacia una query SQL sobre los datos operational en lugar del LLM.

EXAMPLES

Documentos demostrativos que ilustran como aplicar un concepto, un procedimiento, una regla.

  • Ejemplos: casos de estudio, escenarios de aplicacion, FAQ empresariales, ejercicios resueltos, knowledge base de soporte.
  • Chunker: chunker_examples -- un par Q&A o un escenario completo = un chunk. Preserva la integridad del caso ejemplo individual.
  • En retrieval: usados para enriquecer la respuesta con un ejemplo concreto cuando la pregunta lo permite (ej. "tengo un caso similar a..."). Citados con sourceType dedicado.

Colecciones (Document Groups / "Raccolte")

Las Colecciones (DocumentGroup, "Raccolte" en la UI italiana) son conjuntos nombrados de documentos que comparten un proposito comun de corto-medio plazo -- un expediente, un proyecto, un cliente, un dossier, una auditoria, una migracion. Estan disenadas para organizar la ingestion y la consulta de forma transversal a los Topics, sin necesidad de crear nuevos Topics para cada iniciativa.

Diferencia con los Topics

AspectoTopicColeccion
PropositoTaxonomia estable (ej. "Contratos", "HR", "Legal")Agregaciones temporales o de trabajo (ej. "Expediente Rossi 2026", "Auditoria Q1")
Cardinalidad tipica3-15 por empresaDecenas o cientos, pueden nacer y morir
Vida utilLarga, raramente eliminadaAbierta -> Cerrada -> archivada
PermisosRBAC granular por rolHereda ACL de los documentos contenidos
Personalizacion IAPrompt de sistema dedicadoSin prompt dedicado
Identificada en chatSelector Topic arribaFiltro/mention @coleccion en el campo input
Modelo DBTopic (con ragPresetId, systemPrompt)DocumentGroup (con topicId, status)

En la practica: un documento pertenece a uno o mas Topics (categoria estable) y opcionalmente a una o mas Colecciones (agregacion de trabajo). Topic responde a "que tipo de documento es?", Coleccion responde a "para que expediente/proyecto fue subido?".

Estado de la Coleccion

Una Coleccion tiene un campo status con dos valores:

StatusSignificadoEfecto en la chat
OPENTrabajo activoIncluida en sugerencias @coleccion, retrieval prioritario cuando filtrada
CLOSEDTrabajo cerrado, archivoExcluida de las sugerencias, accesible solo si se invoca explicitamente

Cerrar una Coleccion no elimina los documentos: los deja indexados y consultables, pero salen del "working set" del usuario.

Pagina Colecciones (/raccolte)

El admin encuentra en Ingestion > Colecciones una pagina CRUD dedicada:

  • Tabla de Colecciones: nombre, Topic asociado, numero de documentos, status, owner, ultima modificacion, acciones.
  • Toolbar: search por nombre, filtro por empresa (solo SYSTEM_ADMIN), boton + Nueva coleccion.
  • Detail Sheet (al hacer clic en una fila): header con metadatos + tabla anidada de los documentos contenidos (filename, topic, status, accion "Eliminar de la coleccion") + footer "Anadir documentos" con multi-select picker.

Acciones del menu (por fila): Abrir, Renombrar, Cambiar status (OPEN/CLOSED), Eliminar.

Cuando asignar un documento a una Coleccion

Tres puntos de entrada:

  1. Wizard Single (paso Confirm) -- combobox "Coleccion" -- existente o + Nueva.
  2. Perfil Bulk (paso 2) -- misma combobox, aplicada a todos los archivos del run; los overrides Path-rules no tocan la Coleccion (es un unico valor por job).
  3. Review Queue post-bulk -- columna inline editable, o bulk-edit bar para asignar muchas filas juntas.

Un documento puede pertenecer a varias Colecciones simultaneamente (rel. muchos-a-muchos). Frecuente para documentos reutilizados (ej. un contrato marco usado tanto en "Expediente Rossi 2026" como en "Expediente Bianchi 2026").

Uso en la chat

El usuario en chat puede:

  • Escribir @coleccion para activar autocompletado de las Colecciones OPEN accesibles: la conversacion se filtra a esos documentos.
  • Combinar Topic + Coleccion: ej. Topic = Contratos, Coleccion = Expediente Rossi 2026 -> retrieval restringido a contratos de ese expediente.
  • Pedir resumenes por Coleccion: ej. "resume el estado del expediente Rossi 2026" -> el sistema construye una sintesis sobre los solos documentos de la Coleccion.

Lifecycle tipico

Apertura expediente
   |
   v
+ Nueva coleccion "Expediente Rossi 2026"
   |
   v
Bulk ingest 200 archivos (cliente + correspondencia + actos)
   |   perfil Bulk -> Coleccion = "Expediente Rossi 2026"
   v
Review Queue: corrijo topic para ~10 archivos
   |
   v
Trabajo activo: chat filtrada @coleccion-rossi
   |
   v
Cierre expediente
   |
   v
status -> CLOSED (historico consultable, fuera del working set)

Endpoints API

MetodoEndpointProposito
GET/api/document-groupsLista Colecciones (filtros por status, search, owner)
POST/api/document-groupsCrea Coleccion
PATCH/api/document-groups/:idRenombra, cambia status, Topic asociado
DELETE/api/document-groups/:idElimina (los documentos quedan, solo pierden el enlace a la Coleccion)
POST/api/document-groups/:id/documentsAnade documentos
DELETE/api/document-groups/:id/documents/:docIdElimina un documento

Feature flag

La sidebar muestra Colecciones solo si el tenant tiene la feature activa:

bash
ENABLE_DOCUMENT_GROUPS=true

Deshabilitando el flag, la API sigue funcionando (los datos existentes no se pierden) pero desaparecen las entradas de navegacion y los campos Coleccion en los wizards / review.

Best practice

  1. Una Coleccion = un expediente / proyecto -- evita usar Colecciones para hacer mas taxonomia (para eso estan los Topics).
  2. Cierra las Colecciones concluidas -- el working set de un usuario activo deberia tener 5-20 Colecciones OPEN, no cientos.
  3. Nombres parlantes -- Expediente Rossi 2026 es mejor que R-2026-01. La chat usa el nombre como hint para el planner.
  4. Las Colecciones no sustituyen a los Topics -- una Coleccion que no esta en un Topic es un huerfano: asigna siempre el Topic apropiado en la fase de ingestion.

Anatomia de una pipeline INGESTION

+--------+    +--------+    +--------------+    +----------+    +----------+
| source | -> | parser | -> |role_classify | -> | chunker  | -> | embedder |
+--------+    +--------+    +--------------+    +----------+    +----------+
                                                                     |
                                                                     v
                                              +---------+--------+-------------+
                                              | qdrant  | postgres| operational |
                                              | chunks  | document| data        |
                                              +---------+--------+-------------+
                                                              |
                                                              v
                                                    +------------------+
                                                    |  record manager  |
                                                    |  (cleanup state) |
                                                    +------------------+

Nodos fuente

  • source_upload -- subida de usuario (form web, drag&drop, API).
  • source_inchat -- documentos adjuntos al chat (temporales).
  • source_scrape -- paginas web desde URL.
  • source_ocr -- imagenes con OCR (GLM-OCR via vLLM 8004).
  • (V2) source_email, source_gdrive, source_sharepoint -- estructura lista, implementacion futura.

Parser

  • parser_auto -- router automatico por MIME type.
  • parser_docling -- para PDF/DOCX/PPTX/XLSX/HTML/imagenes. Extrae secciones, tablas, orden de lectura.
  • parser_unstructured -- para EML/MSG/EPUB/ODT/RTF, audio (en V2 transcripts).

Ambos delegan al microservicio ingestion-bridge (FastAPI Python Docker).

Classifier y chunker

  • role_classifier -- LLM-based, output uno o mas roles + confidence.
  • chunker_truth, chunker_format, chunker_rules, chunker_operational, chunker_examples -- cada uno con presets de dominio (legal, medical, financial, HR, ...).

Metadata extractors

Nodos LLM especializados: extract_dates, extract_entities, extract_summary, extract_topics, extract_grade, extract_confidentiality. Ejecutados en paralelo donde es posible.

Sinks

  • sink_qdrant -- vectoriza con bge-m3 y escribe chunks.
  • sink_metadata -- actualiza Document en Postgres.
  • sink_operational -- para rol OPERATIONAL, escribe filas tabulares en OperationalDataRow.
  • sink_record_manager -- traza hash de contenido e identidad para el cleanup incremental.

Modos de ingestion

Para gestionar migracion y A/B testing, cada empresa tiene un campo Company.ingestionMode:

ModoComportamiento
LEGACYPipeline antigua (sin rol, parser unico). Default para empresas creadas antes de v3.5.0
SHADOWLEGACY en primer plano, V2 en paralelo. Genera reporte de comparacion, sin impacto al usuario
V2Cutover completo al canvas DSL

Migracion recomendada

  1. Configura SHADOW en un tenant piloto.
  2. Verifica por ~1 semana el shadow_comparison_report (precision, recall, latencia).
  3. Pasa a V2 cuando los numeros sean iguales o mejores.
  4. Recuerda: cambiar modo no re-vectoriza los documentos existentes. Para aplicar los nuevos chunkers a los viejos documentos hace falta un reingest explicito.

Cambio de modo via SQL:

sql
UPDATE "Company" SET "ingestionMode" = 'V2' WHERE id = '<companyId>';

O desde UI: Configuracion empresa > Ingestion > Modo.

Wizard, Bulk y Path-rules

El Hub de Ingestion (/documents/upload) es el punto de partida para subir documentos a Queria. Existen dos intent modes, ambos apoyados en la misma pipeline DSL: solo cambia el flujo UX.

Modo Single -- el Wizard IA en 3 pasos

Pensado para 1-10 archivos subidos manualmente. La pagina monta el UploadWizardPage (3 pasos) con clasificacion asistida por IA:

  1. Upload -- drag & drop o seleccion archivos. Fuente: local, cloud, red, URL.
  2. Classify -- para cada archivo el sistema propone una card "rol sugerido" (ej. RULES — score 0.87) con topic, sector y lengua predichos. El usuario confirma o corrige. Multi-rol: pueden seleccionarse varios roles cuando el documento es hibrido (ej. politica + checklist -> TRUTH + RULES).
  3. Confirm -- resumen, eleccion de visibilidad, lanzamiento del ingestion job.

El wizard es el unico modo que muestra explicitamente el score de confianza del classifier antes del lanzamiento. Util cuando los documentos individuales son criticos (legal, sanitario) y la review pre-lanzamiento es aceptable.

Cuando usarlo

De 1 a unos 10 archivos. Las empresas con poco volumen empiezan aqui por defecto. Por encima de 50 archivos el sistema sugiere automaticamente el modo Bulk.

Modo Bulk -- para cloud, red, URL lists

Pensado para ingerir decenas o miles de archivos de una fuente comun. Tres pasos:

  1. Source selection -- elige una de las cuatro cards: File (drag&drop masivo), Cloud (Drive / S3 / Azure / OneDrive), Network (SMB / NFS), URL list (CSV con N urls).
  2. Perfil de ingestion -- formulario unico con los defaults que se aplican a todos los archivos seleccionados:
    • Topic (multi-select, obligatorio si la Visibilidad es "Solo topics asignados").
    • Coleccion (combobox -- existente o "+ Nueva").
    • Document role -- TRUTH / FORMAT / RULES / OPERATIONAL / EXAMPLES / Auto AI (default).
    • Sector -- preset list o Auto AI.
    • Lengua -- auto-detect o ISO.
    • Visibilidad -- Todos / Solo topics asignados / Solo admin.
    • Path-rules (avanzadas, accordion cerrado por defecto -- ver abajo).
  3. Job in progress -- LiveJobProgress via SSE: estado por archivo (queued / parsed / vectorized / failed). En cuanto un archivo se completa puedes entrar ya a Review Queue sin esperar a que termine el job.

Cuando usarlo

Cuando importas desde una fuente entera (ej. toda una carpeta Drive, un mount SMB, un dump JSONL). De 50 archivos en adelante el sistema prefiere este modo.

Path-rules (reglas por path)

Las Path-rules son overrides por archivo definidos dentro del perfil de ingestion bulk. Cada regla es una fila { glob_pattern, override_topic, override_role }.

Ejemplos:

PatternOverride topicOverride role
gdrive:/Legal/**/*.pdfContratosTRUTH
gdrive:/Legal/Sentencias/**/*.pdfJurisprudenciaRULES
smb://share/Listas/**/*.xlsxListasOPERATIONAL
**/FAQ*.mdHelp centerEXAMPLES

Las reglas se evaluan top-down por archivo, en el momento de ejecucion del job. La primera que hace match gana; si ninguna coincide, se aplican los defaults del perfil (incluido Auto AI para topic/role/sector).

Cuando usarlas:

  • Fuentes cloud o de red con una estructura de carpetas que ya codifica la taxonomia empresarial.
  • Migraciones one-shot donde quieres mantener un mapping path -> topic sin corregir archivo por archivo en Review.
  • Fuentes mixtas donde distintos sub-paths deberian terminar en topics/roles distintos.

Best practice:

  • Ordena las reglas de mas especifica a mas generica (gana la primera).
  • Documenta las reglas en el nombre de la Coleccion o en el changelog del topic: quien las lea despues debe entender el porque.
  • Para pocos archivos aislados, no escribas reglas: mejor editar a posteriori en Review Queue (ver abajo).

Review Queue (post-bulk)

Despues de un job bulk, la Review Queue (/documents/upload?mode=bulk&job=<id>) muestra todos los archivos del run en una DataTable:

ColumnaEditable
Checkbox de seleccion--
Filenamepreview al click (Sheet lateral)
Estadosuccess / warning / error
Topicinline (multi-select)
Coleccioninline
Sectorinline
Visibilidadinline
AI confidenceprogress 0-100 (filtrable)

Toolbar rapida:

  • Search-by-filename (debounce 300ms).
  • 3 chip preset: Sector = OTRO, AI confidence < 70%, Topic faltante -- para pescar al instante los archivos criticos.
  • Export CSV de la seleccion.

Bulk-edit bar: aparece cuando seleccionas >= 1 fila. Permite aplicar topic / sector / visibilidad / coleccion a todos los archivos seleccionados en un solo PATCH (/api/documents/bulk-update). UI optimista con toast Sonner de error si falla.

El job de ingestion no espera la review: los archivos fluyen por la pipeline a medida que se suben. La Review Queue sirve para corregir ex-post topic/role/sector de los archivos con baja confidence o aquellos que requirieron fallback. Los cambios en estos metadatos no requieren reingest (son payload Qdrant + Postgres update).

Reingest

Para aplicar cambios de pipeline a documentos ya vectorizados:

  • Documento unico: pagina Documentos > [doc] > Reingest.
  • Masivo: script src/scripts/reingest-company-onebyone.ts (plantilla disponible).
  • Backfill payload (sin reingest, solo metadatos): scripts idempotentes backfill-qdrant-*.ts para estampar nuevos campos (sector, role, grade, aisummary) sin re-embedding.

Estado y monitorizacion

Cada job de ingestion produce:

  • IngestionJob en Postgres con estado (RUNNING, COMPLETED, FAILED, RESUMED).
  • PipelineSnapshot para resume en caso de crash.
  • IngestionRecord (RecordManager) para cleanup determinista.

Dashboard admin: Ingestion > Live dashboard muestra jobs actuales, colas, errores, latencia por nodo.

Componibilidad por tenant

Un admin puede:

  • Anadir extractores custom -- ej. un nodo que extrae el codigo ATECO para documentos empresariales.
  • Deshabilitar classifier -- forzando todo como TRUTH para casos simples.
  • Cambiar embedder -- ej. para multilingue avanzado (V2).
  • Anadir sinks custom -- ej. mirror a un data lake externo.

Todo sin modificar el backend: basta con duplicar el canvas de ingestion y editarlo.

Migracion de presets

Los presets legacy tenian secciones chunking, retrieval, reranker, etc. Hoy conservan solo chunking (chunkSize, overlapPercentage). Todo el resto esta en el canvas. Ver Presets RAG (slim) para detalles del nuevo schema.

Limites V1

  • Parser audio/video deferred a V2.
  • Conectores email/SharePoint/GDrive aun no activos en los canvas (arquitectura lista, implementacion posterior).
  • Graph ingestion (entity extraction + community detection) excluido permanentemente en V1 por costos LLM altos.
  • OCR para escaneos de baja calidad sigue siendo menos preciso que el parser nativo Docling: para archivos masivamente escaneados considerar mejorar el OCR upstream.

Queria v3.5.0 -- Ingestion DSL canvas-native

Queria - Document Intelligence con Cog-RAG