# 06. GRACE — CAPA COGNITIVA **Manual de Arquitectura Técnica v5.0** **Sistema SFE/HST Enterprise** --- > **TL;DR — GRACE en 30 segundos** > > **Alfred (n8n)** = El cuerpo. Local, determinista, decide flujos. > **GRACE (18 módulos)** = El cerebro. Transforma datos, no decide. > > Alfred envía imagen → GRACE extrae texto → Alfred guarda en The Vault. > > GRACE es **self-hosted** en el servidor del usuario. Los logs quedan locales. > Puede llamar a APIs externas (GPU serverless, LLMs premium) pero son **opcionales y stateless**. > > *"GRACE transforma, Alfred decide."* --- ## Índice 1. [Introducción](#1-introducción) 2. [Modelo de Despliegue](#2-modelo-de-despliegue) 3. [Contrato Común v1.2](#3-contrato-común-v12) 4. [Envelope Universal](#4-envelope-universal) 5. [Sistema de Logging](#5-sistema-de-logging) 6. [Los 18 Módulos](#6-los-18-módulos) 7. [Arquitectura de Providers](#7-arquitectura-de-providers) 8. [Fallback Chains](#8-fallback-chains) 9. [Pipelines Oficiales](#9-pipelines-oficiales) 10. [Implementación de Referencia](#10-implementación-de-referencia) --- ## 1. Introducción ### 1.1 ¿Qué es GRACE? GRACE (antes Jarvis) es la **capa cognitiva determinista** del sistema: un conjunto de 18 microservicios de IA desacoplados. Diligente, amable, no comete errores. Siempre igual. ``` ┌─────────────────────────────────────────────────────────────────┐ │ GRACE │ │ │ │ • NO es chatbot │ │ • NO guarda estado │ │ • NO toma decisiones de flujo │ │ • Transforma datos según request │ │ • Intercambiable: sustituir modelo sin romper sistema │ │ │ │ "GRACE transforma, Alfred decide." │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.2 Principios de GRACE | Principio | Descripción | |-----------|-------------| | **Stateless** | Nunca guarda estado entre llamadas | | **Determinista** | Misma entrada → siempre misma salida | | **Intercambiable** | Sustituir modelo sin romper sistema | | **Trazable** | Toda ejecución registrada vía trace_id | | **No persiste datos** | Archivos solo en Hostinger/Vault | ### 1.3 Arquitectura Híbrida de Dos Tiers ``` ┌──────────────────┐ │ ALFRED (n8n) │ │ Orquestador │ └────────┬─────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ TIER 1 │ │ TIER 1 │ │ TIER 2 │ │ AUTOALOJ.│ │ AUTOALOJ.│ │ APIs │ │ Ligero │ │ Pesado │ │ Premium │ │ (CPU) │ │ (GPU) │ │(Fallback)│ └──────────┘ └──────────┘ └──────────┘ Regex/Rules Whisper Large Claude Sonnet 4 Hashing SDXL/Flux GPT-4o Vision Local Hash InsightFace Gemini 2.5 Pro ``` ### 1.4 ¿Por qué GRACE? El nombre GRACE evoca las cualidades del sistema: - **G**enerativa — Produce outputs estructurados - **R**eliable — Determinista y trazable - **A**gnostic — Independiente del provider - **C**ognitive — Capa de inteligencia - **E**fficient — Optimiza costo/calidad --- ## 2. Modelo de Despliegue ### 2.1 Arquitectura Self-Hosted GRACE se despliega en el **servidor del usuario** (personal o corporativo), no como servicio centralizado. Esto garantiza: - **Soberanía de datos**: Los logs y procesamientos quedan bajo control del usuario - **Multi-tenancy local**: Una instancia de GRACE puede servir a múltiples usuarios/organizaciones - **Flexibilidad**: Posibilidad de peticiones externas si se requiere, pero no como dependencia ``` ┌─────────────────────────────────────────────────────────────────┐ │ SERVIDOR DEL USUARIO (VPS/On-Premise) │ │ Hostinger KVM / AWS / Azure / Bare Metal │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ GRACE │ │ │ │ (Container Docker) │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ TIER 1 │ │ TIER 1 │ │ TIER 2 │ │ │ │ │ │ LOCAL │ │ GPU │ │ PREMIUM │ │ │ │ │ │ (CPU) │ │ Server- │ │ (APIs) │ │ │ │ │ │ │ │ less │ │ │ │ │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ │ │ │ │ └──────┬─────┴────────────┘ │ │ │ │ ▼ │ │ │ │ Buffer Interno ──► Flush │ │ │ │ │ │ │ │ └──────────────────────────────│──────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ SYS_LOG (PostgreSQL/NocoDB) │ │ │ │ Base de datos LOCAL en servidor usuario │ │ │ │ Soporta múltiples usuarios/organizaciones │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────────────┼───────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ SENTINEL │ │ SENTINEL │ │ NOTARIO │ │ │ │ LIGHT │ │ DEEP │ │ (Blockchain)│ │ │ │ (cron 5min) │ │ (cron 1h) │ │ (diario) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ Solo llamadas stateless │ (TIER 1 GPU / TIER 2 Premium) ▼ ┌─────────────────────────────────────────────────────────────────┐ │ SERVICIOS EXTERNOS │ │ (Sin retención de datos, llamadas efímeras) │ ├─────────────────────────────────────────────────────────────────┤ │ TIER 1 GPU Serverless │ TIER 2 APIs Premium │ │ • RunPod │ • Anthropic (Claude) │ │ • Modal │ • OpenAI (GPT-4o) │ │ • Replicate │ • Google (Gemini) │ │ │ • ElevenLabs, Deepgram, etc. │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.2 Qué Vive Dónde | Componente | Ubicación | Notas | |------------|-----------|-------| | **GRACE (API)** | Servidor usuario | Container Docker, stateless | | **SYS_LOG** | Servidor usuario | PostgreSQL/NocoDB local | | **SENTINEL** | Servidor usuario | Procesos cron o workflows n8n | | **NOTARIO** | Servidor usuario | Collector local + escritura blockchain | | **Archivos** | Servidor usuario | Hostinger/Vault local | | **TIER 1 LOCAL** | Servidor usuario | Tesseract, embeddings, hashing | | **TIER 1 GPU** | Externo (opcional) | RunPod/Modal, llamadas stateless | | **TIER 2 Premium** | Externo (opcional) | APIs comerciales, sin retención | ### 2.3 Flujo de Datos y Privacidad ``` ┌─────────────────────────────────────────────────────────────────┐ │ FLUJO DE PRIVACIDAD │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. DATOS SENSIBLES (PII, documentos, audio) │ │ └──► Nunca salen del servidor del usuario │ │ └──► Procesados por TIER 1 LOCAL cuando es posible │ │ └──► Si requieren TIER 2: se envía solo lo necesario │ │ │ │ 2. LLAMADAS EXTERNAS (TIER 1 GPU / TIER 2) │ │ └──► Stateless: no se guarda contexto en el proveedor │ │ └──► Efímeras: payload entra, resultado sale, nada persiste│ │ └──► Opcionales: se pueden deshabilitar completamente │ │ │ │ 3. LOGS Y AUDITORÍA │ │ └──► 100% en servidor del usuario │ │ └──► SENTINEL corre localmente │ │ └──► Usuario tiene control total de sus datos │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.4 Configuración Multi-Usuario Una instancia de GRACE puede servir a múltiples usuarios/organizaciones: ``` ┌─────────────────────────────────────────────────────────────────┐ │ MULTI-TENANCY LOCAL │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ GRACE (única instancia) │ │ │ │ │ ├──► Usuario A (player_id: uuid-A) │ │ │ └── Sus logs en SYS_LOG con player_id filtrado │ │ │ └── Sus archivos en /vault/A/ │ │ │ │ │ ├──► Usuario B (player_id: uuid-B) │ │ │ └── Sus logs en SYS_LOG con player_id filtrado │ │ │ └── Sus archivos en /vault/B/ │ │ │ │ │ └──► Organización C (bandera_id: uuid-C) │ │ └── Múltiples players bajo misma bandera │ │ └── Políticas de acceso por bandera │ │ │ │ Segregación por: player_id + bandera_id en cada request │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.5 Modos de Operación | Modo | TIER 1 LOCAL | TIER 1 GPU | TIER 2 | Uso | |------|--------------|------------|--------|-----| | **Offline** | ✅ | ❌ | ❌ | Máxima privacidad, funcionalidad básica | | **Híbrido** | ✅ | ✅ | ❌ | Balance privacidad/capacidad | | **Completo** | ✅ | ✅ | ✅ | Máxima capacidad, fallbacks completos | Configuración en `settings`: ```python # Modo offline (sin llamadas externas) TIER_1_GPU_ENABLED = False TIER_2_PREMIUM_ENABLED = False # Modo híbrido (solo GPU serverless) TIER_1_GPU_ENABLED = True TIER_2_PREMIUM_ENABLED = False # Modo completo (todos los tiers) TIER_1_GPU_ENABLED = True TIER_2_PREMIUM_ENABLED = True ``` --- ## 3. Contrato Común v1.2 ### 2.1 Propósito El Contrato Común es la **interfaz universal** que garantiza: - Intercambiabilidad de módulos (locales ↔ remotos) - Trazabilidad completa de cada operación - Auditoría automatizada en tiempo real - Tolerancia a fallos con degradación elegante ``` ┌─────────────────────────────────────────────────────────────────┐ │ PRINCIPIO FUNDAMENTAL │ │ │ │ "Un módulo que no cumple el contrato, no existe para Alfred" │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.2 Decisión de Enrutamiento Alfred selecciona el provider según: | Factor | Decisión | |--------|----------| | Complejidad de tarea | Simple → TIER 1 Local, Compleja → TIER 2 Premium | | Latencia requerida | Crítica → TIER 1, Tolerante → TIER 2 | | Costo computacional | Bajo → TIER 1, Alto → TIER 1 GPU Serverless | | Disponibilidad | Fallo TIER 1 → Fallback TIER 2 | | Volumen | Alto volumen → TIER 1 (batch), Puntual → Cualquiera | | Calidad requerida | Máxima → TIER 2 Premium | ### 2.3 Perfiles de Request | Perfil | Campos Incluidos | Uso | |--------|------------------|-----| | `MINIMAL` | envelope, routing, payload | Debug rápido | | `STANDARD` | + context, storage | Producción normal | | `FULL` | + security, metadata | Auditoría completa | --- ## 4. Envelope Universal ### 3.1 REQUEST ```json { "contract_version": "1.2", "profile": "FULL", "envelope": { "trace_id": "uuid-v4-global", "parent_trace_id": "uuid-v4-padre|null", "step_id": "uuid-v4-paso-actual", "step_index": 1, "total_steps": 5, "idempotency_key": "sha256-del-input", "timestamp_init": "2025-12-01T10:30:00.000Z", "ttl_ms": 30000 }, "routing": { "module": "OCR_CORE", "version": "1.0", "tier_preference": ["TIER_1_GPU", "TIER_1_LOCAL", "TIER_2_PREMIUM"], "provider_preference": ["runpod", "modal", "google_vision"], "fallback_chain": ["OCR_LOCAL", "OCR_GOT", "OCR_GEMINI"], "max_fallback_level": 2 }, "context": { "lang": "es", "mode": "strict", "pii_filter": true, "bandera_id": "uuid-jurisdiccion", "player_id": "uuid-actor", "method_hash": "sha256-metodo-activo" }, "payload": { "type": "text|image|audio|document|structured", "encoding": "utf-8|base64|url", "content": "...", "content_hash": "sha256-del-contenido", "schema_expected": "nombre-del-schema-salida" }, "storage": { "input_location": "internal|external", "input_ref": "hostinger://path|s3://bucket/key", "output_location": "internal|external", "output_ref": "hostinger://path", "persist_intermediate": true }, "security": { "encryption_profile": "NONE|E2E_BASIC|E2E_STRICT", "data_sensitivity": "LOW|MEDIUM|HIGH|SECRET", "pii_detected": false } } ``` ### 3.2 Campos del Envelope | Campo | Tipo | Descripción | |-------|------|-------------| | `trace_id` | UUID v4 | Identificador único de la ejecución completa | | `parent_trace_id` | UUID v4 | Si es sub-proceso, referencia al padre | | `step_id` | UUID v4 | Identificador único de este paso específico | | `step_index` | integer | Posición en la cadena de procesamiento | | `total_steps` | integer | Total de pasos estimados | | `idempotency_key` | SHA256 | Hash del contenido para evitar reprocesamiento | | `timestamp_init` | ISO8601 | Momento de creación de la petición | | `ttl_ms` | integer | Tiempo máximo de vida en milisegundos | ### 3.3 RESPONSE ```json { "contract_version": "1.2", "envelope": { "trace_id": "uuid-v4-global", "step_id": "uuid-v4-paso-actual", "timestamp_init": "2025-12-01T10:30:00.000Z", "timestamp_end": "2025-12-01T10:30:00.450Z" }, "status": { "code": "SUCCESS|PARTIAL|ERROR|TIMEOUT|FALLBACK", "fallback_level_used": 0, "tier_used": "TIER_1_GPU", "provider_used": "runpod", "module_instance": "OCR_GOT_RUNPOD_V1" }, "result": { "schema": "nombre-del-schema", "data": { "text": "Contenido extraído...", "blocks": [...], "confidence": 0.98 } }, "quality": { "confidence": 0.985, "coverage": 1.0, "tokens_used": 1250, "validation_passed": true }, "metadata": { "model_id": "got-ocr-2.0", "model_version": "2024-10", "processing_ms": 450, "retries": 0, "cost_units": 0.0012 }, "storage": { "output_persisted": true, "output_ref": "hostinger://processed/2025/12/01/uuid.json", "output_hash": "sha256..." }, "errors": [], "warnings": [] } ``` ### 3.4 Códigos de Status | Código | Significado | Acción Alfred | |--------|-------------|---------------| | `SUCCESS` | Ejecución completa y válida | Continuar flujo | | `PARTIAL` | Resultado incompleto pero usable | Evaluar y decidir | | `ERROR` | Fallo no recuperable | Activar fallback o abortar | | `TIMEOUT` | Excedió TTL | Activar fallback | | `FALLBACK` | Éxito usando módulo alternativo | Log + continuar | --- ## 5. Sistema de Logging ### 4.1 Filosofía ``` ┌─────────────────────────────────────────────────────────────────┐ │ PRINCIPIO DE LOGGING │ │ │ │ "Si no está en el log, no ocurrió. Si ocurrió, está en log." │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 4.2 Arquitectura de Dos Capas ``` ┌─────────────────┐ │ LOG INTERNO │ → Buffer local en GRACE, alta velocidad │ (transitorio) │ → Mantiene últimos N registros en memoria └────────┬────────┘ │ flush cada N segundos o N registros ▼ ┌─────────────────┐ │ LOG EXTERNO │ → PostgreSQL/NocoDB (FUENTE DE VERDAD) │ (permanente) │ → Tabla SYS_LOG └────────┬────────┘ │ ┌────────┴────────┐ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ SENTINEL │ │ SENTINEL │ │ LIGHT │ │ DEEP │ │ │ │ │ │ • Cada 5min │ │ • Cada 1h │ │ • 100% regs │ │ • ~5% + err │ │ • Reglas ML │ │ • LLM pesado│ │ • Costo min │ │ • Análisis │ └─────────────┘ └─────────────┘ ``` ### 4.3 Estructura de SYS_LOG ```sql CREATE TABLE SYS_LOG ( -- Identificación id UUID PRIMARY KEY, trace_id UUID NOT NULL, parent_trace_id UUID, step_id UUID NOT NULL, idempotency_key CHAR(64), -- Secuencia step_index INTEGER NOT NULL, step_type VARCHAR(50) NOT NULL, profile VARCHAR(10) DEFAULT 'FULL', -- Batch batch_id UUID, batch_item_index INTEGER, batch_items_total INTEGER, -- Temporalidad timestamp_created TIMESTAMPTZ DEFAULT NOW(), timestamp_started TIMESTAMPTZ, timestamp_completed TIMESTAMPTZ, duration_ms INTEGER, -- Módulo y Provider module_name VARCHAR(100) NOT NULL, module_version VARCHAR(20), tier_used VARCHAR(20), provider_requested VARCHAR(50), provider_used VARCHAR(50), fallback_level INTEGER DEFAULT 0, model_id VARCHAR(100), -- Estado status_code VARCHAR(20) NOT NULL, error_code VARCHAR(50), error_message TEXT, -- Payload input_hash CHAR(64), input_ref VARCHAR(500), input_type VARCHAR(50), output_hash CHAR(64), output_ref VARCHAR(500), -- Calidad confidence DECIMAL(5,4), coverage DECIMAL(5,4), tokens_input INTEGER, tokens_output INTEGER, validation_passed BOOLEAN, -- Costo cost_units DECIMAL(10,6), -- Contexto bandera_id UUID, player_id UUID, method_hash CHAR(64), human_readable TEXT, -- Seguridad encryption_profile VARCHAR(20), data_sensitivity VARCHAR(20), pii_detected BOOLEAN DEFAULT FALSE, -- Auditoría audit_status VARCHAR(20) DEFAULT 'PENDING', audit_light_ts TIMESTAMPTZ, audit_deep_ts TIMESTAMPTZ, -- Blockchain blockchain_pending BOOLEAN DEFAULT TRUE, blockchain_tx_ref VARCHAR(100), notario_batch_id UUID ); -- Índices críticos CREATE INDEX idx_syslog_trace ON SYS_LOG(trace_id); CREATE INDEX idx_syslog_module ON SYS_LOG(module_name, timestamp_created); CREATE INDEX idx_syslog_status ON SYS_LOG(status_code, timestamp_created); CREATE INDEX idx_syslog_audit ON SYS_LOG(audit_status); ``` ### 4.4 Configuración del Logger Externo ```python # app/core/logging_external.py class ExternalLoggerConfig: """Configuración del sistema de logging externo.""" # Buffer local BUFFER_SIZE: int = 100 # Máximo registros en memoria FLUSH_INTERVAL_SEC: int = 5 # Flush cada N segundos # Conexión externa EXTERNAL_URL: str # URL de NocoDB/PostgreSQL EXTERNAL_TABLE: str = "SYS_LOG" BATCH_INSERT_SIZE: int = 50 # Registros por inserción # Retry MAX_RETRIES: int = 3 RETRY_DELAY_MS: int = 1000 # Fallback FALLBACK_FILE: str = "/var/log/grace/fallback.jsonl" ``` --- ## 6. Los 18 Módulos ### 5.1 Familia: VISIÓN #### Módulo 1: IMAGE_PROCESSOR ```yaml nombre: Procesado de Imagen función: Limpieza, resize, crop, denoise tier_primario: TIER_1_LOCAL fallback: TIER_1_GPU input: image (url/base64) output: image_url, dimensions, format pii: false providers: tier_1_local: [pillow, opencv] tier_1_gpu: [runpod_sdxl_preprocessor] ``` #### Módulo 2: PDF_SCANNER ```yaml nombre: PDF/Escáner función: Recorte automático, deskew, contraste tier_primario: TIER_1_LOCAL fallback: TIER_1_GPU input: image/pdf output: cleaned_image_url pii: false providers: tier_1_local: [pypdf, pdf2image] tier_1_gpu: [runpod_doctr] ``` #### Módulo 3: OCR_CORE ```yaml nombre: OCR función: Lectura de texto modos: fast, strict tier_primario: TIER_1_GPU fallback: TIER_2_PREMIUM input: image output: text, blocks[], confidence pii: puede_contener providers: tier_1_local: [tesseract] tier_1_gpu: [runpod_got_ocr, modal_qwen_vl] tier_2_premium: [google_vision, gemini_2_5_pro] ``` ### 5.2 Familia: VOZ #### Módulo 4: ASR (Speech-to-Text) ```yaml nombre: Transcripción de Audio función: Audio a texto modos: fast, accuracy tier_primario: TIER_1_GPU fallback: TIER_2_PREMIUM input: audio (url/base64) output: text, segments[], language pii: true (voz) providers: tier_1_local: [whisper_tiny, whisper_base] tier_1_gpu: [runpod_faster_whisper_large_v3, groq_whisper] tier_2_premium: [deepgram_nova_3, openai_whisper] notas: | Faster Whisper es la implementación principal (CTranslate2). 2-4x más rápido que Whisper original, mismo modelo large-v3. Soporta diarización y timestamps a nivel de palabra. ``` #### Módulo 5: TTS (Text-to-Speech) ```yaml nombre: Síntesis de Voz función: Texto a audio voces: neutral_f, neutral_m, corporate, soft tier_primario: TIER_1_GPU fallback: TIER_2_PREMIUM input: text output: audio_url, duration_ms pii: true (voz generada) providers: tier_1_gpu: [runpod_xtts_v2, modal_bark] tier_2_premium: [elevenlabs, openai_tts] ``` ### 5.3 Familia: IDENTIDAD #### Módulo 6: FACE_VECTOR ```yaml nombre: Vectores Faciales función: Extracción de vector facial normalizado tier_primario: TIER_1_GPU input: image (rostro) output: vector[512], quality_score pii: true (biométrico) ubicacion_output: VAULT (no SFE) providers: tier_1_gpu: [runpod_insightface, modal_arcface] tier_2_premium: [aws_rekognition] ``` #### Módulo 7: ID_CONSOLIDATION ```yaml nombre: Consolidación de Identidad función: Fusión de múltiples vectores, elimina outliers tier_primario: TIER_1_LOCAL input: vectors[] output: consolidated_vector, confidence pii: true providers: tier_1_local: [numpy_consolidator] ``` #### Módulo 8: AVATAR_GEN ```yaml nombre: Generador de Avatares función: Genera avatar neutral 512px desde vector tier_primario: TIER_1_GPU input: vector facial o imagen output: avatar_url pii: false (avatar sintético) providers: tier_1_gpu: [runpod_sdxl, modal_flux, replicate_flux] tier_2_premium: [dalle_3] ``` ### 5.4 Familia: NLP #### Módulo 9: SUMMARIZER ```yaml nombre: Resumen Automático función: Resume texto modos: simple, structured output_estructurado: - objetivos - hallazgos - acuerdos - riesgos tier_primario: TIER_2_PREMIUM input: text output: summary | structured_summary pii: puede_contener providers: tier_1_local: [local_llama_8b] tier_2_premium: [claude_sonnet_4, gpt_4o] ``` #### Módulo 10: CLASSIFIER ```yaml nombre: Clasificación Automática función: Clasifica documentos por tema/tipo tier_primario: TIER_1_LOCAL fallback: TIER_2_PREMIUM input: text | document output: category, confidence, tags[] pii: false providers: tier_1_local: [embeddings_classifier, rules_classifier] tier_2_premium: [claude_sonnet_4, gpt_4o] ``` #### Módulo 11: TASK_DETECTOR ```yaml nombre: Detección de Tareas función: Extrae acciones de texto tier_primario: TIER_2_PREMIUM input: text output: tasks[] - action - responsible - due_date - dependencies pii: puede_contener (nombres) providers: tier_1_local: [local_llama_8b] tier_2_premium: [claude_sonnet_4, gpt_4o] ``` ### 5.5 Familia: SEMÁNTICA #### Módulo 12: EMBEDDINGS ```yaml nombre: Embeddings función: Texto a vectores semánticos tier_primario: TIER_1_LOCAL fallback: TIER_2_PREMIUM input: text output: vector[1536], model_id pii: false providers: tier_1_local: [sentence_transformers, bge_m3] tier_1_gpu: [runpod_bge_large] tier_2_premium: [voyage_3_large, openai_ada_3] ``` #### Módulo 13: SIMILARITY ```yaml nombre: Similaridad función: Compara vectores métricas: cosine, dot, euclidean tier_primario: TIER_1_LOCAL input: vector_a, vector_b output: score, metric_used pii: false providers: tier_1_local: [numpy_similarity, faiss] ``` ### 5.6 Familia: ESTRUCTURACIÓN #### Módulo 14: FIELD_EXTRACTOR ```yaml nombre: Extracción de Campos función: Extrae datos estructurados campos_típicos: - importe - fecha - proveedor - lineas[] - cif tier_primario: TIER_2_PREMIUM input: text | document output: extracted_fields{} pii: puede_contener providers: tier_1_local: [regex_extractor, local_llama_8b] tier_2_premium: [claude_sonnet_4, gpt_4o, gemini_2_5_pro] ``` #### Módulo 15: HASH_SIGNER ```yaml nombre: Hash/Firma función: Genera SHA256/SHA512/BLAKE2B estable tier_primario: TIER_1_LOCAL input: any output: hash, algorithm, timestamp pii: false providers: tier_1_local: [hashlib_python] ``` #### Módulo 16: INPUT_TRANSLATOR ```yaml nombre: Traductor de Entrada función: Normaliza enums y payloads tier_primario: TIER_1_LOCAL input: raw_payload output: normalized_payload pii: false providers: tier_1_local: [json_transformer] ``` #### Módulo 17: OUTPUT_TRANSLATOR ```yaml nombre: Traductor de Salida función: Convierte outputs a formato legacy tier_primario: TIER_1_LOCAL input: standard_output output: legacy_format pii: false providers: tier_1_local: [json_transformer] ``` #### Módulo 18: LANG_NORMALIZER ```yaml nombre: Normalización de Lenguaje función: Detecta idioma y confianza tier_primario: TIER_1_LOCAL input: text output: language_code, confidence, script pii: false providers: tier_1_local: [langdetect, lingua] tier_2_premium: [google_translate_detect] ``` --- ## 7. Arquitectura de Providers ### 6.1 Definición de Tiers ``` ┌─────────────────────────────────────────────────────────────────┐ │ ARQUITECTURA DE PROVIDERS │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ TIER 1: AUTOALOJADOS │ │ ───────────────────── │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ TIER 1 LOCAL │ │ TIER 1 GPU │ │ │ │ │ │ (Serverless) │ │ │ │ • CPU only │ │ │ │ │ │ • Latencia min │ │ • GPU on-demand│ │ │ │ • Costo fijo │ │ • Pay-per-use │ │ │ │ • Siempre on │ │ • Auto-scale │ │ │ │ │ │ │ │ │ │ Hashing │ │ Whisper Large │ │ │ │ Regex │ │ SDXL/Flux │ │ │ │ Embeddings │ │ InsightFace │ │ │ │ LangDetect │ │ GOT-OCR │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ │ Proveedores GPU Serverless: │ │ • RunPod (principal) │ │ • Modal (alternativo) │ │ • Replicate (fallback) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ TIER 2: APIs PREMIUM (Fallback/Calidad Máxima) │ │ ─────────────────────────────────────────────── │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │ │ │ NLP Complejo │ Visión │ │ │ │ • Claude Sonnet 4 │ • GPT-4o Vision │ │ │ │ • GPT-4o │ • Gemini 2.5 Pro │ │ │ │ │ │ │ │ │ Voz │ Embeddings │ │ │ │ • ElevenLabs TTS │ • Voyage-3-large │ │ │ │ • Deepgram Nova-3 │ • OpenAI Ada-3 │ │ │ │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 6.2 Tabla de Providers por Módulo | Módulo | TIER 1 Local | TIER 1 GPU | TIER 2 Premium | |--------|--------------|------------|----------------| | IMAGE_PROCESSOR | Pillow, OpenCV | RunPod SDXL | - | | PDF_SCANNER | PyPDF, pdf2image | RunPod DocTR | - | | OCR_CORE | Tesseract | RunPod GOT-OCR, Modal Qwen-VL | Google Vision, Gemini 2.5 | | ASR | Whisper tiny/base | RunPod Faster Whisper Large V3, Groq | Deepgram Nova-3, OpenAI | | TTS | - | RunPod XTTS-v2, Modal Bark | ElevenLabs | | FACE_VECTOR | - | RunPod InsightFace | AWS Rekognition | | ID_CONSOLIDATION | NumPy | - | - | | AVATAR_GEN | - | RunPod SDXL, Modal Flux | DALL-E 3 | | SUMMARIZER | LLaMA 8B local | - | Claude Sonnet 4, GPT-4o | | CLASSIFIER | Embeddings + Rules | - | Claude Sonnet 4 | | TASK_DETECTOR | LLaMA 8B local | - | Claude Sonnet 4, GPT-4o | | EMBEDDINGS | Sentence-Transformers | RunPod BGE-Large | Voyage-3-large | | SIMILARITY | NumPy, FAISS | - | - | | FIELD_EXTRACTOR | Regex | - | Claude Sonnet 4, Gemini 2.5 | | HASH_SIGNER | hashlib | - | - | | INPUT_TRANSLATOR | JSON transformer | - | - | | OUTPUT_TRANSLATOR | JSON transformer | - | - | | LANG_NORMALIZER | langdetect, lingua | - | Google Translate | ### 6.3 Configuración de Provider ```python # app/core/providers.py from enum import Enum from pydantic import BaseModel class Tier(str, Enum): TIER_1_LOCAL = "tier_1_local" TIER_1_GPU = "tier_1_gpu" TIER_2_PREMIUM = "tier_2_premium" class ProviderConfig(BaseModel): """Configuración de un provider específico.""" name: str tier: Tier endpoint: str | None = None api_key_env: str | None = None timeout_ms: int = 30000 max_retries: int = 3 cost_per_unit: float = 0.0 class ModuleProviders(BaseModel): """Providers disponibles para un módulo.""" module: str primary_tier: Tier fallback_order: list[str] providers: dict[str, ProviderConfig] # Ejemplo: OCR_CORE OCR_CORE_PROVIDERS = ModuleProviders( module="OCR_CORE", primary_tier=Tier.TIER_1_GPU, fallback_order=[ "runpod_got_ocr", "modal_qwen_vl", "tesseract_local", "google_vision", "gemini_2_5_pro" ], providers={ "tesseract_local": ProviderConfig( name="Tesseract", tier=Tier.TIER_1_LOCAL, cost_per_unit=0.0 ), "runpod_got_ocr": ProviderConfig( name="GOT-OCR 2.0", tier=Tier.TIER_1_GPU, endpoint="https://api.runpod.ai/v2/got-ocr/runsync", api_key_env="RUNPOD_API_KEY", cost_per_unit=0.0004 ), "modal_qwen_vl": ProviderConfig( name="Qwen2.5-VL", tier=Tier.TIER_1_GPU, endpoint="https://your-app.modal.run/ocr", api_key_env="MODAL_API_KEY", cost_per_unit=0.0003 ), "google_vision": ProviderConfig( name="Google Cloud Vision", tier=Tier.TIER_2_PREMIUM, api_key_env="GOOGLE_VISION_API_KEY", cost_per_unit=0.0015 ), "gemini_2_5_pro": ProviderConfig( name="Gemini 2.5 Pro", tier=Tier.TIER_2_PREMIUM, api_key_env="GOOGLE_AI_API_KEY", cost_per_unit=0.00125 ) } ) ``` --- ## 8. Fallback Chains ### 7.1 Concepto Cada módulo define una **cadena de recuperación automática** que combina tiers y providers: ``` ┌─────────────────────────────────────────────────────────────────┐ │ PRINCIPIO DE FALLBACK │ │ │ │ "TIER 1 primero, TIER 2 solo si falla o se requiere calidad" │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 7.2 Ejemplo: OCR_CORE ``` OCR_CORE Request │ ├──► TIER 1 GPU: RunPod GOT-OCR │ │ │ ├── SUCCESS → return │ │ │ └── FAIL → next │ ├──► TIER 1 GPU: Modal Qwen-VL │ │ │ ├── SUCCESS → return (status: FALLBACK) │ │ │ └── FAIL → next │ ├──► TIER 1 LOCAL: Tesseract │ │ │ ├── SUCCESS → return (status: FALLBACK) │ │ │ └── FAIL → next │ ├──► TIER 2: Google Vision │ │ │ ├── SUCCESS → return (status: FALLBACK) │ │ │ └── FAIL → next │ └──► TIER 2: Gemini 2.5 Pro │ ├── SUCCESS → return (status: FALLBACK) │ └── FAIL → ERROR (chain exhausted) ``` ### 7.3 Ejemplo: ASR ``` ASR Request │ ├──► TIER 1 GPU: RunPod Faster Whisper Large V3 │ │ (CTranslate2, 2-4x más rápido) │ │ │ ├── SUCCESS → return │ │ │ └── FAIL/TIMEOUT → next │ ├──► TIER 1 GPU: Groq Whisper │ │ (API ultra-rápida) │ │ │ ├── SUCCESS → return │ │ │ └── FAIL → next │ ├──► TIER 1 LOCAL: Whisper-base │ │ (calidad reducida pero funciona) │ │ │ ├── SUCCESS → return (warning: quality_degraded) │ │ │ └── FAIL → next │ └──► TIER 2: OpenAI Whisper │ ├── SUCCESS → return │ └── FAIL → ERROR ``` ### 7.4 Configuración de Fallback en Request ```json { "routing": { "module": "ASR_ENGINE", "tier_preference": ["TIER_1_GPU", "TIER_1_LOCAL", "TIER_2_PREMIUM"], "provider_preference": ["ASR_FASTER_WHISPER", "ASR_GROQ", "whisper_local", "ASR_OPENAI"], "fallback_chain": [ { "provider": "ASR_FASTER_WHISPER", "timeout_ms": 30000, "retry_on": ["TIMEOUT", "SERVER_ERROR"], "notes": "RunPod Serverless - CTranslate2, 2-4x más rápido" }, { "provider": "ASR_GROQ", "timeout_ms": 15000, "retry_on": ["TIMEOUT", "SERVER_ERROR"], "notes": "Groq API - inferencia ultra-rápida" }, { "provider": "whisper_local", "timeout_ms": 60000, "accept_quality_degradation": true, "notes": "Whisper base local - calidad reducida" }, { "provider": "ASR_OPENAI", "timeout_ms": 30000, "notes": "OpenAI Whisper API - fallback final" } ], "max_fallback_level": 3 } } ``` ### 7.5 Transformaciones Pre-Retry Antes de pasar al siguiente provider, se pueden aplicar transformaciones: | Módulo | Transformación | Descripción | |--------|----------------|-------------| | OCR_CORE | resize(800px) | Reducir tamaño de imagen | | OCR_CORE | binarize | Convertir a blanco/negro | | OCR_CORE | deskew | Corregir rotación | | OCR_CORE | upscale(1.5x) | Aumentar resolución | | ASR | VAD + noise_reduction | Limpiar audio | | ASR | downsample(16kHz) | Reducir sample rate | | IMAGE | compress(quality=80) | Reducir peso | --- ## 9. Pipelines Oficiales ### 8.1 Pipeline: Factura/Ticket ``` ┌─────────────────────────────────────────────────────────────────┐ │ PIPELINE FACTURA │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. IMAGE_PROCESSOR ──► Limpieza imagen │ │ │ (TIER 1 LOCAL) │ │ ▼ │ │ 2. PDF_SCANNER ──► Recorte y deskew │ │ │ (TIER 1 LOCAL) │ │ ▼ │ │ 3. OCR_CORE (strict) ──► Extracción texto │ │ │ (TIER 1 GPU → TIER 2 fallback) │ │ ▼ │ │ 4. FIELD_EXTRACTOR ──► Campos estructurados │ │ │ (TIER 2 PREMIUM: Claude/GPT) │ │ ▼ │ │ 5. CLASSIFIER ──► Categorización │ │ │ (TIER 1 LOCAL: embeddings) │ │ ▼ │ │ 6. HASH_SIGNER ──► Hash del documento │ │ │ (TIER 1 LOCAL) │ │ ▼ │ │ 7. PERSIST ──► The Vault + Hostinger │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 8.2 Pipeline: Reunión ``` ┌─────────────────────────────────────────────────────────────────┐ │ PIPELINE REUNIÓN │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ AUDIO: │ │ 1. ASR ──► Transcripción │ │ │ (TIER 1 GPU: Whisper Large V3) │ │ ▼ │ │ 2. LANG_NORMALIZER ──► Detectar idioma │ │ │ (TIER 1 LOCAL) │ │ ▼ │ │ 3. SUMMARIZER (structured) ──► Resumen estructurado │ │ │ (TIER 2: Claude Sonnet 4) │ │ ▼ │ │ 4. TASK_DETECTOR ──► Extraer acciones │ │ │ (TIER 2: Claude Sonnet 4) │ │ ▼ │ │ 5. EMBEDDINGS ──► Vector para búsqueda │ │ │ (TIER 1 LOCAL) │ │ ▼ │ │ 6. PERSIST ──► The Vault (Milestone: acta) │ │ (Bloques: cada tarea) │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 8.3 Pipeline: Onboarding Facial ``` ┌─────────────────────────────────────────────────────────────────┐ │ PIPELINE ONBOARDING │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. IMAGE_PROCESSOR ──► Normalizar imagen │ │ │ (TIER 1 LOCAL) │ │ ▼ │ │ 2. FACE_VECTOR ──► Extraer vector 512D │ │ │ (TIER 1 GPU: InsightFace) │ │ │ (⚠️ PII: true) │ │ ▼ │ │ 3. ID_CONSOLIDATION ──► Fusionar si hay múltiples │ │ │ (TIER 1 LOCAL) │ │ ▼ │ │ 4. AVATAR_GEN ──► Generar avatar neutral │ │ │ (TIER 1 GPU: SDXL/Flux) │ │ ▼ │ │ 5. PERSIST ──► VAULT (vector facial cifrado E2E_STRICT) │ │ ──► Player.avatar_url (público) │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 10. Implementación de Referencia ### 9.1 Estructura del Proyecto ``` grace/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI app │ │ │ ├── api/ │ │ ├── __init__.py │ │ ├── router.py # Router principal │ │ └── endpoints/ │ │ ├── vision.py # /vision/* │ │ ├── voice.py # /voice/* │ │ ├── identity.py # /identity/* │ │ ├── nlp.py # /nlp/* │ │ ├── semantic.py # /semantic/* │ │ └── structure.py # /structure/* │ │ │ ├── core/ │ │ ├── __init__.py │ │ ├── config.py # Settings │ │ ├── contract.py # Contrato Común v1.2 │ │ ├── providers.py # Configuración de providers │ │ ├── fallback.py # Lógica de fallback │ │ ├── logging_internal.py # Buffer local │ │ └── logging_external.py # Flush a SYS_LOG │ │ │ ├── modules/ │ │ ├── __init__.py │ │ ├── base.py # Clase base de módulo │ │ │ │ │ ├── vision/ │ │ │ ├── image_processor.py │ │ │ ├── pdf_scanner.py │ │ │ └── ocr_core.py │ │ │ │ │ ├── voice/ │ │ │ ├── asr.py │ │ │ └── tts.py │ │ │ │ │ ├── identity/ │ │ │ ├── face_vector.py │ │ │ ├── id_consolidation.py │ │ │ └── avatar_gen.py │ │ │ │ │ ├── nlp/ │ │ │ ├── summarizer.py │ │ │ ├── classifier.py │ │ │ └── task_detector.py │ │ │ │ │ ├── semantic/ │ │ │ ├── embeddings.py │ │ │ └── similarity.py │ │ │ │ │ └── structure/ │ │ ├── field_extractor.py │ │ ├── hash_signer.py │ │ ├── input_translator.py │ │ ├── output_translator.py │ │ └── lang_normalizer.py │ │ │ └── providers/ │ ├── __init__.py │ ├── tier1_local/ │ │ ├── tesseract.py │ │ ├── whisper_local.py │ │ ├── sentence_transformers.py │ │ └── hashlib_provider.py │ │ │ ├── tier1_gpu/ │ │ ├── runpod_client.py │ │ ├── modal_client.py │ │ └── replicate_client.py │ │ │ └── tier2_premium/ │ ├── anthropic_client.py │ ├── openai_client.py │ ├── google_client.py │ ├── elevenlabs_client.py │ ├── deepgram_client.py │ └── voyage_client.py │ ├── tests/ ├── docker-compose.yml ├── Dockerfile ├── requirements.txt └── README.md ``` ### 9.2 Clase Base de Módulo ```python # app/modules/base.py from abc import ABC, abstractmethod from typing import Any from pydantic import BaseModel import logging from app.core.contract import Request, Response, Status from app.core.providers import ModuleProviders, Tier from app.core.fallback import FallbackManager from app.core.logging_internal import InternalLogger logger = logging.getLogger(__name__) class ModuleBase(ABC): """Clase base para todos los módulos de GRACE.""" def __init__( self, name: str, providers: ModuleProviders, internal_logger: InternalLogger ): self.name = name self.providers = providers self.fallback = FallbackManager(providers) self.logger = internal_logger async def execute(self, request: Request) -> Response: """Ejecuta el módulo con fallback automático.""" # Log inicio await self.logger.log_start(request) # Intentar con cada provider en orden de fallback for attempt, provider_name in enumerate(self.fallback.get_chain(request)): try: provider = self.providers.providers[provider_name] # Ejecutar transformación específica del provider result = await self._execute_provider( provider_name=provider_name, request=request ) # Construir response exitosa response = Response( contract_version="1.2", envelope=request.envelope.model_copy( update={"timestamp_end": self._now()} ), status=Status( code="SUCCESS" if attempt == 0 else "FALLBACK", fallback_level_used=attempt, tier_used=provider.tier.value, provider_used=provider_name, module_instance=f"{self.name}_{provider_name.upper()}" ), result=result, quality=self._compute_quality(result), metadata=self._build_metadata(provider, result) ) # Log éxito await self.logger.log_success(request, response) return response except Exception as e: logger.warning( f"Provider {provider_name} failed: {e}. " f"Attempting fallback..." ) await self.logger.log_error(request, provider_name, e) continue # Todos los providers fallaron response = Response( contract_version="1.2", envelope=request.envelope.model_copy( update={"timestamp_end": self._now()} ), status=Status( code="ERROR", fallback_level_used=len(self.fallback.get_chain(request)), tier_used="NONE", provider_used="NONE" ), errors=[{ "code": "FALLBACK_EXHAUSTED", "message": "All providers failed" }] ) await self.logger.log_failure(request, response) return response @abstractmethod async def _execute_provider( self, provider_name: str, request: Request ) -> dict[str, Any]: """Implementación específica del módulo para cada provider.""" pass @abstractmethod def _compute_quality(self, result: dict) -> dict: """Calcula métricas de calidad del resultado.""" pass def _build_metadata(self, provider, result) -> dict: """Construye metadata del procesamiento.""" return { "model_id": getattr(result, "model_id", provider.name), "processing_ms": getattr(result, "processing_ms", 0), "cost_units": provider.cost_per_unit } def _now(self) -> str: from datetime import datetime return datetime.utcnow().isoformat() + "Z" ``` ### 9.3 Ejemplo: OCR_CORE ```python # app/modules/vision/ocr_core.py from typing import Any from app.modules.base import ModuleBase from app.core.contract import Request from app.providers.tier1_local.tesseract import TesseractProvider from app.providers.tier1_gpu.runpod_client import RunPodClient from app.providers.tier2_premium.google_client import GoogleVisionClient class OCRCore(ModuleBase): """Módulo de OCR con fallback multi-tier.""" def __init__(self, *args, **kwargs): super().__init__("OCR_CORE", *args, **kwargs) # Inicializar providers self._providers = { "tesseract_local": TesseractProvider(), "runpod_got_ocr": RunPodClient("got-ocr"), "modal_qwen_vl": RunPodClient("qwen-vl"), # o ModalClient "google_vision": GoogleVisionClient(), } async def _execute_provider( self, provider_name: str, request: Request ) -> dict[str, Any]: """Ejecuta OCR con el provider especificado.""" provider = self._providers[provider_name] image_data = request.payload.content # Aplicar transformaciones si es retry if request.routing.get("apply_transforms"): image_data = await self._apply_transforms( image_data, request.routing["transforms"] ) # Ejecutar OCR result = await provider.ocr( image=image_data, mode=request.context.get("mode", "strict"), lang=request.context.get("lang", "es") ) return { "schema": "ocr_result_v1", "data": { "text": result.text, "blocks": result.blocks, "confidence": result.confidence } } def _compute_quality(self, result: dict) -> dict: """Calcula métricas de calidad del OCR.""" data = result.get("data", {}) return { "confidence": data.get("confidence", 0.0), "coverage": 1.0 if data.get("text") else 0.0, "validation_passed": data.get("confidence", 0) > 0.7 } async def _apply_transforms( self, image_data: str, transforms: list[str] ) -> str: """Aplica transformaciones de imagen para mejorar OCR.""" from app.modules.vision.image_processor import ImageProcessor processor = ImageProcessor() for transform in transforms: if transform == "resize": image_data = await processor.resize(image_data, max_width=800) elif transform == "binarize": image_data = await processor.binarize(image_data) elif transform == "deskew": image_data = await processor.deskew(image_data) elif transform == "upscale": image_data = await processor.upscale(image_data, factor=1.5) return image_data ``` ### 9.4 Validador de Contrato ```python # app/core/contract.py from pydantic import BaseModel, validator from typing import Literal, Optional from enum import Enum class StatusCode(str, Enum): SUCCESS = "SUCCESS" PARTIAL = "PARTIAL" ERROR = "ERROR" TIMEOUT = "TIMEOUT" FALLBACK = "FALLBACK" class ContractValidator: """Validador del Contrato Común v1.2""" REQUIRED_ENVELOPE = ['trace_id', 'step_id', 'idempotency_key'] REQUIRED_ROUTING = ['module'] @classmethod def validate_request(cls, request: dict) -> tuple[bool, list[str]]: errors = [] # Contract version if request.get('contract_version') != "1.2": errors.append("contract_version must be '1.2'") # Envelope envelope = request.get('envelope', {}) for field in cls.REQUIRED_ENVELOPE: if field not in envelope: errors.append(f"Missing envelope.{field}") # Routing routing = request.get('routing', {}) for field in cls.REQUIRED_ROUTING: if field not in routing: errors.append(f"Missing routing.{field}") # Payload if 'payload' not in request: errors.append("Missing: payload") return len(errors) == 0, errors @classmethod def validate_response(cls, response: dict) -> tuple[bool, list[str]]: errors = [] # Status status = response.get('status', {}) if 'code' not in status: errors.append("Missing: status.code") elif status['code'] not in [s.value for s in StatusCode]: errors.append(f"Invalid status.code: {status['code']}") # Result (requerido si SUCCESS o PARTIAL) if status.get('code') in ['SUCCESS', 'PARTIAL', 'FALLBACK']: if 'result' not in response: errors.append("Missing: result") return len(errors) == 0, errors ``` --- ## Resumen GRACE proporciona: | Característica | Descripción | |----------------|-------------| | **18 módulos** | Microservicios cognitivos especializados y desacoplados | | **Contrato Común v1.2** | Interfaz universal para interoperabilidad total | | **Arquitectura 2-Tier** | TIER 1 autoalojado (local + GPU serverless) + TIER 2 APIs premium | | **Fallback chains** | Tolerancia a fallos con degradación elegante | | **Sistema de logging** | Buffer interno + SYS_LOG externo + SENTINEL dual | | **Pipelines oficiales** | Flujos pre-definidos para casos de uso comunes | ### Comparativa Jarvis → GRACE | Aspecto | Jarvis (v4) | GRACE (v5) | |---------|-------------|------------| | Providers | Local + APIs remotas | TIER 1 (autoalojado) + TIER 2 (premium) | | GPU | Dependía de APIs | GPU Serverless (RunPod/Modal) | | Logging | Solo SYS_LOG | Buffer interno + SYS_LOG + SENTINEL | | Fallback | Por provider | Por tier + provider | | Costo | Variable | Optimizado (TIER 1 primero) | | Latencia | Variable | Predecible (local/GPU serverless) | --- **Documento anterior**: `05_ENTIDADES.md` **Siguiente documento**: `07_SEGURIDAD_TRAZABILIDAD.md`