Skip to content

ComunidadIA-OS/evolveai-emdi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EMDI

Enterprise-Managed Multimodal Document Intelligence

  • 🌟 Proyecto Finalista - Hackathon de la Secretaría de Estado de Digitalización e Inteligencia Artificial (SEDIA), Gobierno de España 🇪🇸
  • 🛡️ Evaluado bajo el framework de Derechos Humanos del PNUD (UN): Este proyecto incluye un informe de impacto detallado basado en los estándares internacionales de IA Confiable (ver GOVERNANCE_HRIA.md).
  • 💰 Para el análisis económico completo (TCO, ROI, economía del token, riesgo de privacidad y coste energético), consultar el Caso de Negocio.

🚀 Acerca de EvolveAI

EvolveAI es una startup tecnológica boutique nacida con la misión de redefinir cómo las organizaciones escalan su inteligencia operativa. Fundada por dos referentes en el sector de la Inteligencia Artificial con trayectorias complementarias en soluciones de nivel industrial:

  • Antonio Zarauz: Líder del proyecto EMDI y perfil R&D con formación en Stanford. Arquitecto de soluciones de vanguardia, Antonio dirige la investigación y el desarrollo de arquitecturas complejas (multimodales, orquestación de GPUs, Kubernetes), transformando la investigación de frontera en sistemas de producción robustos.
  • Mateo Álvarez: Director de Estrategia y Perfil Ejecutivo. Con una sólida base en ingeniería aeroespacial y formación en MIT/Harvard, Mateo lidera la visión de negocio y la integración de IA en grandes corporaciones (como Credicorp), enfocándose en la gobernanza y la eficiencia escalable.

La sinergia entre la visión estratégica y el rigor técnico ha permitido a EvolveAI diseñar proyectos disruptivos en entornos de alta exigencia, demostrando que la soberanía tecnológica y el máximo rendimiento pueden ir de la mano. EMDI es el resultado de este ADN: ingeniería de primer nivel para la soberanía del dato empresarial.


🌍 Nuestra Visión: Democratizando la IA para las PYMES

El tejido empresarial español y europeo está impulsado por pequeñas y medianas empresas (PYMES). Hasta ahora, la adopción de Inteligencia Artificial Generativa y Multimodal de vanguardia implicaba un peaje inasumible: depender de APIs privativas, perder la soberanía de los datos y afrontar costes escalables prohibitivos (vendor lock-in).

EMDI nace para romper esa barrera. Nuestro objetivo es empoderar a las empresas para que desplieguen su propia infraestructura y dominen su tech stack. Al aprovechar el poder de la comunidad Open Source y la orquestación inteligente de hardware, EMDI permite a cualquier empresa tener IA de nivel corporativo en su propio entorno, garantizando:

  • 🛡️ Soberanía del Dato Absoluta: Tus documentos, imágenes y conversaciones nunca abandonan tu infraestructura.
  • 🤝 Impulso al Open Source: Integramos los mejores modelos abiertos de la comunidad (Qwen, GLM), demostrando que lo abierto compite con lo privativo.
  • 📉 Accesibilidad Financiera: Una arquitectura revolucionaria que divide los costes de hardware, haciendo viable la IA para la economía real.

Si bien EMDI emplea una colección de modelos orientados a soluciones de document intelligence, en EvolveAI contamos con un catálogo completo de componentes tecnológicos que nos permiten flexiblizarnos a multitud de escenarios.


🏗️ Pilares Técnicos: Eficiencia y Soberanía Extrema

EMDI no es solo un conjunto de modelos; es un motor de inferencia optimizado para la realidad operativa de la empresa. Nuestra arquitectura se asienta sobre tres pilares de ingeniería avanzada que permiten desplegar un pipeline de orquestación de inteligencia documental multimodal sobre cualquier hardware NVIDIA con capacidad CUDA superior a 8.0, eliminando los cuellos de botella tradicionales del cloud. Ejemplificaremos este desarrollo sobre una única GPU NVIDIA A100 de 80GB, aunqwue será igualmente válido para cualquier hardware con características similares o superiores en términos de VRAM y CPU cores.

1. Orquestación Asimétrica con MIG Heterogéneo

Utilizamos MIG (Multi-Instance GPU) en modo Mixed Strategy para particionar físicamente el hardware. A diferencia de las soluciones por software, MIG garantiza un aislamiento total de memoria y SMs (Streaming Multiprocessors).

Esta orquestación se basa en una coordinación de doble capa definida en nuestros manifiestos:

  • Capa de Nodo (nodeSelector): Todos los servicios utilizan el selector nvidia.com/mig.config: rag-balanced. Esto asegura que los pods solo se desplieguen en nodos donde la GPU ha sido físicamente configurada con nuestra geometría específica (10/20/40), evitando errores de programación en hardware no preparado.

  • Capa de Partición (resources.limits): Aunque comparten el mismo nodo, cada microservicio solicita una instancia de recurso única del driver de NVIDIA. Esto fuerza a Kubernetes a realizar un "bin-packing" perfecto dentro de la misma tarjeta:

    • 10GB (nvidia.com/mig-1g.10gb): Detección de Layout & Orquestación de Visión (PP-DocLayoutV3).
    • 20GB (nvidia.com/mig-2g.20gb): Inferencia de Embeddings Multimodales (Qwen3-VL-2B).
    • 40GB (nvidia.com/mig-3g.40gb): Razonamiento, Chat & OCR de Regiones (Qwen3.5-4B).
  • Aislamiento de SLA: Gracias a esta partición física, un pico de carga en el OCR nunca degradará la latencia del Chat LLM, ya que no compiten por ancho de banda de memoria ni núcleos de cómputo.

