0Pricing
AI Agents · Lección

Sistemas proactivos de notificaciones y alertas

Agentes que muestran información importante sin que tenga que solicitarla.

Sistemas proactivos de notificaciones y alertas es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.

Agentes proactivos frente a reactivos

La mayoría de los agentes son reactivos: responden a solicitudes. Un agente proactivo supervisa las condiciones y se pone en contacto con el usuario cuando ocurre algo relevante, sin que se lo soliciten. Es más parecido a tener un asistente personal.

Bucle de sondeo en segundo plano

El patrón proactivo más sencillo consiste en un bucle en segundo plano que realiza una comprobación cada N minutos y envía una alerta si se cumple una condición. Utilice un hilo o un bucle asíncrono para evitar bloquear la ejecución.

import asyncio
import httpx
from datetime import datetime

async def poll_price(ticker: str, alert_threshold: float) -> None:
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f'https://api.finance.example.com/quote/{ticker}',
            headers={'Authorization': 'Bearer your-api-key'}
        )
        data = response.json()
        price = float(data.get('price', 0))
        
        if price < alert_threshold:
            await send_push_notification(
                title=f'{ticker} Price Alert',
                message=f'{ticker} is now ${price:.2f}, below your threshold of ${alert_threshold:.2f}'
            )

async def price_alert_loop(ticker: str, threshold: float, interval_seconds: int = 300):
    print(f'Monitoring {ticker} every {interval_seconds}s, alert below ${threshold}')
    while True:
        try:
            await poll_price(ticker, threshold)
        except Exception as e:
            print(f'Polling error: {e}')
        await asyncio.sleep(interval_seconds)

print('Price alert loop defined')

Alertas basadas en umbrales

Las alertas basadas en umbrales se activan cuando una métrica cruza un límite definido. Algunos buenos umbrales son valores absolutos (precio < $100), cambios porcentuales (caída > 5 %) o comparaciones con promedios históricos.

from dataclasses import dataclass
from typing import Optional, Callable

@dataclass
class AlertRule:
    name: str
    check_fn: Callable  # Returns a float value
    condition: str      # 'lt', 'gt', 'lte', 'gte'
    threshold: float
    cooldown_minutes: int = 60  # Don't re-alert for this long
    last_alerted: Optional[float] = None

def evaluate_rule(rule: AlertRule) -> Optional[str]:
    import time
    
    # Respect cooldown
    if rule.last_alerted:
        elapsed = time.time() - rule.last_alerted
        if elapsed < rule.cooldown_minutes * 60:
            return None
    
    current_value = rule.check_fn()
    
    triggered = (
        (rule.condition == 'lt' and current_value < rule.threshold) or
        (rule.condition == 'gt' and current_value > rule.threshold) or
        (rule.condition == 'lte' and current_value <= rule.threshold) or
        (rule.condition == 'gte' and current_value >= rule.threshold)
    )
    
    if triggered:
        rule.last_alerted = time.time()
        return f'Alert: {rule.name} = {current_value:.2f} ({rule.condition} {rule.threshold})'
    return None

# Example rule
rule = AlertRule(
    name='CPU Usage',
    check_fn=lambda: 85.0,  # In reality: psutil.cpu_percent()
    condition='gt',
    threshold=80.0,
    cooldown_minutes=30
)
print(evaluate_rule(rule))

Criterios de coincidencia para nuevos correos electrónicos

Un agente de correo electrónico proactivo supervisa la bandeja de entrada y envía una alerta cuando llegan mensajes que cumplen criterios específicos: proceden de un remitente VIP, contienen determinadas palabras clave o tienen indicadores de alta importancia.

from dataclasses import dataclass
from typing import List
import re

@dataclass
class EmailAlertCriteria:
    from_domains: List[str] = None
    from_emails: List[str] = None
    subject_keywords: List[str] = None
    body_keywords: List[str] = None
    min_importance: str = None  # 'high', 'medium'

