ระบบการแจ้งเตือนและการเตือนเชิงรุก
เอเจนต์ที่นำเสนอข้อมูลสำคัญโดยไม่ต้องรอให้ผู้ใช้ร้องขอ
ระบบการแจ้งเตือนและการเตือนเชิงรุก เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
เอเจนต์เชิงรุกกับเอเจนต์เชิงรับ
เอเจนต์ส่วนใหญ่เป็นเอเจนต์เชิงรับ ซึ่งตอบสนองต่อคำขอ ส่วนเอเจนต์เชิงรุกจะตรวจติดตามเงื่อนไขและติดต่อผู้ใช้เมื่อเกิดสิ่งที่น่าสนใจ โดยไม่ต้องรอให้ผู้ใช้ร้องขอ ลักษณะนี้คล้ายการมีผู้ช่วยส่วนตัวมากกว่า
ลูปตรวจสอบเบื้องหลัง
รูปแบบเอเจนต์เชิงรุกที่ง่ายที่สุดคือลูปเบื้องหลังที่ตรวจสอบทุก ๆ N นาที และส่งการแจ้งเตือนเมื่อเงื่อนไขเป็นจริง ใช้ Thread หรือลูปแบบอะซิงก์เพื่อหลีกเลี่ยงการบล็อกการทำงาน
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')การแจ้งเตือนตามค่าเกณฑ์
การแจ้งเตือนตามค่าเกณฑ์จะทำงานเมื่อค่าตัวชี้วัดข้ามขอบเขตที่กำหนด ค่าเกณฑ์ที่ดีอาจเป็น ค่าสัมบูรณ์ (ราคา < $100) การเปลี่ยนแปลงเป็นเปอร์เซ็นต์ (ลดลง > 5%) หรือการเปรียบเทียบกับค่าเฉลี่ยในอดีต
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))เกณฑ์จับคู่อีเมลใหม่
เอเจนต์อีเมลเชิงรุกจะตรวจสอบกล่องจดหมายและแจ้งเตือนเมื่อมีข้อความที่ตรงตามเกณฑ์เฉพาะเข้ามา เช่น มาจากผู้ส่งที่เป็น VIP มีคำสำคัญ หรือมีเครื่องหมายระบุความสำคัญสูง
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))การแจ้งเตือนด้วย Pushover
Pushover ส่งการแจ้งเตือนแบบพุชไปยัง iOS และ Android โดยมี REST API ที่เรียบง่ายและมีแพ็กเกจใช้งานฟรี เหมาะอย่างยิ่งสำหรับเอเจนต์ส่วนตัวที่ต้องแจ้งเตือนผู้ใช้เพียงคนเดียว
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)การแจ้งเตือนทาง Slack DM
ส่งการแจ้งเตือนเชิงรุกเป็นข้อความส่วนตัวทาง Slack ซึ่งต่างจากข้อความในช่องสนทนา DM ช่วยให้ผู้ใช้ได้รับการแจ้งเตือนแม้ไม่ได้อยู่ในช่องสนทนาใดช่องสนทนาหนึ่ง
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")')การแจ้งเตือนทางอีเมล
อีเมลเหมาะสำหรับการแจ้งเตือนที่ไม่เร่งด่วน ให้ใช้ smtplib ของ Python หรือ API ของ SendGrid เพื่อส่งอีเมลจากเอเจนต์ด้วยโปรแกรม
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')ตัวกำหนดเส้นทางการแจ้งเตือนหลายช่องทาง
กำหนดเส้นทางการแจ้งเตือนไปยังช่องทางที่เหมาะสมตามระดับความเร่งด่วน การแจ้งเตือนวิกฤตให้ส่งเป็นการแจ้งเตือนแบบพุช การแจ้งเตือนเพื่อให้ข้อมูลให้ส่งทางอีเมล และสรุปประจำวันให้ส่งทาง Slack DM
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))การกำจัดการแจ้งเตือนซ้ำและช่วงพัก
หากไม่กำจัดการแจ้งเตือนซ้ำ เงื่อนไขที่คงอยู่เป็นเวลาหลายชั่วโมงจะส่งการแจ้งเตือนหลายร้อยครั้ง ช่วงพักจะป้องกันการแจ้งเตือนซ้ำสำหรับเงื่อนไขเดิมภายในช่วงเวลาที่กำหนด
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การกรองตามการตั้งค่าของผู้ใช้
ผู้ใช้แต่ละคนไม่ได้ต้องการการแจ้งเตือนทุกประเภท ให้จัดเก็บการตั้งค่าของผู้ใช้ว่าต้องการการแจ้งเตือนประเภทใด ที่ค่าเกณฑ์เท่าใด และผ่านช่องทางใด จากนั้นเอเจนต์จะตรวจสอบการตั้งค่าก่อนส่งการแจ้งเตือน
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))การรวบรวมการแจ้งเตือนเป็นสรุป
แทนที่จะส่งการแจ้งเตือนแยกกันสำหรับแต่ละเหตุการณ์ ให้รวบรวมเหตุการณ์เหล่านั้นแล้วส่งเป็นสรุป วิธีนี้ช่วยลดความเหนื่อยล้าจากการแจ้งเตือนสำหรับผู้ใช้ที่มีเงื่อนไขจำนวนมากที่ต้องติดตาม
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')ตรวจสอบความรู้: การแจ้งเตือนเชิงรุก
ทดสอบความเข้าใจเกี่ยวกับระบบการแจ้งเตือนและการแจ้งเตือนเชิงรุก
สรุปการแจ้งเตือนเชิงรุก
เอเจนต์เชิงรุกใช้ลูปตรวจสอบเบื้องหลังเพื่อติดตามเงื่อนไขและส่งการแจ้งเตือนผ่านการแจ้งเตือนแบบพุช (Pushover), Slack DM หรืออีเมล องค์ประกอบสำคัญของการออกแบบ ได้แก่ กฎตามค่าเกณฑ์พร้อมช่วงพักเพื่อป้องกันความเหนื่อยล้าจากการแจ้งเตือน การกรองตามการตั้งค่าของผู้ใช้เพื่อปรับให้เหมาะกับแต่ละบุคคล การกำหนดเส้นทางหลายช่องทางตามระดับความเร่งด่วน การรองรับช่วงเวลางดแจ้งเตือน และสรุปประจำวันสำหรับการแจ้งเตือนที่มีความสำคัญต่ำซึ่งสะสมไว้
คำถามที่พบบ่อย
บทเรียน “ระบบการแจ้งเตือนและการเตือนเชิงรุก” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ระบบการแจ้งเตือนและการเตือนเชิงรุก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ระบบการแจ้งเตือนและการเตือนเชิงรุก”
เอเจนต์ที่นำเสนอข้อมูลสำคัญโดยไม่ต้องรอให้ผู้ใช้ร้องขอ คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “ระบบการแจ้งเตือนและการเตือนเชิงรุก” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม
ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- รูปแบบการออกแบบเอเจนต์ที่ทำงานตลอดเวลา
- ระบบการแจ้งเตือนและการเตือนเชิงรุก
- การคงอยู่ของบริบทข้ามเซสชัน
- การสร้างเอเจนต์สรุปข้อมูลประจำวัน