0Pricing
AI Agents · Lezione

Sistemi proattivi di notifica e avviso

Agenti che portano all’attenzione informazioni importanti senza che venga loro richiesto.

Sistemi proattivi di notifica e avviso è una lezione AI Agents gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Agents, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Agents include 4 lezioni in totale.

Agenti proattivi e reattivi

La maggior parte degli agenti è reattiva: risponde alle richieste. Un agente proattivo monitora le condizioni e contatta l'utente quando si verifica qualcosa di significativo, senza che gli venga richiesto. È più simile ad avere un assistente personale.

Ciclo di polling in background

Il pattern proattivo più semplice consiste in un ciclo in background che esegue un controllo ogni N minuti e invia un avviso quando viene soddisfatta una condizione. Utilizzi un thread o un ciclo asincrono per evitare di bloccare l'esecuzione.

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')

Avvisi basati su soglie

Gli avvisi basati su soglie vengono attivati quando una metrica supera un limite definito. Le soglie efficaci possono essere: valori assoluti (prezzo < $100), variazioni percentuali (calo > 5%) o confronti con le medie storiche.

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))

Nuove email corrispondenti ai criteri

Un agente email proattivo monitora la posta in arrivo e invia un avviso quando arrivano messaggi che soddisfano criteri specifici: provenienza da un mittente importante, presenza di determinate parole chiave o indicatori di alta priorità.

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))

Notifiche Pushover

Pushover distribuisce notifiche push su iOS e Android. Dispone di una semplice API REST e di un piano gratuito. È ideale per gli agenti personali che devono avvisare un singolo utente.

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)

Notifiche tramite messaggi diretti Slack

Invii gli avvisi proattivi come messaggi diretti Slack. A differenza dei messaggi nei canali, i messaggi diretti assicurano che l'utente riceva la notifica anche se non si trova in un canale specifico.

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")')

Notifiche email

L'email è affidabile per gli avvisi non urgenti. Utilizzi smtplib di Python o l'API di SendGrid per inviare email programmaticamente dal suo 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')

Router per gli avvisi multicanale

Instradi gli avvisi verso il canale appropriato in base all'urgenza. Gli avvisi critici vanno inviati tramite notifiche push; quelli informativi tramite email; i riepiloghi giornalieri tramite messaggi diretti 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))

Deduplicazione e periodi di cooldown degli avvisi

Senza la deduplicazione, una condizione che persiste per ore invierebbe centinaia di avvisi. I periodi di cooldown impediscono di inviare nuovamente un avviso per la stessa condizione entro un intervallo specificato.

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

Filtraggio in base alle preferenze dell'utente

Non tutti gli utenti desiderano ricevere ogni avviso. Salvi le preferenze dell'utente relative ai tipi di avviso desiderati, alle soglie e ai canali da utilizzare. L'agente controlla le preferenze prima dell'invio.

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))

Aggregazione degli avvisi in riepiloghi

Invece di inviare un avviso separato per ogni evento, li raccolga e invii un riepilogo. In questo modo riduce l'affaticamento da notifiche per gli utenti che monitorano molte condizioni.

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')

Verifica delle conoscenze: notifiche proattive

Verifichi la sua comprensione dei sistemi di notifica e avviso proattivi.

Riepilogo delle notifiche proattive

Gli agenti proattivi utilizzano cicli di polling in background per monitorare le condizioni e inviare avvisi tramite notifiche push (Pushover), messaggi diretti Slack o email. Gli elementi principali della progettazione sono: regole basate su soglie con periodi di cooldown per evitare l'affaticamento da notifiche, filtraggio in base alle preferenze dell'utente per la personalizzazione, instradamento multicanale in base all'urgenza, supporto per le ore di silenzio e riepiloghi giornalieri degli avvisi a bassa priorità raccolti nel tempo.

Domande Frequenti

La lezione «Sistemi proattivi di notifica e avviso» è gratuita?

Sì — il testo completo di «Sistemi proattivi di notifica e avviso» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Agents, passa a CoddyKit PRO. Il corso AI Agents include 4 lezioni in totale.

Cosa imparerò in «Sistemi proattivi di notifica e avviso»?

Agenti che portano all’attenzione informazioni importanti senza che venga loro richiesto. Eserciti AI Agents con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Agents?

Non è richiesta alcuna esperienza precedente. AI Agents su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Sistemi proattivi di notifica e avviso»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Agents?

Sì. Ogni lezione AI Agents include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Pattern di progettazione per agenti sempre attivi
  2. Sistemi proattivi di notifica e avviso
  3. Persistenza del contesto tra le sessioni
  4. Creazione di un agente per il briefing quotidiano
← Torna a AI Agents