def matches_criteria(email: dict, criteria: EmailAlertCriteria) -> bool:
    sender = email.get('from', '').lower()
    subject = email.get('subject', '').lower()
    body = email.get('body', '').lower()
    
    if criteria.from_domains:
        domain_match = any(domain.lower() in sender for domain in criteria.from_domains)
        if not domain_match:
            return False
    
    if criteria.from_emails:
        email_match = any(e.lower() in sender for e in criteria.from_emails)
        if not email_match:
            return False
    
    if criteria.subject_keywords:
        keyword_match = any(kw.lower() in subject for kw in criteria.subject_keywords)
        if not keyword_match:
            return False
    
    return True

criteria = EmailAlertCriteria(
    from_domains=['@important-client.com', '@boss.company.com'],
    subject_keywords=['urgent', 'action required', 'ASAP']
)

email = {'from': 'john@important-client.com', 'subject': 'Urgent: Contract Issue'}
print('Matches:', matches_criteria(email, criteria))

Notificaciones de Pushover

Pushover envía notificaciones push a iOS y Android. Tiene una API REST sencilla y un nivel gratuito. Es ideal para agentes personales que necesitan alertar a un único usuario.

import httpx
import os

PUSHOVER_TOKEN = os.environ.get('PUSHOVER_APP_TOKEN', 'your-app-token')
PUSHOVER_USER = os.environ.get('PUSHOVER_USER_KEY', 'your-user-key')

async def send_pushover(title: str, message: str, priority: int = 0, url: str = None) -> bool:
    '''
    priority: -2 (lowest) to 2 (emergency with acknowledgment)
    0 = normal, 1 = high priority (bypass quiet hours)
    '''
    async with httpx.AsyncClient() as client:
        data = {
            'token': PUSHOVER_TOKEN,
            'user': PUSHOVER_USER,
            'title': title,
            'message': message,
            'priority': priority
        }
        if url:
            data['url'] = url
        
        response = await client.post('https://api.pushover.net/1/messages.json', data=data)
        result = response.json()
        
        if result.get('status') == 1:
            print(f'Push sent: {title}')
            return True
        else:
            print(f'Push failed: {result.get("errors")}')
            return False

async def send_push_notification(title: str, message: str):
    await send_pushover(title, message)

Notificaciones por mensaje directo de Slack

Envíe alertas proactivas como mensajes directos de Slack. A diferencia de los mensajes en canales, los mensajes directos garantizan que el usuario reciba la notificación aunque no se encuentre en un canal específico.

from slack_sdk import WebClient
import os

slack_client = WebClient(token=os.environ.get('SLACK_BOT_TOKEN', 'xoxb-...'))

def send_slack_dm(user_id: str, title: str, message: str, urgency: str = 'normal') -> bool:
    # Build blocks for rich formatting
    blocks = [
        {
            'type': 'section',
            'text': {
                'type': 'mrkdwn',
                'text': f'*{title}*\n{message}'
            }
        }
    ]
    
    if urgency == 'high':
        # Add urgent emoji prefix
        blocks[0]['text']['text'] = '🚨 ' + blocks[0]['text']['text']
    
    try:
        response = slack_client.chat_postMessage(
            channel=user_id,  # DM: channel = user_id
            text=f'{title}: {message}',  # Fallback text
            blocks=blocks
        )
        return True
    except Exception as e:
        print(f'Slack DM failed: {e}')
        return False

print('Slack DM function defined')
print('Usage: send_slack_dm("U0123ABCD", "Price Alert", "AAPL dropped below $150")')

Notificación por correo electrónico

El correo electrónico es fiable para las alertas que no son urgentes. Utilice smtplib de Python o la API de SendGrid para enviar correos electrónicos mediante programación desde su agente.

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import os

def send_email_alert(to_email: str, subject: str, html_body: str) -> bool:
    smtp_host = os.environ.get('SMTP_HOST', 'smtp.gmail.com')
    smtp_port = int(os.environ.get('SMTP_PORT', '587'))
    smtp_user = os.environ.get('SMTP_USER', '')
    smtp_pass = os.environ.get('SMTP_PASS', '')
    
    try:
        msg = MIMEMultipart('alternative')
        msg['Subject'] = subject
        msg['From'] = smtp_user
        msg['To'] = to_email
        
        html_part = MIMEText(html_body, 'html')
        msg.attach(html_part)
        
        with smtplib.SMTP(smtp_host, smtp_port) as server:
            server.starttls()
            server.login(smtp_user, smtp_pass)
            server.sendmail(smtp_user, to_email, msg.as_string())
        
        print(f'Email sent to {to_email}: {subject}')
        return True
    except Exception as e:
        print(f'Email failed: {e}')
        return False