2. Eliminación del Cold-Start (De minutos a segundos)

El mayor reto de las arquitecturas basadas en GPU es el tiempo de arranque. EMDI implementa una triple optimización de carga única:

  • ACR Artifact Streaming: Las imágenes de contenedor (que contienen las pesadas librerías CUDA) se transmiten bajo demanda. El pod arranca en segundos, descargando solo los bits necesarios para iniciar la ejecución mientras el resto llega en segundo plano.
  • Carga Ultra-Rápida con InstantTensor: Sustituimos los cargadores tradicionales por instanttensor. Esta tecnología permite una ingesta masiva de pesos .safetensors desde el almacenamiento persistente a la VRAM mediante E/S directa y prefetching en pipeline, reduciendo el tiempo de carga del modelo en un ~70%.
  • Datacenter-to-Datacenter Ingestion: Los modelos nunca tocan tu red local. Se ingieren directamente de los servidores de Hugging Face a tu Azure Blob PVC mediante Jobs distribuidos, garantizando que el almacenamiento esté siempre "caliente" y listo para el escalado.

3. AI Gateway de Alto Rendimiento (Rust Core) & Arquitectura "Zero-Hops"

En lugar de un proxy pesado, EMDI utiliza un Gateway Unificado escrito en Rust, diseñado para actuar como el sistema nervioso central del nodo.

  • Co-localización y Latencia CERO: Mediante el uso de nodeSelector y tolerations de Kubernetes, garantizamos que el Gateway se despliegue en el mismo nodo físico que la GPU. Esto elimina saltos de red externos; la comunicación entre el Gateway y los motores de inferencia (Embeddings, OCR, Chat) se realiza a través de la interfaz virtual interna del nodo, garantizando una latencia de tránsito despreciable.
  • Descubrimiento de Servicios Portable: Utilizamos DNS interno de Kubernetes (service-names). Esto permite que el stack sea 100% agnóstico al hardware: la misma configuración funciona sin cambios en una A100 de Azure, un sistema NVIDIA DGX o una estación de trabajo RTX Blackwell.
  • Security por Diseño: Control estricto de los flujos de datos y formateo dinámico de multimedia para los modelos de visión, asegurando que la soberanía del dato se mantenga desde la entrada hasta la generación sin que los datos abandonen nunca el "perímetro de memoria" del nodo.

4. Autoescalado Inteligente con KEDA (Soberanía Financiera)

Para maximizar el ahorro en infraestructuras de alto rendimiento (A100/H100), EMDI integra KEDA (Kubernetes Event-driven Autoscaling).

  • Escalado a CERO: Fuera del horario laboral, el sistema escala automáticamente todos los microservicios a 0 réplicas. Esto permite que el autoscaler del clúster (AKS/On-prem) apague los nodos de GPU, eliminando el coste de facturación por hardware inactivo.
  • Warm-up Programado: Configurado específicamente para el horario de oficina de Nueva York (EST), el sistema levanta automáticamente una réplica funcional de todo el stack de lunes a viernes (9:00 AM - 5:00 PM), asegurando que el sistema esté "caliente" y listo para los usuarios cuando comience la jornada.

5. Sinergia Cognitiva: El Doble Rol de Qwen 3.5 4B

A diferencia de los pipelines tradicionales que separan la extracción de texto de la interpretación visual, EMDI unifica ambos procesos bajo un mismo "cerebro" multimodal. El modelo Qwen 3.5 4B no solo actúa como un motor de OCR de altísimo rendimiento, sino que desempeña un doble rol crítico en la arquitectura:

  • Reconocimiento Estructurado (OCR Dinámico): Basándose en las regiones detectadas por PP-DocLayoutV3, Qwen transforma fragmentos de imagen en texto Markdown, tablas estructuradas o fórmulas LaTeX con una precisión superior a los modelos de OCR convencionales, gracias a su comprensión contextual del lenguaje.
  • Agente de Razonamiento Visual (VLM): Cuando el sistema detecta elementos no textuales (gráficos, imágenes, diagramas), Qwen cambia su modo de operación para generar descripciones semánticas ricas. Esto permite que EMDI "entienda" lo que representa un gráfico de barras o una fotografía, inyectando metadatos visuales en el flujo de datos que de otro modo se perderían, superando las limitaciones de la IA documental tradicional.

🏢 Modelo de Operación y Viabilidad Económica

Desde EvolveAI, concebimos EMDI como el motor central de la inteligencia de tu negocio. Dado que este despliegue actúa como un servidor de inferencia de alto rendimiento, el esquema ideal de consumo es mediante arquitectura cliente-servidor:

