Saltar a contenido

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_bytes del 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 Failure temprano si inválido

3. Extracción Híbrida: Texto Nativo + OCR (0-120 segundos)

  • Paso 1 — Texto nativo: extract_native_text_per_page() usa PyMuPDF page.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 = NATIVE si 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):

  1. MinHash LSH (peso 0.25): Similitud aproximada por shingles
  2. TF-IDF (peso 0.40): Similitud léxica con caché incremental
  3. 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_PROCESSED en audit_events
  • Incluir user_id del 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

  1. Gate Pre-OCR: Si hash+size coinciden → short-circuit
  2. Extracción nativa: Si página tiene ≥50 chars de texto → usar directo (engine=NATIVE)
  3. Routing OCR: Solo para páginas sin texto nativo, basado en quality score (umbral 0.45)
  4. Fallback OCR: Si Tesseract confidence <70%
  5. Medida provisional: Alerta urgente si keywords detectadas
  6. Secciones faltantes: Aviso si falta hechos/pretensiones (9+10 patrones regex)
  7. Auto-exclusión duplicados: Documento no puede aparecer como su propio duplicado
  8. Acción duplicados recomendada:
  9. EXACT/HIGH → rechazar
  10. MEDIUM → revisar manualmente
  11. LOW → aceptar

Última actualización: 2026-03-06 (GOB-05 auth + GOB-06 audit trail paso 7)