html = '<p>Your agent detected a price drop: <strong>AAPL is now $147.50</strong></p>'
print('Email alert function defined')

Enrutador de alertas multicanal

Enrute las alertas al canal adecuado según su urgencia. Las alertas críticas deben enviarse mediante notificaciones push; las alertas informativas, por correo electrónico; y los resúmenes diarios, mediante un mensaje directo de Slack.

import asyncio
from enum import Enum

class AlertLevel(Enum):
    INFO = 'info'        # Email/Daily summary
    WARNING = 'warning'  # Slack DM
    CRITICAL = 'critical'  # Push notification immediately

async def route_alert(title: str, message: str, level: AlertLevel, user_config: dict):
    user_id = user_config.get('user_id')
    email = user_config.get('email')
    push_enabled = user_config.get('push_enabled', True)
    
    if level == AlertLevel.CRITICAL and push_enabled:
        success = await send_pushover(title, message, priority=1)
        if not success:
            # Fallback to Slack DM
            slack_user = user_config.get('slack_user_id')
            if slack_user:
                send_slack_dm(slack_user, title, message, urgency='high')
    
    elif level == AlertLevel.WARNING:
        slack_user = user_config.get('slack_user_id')
        if slack_user:
            send_slack_dm(slack_user, title, message)
    
    else:  # INFO
        if email:
            send_email_alert(email, title, f'<p>{message}</p>')

user_config = {
    'user_id': 'user-42',
    'email': 'user@example.com',
    'slack_user_id': 'U0123ABCD',
    'push_enabled': True
}

asyncio.run(route_alert('Price Alert', 'AAPL dropped to $147', AlertLevel.WARNING, user_config))

Deduplicación de alertas y períodos de espera

Sin deduplicación, una condición que persiste durante horas enviará cientos de alertas. Los períodos de espera evitan volver a alertar sobre la misma condición dentro de un intervalo especificado.

import time
import redis
import json

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def should_send_alert(alert_key: str, cooldown_seconds: int = 3600) -> bool:
    redis_key = f'alert:cooldown:{alert_key}'
    if r.exists(redis_key):
        ttl = r.ttl(redis_key)
        print(f'Alert {alert_key} on cooldown. {ttl}s remaining')
        return False
    return True

def record_alert_sent(alert_key: str, cooldown_seconds: int = 3600):
    redis_key = f'alert:cooldown:{alert_key}'
    r.setex(redis_key, cooldown_seconds, '1')

def maybe_send_alert(alert_type: str, metric_value: float, user_id: str, cooldown_hours: int = 1):
    # Create unique key per alert type per user
    alert_key = f'{user_id}:{alert_type}'
    
    if not should_send_alert(alert_key, cooldown_seconds=cooldown_hours * 3600):
        return False
    
    # Send the alert
    print(f'Sending alert: {alert_type} = {metric_value} for user {user_id}')
    record_alert_sent(alert_key, cooldown_seconds=cooldown_hours * 3600)
    return True

maybe_send_alert('price_drop_AAPL', 147.50, 'user-42')
maybe_send_alert('price_drop_AAPL', 146.00, 'user-42')  # Blocked by cooldown

Filtrado basado en las preferencias del usuario

No todos los usuarios desean recibir todas las alertas. Almacene las preferencias del usuario sobre los tipos de alerta que desea, los umbrales correspondientes y los canales que prefiere. El agente comprueba las preferencias antes de enviar una alerta.

from dataclasses import dataclass, field
from typing import Dict, List

@dataclass
class UserAlertPreferences:
    user_id: str
    enabled_channels: List[str] = field(default_factory=lambda: ['push'])
    alert_rules: Dict[str, dict] = field(default_factory=dict)
    quiet_hours_start: int = 22  # 10pm
    quiet_hours_end: int = 8    # 8am