graph TD
    subgraph Clients ["💻 Recomendación Core: Clientes Ligeros"]
        direction LR
        PC1[PC Usuario]
        PC2[App Móvil]
        PC3[Navegador]
    end

    subgraph Cluster ["🛡️ Infraestructura EMDI (Soberanía del Dato)"]
        direction TB
        GW[AI Gateway - Rust]
        subgraph GPU ["NVIDIA A100 (Estrategia MIG Mixed)"]
            MIG1[10GB: Layout]
            MIG2[20GB: Embeddings]
            MIG3[40GB: LLM / Chat / OCR]
        end
    end

    subgraph Managed ["🌐 Soluciones Enterprise EvolveAI"]
        EVO[Expertos DevOps & AI]
    end

    Clients -- "API Calls (HTTPS)" --> GW
    GW --> MIG1
    GW --> MIG2
    GW --> MIG3
    
    MIG1 -- "Orchestrate Region OCR" --> MIG3
    
    EVO -- "Aprovisionamiento" --> Cluster
    EVO -- "Mantenimiento" --> Cluster
    EVO -- "Seguridad" --> Cluster
Loading
  • 💻 Recomendación Core (Despliegue Local): Recomendamos desplegar aplicaciones cliente ligeras directamente en los ordenadores locales de los empleados (ideal para agencias, oficinas o despachos de abogados). La carga pesada de IA se procesa en el clúster EMDI (en vuestra infraestructura local o cloud soberano), minimizando los requisitos de hardware de los usuarios.
  • 🌐 Soluciones Enterprise Integrales: EvolveAI ofrece servicios Enterprise "llave en mano" que incluyen aprovisionamiento, despliegue, mantenimiento y securización, permitiendo a tu empresa centrarse únicamente en extraer valor de sus datos.

💰 Viabilidad y Caso de Negocio

EMDI permite a las empresas desplegar su propia infraestructura de IA con un ahorro de OPEX superior al 90% en comparación con APIs de cloud público. Al anclar el coste a la electricidad y la amortización del hardware, se elimina el riesgo de inflación del token y se garantiza la soberanía absoluta.

📊 Análisis Detallado: Para consultar las matrices completas de TCO, ROI, economía del token, riesgo de privacidad y coste energético, consulta el Caso de Negocio Completo.

Línea de ingreso Tipo Qué incluye
EMDI Core Open Source (€0) Stack de orquestación, manifiestos K8s, gateway, cliente de muestra
Enterprise License Suscripción Logging inmutable, RBAC, autoescalado avanzado, soporte SLA
EvolveAI Managed Recurrente Aprovisionamiento, mantenimiento y SLA 99,9% "llave en mano"

🗺️ Roadmap & Futuro: Más allá del Cloud

Aunque el despliegue actual de EMDI está optimizado para Azure (AKS) con fines de demostración y agilidad para la Hackathon SEDIA, el stack tecnológico ha sido diseñado para ser agnóstico a la infraestructura y altamente escalable:

  1. Hardware Enterprise On-Premise: El motor de orquestación de EMDI es 100% compatible con infraestructuras soberanas de alto rendimiento, como sistemas NVIDIA DGX o estaciones de trabajo equipadas con NVIDIA RTX 6000 (Ada/Blackwell Edition). Esto permite a las empresas eliminar completamente la dependencia del cloud público si así lo requieren.
  2. Orquestación Client-Side Avanzada: Estamos trabajando en capas de orquestación que permitan mover parte de la lógica de pre-procesamiento y razonamiento ligero directamente al dispositivo del cliente (Edge AI), optimizando el ancho de banda y garantizando latencias ultra-bajas en aplicaciones críticas.
  3. Soporte Multimodal Expandido: Integración de modelos especializados en audio y vídeo bajo la misma arquitectura de particionado físico, convirtiendo a EMDI en el cerebro operativo integral de la empresa.

🛠️ Guía de Despliegue

🌎 1. Variables de Entorno

Define los parámetros de tu despliegue en Azure.

💡 Nota: Para garantizar la compatibilidad "Plug & Play", hemos pre-configurado el nombre del registro como emdiacr. Si deseas usar otro nombre, deberás actualizarlo manualmente en los archivos bajo k8s/apps/ y en k8s/kustomization.yaml.

# Parámetros del Clúster y Registro
export LOCATION="XXXXXX"
export SUBSCRIPTION_ID="XXXXXX"
export RESOURCE_GROUP="XXXXXX"
export AKS_NAME="aksemdi"
export ACR_NAME="acremdi"

# Configurar suscripción
az account set --subscription $SUBSCRIPTION_ID

🏎️ 2. Creación del Clúster y Node Pool Soberano

La configuración por defecto de los servicios cloud suele imponer tamaños de GPU uniformes y drivers pre-configurados que limitan la flexibilidad. Para lograr nuestra división asimétrica (10/20/40), desplegamos un "lienzo en blanco" y tomamos el control manual del ciclo de vida del hardware.

# 1. Crear el ACR (Premium requerido para Artifact Streaming)
az acr create -g $RESOURCE_GROUP -n $ACR_NAME --sku Premium --location $LOCATION

