Inbox Unificado (Alert Aggregator)
Un único inbox de alertas por activo: las detecciones de anomalías, energía y pronóstico colapsan en una sola fila. Severidad canónica, dedup por ventana, re-open automático en escalación.
Inbox Unificado — Alert Aggregator
El problema clásico: tres sistemas de detección independientes (anomalías mecánicas, anomalías energéticas, pronóstico de vida útil) producen tres alertas "críticas" cuando una bomba se degrada. El operador ve tres filas, duda cuál atender, y el ruido mata la señal.
La solución: un único inbox que consolida las tres fuentes en una fila por activo, con severidad = la más alta observada, fuente(s) = unión de detectores, y un trail de cambios de severidad para auditar cómo escaló el incidente.
Resumen ejecutivo
Un pico → una alerta. Antes: 3 rojos en el inbox por el mismo evento físico. Después: 1 fila con
source_systems = [anomaly, energy, prognostics]. El operador actúa una vez, el sistema cierra las tres detecciones juntas.
¿Para qué sirve?
- Eliminar redundancia: el mismo fenómeno (spike de vibración) puede disparar detección ML + residual energético + RUL crítico en segundos. Son la misma historia.
- Preservar el trail: la consolidación no pierde información — cada detector aporta al campo
sourcesde la alerta consolidada con su summary y su momento. - Severidad canónica max: la alerta refleja el peor diagnóstico. Si anomalía dice
warningy prognostics dicecritical, la fila quedacritical. - Escalación automática: si el operador marca la alerta como "reconocida" pero llega una nueva detección de severidad mayor, la alerta se reabre (
reopened_reason = severity_upgrade). Nadie pierde visibilidad.
¿Cómo funciona?
Contrato de entrada
Cada uno de los tres detectores llama al aggregator después de persistir su propio registro:
ingest_alert(
tenant_id,
asset_id,
source_system="anomaly" | "energy" | "prognostics",
severity="warning" | "high" | "critical",
summary="...",
source_doc_id="<opcional, id en la colección origen>",
extra={...}
)Filtro de entrada: solo severity >= warning alimenta el aggregator. info es ruido, no incidente.
Reglas de merge
flowchart TD
IN[Nueva detección<br/>asset_id + severity] --> Q{¿Hay fila activa<br/>para ese asset<br/>en la ventana?}
Q -- No --> INS[Insertar fila nueva<br/>status=open]
Q -- Sí --> CMP{¿Severidad entrante<br/>> stored?}
CMP -- Sí --> UP[Upgrade<br/>severity=max<br/>push sources<br/>re-open si estaba ack]
CMP -- No --> EQ[Merge silencioso<br/>count += 1<br/>source_systems addToSet<br/>NO push sources]- Ventana de dedup:
alert_dedup_window_minutes(default 60, configurable por tenant). - Búsqueda: se consideran filas
openyacknowledged. Una alerta reconocida dentro de la ventana sigue siendo "live" para dedup. - Upgrade de severidad:
severity := max(stored, incoming)según la escala canónicainfo < warning < high < critical. - Re-open en escalación: si la fila estaba
acknowledgedy entra una severidad mayor → vuelve aopenconreopened_reason = severity_upgrade. - Hysteresis del trail: si la severidad entrante es igual o menor a la almacenada,
countysource_systemsse actualizan pero la listasourcesno crece. La idea: el trail registra cambios de severidad, no repeticiones triviales que ensuciarían la UI.
Persistencia
Colección _alerts per-tenant. Campos clave:
| Campo | Significado |
|---|---|
asset_id | Identidad del activo afectado. Llave de dedup. |
status | open / acknowledged / (con reopened_* si reabrió). |
severity | Max histórica dentro de la fila. |
source_systems | Set de detectores que aportaron: ["anomaly","energy","prognostics"]. |
sources | Lista append-only con el trail de upgrades: cada entry tiene system, severity, summary, doc_id, at. |
count | Total de detecciones ingeridas (incluye mergeos silenciosos). |
first_seen_at, last_seen_at | Marcas temporales. |
Tests que codifican el contrato
El comportamiento está codificado en tests de integración en tests/predictive/test_alert_aggregator_cross_system.py:
test_single_spike_consolidates_into_one_alert— simula los 3 detectores disparando en la misma ventana sobre el mismo activo →len(_alerts) == 1,source_systems == {anomaly, energy, prognostics},severity == critical,count == 3,len(sources) == 3(todas upgrades).test_two_assets_two_separate_alerts— spikes en assets distintos → 2 filas separadas, sin bleed.test_upgrade_reopens_acknowledged_alert— ACK + critical entrante →statusvuelve aopenconreopened_reason.test_same_severity_on_ack_keeps_it_acknowledged— ACK + mismo severity entrante → no reabre.test_lower_severity_does_not_push_source_trail— stored critical + warning entrante →$pushal trail NO ocurre.
Si algún refactor cambia la semántica, el test rompe.
¿Cómo usarlo?
Ver el inbox
Desde el dashboard:
- Operaciones → Alertas — lista del tenant, orden por
last_seen_atdescendente. - Columnas: Asset · Severity · Source systems · Count · First seen · Last seen · Actions.
- Filtros: por severidad, por fuente, por status, por rango de fechas.
Acciones por alerta
| Acción | Efecto |
|---|---|
| Acknowledge | status := acknowledged, registra actor. Útil para "ya vi, voy a investigar". |
| Create task | Convierte la alerta en orden de trabajo, vinculando todas las sources. |
| Notify | Dispara WhatsApp/email (via reglas o manual). |
| Close | Cierra el incidente. Si entra una nueva detección, es un nuevo incidente. |
Configuración per-tenant
Ajustes vía Configuración Predictiva:
alert_dedup_window_minutes— default 60. Reducir = menos consolidación, más filas. Aumentar = más consolidación, puede mezclar incidentes genuinamente distintos si están cerca en tiempo.
Antes / Después
Inbox antes del aggregator
[14:02:15] CRITICAL anomaly_detection pump-B07 vibration ensemble score 0.92
[14:02:17] CRITICAL energy_anomaly pump-B07 z=3.4 on kwh
[14:02:21] CRITICAL prognostics pump-B07 RUL 18h3 filas. ¿Cuál atiendo primero? ¿Son el mismo problema? ¿Cierro una o las tres?
Inbox después del aggregator
[14:02:15→14:02:21] CRITICAL pump-B07 [anomaly,energy,prognostics] count=3
└ sources:
14:02:15 anomaly warning→ ensemble score 0.75
14:02:17 energy →high z=3.4 on kwh
14:02:21 prognostics →critical RUL 18hUna sola fila. Severidad es la peor. El trail cuenta la historia de escalación. Un solo ACK cierra las tres.
Limitaciones y supuestos
- Dedup es por
asset_id. Dos métricas distintas del mismo activo colapsan en la misma fila. Si quieres filas por métrica, es otro modelo — no es el caso de este aggregator. - La ventana es deslizante sobre
last_seen_at. Una detección que llega fuera de la ventana abre una fila nueva aunque el incidente sea conceptualmente el mismo. Ajustaralert_dedup_window_minutespara tu patrón operativo. - El trail captura upgrades, no repeticiones. Si tu auditoría necesita saber cuántas veces dispara cada detector, el campo
countlo tiene —sourcessolo guarda los cambios de severidad. - No es un sistema de workflow completo. ACK / notify / create task son endpoints del aggregator; la gestión de turnos, asignaciones y SLAs vive en Órdenes de Trabajo.
Beneficios clave
- Un solo inbox por activo — menos ruido, más acción.
- Severidad canónica
maxentre los tres detectores, con trail auditable. - Re-open automático en escalación — no se pierde visibilidad post-ACK.
- Hysteresis inteligente — el trail refleja cambios de severidad, no repeticiones.
- Window dedup configurable por tenant.
- Tests de integración E2E que garantizan el contrato.
Pronósticos y Recomendaciones
El sistema estima cuántos días le quedan al equipo antes de necesitar mantenimiento y genera recomendaciones específicas basadas en el historial de salud, tendencias y alarmas recientes.
Registro de Fallas
Cómo registrar fallas de equipos para alimentar el modelo predictivo