def is_quiet_hours(prefs: UserAlertPreferences) -> bool:
    from datetime import datetime
    hour = datetime.now().hour
    start = prefs.quiet_hours_start
    end = prefs.quiet_hours_end
    if start > end:  # Spans midnight
        return hour >= start or hour < end
    return start <= hour < end

def should_alert_user(prefs: UserAlertPreferences, alert_type: str, value: float) -> bool:
    rule = prefs.alert_rules.get(alert_type)
    if not rule:
        return False  # User hasn't set up this alert type
    
    threshold = rule.get('threshold')
    condition = rule.get('condition', 'lt')
    urgent = rule.get('urgent', False)
    
    if is_quiet_hours(prefs) and not urgent:
        print(f'Suppressing non-urgent alert during quiet hours')
        return False
    
    return (
        (condition == 'lt' and value < threshold) or
        (condition == 'gt' and value > threshold)
    )

prefs = UserAlertPreferences(
    user_id='user-42',
    alert_rules={'price_drop': {'threshold': 150.0, 'condition': 'lt'}}
)
print('Should alert:', should_alert_user(prefs, 'price_drop', 147.50))

Agregación de alertas en resúmenes

En lugar de enviar alertas individuales para cada evento, acumúlelas y envíe un resumen. Esto reduce la fatiga de notificaciones de los usuarios que tienen muchas condiciones supervisadas.

import redis
import json
from datetime import datetime

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def add_to_digest(user_id: str, alert: dict):
    key = f'digest:{user_id}:{datetime.now().strftime("%Y%m%d")}'
    r.rpush(key, json.dumps(alert))
    r.expire(key, 86400 * 2)  # Keep for 2 days

def send_and_clear_digest(user_id: str) -> int:
    key = f'digest:{user_id}:{datetime.now().strftime("%Y%m%d")}'
    raw_alerts = r.lrange(key, 0, -1)
    if not raw_alerts:
        print(f'No alerts for user {user_id} today')
        return 0
    
    alerts = [json.loads(a) for a in raw_alerts]
    digest_text = f'Daily Digest ({len(alerts)} alerts):\n'
    digest_text += '\n'.join([f'- {a["title"]}: {a["message"]}' for a in alerts])
    
    # Send as single notification
    print(f'Sending digest to {user_id}:\n{digest_text}')
    # send_email_alert(user_email, 'Daily Alert Digest', digest_text)
    
    r.delete(key)
    return len(alerts)

add_to_digest('user-42', {'title': 'AAPL Alert', 'message': 'Price at $147.50'})
add_to_digest('user-42', {'title': 'GOOG Alert', 'message': 'Price at $175.00'})
send_and_clear_digest('user-42')

Comprobación de conocimientos: notificaciones proactivas

Compruebe su comprensión de los sistemas de notificaciones y alertas proactivas.

Resumen de las notificaciones proactivas

Los agentes proactivos utilizan bucles de sondeo en segundo plano para supervisar las condiciones y enviar alertas mediante notificaciones push (Pushover), mensajes directos de Slack o correo electrónico. Entre los elementos clave de diseño se incluyen reglas basadas en umbrales con períodos de espera para evitar la fatiga, filtrado según las preferencias del usuario para personalizar la experiencia, enrutamiento multicanal según la urgencia, compatibilidad con horas de silencio y resúmenes diarios de las alertas acumuladas de baja prioridad.

Preguntas frecuentes

¿La lección «Sistemas proactivos de notificaciones y alertas» es gratis?

Sí — el texto completo de «Sistemas proactivos de notificaciones y alertas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.

¿Qué aprenderé en «Sistemas proactivos de notificaciones y alertas»?

Agentes que muestran información importante sin que tenga que solicitarla. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Agents?

No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Sistemas proactivos de notificaciones y alertas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Agents?

Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Patrones de diseño para agentes siempre activos
  2. Sistemas proactivos de notificaciones y alertas
  3. Persistencia del contexto entre sesiones
  4. Creación de un agente informativo diario
← Volver a AI Agents