# 2. Crear el recurso de cómputo y vincular el ACR
# EXPLICACIÓN: --enable-blob-driver activa el soporte nativo para Azure Blob Storage,
# permitiendo usar la clase 'azureblob-fuse-premium' para el almacenamiento de modelos.
az aks create \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION \
  --name $AKS_NAME \
  --attach-acr $ACR_NAME \
  --node-count 1 \
  --enable-managed-identity \
  --enable-blob-driver \
  --generate-ssh-keys

# 3. Obtener credenciales
az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_NAME

# 4. Añadir el Node Pool de GPU con Autoescalado (0 a 4)
# EXPLICACIÓN CRÍTICA: Se incluyen labels de 'node-template-resources' para que el 
# Cluster Autoscaler sepa que este pool proveerá recursos MIG incluso cuando esté en 0.
# También se incluye 'nvidia.com/mig.config' para que la configuración sea persistente
# tras re-escalados (los labels manuales se pierden al escalar a cero).
az aks nodepool add \
    --resource-group $RESOURCE_GROUP \
    --cluster-name $AKS_NAME \
    --name mignode \
    --node-vm-size Standard_NC24ads_A100_v4 \
    --gpu-driver none \
    --enable-cluster-autoscaler \
    --min-count 0 \
    --max-count 4 \
    --node-taints sku=gpu:NoSchedule \
    --labels \
        sku=gpu \
        app=emdi-inference \
        nvidia.com/mig.config=rag-balanced \
    --tags \
        "k8s.io_cluster-autoscaler_node-template_resources_nvidia.com_mig-1g.10gb=1" \
        "k8s.io_cluster-autoscaler_node-template_resources_nvidia.com_mig-2g.20gb=1" \
        "k8s.io_cluster-autoscaler_node-template_resources_nvidia.com_mig-3g.40gb=1"

🧩 3. Configuración del NVIDIA GPU Operator (Estrategia MIG Mixta)

El GPU Operator es el "orquestador del hardware". Lo configuramos en modo mixed para permitir particiones de distintos tamaños en una misma tarjeta (algo que el modo single no permite).

# 1. Instalar el operador mediante Helm
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# EXPLICACIÓN DE VALORES:
# - mig.strategy=mixed: Habilita la creación de instancias de GPU de distintos perfiles (1g, 2g, 3g).
# - WITH_REBOOT=true (en values): El particionado MIG requiere cambios a nivel de firmware/GPU-reset.
#   Esta flag permite al operador reiniciar el nodo automáticamente para aplicar la nueva geometría.
helm install gpu-operator nvidia/gpu-operator \
    -n gpu-operator --create-namespace \
    -f k8s/infra/gpu-operator-values.yaml

# 2. Aplicar la configuración de geometría EMDI (10GB/20GB/40GB)
kubectl apply -f k8s/infra/mig-config.yaml

# 3. Vincular el ConfigMap de MIG al Operador
# EXPLICACIÓN: Este parche vincula nuestra geometría personalizada (10/20/40) definida en 
# 'k8s/infra/mig-config.yaml' con el motor del operador. 
kubectl patch clusterpolicies.nvidia.com/cluster-policy \
    --type='json' -p='[{"op":"replace", "path":"/spec/migManager/config/name", "value":"device-plugin-config"}]'

✅ Verificación del Particionado (Crucial)

Una vez aplicados los comandos anteriores, el autoscaler levantará un nodo (o usará el existente). El 'mig-manager' detectará automáticamente el label nvidia.com/mig.config=rag-balanced que definimos en el Node Pool y procederá a reconfigurar la GPU físicamente.

1. Comprobar el estado del particionado: Verifica que el operador haya terminado de configurar el hardware. El estado debe ser success.

kubectl get nodes -l kubernetes.azure.com/agentpool=mignode -o custom-columns=NAME:.metadata.name,MIG_CONFIG:".metadata.labels.nvidia\.com\/mig\.config",MIG_STATE:".metadata.labels.nvidia\.com\/mig\.config\.state"
  • Salida Esperada:
    NAME                              MIG_CONFIG     MIG_STATE
    aks-mignode-35982219-vmss000001   rag-balanced   success
    

2. Ver los recursos asignables (Allocatable): Este comando confirma que Kubernetes reconoce las tres particiones como recursos independientes.

kubectl get nodes -l kubernetes.azure.com/agentpool=mignode -o custom-columns=NAME:.metadata.name,MIG_10G:".status.allocatable.nvidia\.com\/mig-1g\.10gb",MIG_20G:".status.allocatable.nvidia\.com\/mig-2g\.20gb",MIG_40G:".status.allocatable.nvidia\.com\/mig-3g\.40gb"
  • Salida Esperada:
    NAME                              MIG_10G   MIG_20G   MIG_40G
    aks-mignode-35982219-vmss000001   1         1         1
    

3. Ver la partición real desde dentro del hardware: Lanza un pod temporal para ejecutar nvidia-smi y confirmar la geometría física detectada por el driver:

kubectl run --rm -it v-gpu --image=nvidia/cuda:12.1.0-base-ubuntu22.04 \
  --overrides='{"spec": {"containers": [{"name": "v-gpu", "image": "nvidia/cuda:12.1.0-base-ubuntu22.04", "command": ["nvidia-smi", "-L"], "resources": {"limits": {"nvidia.com/gpu": 1}}}]}}' \
  --restart=Never
  • Salida Esperada:
    GPU 0: NVIDIA A100-SXM4-80GB (UUID: GPU-...)
      MIG 3g.40gb      Device  0: (UUID: MIG-...)
      MIG 2g.20gb      Device  1: (UUID: MIG-...)
      MIG 1g.10gb      Device  2: (UUID: MIG-...)
    

