1159 lines
50 KiB
Markdown
1159 lines
50 KiB
Markdown
# 07a. SENTINEL — Sistema de Auditoría
|
|
**Manual de Arquitectura Técnica — Documento 07a**
|
|
**Versión:** 1.2
|
|
**Dependencia:** `S-CONTRACT.md`
|
|
**Estado:** Enterprise Standard
|
|
|
|
---
|
|
|
|
# A.1 Introducción a SENTINEL
|
|
|
|
SENTINEL es el **sistema de auditoría automatizada** del ecosistema GRACE. Opera como un guardián silencioso que verifica la integridad, calidad y conformidad de todas las operaciones.
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ PRINCIPIO SENTINEL │
|
|
│ │
|
|
│ "Confía, pero verifica. Verifica todo, siempre, sin excepción"│
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## A.1.1 Objetivos
|
|
|
|
1. **Integridad**: Verificar que los datos no han sido alterados.
|
|
2. **Conformidad**: Validar cumplimiento del contrato común.
|
|
3. **Calidad**: Detectar degradación en los módulos IA.
|
|
4. **Anomalías**: Identificar patrones sospechosos o errores sistemáticos.
|
|
5. **Trazabilidad**: Asegurar que todo evento tiene su registro.
|
|
|
|
## A.1.2 Filosofía Dual
|
|
|
|
SENTINEL implementa una estrategia de **dos velocidades**:
|
|
|
|
| Aspecto | SENTINEL-LIGHT | SENTINEL-DEEP |
|
|
|---------|----------------|---------------|
|
|
| Analogía | Guardia de seguridad | Detective investigador |
|
|
| Enfoque | Exhaustivo, superficial | Selectivo, profundo |
|
|
| Motor | Reglas + ML ligero | LLM pesado |
|
|
| Frecuencia | Continua (cada 5 min) | Periódica (cada hora) |
|
|
| Costo | Mínimo | Variable |
|
|
|
|
---
|
|
|
|
# A.2 SENTINEL-LIGHT (Centinela Rápido)
|
|
|
|
## A.2.1 Arquitectura
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ SENTINEL-LIGHT │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ RULES │ │ STATS │ │ ALERTS │ │
|
|
│ │ ENGINE │───▶│ COLLECTOR │───▶│ DISPATCHER │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
│ │ │ │ │
|
|
│ ▼ ▼ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────┤
|
|
│ │ SYS_LOG (Source) │
|
|
│ └─────────────────────────────────────────────────────────────┤
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌─────────────────────────────────────────────────────────────┤
|
|
│ │ AUDIT_RESULTS (Sink) │
|
|
│ └─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## A.2.2 Ciclo de Ejecución
|
|
|
|
```
|
|
CADA 5 MINUTOS:
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 1. QUERY │
|
|
│ SELECT * FROM SYS_LOG │
|
|
│ WHERE audit_status = 'PENDING' │
|
|
│ AND timestamp_created > NOW() - INTERVAL '10 minutes' │
|
|
│ ORDER BY timestamp_created │
|
|
│ LIMIT 1000 │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 2. APPLY RULES │
|
|
│ Para cada registro: │
|
|
│ - Ejecutar reglas de integridad │
|
|
│ - Ejecutar reglas de conformidad │
|
|
│ - Ejecutar reglas de rendimiento │
|
|
│ - Acumular resultados │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 3. CLASSIFY │
|
|
│ - PASS: Todas las reglas OK │
|
|
│ - WARN: Reglas de severidad MEDIUM fallaron │
|
|
│ - FAIL: Reglas de severidad HIGH/CRITICAL fallaron │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 4. UPDATE & ESCALATE │
|
|
│ - UPDATE SYS_LOG SET audit_status, audit_light_ts │
|
|
│ - INSERT AUDIT_RESULTS con resumen del batch │
|
|
│ - Si FAIL → marcar para SENTINEL-DEEP │
|
|
│ - Si CRITICAL → alerta inmediata │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## A.2.3 Catálogo de Reglas
|
|
|
|
### Reglas de Integridad (I-*)
|
|
|
|
```yaml
|
|
rules:
|
|
I-001:
|
|
name: "Hash de entrada coincide"
|
|
description: "Verificar que input_hash == SHA256(archivo en input_ref)"
|
|
severity: CRITICAL
|
|
condition: |
|
|
VERIFY_HASH(input_ref, input_hash) == true
|
|
on_fail:
|
|
action: ESCALATE_DEEP
|
|
alert: IMMEDIATE
|
|
|
|
I-002:
|
|
name: "Hash de salida coincide"
|
|
description: "Verificar que output_hash == SHA256(archivo en output_ref)"
|
|
severity: CRITICAL
|
|
condition: |
|
|
output_ref IS NULL OR VERIFY_HASH(output_ref, output_hash) == true
|
|
on_fail:
|
|
action: ESCALATE_DEEP
|
|
alert: IMMEDIATE
|
|
|
|
I-003:
|
|
name: "Cadena de trazas válida"
|
|
description: "Si tiene parent_trace_id, el padre debe existir"
|
|
severity: HIGH
|
|
condition: |
|
|
parent_trace_id IS NULL
|
|
OR EXISTS(SELECT 1 FROM SYS_LOG WHERE trace_id = parent_trace_id)
|
|
on_fail:
|
|
action: LOG_WARN
|
|
alert: BATCH
|
|
|
|
I-004:
|
|
name: "Referencias no huérfanas"
|
|
description: "input_ref y output_ref deben apuntar a archivos existentes"
|
|
severity: HIGH
|
|
condition: |
|
|
FILE_EXISTS(input_ref) == true
|
|
AND (output_ref IS NULL OR FILE_EXISTS(output_ref) == true)
|
|
on_fail:
|
|
action: ESCALATE_DEEP
|
|
alert: BATCH
|
|
|
|
I-005:
|
|
name: "Idempotency key única por trace"
|
|
description: "No debe haber duplicados de idempotency_key con distinto resultado"
|
|
severity: MEDIUM
|
|
condition: |
|
|
NOT EXISTS(
|
|
SELECT 1 FROM SYS_LOG
|
|
WHERE idempotency_key = {current.idempotency_key}
|
|
AND trace_id != {current.trace_id}
|
|
AND output_hash != {current.output_hash}
|
|
)
|
|
on_fail:
|
|
action: LOG_WARN
|
|
alert: BATCH
|
|
```
|
|
|
|
### Reglas de Conformidad (C-*)
|
|
|
|
```yaml
|
|
rules:
|
|
C-001:
|
|
name: "Campos obligatorios presentes"
|
|
description: "Verificar campos requeridos según perfil"
|
|
severity: HIGH
|
|
condition: |
|
|
IF profile == 'FULL':
|
|
ALL_PRESENT(trace_id, step_id, idempotency_key, module_name,
|
|
input_hash, input_ref, status_code)
|
|
ELSE:
|
|
ALL_PRESENT(trace_id, idempotency_key, module_name, status_code)
|
|
on_fail:
|
|
action: LOG_ERROR
|
|
alert: BATCH
|
|
|
|
C-002:
|
|
name: "Status code válido"
|
|
description: "status_code debe ser un valor permitido"
|
|
severity: HIGH
|
|
condition: |
|
|
status_code IN ('PENDING', 'RUNNING', 'SUCCESS', 'PARTIAL',
|
|
'ERROR', 'TIMEOUT', 'FALLBACK', 'CANCELLED')
|
|
on_fail:
|
|
action: LOG_ERROR
|
|
alert: BATCH
|
|
|
|
C-003:
|
|
name: "Step type válido"
|
|
description: "step_type debe ser un valor permitido"
|
|
severity: HIGH
|
|
condition: |
|
|
step_type IN ('INIT', 'VALIDATE', 'ROUTE', 'TRANSFORM', 'FALLBACK',
|
|
'PERSIST', 'NOTIFY', 'COMPLETE', 'ERROR', 'AUDIT', 'CANCEL')
|
|
on_fail:
|
|
action: LOG_ERROR
|
|
alert: BATCH
|
|
|
|
C-004:
|
|
name: "Confidence en rango"
|
|
description: "Si existe confidence, debe estar entre 0 y 1"
|
|
severity: MEDIUM
|
|
condition: |
|
|
confidence IS NULL OR (confidence >= 0 AND confidence <= 1)
|
|
on_fail:
|
|
action: LOG_WARN
|
|
alert: BATCH
|
|
|
|
C-005:
|
|
name: "Timestamps coherentes"
|
|
description: "timestamp_completed >= timestamp_started >= timestamp_created"
|
|
severity: MEDIUM
|
|
condition: |
|
|
timestamp_started IS NULL OR timestamp_started >= timestamp_created
|
|
AND timestamp_completed IS NULL OR timestamp_completed >= timestamp_started
|
|
on_fail:
|
|
action: LOG_WARN
|
|
alert: BATCH
|
|
|
|
C-006:
|
|
name: "Batch coherente"
|
|
description: "Si is_batch, debe tener batch_id y item_index válidos"
|
|
severity: MEDIUM
|
|
condition: |
|
|
batch_id IS NULL
|
|
OR (batch_item_index > 0 AND batch_items_total >= batch_item_index)
|
|
on_fail:
|
|
action: LOG_WARN
|
|
alert: BATCH
|
|
```
|
|
|
|
### Reglas de Rendimiento (P-*)
|
|
|
|
```yaml
|
|
rules:
|
|
P-001:
|
|
name: "Duración dentro de umbral"
|
|
description: "duration_ms no debe exceder 3x el esperado para el módulo"
|
|
severity: MEDIUM
|
|
params:
|
|
thresholds:
|
|
CLASSIFIER: 1500
|
|
OCR_CORE: 10000
|
|
ASR_ENGINE: 60000
|
|
SUMMARIZER: 5000
|
|
EMBEDDINGS: 2000
|
|
default: 30000
|
|
condition: |
|
|
duration_ms IS NULL
|
|
OR duration_ms < GET_THRESHOLD(module_name) * 3
|
|
on_fail:
|
|
action: LOG_WARN
|
|
alert: BATCH
|
|
|
|
P-002:
|
|
name: "Tasa de error por módulo"
|
|
description: "Tasa de errores en última hora < 5%"
|
|
severity: HIGH
|
|
scope: AGGREGATE
|
|
window: 1 HOUR
|
|
condition: |
|
|
(SELECT COUNT(*) FILTER(WHERE status_code IN ('ERROR', 'TIMEOUT'))::float
|
|
/ NULLIF(COUNT(*), 0)
|
|
FROM SYS_LOG
|
|
WHERE module_name = {current.module_name}
|
|
AND timestamp_created > NOW() - INTERVAL '1 hour'
|
|
) < 0.05
|
|
on_fail:
|
|
action: ESCALATE_DEEP
|
|
alert: IMMEDIATE
|
|
|
|
P-003:
|
|
name: "Fallback excesivo"
|
|
description: "Tasa de fallback en última hora < 20%"
|
|
severity: MEDIUM
|
|
scope: AGGREGATE
|
|
window: 1 HOUR
|
|
condition: |
|
|
(SELECT COUNT(*) FILTER(WHERE status_code = 'FALLBACK')::float
|
|
/ NULLIF(COUNT(*), 0)
|
|
FROM SYS_LOG
|
|
WHERE module_name = {current.module_name}
|
|
AND timestamp_created > NOW() - INTERVAL '1 hour'
|
|
) < 0.20
|
|
on_fail:
|
|
action: LOG_WARN
|
|
alert: BATCH
|
|
|
|
P-004:
|
|
name: "Confidence por debajo de baseline"
|
|
description: "Confidence promedio no debe caer más de 0.1 del baseline"
|
|
severity: MEDIUM
|
|
scope: AGGREGATE
|
|
window: 1 HOUR
|
|
params:
|
|
baselines:
|
|
CLASSIFIER: 0.90
|
|
OCR_CORE: 0.85
|
|
ASR_ENGINE: 0.80
|
|
default: 0.80
|
|
condition: |
|
|
(SELECT AVG(confidence)
|
|
FROM SYS_LOG
|
|
WHERE module_name = {current.module_name}
|
|
AND confidence IS NOT NULL
|
|
AND timestamp_created > NOW() - INTERVAL '1 hour'
|
|
) >= GET_BASELINE(module_name) - 0.10
|
|
on_fail:
|
|
action: ESCALATE_DEEP
|
|
alert: BATCH
|
|
```
|
|
|
|
### Reglas de Seguridad (S-*)
|
|
|
|
```yaml
|
|
rules:
|
|
S-001:
|
|
name: "PII con cifrado adecuado"
|
|
description: "Si pii_detected=true, encryption_profile debe ser E2E_BASIC o superior"
|
|
severity: HIGH
|
|
condition: |
|
|
pii_detected == false
|
|
OR encryption_profile IN ('E2E_BASIC', 'E2E_STRICT')
|
|
on_fail:
|
|
action: ESCALATE_DEEP
|
|
alert: IMMEDIATE
|
|
|
|
S-002:
|
|
name: "Datos SECRET con cifrado estricto"
|
|
description: "Si data_sensitivity=SECRET, encryption_profile debe ser E2E_STRICT"
|
|
severity: CRITICAL
|
|
condition: |
|
|
data_sensitivity != 'SECRET'
|
|
OR encryption_profile == 'E2E_STRICT'
|
|
on_fail:
|
|
action: ESCALATE_DEEP
|
|
alert: IMMEDIATE
|
|
|
|
S-003:
|
|
name: "Key Vault referenciado para cifrado"
|
|
description: "Si encryption_profile != NONE, debe existir key_vault_ref"
|
|
severity: HIGH
|
|
condition: |
|
|
encryption_profile == 'NONE'
|
|
OR key_vault_ref IS NOT NULL
|
|
on_fail:
|
|
action: LOG_ERROR
|
|
alert: BATCH
|
|
```
|
|
|
|
## A.2.4 Output de SENTINEL-LIGHT
|
|
|
|
```json
|
|
{
|
|
"sentinel_type": "LIGHT",
|
|
"version": "1.2",
|
|
"execution_id": "uuid-ejecucion",
|
|
"timestamp_start": "2025-12-01T10:30:00.000Z",
|
|
"timestamp_end": "2025-12-01T10:30:03.450Z",
|
|
"duration_ms": 3450,
|
|
|
|
"scope": {
|
|
"window_start": "2025-12-01T10:20:00.000Z",
|
|
"window_end": "2025-12-01T10:30:00.000Z",
|
|
"records_queried": 1250,
|
|
"records_processed": 1250
|
|
},
|
|
|
|
"summary": {
|
|
"passed": 1230,
|
|
"warnings": 15,
|
|
"failures": 5,
|
|
"pass_rate": 0.984
|
|
},
|
|
|
|
"by_severity": {
|
|
"CRITICAL": 1,
|
|
"HIGH": 3,
|
|
"MEDIUM": 16,
|
|
"LOW": 0
|
|
},
|
|
|
|
"by_rule": {
|
|
"I-001": {"passed": 1250, "failed": 0},
|
|
"I-002": {"passed": 1249, "failed": 1},
|
|
"C-001": {"passed": 1248, "failed": 2},
|
|
"P-001": {"passed": 1235, "failed": 15},
|
|
"S-001": {"passed": 1248, "failed": 2}
|
|
},
|
|
|
|
"by_module": {
|
|
"CLASSIFIER": {"total": 500, "passed": 498, "failed": 2},
|
|
"OCR_CORE": {"total": 300, "passed": 295, "failed": 5},
|
|
"ASR_ENGINE": {"total": 50, "passed": 48, "failed": 2}
|
|
},
|
|
|
|
"escalations": [
|
|
{
|
|
"trace_id": "uuid-problema-1",
|
|
"step_id": "uuid-step-1",
|
|
"rule_violated": "I-002",
|
|
"severity": "CRITICAL",
|
|
"details": "Output hash mismatch: expected abc123, got def456",
|
|
"escalate_to": "DEEP",
|
|
"alert_sent": true
|
|
},
|
|
{
|
|
"trace_id": "uuid-problema-2",
|
|
"step_id": "uuid-step-2",
|
|
"rule_violated": "S-001",
|
|
"severity": "HIGH",
|
|
"details": "PII detected but encryption_profile=NONE",
|
|
"escalate_to": "DEEP",
|
|
"alert_sent": true
|
|
}
|
|
],
|
|
|
|
"aggregate_alerts": [
|
|
{
|
|
"rule": "P-002",
|
|
"module": "OCR_CORE",
|
|
"message": "Error rate 6.2% exceeds threshold 5%",
|
|
"severity": "HIGH",
|
|
"escalate_to": "DEEP"
|
|
}
|
|
],
|
|
|
|
"next_run": "2025-12-01T10:35:00.000Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
# A.3 SENTINEL-DEEP (Analista Profundo)
|
|
|
|
## A.3.1 Arquitectura
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ SENTINEL-DEEP │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ QUEUE │ │ LLM │ │ REPORT │ │
|
|
│ │ MANAGER │───▶│ ANALYZER │───▶│ GENERATOR │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
│ │ │ │ │
|
|
│ │ │ │ │
|
|
│ ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ │
|
|
│ │ Escalations │ │ Claude/ │ │ Findings │ │
|
|
│ │ + Sampling │ │ GPT-4o │ │ + Actions │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## A.3.2 Triggers de Ejecución
|
|
|
|
| Trigger | Condición | Prioridad | Acción |
|
|
|---------|-----------|-----------|--------|
|
|
| `ESCALATION` | SENTINEL-LIGHT marcó para DEEP | ALTA | Analizar inmediatamente |
|
|
| `ERROR_CRITICAL` | Regla CRITICAL violada | CRÍTICA | Analizar + alertar |
|
|
| `HOURLY_SAMPLE` | Cron cada hora | NORMAL | Muestreo aleatorio |
|
|
| `PATTERN_DETECTED` | Mismo error 3+ veces | ALTA | Investigar patrón |
|
|
| `DRIFT_ALERT` | Confidence < baseline - 0.1 | MEDIA | Analizar tendencia |
|
|
| `MANUAL` | Solicitud de operador | VARIABLE | Según solicitud |
|
|
|
|
## A.3.3 Estrategia de Muestreo
|
|
|
|
```python
|
|
def select_for_deep_analysis(window_hours=1):
|
|
"""
|
|
Selecciona registros para análisis profundo.
|
|
"""
|
|
candidates = []
|
|
|
|
# 1. TODOS los escalados de LIGHT (prioridad máxima)
|
|
escalated = query("""
|
|
SELECT * FROM SYS_LOG
|
|
WHERE audit_status IN ('LIGHT_WARN', 'LIGHT_FAIL')
|
|
AND audit_deep_ts IS NULL
|
|
AND timestamp_created > NOW() - INTERVAL '{window_hours} hours'
|
|
""")
|
|
candidates.extend(escalated)
|
|
|
|
# 2. TODOS los errores no analizados
|
|
errors = query("""
|
|
SELECT * FROM SYS_LOG
|
|
WHERE status_code IN ('ERROR', 'TIMEOUT')
|
|
AND audit_deep_ts IS NULL
|
|
AND timestamp_created > NOW() - INTERVAL '{window_hours} hours'
|
|
""")
|
|
candidates.extend(errors)
|
|
|
|
# 3. Muestreo aleatorio de exitosos (5% o mínimo 10)
|
|
success_count = query("""
|
|
SELECT COUNT(*) FROM SYS_LOG
|
|
WHERE status_code = 'SUCCESS'
|
|
AND audit_deep_ts IS NULL
|
|
AND timestamp_created > NOW() - INTERVAL '{window_hours} hours'
|
|
""")
|
|
sample_size = max(10, int(success_count * 0.05))
|
|
|
|
sampled = query(f"""
|
|
SELECT * FROM SYS_LOG
|
|
WHERE status_code = 'SUCCESS'
|
|
AND audit_deep_ts IS NULL
|
|
AND timestamp_created > NOW() - INTERVAL '{window_hours} hours'
|
|
ORDER BY RANDOM()
|
|
LIMIT {sample_size}
|
|
""")
|
|
candidates.extend(sampled)
|
|
|
|
# 4. Registros con confidence anormalmente baja
|
|
low_confidence = query("""
|
|
SELECT * FROM SYS_LOG
|
|
WHERE confidence IS NOT NULL
|
|
AND confidence < 0.6
|
|
AND audit_deep_ts IS NULL
|
|
AND timestamp_created > NOW() - INTERVAL '{window_hours} hours'
|
|
""")
|
|
candidates.extend(low_confidence)
|
|
|
|
return deduplicate(candidates)
|
|
```
|
|
|
|
## A.3.4 Prompt del Sistema SENTINEL-DEEP
|
|
|
|
```markdown
|
|
# ROL
|
|
Eres SENTINEL-DEEP, el sistema de auditoría profunda del ecosistema GRACE.
|
|
Tu función es analizar registros del sistema para:
|
|
1. Verificar cumplimiento del contrato común
|
|
2. Investigar causa raíz de errores
|
|
3. Detectar patrones y anomalías
|
|
4. Validar calidad y coherencia de resultados
|
|
|
|
# CONTEXTO DEL SISTEMA
|
|
- **Arquitectura**: GRACE (18 microservicios cognitivos) + ALFRED (orquestador n8n)
|
|
- **Filosofía**: "Alfred Decide, GRACE Transforma"
|
|
- **Contrato**: S-CONTRACT v1.2
|
|
- **Perfiles**: FULL (trazabilidad completa), LITE (mínimo necesario)
|
|
|
|
# CONOCIMIENTO DEL CONTRATO
|
|
## Campos obligatorios FULL
|
|
- envelope: trace_id, step_id, idempotency_key, timestamp_init
|
|
- routing: module
|
|
- payload: type, content
|
|
- storage: input_ref, input_hash
|
|
|
|
## Campos obligatorios LITE
|
|
- envelope: trace_id, idempotency_key
|
|
- routing: module
|
|
- payload: type, content
|
|
|
|
## Status codes válidos
|
|
SUCCESS, PARTIAL, ERROR, TIMEOUT, FALLBACK, PENDING, RUNNING, CANCELLED
|
|
|
|
## Step types válidos
|
|
INIT, VALIDATE, ROUTE, TRANSFORM, FALLBACK, PERSIST, NOTIFY, COMPLETE, ERROR, AUDIT, CANCEL
|
|
|
|
# TAREA
|
|
Analiza los registros proporcionados y genera un informe estructurado.
|
|
|
|
Para cada registro, evalúa:
|
|
|
|
## 1. CONFORMIDAD DEL CONTRATO
|
|
- ¿Tiene todos los campos obligatorios según su perfil?
|
|
- ¿Los valores están en rangos válidos?
|
|
- ¿La estructura es coherente internamente?
|
|
|
|
## 2. INTEGRIDAD DE DATOS
|
|
- ¿Los hashes son consistentes?
|
|
- ¿Las referencias apuntan a recursos válidos?
|
|
- ¿La cadena de trazas es válida?
|
|
|
|
## 3. CALIDAD DEL RESULTADO
|
|
- ¿El confidence reportado es creíble?
|
|
- ¿El resultado tiene sentido para el tipo de input?
|
|
- ¿Hay señales de alucinación o error del modelo?
|
|
|
|
## 4. CAUSA RAÍZ (si hay error)
|
|
- ¿Qué falló exactamente?
|
|
- ¿Es problema de datos, módulo, infraestructura, o configuración?
|
|
- ¿Es caso aislado o patrón recurrente?
|
|
|
|
## 5. RECOMENDACIÓN
|
|
- Acción correctiva inmediata
|
|
- Prevención para el futuro
|
|
- ¿Requiere escalado a humano?
|
|
|
|
# OUTPUT OBLIGATORIO
|
|
Responde ÚNICAMENTE con un JSON válido siguiendo este schema exacto:
|
|
|
|
```json
|
|
{
|
|
"analysis_id": "uuid",
|
|
"timestamp": "ISO8601",
|
|
"records_analyzed": 0,
|
|
"findings": [
|
|
{
|
|
"trace_id": "uuid",
|
|
"step_id": "uuid",
|
|
"diagnosis": {
|
|
"contract_compliant": true,
|
|
"integrity_valid": true,
|
|
"quality_credible": true,
|
|
"issues": []
|
|
},
|
|
"root_cause": {
|
|
"category": "DATA|MODULE|INFRA|CONFIG|UNKNOWN|NONE",
|
|
"description": "string",
|
|
"is_pattern": false,
|
|
"pattern_id": null,
|
|
"related_traces": []
|
|
},
|
|
"recommendation": {
|
|
"action": "string",
|
|
"prevention": "string",
|
|
"urgency": "LOW|MEDIUM|HIGH|CRITICAL",
|
|
"escalate_human": false,
|
|
"escalate_reason": null
|
|
},
|
|
"confidence": 0.95
|
|
}
|
|
],
|
|
"patterns_detected": [],
|
|
"summary": {
|
|
"total_issues": 0,
|
|
"critical": 0,
|
|
"high": 0,
|
|
"medium": 0,
|
|
"low": 0,
|
|
"human_escalation_needed": false
|
|
}
|
|
}
|
|
```
|
|
|
|
# REGLAS CRÍTICAS
|
|
1. NUNCA inventes datos que no estén en los registros
|
|
2. Si no puedes determinar algo, usa "UNKNOWN" y confidence bajo
|
|
3. Sé conservador con las recomendaciones de escalado a humano
|
|
4. Busca PATRONES entre múltiples registros
|
|
5. El output debe ser JSON válido, sin texto adicional
|
|
```
|
|
|
|
## A.3.5 Flujo de Análisis
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 1. COLLECT │
|
|
│ - Recoger registros según estrategia de muestreo │
|
|
│ - Agrupar por trace_id para contexto completo │
|
|
│ - Cargar datos relacionados (input/output refs) │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 2. ENRICH │
|
|
│ - Añadir contexto del M-CONTRACT del módulo │
|
|
│ - Añadir historial reciente del mismo módulo │
|
|
│ - Añadir métricas de baseline │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 3. ANALYZE (LLM) │
|
|
│ - Enviar al LLM con prompt del sistema │
|
|
│ - Timeout: 60 segundos │
|
|
│ - Fallback: GPT-4o → Claude → Local-Summary │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 4. VALIDATE │
|
|
│ - Parsear JSON de respuesta │
|
|
│ - Validar schema │
|
|
│ - Verificar coherencia de findings │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 5. PERSIST │
|
|
│ - INSERT en AUDIT_RESULTS │
|
|
│ - UPDATE SYS_LOG.audit_status, audit_deep_ts │
|
|
│ - Registrar patrones detectados │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ 6. ESCALATE │
|
|
│ - Si human_escalation_needed → notificar │
|
|
│ - Si pattern detectado → crear alerta │
|
|
│ - Si acción urgente → trigger workflow │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## A.3.6 Output de SENTINEL-DEEP
|
|
|
|
```json
|
|
{
|
|
"sentinel_type": "DEEP",
|
|
"version": "1.2",
|
|
"analysis_id": "uuid-analysis",
|
|
"trigger": "ESCALATION",
|
|
"timestamp_start": "2025-12-01T11:00:00.000Z",
|
|
"timestamp_end": "2025-12-01T11:00:12.350Z",
|
|
"duration_ms": 12350,
|
|
|
|
"model_used": {
|
|
"provider": "openrouter",
|
|
"model_id": "anthropic/claude-sonnet-4-20250514",
|
|
"tokens_input": 4500,
|
|
"tokens_output": 1200,
|
|
"cost_units": 0.0089
|
|
},
|
|
|
|
"scope": {
|
|
"records_queried": 25,
|
|
"records_analyzed": 25,
|
|
"trace_ids": ["uuid-1", "uuid-2", "..."]
|
|
},
|
|
|
|
"findings": [
|
|
{
|
|
"trace_id": "uuid-problema-1",
|
|
"step_id": "uuid-step-1",
|
|
"diagnosis": {
|
|
"contract_compliant": false,
|
|
"integrity_valid": true,
|
|
"quality_credible": true,
|
|
"issues": [
|
|
"Campo 'coverage' ausente en response de OCR_CORE",
|
|
"Schema output_v1 requiere coverage cuando confidence < 0.8"
|
|
]
|
|
},
|
|
"root_cause": {
|
|
"category": "MODULE",
|
|
"description": "OCR_CORE v1.0 no incluye campo coverage cuando la imagen tiene baja calidad. El módulo debería retornar coverage=0 en estos casos.",
|
|
"is_pattern": true,
|
|
"pattern_id": "PAT-2025-001",
|
|
"related_traces": ["uuid-2", "uuid-5", "uuid-12"]
|
|
},
|
|
"recommendation": {
|
|
"action": "Actualizar OCR_CORE para incluir coverage=0.0 cuando confidence < 0.5",
|
|
"prevention": "Añadir validación de schema en Alfred antes de persistir",
|
|
"urgency": "MEDIUM",
|
|
"escalate_human": true,
|
|
"escalate_reason": "Requiere cambio de código en módulo"
|
|
},
|
|
"confidence": 0.92
|
|
}
|
|
],
|
|
|
|
"patterns_detected": [
|
|
{
|
|
"pattern_id": "PAT-2025-001",
|
|
"description": "OCR_CORE omite campo coverage en respuestas de baja confianza",
|
|
"occurrences": 4,
|
|
"first_seen": "2025-12-01T08:15:00.000Z",
|
|
"affected_module": "OCR_CORE",
|
|
"severity": "MEDIUM",
|
|
"recommended_fix": "Patch en M-CONTRACT de OCR_CORE"
|
|
}
|
|
],
|
|
|
|
"contract_audit": {
|
|
"total_checked": 25,
|
|
"compliant": 21,
|
|
"non_compliant": 4,
|
|
"compliance_rate": 0.84,
|
|
"common_violations": [
|
|
{"field": "coverage", "count": 4},
|
|
{"field": "tokens_output", "count": 1}
|
|
]
|
|
},
|
|
|
|
"summary": {
|
|
"total_issues": 5,
|
|
"critical": 0,
|
|
"high": 1,
|
|
"medium": 3,
|
|
"low": 1,
|
|
"human_escalation_needed": true,
|
|
"patterns_found": 1,
|
|
"new_patterns": 1
|
|
},
|
|
|
|
"actions_triggered": [
|
|
{
|
|
"type": "NOTIFICATION",
|
|
"target": "admin@tzzr.pro",
|
|
"template": "sentinel_escalation",
|
|
"sent_at": "2025-12-01T11:00:15.000Z"
|
|
}
|
|
],
|
|
|
|
"next_scheduled": "2025-12-01T12:00:00.000Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
# A.4 Tabla AUDIT_RESULTS
|
|
|
|
```sql
|
|
CREATE TABLE AUDIT_RESULTS (
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- IDENTIFICACIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
sentinel_type VARCHAR(10) NOT NULL,
|
|
execution_id UUID NOT NULL,
|
|
timestamp TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- TRIGGER Y SCOPE
|
|
-- ═══════════════════════════════════════════════════════════
|
|
trigger_type VARCHAR(50),
|
|
window_start TIMESTAMPTZ,
|
|
window_end TIMESTAMPTZ,
|
|
records_queried INTEGER,
|
|
records_processed INTEGER,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- EJECUCIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
duration_ms INTEGER,
|
|
model_used VARCHAR(100),
|
|
tokens_input INTEGER,
|
|
tokens_output INTEGER,
|
|
cost_units DECIMAL(12,8),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- RESULTADOS AGREGADOS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
passed_count INTEGER,
|
|
warning_count INTEGER,
|
|
failure_count INTEGER,
|
|
pass_rate DECIMAL(5,4),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- DETALLES (JSONB)
|
|
-- ═══════════════════════════════════════════════════════════
|
|
findings JSONB,
|
|
patterns_detected JSONB,
|
|
by_severity JSONB,
|
|
by_rule JSONB,
|
|
by_module JSONB,
|
|
escalations JSONB,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- ESCALADO
|
|
-- ═══════════════════════════════════════════════════════════
|
|
human_escalation BOOLEAN DEFAULT FALSE,
|
|
escalation_notified BOOLEAN DEFAULT FALSE,
|
|
escalation_notified_at TIMESTAMPTZ,
|
|
escalation_acknowledged BOOLEAN,
|
|
escalation_acknowledged_by VARCHAR(100),
|
|
escalation_resolved BOOLEAN,
|
|
resolution_notes TEXT,
|
|
resolution_timestamp TIMESTAMPTZ,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- REFERENCIAS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
related_traces UUID[],
|
|
pattern_ids VARCHAR(50)[],
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CONSTRAINTS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
CONSTRAINT valid_sentinel_type CHECK (
|
|
sentinel_type IN ('LIGHT', 'DEEP')
|
|
)
|
|
);
|
|
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
-- ÍNDICES
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
CREATE INDEX idx_audit_timestamp ON AUDIT_RESULTS(timestamp);
|
|
CREATE INDEX idx_audit_type ON AUDIT_RESULTS(sentinel_type);
|
|
CREATE INDEX idx_audit_escalation ON AUDIT_RESULTS(human_escalation, escalation_resolved)
|
|
WHERE human_escalation = TRUE;
|
|
CREATE INDEX idx_audit_patterns ON AUDIT_RESULTS USING GIN(pattern_ids);
|
|
```
|
|
|
|
---
|
|
|
|
# A.5 Tabla PATTERNS (Detección de Patrones)
|
|
|
|
```sql
|
|
CREATE TABLE AUDIT_PATTERNS (
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- IDENTIFICACIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
pattern_id VARCHAR(50) UNIQUE NOT NULL,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- DESCRIPCIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
description TEXT NOT NULL,
|
|
category VARCHAR(50),
|
|
affected_module VARCHAR(50),
|
|
severity VARCHAR(20),
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- ESTADÍSTICAS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
first_seen TIMESTAMPTZ NOT NULL,
|
|
last_seen TIMESTAMPTZ NOT NULL,
|
|
occurrence_count INTEGER DEFAULT 1,
|
|
affected_traces UUID[],
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- RESOLUCIÓN
|
|
-- ═══════════════════════════════════════════════════════════
|
|
status VARCHAR(20) DEFAULT 'OPEN',
|
|
recommended_fix TEXT,
|
|
fix_implemented BOOLEAN DEFAULT FALSE,
|
|
fix_implemented_at TIMESTAMPTZ,
|
|
fix_notes TEXT,
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- METADATA
|
|
-- ═══════════════════════════════════════════════════════════
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by VARCHAR(50) DEFAULT 'SENTINEL-DEEP',
|
|
|
|
-- ═══════════════════════════════════════════════════════════
|
|
-- CONSTRAINTS
|
|
-- ═══════════════════════════════════════════════════════════
|
|
CONSTRAINT valid_status CHECK (
|
|
status IN ('OPEN', 'INVESTIGATING', 'FIXING', 'RESOLVED', 'WONT_FIX', 'DUPLICATE')
|
|
),
|
|
CONSTRAINT valid_severity CHECK (
|
|
severity IN ('LOW', 'MEDIUM', 'HIGH', 'CRITICAL')
|
|
)
|
|
);
|
|
|
|
-- Índices
|
|
CREATE INDEX idx_patterns_status ON AUDIT_PATTERNS(status) WHERE status != 'RESOLVED';
|
|
CREATE INDEX idx_patterns_module ON AUDIT_PATTERNS(affected_module);
|
|
CREATE INDEX idx_patterns_severity ON AUDIT_PATTERNS(severity);
|
|
```
|
|
|
|
---
|
|
|
|
# A.6 Integración con Alfred (n8n)
|
|
|
|
## A.6.1 Workflow SENTINEL-LIGHT
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ WORKFLOW: SENTINEL-LIGHT │
|
|
│ Trigger: Schedule (*/5 * * * *) │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ [Schedule Trigger] │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [NocoDB: Query SYS_LOG] │
|
|
│ "audit_status = PENDING AND created > -10min" │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [Function: Apply Rules] │
|
|
│ - Integrity rules │
|
|
│ - Conformity rules │
|
|
│ - Performance rules │
|
|
│ - Security rules │
|
|
│ │ │
|
|
│ ├─────────────┬─────────────┐ │
|
|
│ ▼ ▼ ▼ │
|
|
│ [If: PASS] [If: WARN] [If: FAIL] │
|
|
│ │ │ │ │
|
|
│ ▼ ▼ ▼ │
|
|
│ [NocoDB: [NocoDB: [NocoDB: │
|
|
│ Update Update Update │
|
|
│ LIGHT_PASS] LIGHT_WARN] LIGHT_FAIL] │
|
|
│ │ │ │ │
|
|
│ │ │ ▼ │
|
|
│ │ │ [Queue for DEEP] │
|
|
│ │ │ │ │
|
|
│ └─────────────┴─────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [NocoDB: Insert AUDIT_RESULTS] │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [If: Critical Alert] │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [Send Notification] │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## A.6.2 Workflow SENTINEL-DEEP
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ WORKFLOW: SENTINEL-DEEP │
|
|
│ Trigger: Schedule (0 * * * *) + Webhook │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ [Schedule/Webhook Trigger] │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [Function: Select Candidates] │
|
|
│ - Escalations from LIGHT │
|
|
│ - Errors not analyzed │
|
|
│ - Random sampling │
|
|
│ - Low confidence records │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [NocoDB: Fetch Full Records + Context] │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [Function: Enrich with M-CONTRACT] │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [OpenRouter: Claude/GPT-4] │
|
|
│ - System prompt: SENTINEL-DEEP │
|
|
│ - User prompt: Records JSON │
|
|
│ - Response: Analysis JSON │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [Function: Validate Response] │
|
|
│ │ │
|
|
│ ├─────────────┬─────────────┐ │
|
|
│ ▼ ▼ ▼ │
|
|
│ [NocoDB: [NocoDB: [NocoDB: │
|
|
│ Update Insert Upsert │
|
|
│ SYS_LOG] AUDIT_ PATTERNS] │
|
|
│ RESULTS] │
|
|
│ │ │ │ │
|
|
│ └─────────────┴─────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [If: Human Escalation] │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ [Send Detailed Notification] │
|
|
│ - Email with findings │
|
|
│ - Link to dashboard │
|
|
│ - Recommended actions │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
# A.7 Métricas y Dashboard
|
|
|
|
## A.7.1 KPIs de SENTINEL
|
|
|
|
| Métrica | Fórmula | Target | Alerta |
|
|
|---------|---------|--------|--------|
|
|
| Pass Rate (LIGHT) | passed / total | > 98% | < 95% |
|
|
| Error Rate | errors / total | < 2% | > 5% |
|
|
| Escalation Rate | escalated / total | < 5% | > 10% |
|
|
| MTTR (Mean Time To Resolve) | avg(resolution_time) | < 4h | > 8h |
|
|
| Pattern Detection Rate | patterns_new / week | informativo | > 5/semana |
|
|
| False Positive Rate | false_positives / escalations | < 10% | > 20% |
|
|
|
|
## A.7.2 Queries para Dashboard
|
|
|
|
```sql
|
|
-- Pass rate últimas 24h
|
|
SELECT
|
|
DATE_TRUNC('hour', timestamp) as hour,
|
|
passed_count::float / NULLIF(records_processed, 0) as pass_rate
|
|
FROM AUDIT_RESULTS
|
|
WHERE sentinel_type = 'LIGHT'
|
|
AND timestamp > NOW() - INTERVAL '24 hours'
|
|
ORDER BY hour;
|
|
|
|
-- Top 5 reglas más violadas
|
|
SELECT
|
|
rule_key,
|
|
SUM((rule_data->>'failed')::int) as total_failures
|
|
FROM AUDIT_RESULTS,
|
|
jsonb_each(by_rule) as rules(rule_key, rule_data)
|
|
WHERE timestamp > NOW() - INTERVAL '7 days'
|
|
GROUP BY rule_key
|
|
ORDER BY total_failures DESC
|
|
LIMIT 5;
|
|
|
|
-- Patrones abiertos por severidad
|
|
SELECT severity, COUNT(*)
|
|
FROM AUDIT_PATTERNS
|
|
WHERE status = 'OPEN'
|
|
GROUP BY severity;
|
|
|
|
-- Escalaciones pendientes
|
|
SELECT * FROM AUDIT_RESULTS
|
|
WHERE human_escalation = TRUE
|
|
AND escalation_resolved = FALSE
|
|
ORDER BY timestamp DESC;
|
|
```
|
|
|
|
---
|
|
|
|
# A.8 Checklist de Implementación SENTINEL
|
|
|
|
## SENTINEL-LIGHT:
|
|
|
|
- [ ] Crear workflow en n8n con trigger cada 5 min
|
|
- [ ] Implementar todas las reglas I-*, C-*, P-*, S-*
|
|
- [ ] Configurar umbrales por módulo
|
|
- [ ] Crear notificaciones para alertas CRITICAL
|
|
- [ ] Implementar actualización de SYS_LOG
|
|
- [ ] Crear inserts en AUDIT_RESULTS
|
|
|
|
## SENTINEL-DEEP:
|
|
|
|
- [ ] Crear workflow con trigger horario + webhook
|
|
- [ ] Implementar estrategia de muestreo
|
|
- [ ] Configurar integración con OpenRouter (Claude/GPT-4)
|
|
- [ ] Implementar validación de respuesta JSON
|
|
- [ ] Crear sistema de detección de patrones
|
|
- [ ] Implementar notificaciones con findings detallados
|
|
|
|
## Tablas:
|
|
|
|
- [ ] Crear tabla AUDIT_RESULTS con índices
|
|
- [ ] Crear tabla AUDIT_PATTERNS con índices
|
|
- [ ] Configurar políticas de retención
|
|
|
|
## Monitoreo:
|
|
|
|
- [ ] Dashboard con KPIs
|
|
- [ ] Alertas por Slack/Email
|
|
- [ ] Reportes semanales automáticos
|
|
|
|
---
|
|
|
|
**Fin del Documento 07a — SENTINEL**
|
|
|
|
*Referencia: `S-CONTRACT.md` v1.2*
|