1666 lines
63 KiB
Markdown
1666 lines
63 KiB
Markdown
# 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`
|