El orquestador de Kubernetes ahora reconocerá mágicamente tres tarjetas gráficas independientes y ajustadas a cada tarea.

📦 4. Model Ingestion: Datacenter‑to‑Datacenter

En EMDI, tratamos los modelos como datos pesados, no como código. En lugar de descargar ~100GB localmente para luego re‑subirlos, realizamos una ingesta in‑cluster mediante un Kubernetes Job.

Este Job descarga los pesos directamente desde Hugging Face hacia un volumen persistente (PVC) respaldado por Azure Blob. Una vez completado, los modelos están disponibles para todos los pods de inferencia sin descargas repetidas.

4.1 Provisionar el Almacenamiento (PVC)

Los pesos de los modelos son almacenados en un Blob CSI‑backed PVC. Este debe existir y estar vinculado antes de lanzar el Job.

# 1. Aplicar el PVC
kubectl apply -f k8s/provisioning/pvc.yaml

# 2. Verificar que el estado sea 'Bound'
kubectl get pvc model-weights-pvc

Salida Esperada:

NAME                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS             AGE
model-weights-pvc   Bound    pvc-37837be3-65b2-4cf0-b6ce-f7fe61b13adb   300Gi      RWX            azureblob-fuse-premium   29s

4.2 Lanzar el Ingestion Job

El Job utiliza variables de entorno para gestionar la descarga paralela.

# Lanzar el job
kubectl apply -f k8s/provisioning/ingest-job.yaml

# Confirmar creación y estado detallado
kubectl get job model-weight-ingest
kubectl describe job model-weight-ingest

4.3 Observar el Progreso

No utilices nombres de Pods manuales; utiliza la abstracción del Job para seguir los logs de la descarga desde Hugging Face:

kubectl logs -f job/model-weight-ingest

(Una vez veas el mensaje ✅ Ingestion complete, puedes limpiar el job con kubectl delete job model-weight-ingest)

4.4 Debugging & Inspección Manual (Opcional)

Si necesitas verificar manualmente la integridad de los archivos .safetensors descargados, lanza este pod de depuración:

kubectl run weights-debug \
  --rm -it \
  --image=ubuntu:22.04 \
  --overrides='
{
  "spec": {
    "containers": [{
      "name": "debug",
      "image": "ubuntu:22.04",
      "command": ["bash"],
      "stdin": true,
      "tty": true,
      "volumeMounts": [{
        "name": "weights",
        "mountPath": "/mnt/models"
      }]
    }],
    "volumes": [{
      "name": "weights",
      "persistentVolumeClaim": {
        "claimName": "model-weights-pvc"
      }
    }]
  }
}'

Dentro del pod, puedes inspeccionar los modelos:

cd /mnt/models
ls -lhR
exit

La salida esperada será algo como lo siguiente:

root@weights-debug:/mnt/models# ls -lhR
.:
total 0
drwxrwxrwx 2 root root 4.0K May 22 22:10 Qwen

./Qwen:
total 0
drwxrwxrwx 2 root root 4.0K May 22 22:10 Qwen3-VL-Embedding-2B
drwxrwxrwx 2 root root 4.0K May 22 22:11 Qwen3.5-4B

./Qwen/Qwen3-VL-Embedding-2B:
total 0
-rwxrwxrwx 1 root root 4.0G May 22 22:11 model.safetensors
-rwxrwxrwx 1 root root 1.6K May 22 22:10 config.json
-rwxrwxrwx 1 root root  11M May 22 22:11 tokenizer.json
-rwxrwxrwx 1 root root 5.3K May 22 22:11 tokenizer_config.json

./Qwen/Qwen3.5-4B:
total 0
-rwxrwxrwx 1 root root  12K May 22 22:11 LICENSE
-rwxrwxrwx 1 root root  76K May 22 22:11 README.md
-rwxrwxrwx 1 root root 7.6K May 22 22:11 chat_template.jinja
-rwxrwxrwx 1 root root 3.1K May 22 22:11 config.json
-rwxrwxrwx 1 root root 3.2M May 22 22:11 merges.txt
-rwxrwxrwx 1 root root 5.0G May 22 22:11 model.safetensors-00001-of-00002.safetensors
-rwxrwxrwx 1 root root 3.8G May 22 22:12 model.safetensors-00002-of-00002.safetensors
-rwxrwxrwx 1 root root  75K May 22 22:12 model.safetensors.index.json
-rwxrwxrwx 1 root root  390 May 22 22:12 preprocessor_config.json
-rwxrwxrwx 1 root root  13M May 22 22:12 tokenizer.json
-rwxrwxrwx 1 root root  17K May 22 22:12 tokenizer_config.json
-rwxrwxrwx 1 root root  385 May 22 22:12 video_preprocessor_config.json
-rwxrwxrwx 1 root root 6.5M May 22 22:12 vocab.json

⚙️ 5. Generación de Microservicios (High‑Performance Build)

