Flujo de Procesamiento de Documentos¶
Descripción¶
Diagrama de secuencia que muestra el flujo completo de procesamiento de un documento legal, desde la carga del PDF hasta la persistencia con detección de duplicados.
Incluye manejo de errores con Railway-Oriented Programming (Result pattern).
Diagrama¶
sequenceDiagram
actor Usuario
participant GUI as Streamlit GUI
participant UC as ProcessDocumentUseCase
participant OCR as OCR Router
participant Tess as Tesseract
participant Paddle as PaddleOCR
participant NER as SpaCy NER
participant PD as Provisional Detector
participant SD as Sections Detector
participant Dedup as Hybrid Ensemble<br/>Detector
participant Repo as SQLite Repository
Usuario->>GUI: Cargar PDF (tutela/habeas corpus)
GUI->>UC: execute(ProcessDocumentRequest)
rect rgb(255, 245, 238)
Note over UC,Repo: Paso 1: Gate Pre-OCR (SHA-256 + file_size)
UC->>UC: Calcular SHA-256 hash binario
UC->>UC: Obtener file_size_bytes
UC->>Repo: find_by_hash_and_size(hash, size)
alt Documento ya existe en BD
Repo-->>UC: Document existente
UC-->>GUI: Success(documento existente)
GUI-->>Usuario: Documento ya procesado previamente
Note over UC: Short-circuit: no OCR/NER
else Documento nuevo
Repo-->>UC: None
Note over UC: Continuar pipeline completo
end
end
rect rgb(240, 248, 255)
Note over UC: Paso 2: Validación
UC->>UC: Validar archivo existe
UC->>UC: Validar formato PDF
alt Archivo inválido
UC-->>GUI: Failure(FILE_NOT_FOUND / INVALID_FORMAT)
GUI-->>Usuario: Mostrar error
end
end
rect rgb(255, 250, 240)
Note over UC,Paddle: Paso 3: Extracción Híbrida (Nativo + OCR)
UC->>OCR: extract_text(file_path)
OCR->>OCR: extract_native_text_per_page()<br/>(PyMuPDF get_text)
alt Todas las páginas tienen texto nativo (≥50 chars)
OCR->>OCR: Usar texto nativo directo<br/>engine=NATIVE, confidence=0.99
Note over OCR: Sin OCR — extracción instantánea
else Páginas mixtas (algunas nativas, algunas escaneadas)
OCR->>OCR: Páginas con texto → usar nativo
Note over OCR: Solo convertir a imagen<br/>las páginas sin texto
alt Calidad Alta (≥45%)
OCR->>Tess: extract_from_image() [solo págs sin texto]
Tess-->>OCR: OCRResult(text, confidence)
alt Confidence baja (<70%)
OCR->>Paddle: extract_from_image() [Fallback]
Paddle-->>OCR: OCRResult(text, confidence)
end
else Calidad Baja (<45%)
OCR->>Paddle: extract_from_image() [solo págs sin texto]
Paddle-->>OCR: OCRResult(text, confidence)
end
else Todas escaneadas (0 chars nativos)
OCR->>OCR: Routing normal por calidad
OCR->>Tess: extract_text() / Paddle fallback
end
OCR-->>UC: Success(OCRResult)
alt OCR falla o texto vacío
OCR-->>UC: Failure(OCR_ERROR / OCR_EMPTY)
UC-->>GUI: Failure(ProcessingError)
GUI-->>Usuario: Mostrar error OCR
end
end
rect rgb(240, 255, 240)
Note over UC,NER: Paso 4: Extracción de Entidades (NER)
UC->>NER: extract_entities(text)
NER->>NER: Ensemble 5 extractores:<br/>Marker, SpaCy, Regex,<br/>Address, Contact
NER->>NER: 4 validadores en cadena
NER-->>UC: Success(EntityResult)
alt NER falla
NER-->>UC: Failure(NER_ERROR)
UC-->>GUI: Failure(ProcessingError)
GUI-->>Usuario: Mostrar error NER
end
end
rect rgb(255, 255, 230)
Note over UC,SD: Paso 4.5: Detección Provisional + Secciones
UC->>PD: detect_provisional_measure(text)
PD-->>UC: ProvisionalMeasureResult
alt Medida provisional detectada
Note over UC: ⚠️ URGENTE: requiere atención
end
UC->>SD: detect_sections(text)
SD-->>UC: SectionsDetectionResult
alt Faltan hechos o pretensiones
Note over UC: ⚠️ Documento posiblemente incompleto
end
end
rect rgb(255, 240, 240)
Note over UC,Dedup: Paso 5: Detección de Duplicados (Ensemble)
UC->>UC: Generar UUID documento
UC->>Dedup: detect_duplicates(text, doc_id)
Note over Dedup: Hash exacto ya verificado en Paso 1
Dedup->>Dedup: MinHash LSH (peso 0.25)
Dedup->>Dedup: TF-IDF Similarity (peso 0.40)
Dedup->>Dedup: Entity Matching (peso 0.35)
Dedup->>Dedup: Ensemble: ponderar scores
Dedup-->>UC: DuplicateDetectionResult<br/>(candidates, confidence, action)
end
rect rgb(248, 248, 255)
Note over UC,Repo: Paso 6: Persistencia
UC->>UC: Crear entidad Document
UC->>Repo: save(document)
Repo->>Repo: Insertar en SQLite
Repo->>Repo: Indexar en FTS5
Repo-->>UC: Document (saved)
UC->>Dedup: index_document(doc_id, text)
Note over Dedup: Indexar para futuras<br/>comparaciones
end
rect rgb(255, 248, 240)
Note over UC,Repo: Paso 7: Audit Trail (GOB-06)
UC->>Repo: audit_service.log(DOCUMENT_PROCESSED, user_id)
Note over Repo: Registrar en audit_events
end
UC-->>GUI: Success(ProcessDocumentResponse<br/>+ duplicate_candidates)
alt Duplicado Detectado (HIGH/EXACT)
GUI->>GUI: Mostrar alerta roja
GUI-->>Usuario: ⚠️ Duplicado detectado:<br/>similitud 95%, recomendación: rechazar
else Posible Duplicado (MEDIUM)
GUI->>GUI: Mostrar alerta amarilla
GUI-->>Usuario: ⚠️ Posible duplicado:<br/>similitud 75%, revisar manualmente
else Medida Provisional
GUI->>GUI: Mostrar alerta naranja
GUI-->>Usuario: 🚨 Medida provisional detectada
else Secciones Faltantes
GUI->>GUI: Mostrar aviso
GUI-->>Usuario: 📄 Faltan hechos/pretensiones
else Sin Duplicados (LOW)
GUI-->>Usuario: ✅ Documento procesado exitosamente
end
Usuario->>GUI: Validar/corregir entidades extraídas
GUI->>Repo: Guardar correcciones (feedback loop)
Pasos del Pipeline¶
1. Gate Pre-OCR (< 50ms)¶
- Calcular SHA-256 del archivo binario
- Obtener
file_size_bytesdel archivo - Buscar en BD si existe un documento con el mismo hash y tamaño
- Short-circuit: Si existe, retornar documento existente sin ejecutar OCR/NER
- Ahorra 60-170 segundos de procesamiento en duplicados exactos
2. Validación (< 1ms)¶
- Verificar existencia del archivo
- Validar formato PDF
- Railway-Oriented: retornar
Failuretemprano si inválido
3. Extracción Híbrida: Texto Nativo + OCR (0-120 segundos)¶
- Paso 1 — Texto nativo:
extract_native_text_per_page()usa PyMuPDFpage.get_text("text") - Si una página tiene ≥50 caracteres de texto nativo → usar directamente (confidence=0.99)
- Si tiene <50 caracteres → marcar como "necesita OCR"
engine_used = NATIVEsi todas las páginas son nativas- Paso 2 — OCR solo para páginas sin texto: Solo se convierten a imagen las páginas que lo necesitan
- Routing inteligente basado en calidad del documento:
- Alta calidad (≥45%) → Tesseract (CER 3.2%)
- Baja calidad (<45%) → PaddleOCR (CER 4.5%)
- Fallback automático: Si Tesseract confidence <70%, reintentar con PaddleOCR
- Impacto: PDFs digitales se procesan en <100ms en vez de 30-120s
- Ejemplo real: tutela_014 (145 págs) → 144 nativas + 1 OCR
4. Extracción de Entidades NER (1-5 segundos)¶
- Ensemble de 5 extractores con prioridad:
- MarkerExtractor (p=1): Radicado, juzgado por patrones
- SpaCyExtractor (p=2): Demandante, demandado con
es_core_news_lg - RegexExtractor (p=3): Fecha, cédula por regex
- AddressExtractor (p=4): Direcciones demandante/demandado
- ContactExtractor (p=5): Correo electrónico, cédula
- 4 validadores en cadena: formato, temporal, largo, semántico
- F1 Score: 85.3% global
- Validación humana requerida: Usuario confirma/corrige en GUI
4.5 Detección de Medida Provisional y Secciones (< 10ms)¶
- Medida provisional: Búsqueda de 15 keywords legales (medida cautelar, suspensión provisional, etc.)
- Secciones: Detección de hechos y pretensiones por patrones regex
- Hechos (9 patrones):
HECHOS:,DE LOS HECHOS,FUNDAMENTOS DE HECHO,relación de hechos,hechos relevantes,PRIMERO.-, capítulo, numeración - Pretensiones (10 patrones):
PRETENSIONES:,SOLICITO,mis pretensiones,solicitudes/peticiones,se sirva ordenar/tutelar,ruego al despacho,pido/requiero que - Alertas en GUI cuando se detecta medida provisional o faltan secciones
5. Detección de Duplicados Ensemble (50-200ms)¶
Pipeline de 3 niveles (hash exacto ya verificado en paso 1):
- MinHash LSH (peso 0.25): Similitud aproximada por shingles
- TF-IDF (peso 0.40): Similitud léxica con caché incremental
- Entity Matching (peso 0.35): Coincidencia de partes procesales
Ensemble: Promedio ponderado de scores, clasificación en confidence levels.
6. Persistencia (10-50ms)¶
- Guardar en SQLite con metadatos (incluyendo hash, file_size, provisional, secciones)
- Indexar contenido en FTS5 para búsqueda full-text
- Actualizar índices de detección de duplicados
7. Audit Trail (GOB-06) (< 5ms)¶
- Registrar evento
DOCUMENT_PROCESSEDenaudit_events - Incluir
user_iddel operador autenticado (GOB-05) - Detalles: motor OCR, confianza, tiempo, flag duplicado
- No-blocking: fallos de auditoría se loguean pero no interrumpen el pipeline
Manejo de Errores¶
Usando Result Pattern (returns library):
result = (
extract_text(file_path) # Step 3
.bind(extract_entities) # Step 4
.bind(detect_duplicates) # Step 5
.map(save_document) # Step 6
)
match result:
case Success(response): return response
case Failure(error): log_and_alert(error)
Métricas de Performance¶
| Fase | Tiempo Objetivo | Tiempo Actual |
|---|---|---|
| Gate Pre-OCR | <50ms | 10-30ms |
| Validación | <1ms | <1ms |
| Texto nativo (digital) | <100ms | 2-50ms |
| OCR (solo págs escaneadas) | <90s | 0-60s* |
| NER | <5s | 1-3s |
| Provisional + Secciones | <10ms | <5ms |
| Detección Duplicados | <200ms | 50-150ms |
| Persistencia | <50ms | 10-30ms |
| TOTAL (digital) | <10s | 2-5s |
| TOTAL (escaneado) | <120s | 60-80s |
*OCR = 0s si todas las páginas tienen texto nativo
Puntos de Decisión¶
- Gate Pre-OCR: Si hash+size coinciden → short-circuit
- Extracción nativa: Si página tiene ≥50 chars de texto → usar directo (engine=NATIVE)
- Routing OCR: Solo para páginas sin texto nativo, basado en quality score (umbral 0.45)
- Fallback OCR: Si Tesseract confidence <70%
- Medida provisional: Alerta urgente si keywords detectadas
- Secciones faltantes: Aviso si falta hechos/pretensiones (9+10 patrones regex)
- Auto-exclusión duplicados: Documento no puede aparecer como su propio duplicado
- Acción duplicados recomendada:
- EXACT/HIGH → rechazar
- MEDIUM → revisar manualmente
- LOW → aceptar
Última actualización: 2026-03-06 (GOB-05 auth + GOB-06 audit trail paso 7)