Sistemas proativos de notificações e alertas
Agentes que apresentam informações importantes sem que seja necessário solicitá-las.
Sistemas proativos de notificações e alertas é uma aula grátis de AI Agents no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Agents, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Agents inclui 4 aulas no total.
Agentes proativos e reativos
A maioria dos agentes é reativa — responde a solicitações. Um agente proativo monitora condições e entra em contato com o usuário quando algo importante acontece, sem que seja solicitado. É mais parecido com ter um assistente pessoal.
Loop de sondagem em segundo plano
O padrão proativo mais simples é um loop em segundo plano que executa uma verificação a cada N minutos e envia um alerta quando uma condição é atendida. Use uma thread ou um loop assíncrono para evitar bloqueios.
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 baseados em limites
Alertas baseados em limites são acionados quando uma métrica ultrapassa um limite definido. Bons limites podem ser: valores absolutos (preço < $100), mudanças percentuais (caiu > 5%) ou comparações com médias históricas.
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))Critérios para identificar novos e-mails
Um agente de e-mail proativo monitora a caixa de entrada e alerta quando chegam mensagens que atendem a critérios específicos: enviadas por um remetente VIP, contendo palavras-chave ou com marcadores de alta importância.
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))Notificações do Pushover
O Pushover envia notificações instantâneas para iOS e Android. Ele tem uma API REST simples e um plano gratuito. É ideal para agentes pessoais que precisam alertar um único usuário.
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)Notificações por DM do Slack
Envie alertas proativos como mensagens diretas do Slack. Ao contrário das mensagens em canais, as DMs garantem que o usuário seja notificado mesmo que não esteja em um 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")')Notificação por e-mail
O e-mail é confiável para alertas que não são urgentes. Use o smtplib do Python ou a API do SendGrid para enviar e-mails programaticamente a partir do 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')Roteador de alertas multicanal
Encaminhe os alertas para o canal adequado com base na urgência. Alertas críticos devem ser enviados por notificações instantâneas; alertas informativos, por e-mail; e resumos diários, por DM do 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))Deduplicação de alertas e períodos de espera
Sem deduplicação, uma condição que persiste por horas enviará centenas de alertas. Períodos de espera impedem o envio de novos alertas para a mesma condição dentro de um 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 cooldownFiltragem com base nas preferências do usuário
Nem todo usuário quer receber todos os alertas. Armazene as preferências do usuário sobre quais tipos de alerta deseja receber, em quais limites e por quais canais. O agente verifica as preferências antes de enviar o 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))Agrupamento de alertas em resumos
Em vez de enviar alertas individuais para cada evento, acumule-os e envie um resumo. Isso reduz a fadiga causada por notificações para usuários que monitoram muitas condições.
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ção de compreensão: notificações proativas
Verifique sua compreensão dos sistemas de notificação e alerta proativos.
Resumo das notificações proativas
Agentes proativos usam loops de sondagem em segundo plano para monitorar condições e enviar alertas por notificações instantâneas (Pushover), DM do Slack ou e-mail. Elementos essenciais do design: regras baseadas em limites com períodos de espera para evitar fadiga, filtragem pelas preferências do usuário para personalização, roteamento multicanal com base na urgência, suporte a horários de silêncio e resumos diários de alertas acumulados de baixa prioridade.
Perguntas Frequentes
A aula “Sistemas proativos de notificações e alertas” é grátis?
Sim — o texto completo de “Sistemas proativos de notificações e alertas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Agents, atualize para CoddyKit PRO. O curso de AI Agents inclui 4 aulas no total.
O que vou aprender em “Sistemas proativos de notificações e alertas”?
Agentes que apresentam informações importantes sem que seja necessário solicitá-las. Você pratica AI Agents com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Agents?
Nenhuma experiência prévia é necessária. AI Agents no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Sistemas proativos de notificações e alertas”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Agents?
Sim. Cada aula de AI Agents inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Padrões de projeto para agentes sempre ativos
- Sistemas proativos de notificações e alertas
- Persistência de contexto entre sessões
- Construindo um agente de briefing diário