Construimos todas las imágenes directamente en Azure Container Registry (ACR). Dado que compilamos binarios de Rust (Gateway) y librerías de GPU pesadas, utilizamos Agent Pools dedicados para evitar errores de memoria (OOM).

⚡ 5.1 Compilación de Alto Rendimiento (Recomendado)

Construimos todas las imágenes directamente en Azure Container Registry (ACR). Con las siguientes instrucciones, seremos capaces de disponibilizar nuestros paquetes de código de forma rápida.

# 1. Build & Push con Artifact Streaming (Indispensable para GPU)
# Ejecutar desde la raíz del proyecto
az acr build --registry $ACR_NAME --image unified-gateway:latest ./nodes/gateway
az acr build --registry $ACR_NAME --image layout-api:latest ./nodes/layout
az acr build --registry $ACR_NAME --image vllm-qwen-embedding:latest ./nodes/embeddings
az acr build --registry $ACR_NAME --image vllm-qwen-chat:latest ./nodes/llm_server

# 2. Habilitar Artifact Streaming para acelerar el arranque en AKS
az acr artifact-streaming create --name $ACR_NAME --image layout-api:latest --no-wait
az acr artifact-streaming create --name $ACR_NAME --image vllm-qwen-embedding:latest --no-wait
az acr artifact-streaming create --name $ACR_NAME --image vllm-qwen-chat:latest --no-wait

🔍 5.2 Inspección de Tareas en la Nube

Las builds de ACR son trabajos asíncronos administrados. Puedes cerrar tu terminal y volver después para ver el estado:

# Listar ejecuciones recientes
az acr task list-runs --registry $ACR_NAME --output table

# Recuperar logs de una build después de que el terminal se cerró
export LAST_RUN_ID=$(az acr task list-runs --registry $ACR_NAME --top 1 --query "[0].runId" -o tsv)
az acr task logs --registry $ACR_NAME --run-id $LAST_RUN_ID

🧹 5.3 Limpieza de Task Runs (ACR Jobs)

Si lanzaste builds que ya no necesitas, puedes cancelar ejecuciones en curso o eliminar runs históricos para mantener el registro limpio.

# 1. Cancelar una ejecución activa
az acr task list-runs --registry $ACR_NAME --output table

export RUN_ID=<RUN_ID_EN_EJECUCION>
az acr task cancel-run --registry $ACR_NAME --run-id $RUN_ID


# 2. Eliminar runs antiguos (requiere ACR Tasks preview / purge)
# Borra ejecuciones con más de X tiempo (ej: 1 día)
az acr task purge \
  --registry $ACR_NAME \
  --ago 1d \
  --filter "RunType=Build" \
  --yes

💡 Nota: ACR no soporta borrado manual individual de runs completados de forma directa, pero purge permite limpiar históricamente. Esto es útil para evitar ruido en auditoría y mantener el control de costos/diagnóstico.

💡 Nota: Si ya tienes un clúster AKS y quieres vincular el ACR a posteriori, usa: az aks update -n $AKS_NAME -g $RESOURCE_GROUP --attach-acr $ACR_NAME


🚀 6. Despliegue del Sistema EMDI

Antes de activar el stack de EMDI, debemos preparar el clúster con las herramientas de orquestación de eventos (KEDA) y monitoreo (Prometheus) necesarias para el autoescalado y la observabilidad.

6.1 Instalación de Prerrequisitos (Helm)

# 1. Instalar KEDA (Requerido para el autoescalado programado)
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda -n keda --create-namespace

# 2. Instalar Prometheus Stack (Requerido para observabilidad y métricas de GPU)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace

6.2 Activación del Entorno EMDI

Con el hardware optimizado, la "inteligencia" descargada y los operadores (GPU, KEDA, Prometheus) listos, lanzamos todo el entorno. Este paso final orquesta no solo los pods, sino también la infraestructura de red necesaria para la soberanía y accesibilidad.

# Desplegar el stack completo (Gateway, Motores, Red y Reglas de Autoescalado)
kubectl apply -k k8s/

🌐 Arquitectura de Red y Conectividad (k8s/networking)

El despliegue incluye una configuración de red de doble capa diseñada para maximizar la seguridad y el rendimiento:

  1. Cableado Interno Estricto (services.yaml):

    • Inferencia Aislada: Los motores (Layout, Embeddings, LLM) se exponen mediante ClusterIP. Esto significa que son invisibles desde fuera del clúster, forzando a que todo el tráfico pase obligatoriamente por el Gateway en Rust para su validación y orquestación.
    • Gateway como LoadBalancer: El servicio del Gateway se expone hacia el exterior, actuando como el único punto de entrada (Single Point of Entry) para las aplicaciones de la empresa.
  2. Capa de Aplicación y Dominios (ingress.yaml):

    • Gestión de Carga Multimedia: Configuramos anotaciones específicas en el Ingress (proxy-body-size: 50m) para permitir el envío de documentos e imágenes de alta resolución sin que el balanceador de carga corte la conexión.
    • Abstracción de DNS: El Ingress permite mapear el servicio a un nombre de dominio amigable, facilitando la integración con certificados SSL y políticas de seguridad corporativas.

6.3 Monitoreo y Diagnóstico

Una vez desplegado el sistema, es fundamental verificar que la orquestación asimétrica y el autoescalado estén funcionando correctamente.

1. Verificación de Pods y Escalamiento

Dado que hemos configurado el escalado a cero con KEDA, es posible que los pods no aparezcan inmediatamente si estás fuera del horario configurado. Puedes verificar el estado de los "objetos escalables" y los pods con:

# Ver el estado de las reglas de autoescalado
kubectl get scaledobjects

# Ver los pods (pueden estar en estado 'Terminating' o no existir si están escalados a 0)
kubectl get pods -l app=emdi-inference
2. Inspección de Logs (Debugging)

Si los pods están en ejecución (Running), puedes ver lo que ocurre dentro de cada motor.

💡 Tip: Si los comandos no devuelven nada, asegúrate de que los pods no hayan sido escalados a 0 por KEDA. Puedes comprobarlo con kubectl get pods.

# Gateway (Rust): Orquestación de peticiones
kubectl logs -l service=unified-gateway --all-containers=true -f --prefix=true

# Layout (PP-DocLayoutV3): Detección y Orquestación de regiones
kubectl logs -l service=layout-api --all-containers=true -f --prefix=true

# Embeddings (Qwen-VL): Generación de vectores
kubectl logs -l service=rag-embedding --all-containers=true -f --prefix=true

# Chat LLM & OCR (Qwen-3.5): Razonamiento, Chat y Visión (delegado)
kubectl logs -l service=rag-llm-chat --all-containers=true -f --prefix=true
3. Verificación de la GPU (MIG)

Para asegurar que los microservicios están utilizando sus particiones físicas dedicadas, puedes ejecutar nvidia-smi directamente en uno de los pods activos:

# Ejemplo: Verificar la partición de 40GB en el pod de Chat
kubectl exec -it $(kubectl get pods -l service=rag-llm-chat -o name | head -n 1) -- nvidia-smi
4. Acceso a Dashboards (Prometheus/Grafana)

Si instalaste el Prometheus Stack en el paso 6.1, puedes acceder a la interfaz visual para ver métricas en tiempo real:

# Port-forward para acceder a Grafana (Usuario: admin / Password: prom-operator)
kubectl port-forward deployment/prometheus-grafana 3000:3000 -n monitoring
# Abre http://localhost:3000 en tu navegador

🎯 Endpoints de la API Unificada

El Gateway en Rust expone una interfaz limpia y corporativa para tu empresa:

  • POST /ocr: Recibe {"data": "<base64_string>"} (soporta imagen o PDF) y devuelve un objeto estructurado con markdown y metadatos de layout.
  • POST /embeddings: Endpoint estándar para integración de vectores en tus bases de datos.
  • POST /chat/completions: Interfaz conversacional compatible con el estándar de OpenAI para interactuar con tus documentos.

🧪 Cómo invocar los servicios

Una vez desplegado el sistema, puedes obtener la dirección pública del Gateway con el siguiente comando:

export GATEWAY_IP=$(kubectl get svc unified-gateway-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export GATEWAY_URL="http://$GATEWAY_IP"
echo "EMDI Gateway disponible en: $GATEWAY_URL"
1. Procesamiento de Documentos (Layout & OCR)

El endpoint de OCR ahora devuelve la estructura completa del documento y soporta la ingesta de archivos PDF.

import os
import requests
import base64
from pathlib import Path
from typing import Union

def sniff_mime_from_magic(data: bytes) -> str:
    # Minimal magic-byte sniffing for common types
    if data.startswith(b"%PDF-"):
        return "application/pdf"
    if data.startswith(b"\xFF\xD8\xFF"):
        return "image/jpeg"
    if data.startswith(b"\x89PNG\r\n\x1a\n"):
        return "image/png"
    if data[:6] in (b"GIF87a", b"GIF89a"):
        return "image/gif"
    if data.startswith(b"RIFF") and data[8:12] == b"WEBP":
        return "image/webp"
    return "application/octet-stream"

def file_to_data_uri_sniffed(filepath: Union[str, Path]) -> str:
    path = Path(filepath)
    raw = path.read_bytes()
    mime_type = sniff_mime_from_magic(raw)
    b64 = base64.b64encode(raw).decode("utf-8")
    return f"data:{mime_type};base64,{b64}"

# Fichero de muestra
payload = {
    "data": file_to_data_uri_sniffed('./sample_files/qwen3_technical_report.pdf')
}
response = requests.post(f"{GATEWAY_URL}/ocr", json=payload, timeout=600)
response.raise_for_status()

with open('./sample_files/qwen3_technical_report.json', 'w', encoding='utf-8') as f:
    json.dump(response.json(), f, ensure_ascii=False, indent=2)
2. Embeddings Multimodales (Qwen VL embeddings)

El endpoint de embeddings soporta representaciones densas de texto e imagen simultáneamente, ideal para sistemas de búsqueda semántica híbrida.

import requests
import json

# Ejemplo sofisticado: Embedding de un documento con contexto de sistema
payload = {
    "model": "Qwen/Qwen3-VL-Embedding-2B",
    "messages": [
        {
            "role": "user",
            "content": [
                {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,...(base64_doc)..."}},
                {"type": "text", "text": "Representa este balance de situación para comparación financiera:"}
            ]
        },
        {"role": "assistant", "content": ""}
    ],
    "encoding_format": "float",
    "continue_final_message": True,
    "add_special_tokens": True
}

response = requests.post(f"{GATEWAY_URL}/v1/embeddings", json=payload)
embedding = response.json()["data"][0]["embedding"]
print(f"Dimensiones del vector: {len(embedding)}")
3. Chat y Razonamiento (Qwen3.5-4B)

Interfaz totalmente compatible con OpenAI. Permite inyectar lógica de negocio mediante system prompts y controlar el flujo de razonamiento.

from openai import OpenAI

client = OpenAI(base_url=f"{GATEWAY_URL}/v1", api_key="emdi-key")

# Ejemplo sofisticado: Análisis de cláusulas con control de temperatura
stream = client.chat.completions.create(
    model="Qwen/Qwen3.5-4B",
    messages=[
        {
            "role": "system", 
            "content": (
                "Eres un asistente conversacional diseñado para el "
                "Hackathon de la Secretaría de Estado de Digitalización e Inteligencia Artificial (SEDIA) "
            )
        },
        {
            "role": "user", 
            "content": [
              {"type": "image_url", "image_url": {"url": file_to_data_uri_sniffed('./sample_files/hackaton_poster.png')}},
              {"type": "text", "text": "Describe brevemente esta imagen."}
            ]
        }
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024,
    stream=True,
)

for chunk in stream:
    if chunk.choices[0].delta.content is not None:
        print(chunk.choices[0].delta.content, end="", flush=True)

🖥️ Interfaz de Usuario: EMDI Command Center (Sample Client)

Para facilitar la adopción inmediata de nuestra tecnología, EMDI incluye un Command Center (ubicado en la carpeta /client). Es importante destacar que este es un cliente de muestra (sample client) diseñado para ilustrar las capacidades del sistema.

El verdadero potencial reside en el Servidor de Inferencia EMDI, que actúa como una base sólida y flexible sobre la cual se pueden construir múltiples proyectos, automatizaciones e ideas personalizadas integrando su API en cualquier flujo de trabajo empresarial.

🧠 Capacidades del Cliente EMDI (Referencia)

  1. Ingesta con Inferencia Híbrida (Hybrid Chunking): A diferencia de los sistemas RAG tradicionales que solo procesan texto, nuestro cliente utiliza una estrategia de chunking visual-semántico. Por cada página del documento:

    • Capa Visual: Se genera un embedding denso de la página completa como imagen (Qwen3-VL-2B), capturando la disposición espacial, gráficos y estilo.
    • Capa Metadatos (OCR): Se asocia el texto estructurado (Markdown/Tablas) extraído por el LLM (Qwen3.5-4B) como metadatos del vector.
    • Resultado: Las búsquedas son mucho más precisas al "entender" tanto el contenido textual como el contexto visual de la información.
  2. Exploración Documental Interactiva: El visor integrado permite navegar por los documentos con una capa de Bounding Boxes generada dinámicamente. Al hacer clic en cualquier región detectada (párrafos, tablas, imágenes), el sistema resalta el contenido extraído y viceversa, permitiendo una auditoría humana rápida de la IA.

  3. Asistente de Razonamiento (Chat RAG): Interfaz de chat fluida compatible con streaming que permite interrogar a los documentos procesados. El asistente utiliza el conocimiento vectorial almacenado en Qdrant para responder con precisión quirúrgica, citando partes específicas del documento.

🛠️ Configuración Local del Cliente (Quick Start)

Si deseas probar la interfaz de usuario de forma local conectándola a un despliegue de EMDI (o a un entorno de desarrollo), sigue estos pasos:

  1. Prerrequisitos: Asegúrate de tener instalado Docker Desktop y que el motor de Docker esté en ejecución.
  2. Configurar Variables de Entorno: Crea un archivo llamado .env dentro de la carpeta client/backend/ con la URL de tu Gateway de EMDI:
    # client/backend/.env
    GATEWAY_URL=http://<IP_DE_TU_GATEWAY>
  3. Lanzar el Entorno: Desde la raíz del proyecto, ejecuta el siguiente comando para levantar el backend (Python/FastAPI) y el frontend (Nginx) junto con la base de datos vectorial local (Qdrant):
    docker compose -f client/docker-compose.yml up --build
  4. Acceso: Una vez levantado, abre tu navegador en http://localhost:8001 para empezar a procesar tus documentos.

🎥 Demostraciones en vídeo

🔍 1. Exploración y Navegación de Documentos

En este vídeo se muestra cómo EMDI segmenta un PDF técnico, permitiendo la exploración visual de sus componentes mediante bounding boxes y la visualización instantánea del OCR estructurado.

Exploración PDF Demo

💬 2. Chat Inteligente y Consulta de Datos

Demostración del asistente conversacional interactuando con los documentos cargados, extrayendo información compleja y respondiendo en tiempo real utilizando el pipeline RAG de EMDI.

Chat Interface Demo


🌟 Construyendo un futuro tecnológico soberano, eficiente y abierto para las empresas. 🌟

About

Repositorio de código del equipo EvolveAI para la iniciativa EMDI, presentada en el reto de IA responsable y abierta en industria 2026

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors