การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์
หากอุณหภูมิ > ค่าเกณฑ์ → แจ้งเตือน → สั่งงาน: วงจรควบคุม IoT ที่ขับเคลื่อนด้วยเอเจนต์
การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์ เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
การตอบสนองอัตโนมัติของ Agent ที่ขับเคลื่อนด้วยเหตุการณ์
เมื่อเซนเซอร์มีค่าเกินเกณฑ์ Agent ต้องตอบสนองโดยอัตโนมัติโดยไม่ต้องมีมนุษย์เข้ามาดำเนินการ ความท้าทายหลักคือการตัดสินใจว่า ควรดำเนินการใด การทำให้แน่ใจว่าเหตุการณ์เดียวกันจะไม่เรียกใช้การดำเนินการซ้ำ และการเคารพระยะพักเพื่อไม่ให้ Agent ส่งคำสั่งไปยังอุปกรณ์กระตุ้นถี่เกินไป
การกำหนดนโยบายการดำเนินการ
นโยบายการดำเนินการ จะจับคู่เงื่อนไขของเซนเซอร์กับการตอบสนองของ Agent ให้กำหนดนโยบายในรูปแบบประกาศ เพื่อให้อ่านและแก้ไขได้ง่ายโดยไม่ต้องแก้โค้ดตรรกะ แต่ละนโยบายมีเงื่อนไข ลำดับความสำคัญ และการดำเนินการอย่างน้อยหนึ่งรายการ
ACTION_POLICIES = [
{
'name': 'HIGH_TEMP_ALERT',
'topic': 'sensors/temperature',
'condition': lambda v: v > 38,
'priority': 'critical',
'actions': ['TURN_ON_COOLING', 'ALERT_MAINTENANCE', 'LOG_EVENT']
},
{
'name': 'HIGH_TEMP_WARNING',
'topic': 'sensors/temperature',
'condition': lambda v: 35 < v <= 38,
'priority': 'warning',
'actions': ['ALERT_MAINTENANCE', 'LOG_EVENT']
},
{
'name': 'LOW_HUMIDITY',
'topic': 'sensors/humidity',
'condition': lambda v: v < 30,
'priority': 'warning',
'actions': ['TURN_ON_HUMIDIFIER', 'LOG_EVENT']
}
]
def match_policies(topic: str, value: float) -> list:
return [
p for p in ACTION_POLICIES
if p['topic'] == topic and p['condition'](value)
]
if __name__ == '__main__':
matches = match_policies('sensors/temperature', 39)
print('Matched policies for temperature=39:')
for p in matches:
print(f" {p['name']} ({p['priority']}): {p['actions']}")
คิวการดำเนินการ
คิวการดำเนินการช่วยแยกการตรวจจับเหตุการณ์ออกจากการดำเนินการ เหตุการณ์จะถูกใส่ลงในคิว จากนั้นผู้ปฏิบัติงานจะนำออกมาและดำเนินการ วิธีนี้ป้องกันไม่ให้วงจรรับข้อมูล MQTT ถูกบล็อก และอนุญาตให้ลองใหม่ได้หากการดำเนินการล้มเหลว
import queue
import threading
from datetime import datetime
action_queue: queue.Queue = queue.Queue(maxsize=500)
def enqueue_action(action_name: str, context: dict, priority: str = 'normal'):
item = {
'action': action_name,
'context': context,
'priority': priority,
'enqueued_at': datetime.utcnow().isoformat()
}
try:
action_queue.put_nowait(item)
print(f'Enqueued: {action_name}')
except queue.Full:
print(f'WARNING: Action queue full, dropping {action_name}')
def action_worker(executor_fn):
"""Run in a background thread, executing actions from the queue."""
while True:
item = action_queue.get()
try:
executor_fn(item['action'], item['context'])
except Exception as e:
print(f'Action failed: {item["action"]} — {e}')
finally:
action_queue.task_done()
# Start worker thread:
# worker_thread = threading.Thread(target=action_worker, args=(execute_action,), daemon=True)
# worker_thread.start()
if __name__ == '__main__':
enqueue_action('TURN_ON_COOLING', {'zone': 'server-room'}, priority='critical')
enqueue_action('LOG_EVENT', {'msg': 'temperature nominal'})
print('Queue size:', action_queue.qsize())
การขจัด Event ซ้ำ
หากไม่มีการขจัดรายการซ้ำ อุณหภูมิที่สูงกว่า 38°C เป็นเวลา 10 นาที โดยอ่านค่า 1 ครั้งต่อวินาที จะสร้างเหตุการณ์ที่เหมือนกัน 600 รายการ การขจัดรายการซ้ำช่วยให้ชุดเดียวกันของ (หัวข้อ เงื่อนไข การดำเนินการ) เรียกใช้ได้เพียงครั้งเดียวต่อเหตุการณ์ และจะเริ่มนับใหม่เมื่อเงื่อนไขสิ้นสุดลง
class EventDeduplicator:
def __init__(self):
# active_events: (topic, policy_name) -> event_start_time
self._active: dict = {}
def is_new_event(self, topic: str, policy_name: str) -> bool:
key = (topic, policy_name)
return key not in self._active
def mark_active(self, topic: str, policy_name: str):
self._active[(topic, policy_name)] = datetime.utcnow()
def clear_event(self, topic: str, policy_name: str):
key = (topic, policy_name)
if key in self._active:
duration = (datetime.utcnow() - self._active.pop(key)).seconds
print(f'Event cleared: {policy_name} (lasted {duration}s)')
def clear_topic_if_normal(
self, topic: str, value: float, normal_fn
):
if normal_fn(value):
keys = [k for k in self._active if k[0] == topic]
for k in keys:
self.clear_event(k[0], k[1])
dedup = EventDeduplicator()
dedup.mark_active('sensors/temperature', 'HIGH_TEMP_ALERT')
print('New event?', dedup.is_new_event('sensors/temperature', 'HIGH_TEMP_ALERT'))ระยะพัก
แม้เหตุการณ์จะสิ้นสุดลงแล้วกลับมาเกิดซ้ำ ระยะพักก็ยังป้องกันการเรียกใช้ซ้ำอย่างรวดเร็วได้ ให้ผสานการขจัดรายการซ้ำ (เรียกใช้ครั้งเดียวขณะที่เงื่อนไขยังเป็นจริง) เข้ากับระยะพัก (รอ N นาทีหลังเงื่อนไขสิ้นสุด ก่อนอนุญาตให้การแจ้งเตือนเดิมเรียกใช้อีกครั้ง)
from datetime import datetime, timedelta
class CoolDownManager:
def __init__(self, cool_down_minutes: int = 15):
self.cool_down = timedelta(minutes=cool_down_minutes)
self._cleared_at: dict = {} # (topic, policy) -> cleared_datetime
def is_in_cool_down(self, topic: str, policy_name: str) -> bool:
key = (topic, policy_name)
cleared_at = self._cleared_at.get(key)
if cleared_at is None:
return False
return datetime.utcnow() - cleared_at < self.cool_down
def record_clear(self, topic: str, policy_name: str):
self._cleared_at[(topic, policy_name)] = datetime.utcnow()
def time_remaining(self, topic: str, policy_name: str) -> int:
key = (topic, policy_name)
cleared_at = self._cleared_at.get(key)
if cleared_at is None:
return 0
elapsed = datetime.utcnow() - cleared_at
remaining = self.cool_down - elapsed
return max(0, int(remaining.total_seconds()))
cooldown = CoolDownManager(cool_down_minutes=15)
cooldown.record_clear('sensors/temperature', 'HIGH_TEMP_ALERT')
print('In cool-down?', cooldown.is_in_cool_down('sensors/temperature', 'HIGH_TEMP_ALERT'))การตัดสินใจดำเนินการโดยมี LLM ช่วย
สำหรับสถานการณ์ซับซ้อน เช่น การแจ้งเตือนหลายรายการเกิดขึ้นพร้อมกัน นโยบายขัดแย้งกัน หรือมีชุดค่าที่อ่านได้ผิดปกติ ให้มอบหมายการตัดสินใจแก่ LLM LLM จะได้รับบริบทของเซนเซอร์ทั้งหมดและแนะนำแผนการดำเนินการตามลำดับความสำคัญ
import anthropic
import json
def llm_decide_actions(
sensor_readings: dict,
active_policies: list
) -> list:
client = anthropic.Anthropic(api_key='YOUR_API_KEY')
context = json.dumps({
'readings': sensor_readings,
'triggered_policies': [p['name'] for p in active_policies]
}, indent=2)
prompt = (
f'Current sensor state:\n{context}\n\n'
'Multiple alert policies are active. '
'Recommend an ordered list of actions to take. '
'Consider conflicting effects (e.g., humidifier and cooling may conflict).\n'
'Return JSON: {"recommended_actions": [str], "reasoning": str}'
)
response = client.messages.create(
model='claude-opus-4-5', max_tokens=512,
messages=[{'role': 'user', 'content': prompt}]
)
return json.loads(response.content[0].text)การดำเนินการผ่าน MQTT
การดำเนินการทำได้โดยเผยแพร่ข้อความคำสั่งไปยังหัวข้อ MQTT เฉพาะอุปกรณ์ ส่วนข้อมูลคำสั่งเป็นไปตามโครงสร้างมาตรฐาน ได้แก่ ชื่อการดำเนินการ พารามิเตอร์ ID คำขอสำหรับการยืนยัน และ TTL (คำสั่งจะหมดอายุหากอุปกรณ์ออฟไลน์นานเกินไป)
import json
import uuid
from datetime import datetime, timedelta
ACTION_TOPICS = {
'TURN_ON_COOLING': 'devices/hvac/commands',
'TURN_OFF_COOLING': 'devices/hvac/commands',
'TURN_ON_HUMIDIFIER': 'devices/humidifier/commands',
'ALERT_MAINTENANCE': 'notifications/maintenance',
'LOG_EVENT': 'logs/agent_events'
}
def execute_action(action_name: str, context: dict, mqtt_client) -> str:
topic = ACTION_TOPICS.get(action_name)
if not topic:
print(f'No topic defined for action: {action_name}')
return 'unknown_action'
request_id = str(uuid.uuid4())[:8]
ttl = (datetime.utcnow() + timedelta(minutes=5)).isoformat()
payload = json.dumps({
'action': action_name,
'request_id': request_id,
'context': context,
'ttl': ttl
})
mqtt_client.publish(topic, payload, qos=1)
print(f'Executed {action_name} -> {topic} (req={request_id})')
return request_id
if __name__ == '__main__':
class FakeMQTT:
def publish(self, topic, payload, qos=1):
pass
execute_action('TURN_ON_COOLING', {'zone': 'server-room'}, FakeMQTT())
การยืนยันการดำเนินการ
อุปกรณ์ควรยืนยันคำสั่งที่ได้รับด้วยการเผยแพร่ไปยังหัวข้อยืนยัน Agent สมัครรับหัวข้อยืนยันและสามารถลองใหม่ได้ หากไม่ได้รับการยืนยันภายในระยะหมดเวลา
import threading
from collections import defaultdict
class AckTracker:
def __init__(self, timeout_seconds: int = 30):
self.timeout = timeout_seconds
self._pending: dict = {} # request_id -> {'action', 'send_time', 'ack_event'}
def register(self, request_id: str, action_name: str):
event = threading.Event()
self._pending[request_id] = {
'action': action_name,
'send_time': datetime.utcnow(),
'ack_event': event
}
# Schedule timeout check
t = threading.Timer(self.timeout, self._on_timeout, args=[request_id])
t.daemon = True
t.start()
def acknowledge(self, request_id: str):
entry = self._pending.pop(request_id, None)
if entry:
entry['ack_event'].set()
print(f'Ack received for {entry["action"]} (req={request_id})')
def _on_timeout(self, request_id: str):
if request_id in self._pending:
action = self._pending.pop(request_id)['action']
print(f'TIMEOUT: No ack for {action} (req={request_id}) — retry?')กระบวนการทำงานของ Event จากเซนเซอร์ทั้งหมด
เมื่อนำองค์ประกอบทั้งหมดมาประกอบกัน: การรับข้อมูล MQTT → การจับคู่นโยบาย → การขจัดรายการซ้ำและตรวจสอบระยะพัก → การใส่การดำเนินการลงคิว → ผู้ปฏิบัติงานดำเนินการผ่านการเผยแพร่ MQTT → การติดตามการยืนยัน สถาปัตยกรรมนี้รองรับเหตุการณ์จากเซนเซอร์หลายพันรายการต่อนาทีโดยไม่บล็อกการทำงาน
class IoTAgentPipeline:
def __init__(self, mqtt_client):
self.mqtt = mqtt_client
self.dedup = EventDeduplicator()
self.cooldown = CoolDownManager(cool_down_minutes=15)
self.ack_tracker = AckTracker(timeout_seconds=30)
def on_sensor_message(self, topic: str, value: float):
policies = match_policies(topic, value)
self.dedup.clear_topic_if_normal(
topic, value,
normal_fn=lambda v: v <= 35 # below warning threshold
)
for policy in policies:
name = policy['name']
if not self.dedup.is_new_event(topic, name):
continue # already active, skip
if self.cooldown.is_in_cool_down(topic, name):
print(f'In cool-down: {name}')
continue
self.dedup.mark_active(topic, name)
ctx = {'topic': topic, 'value': value, 'policy': name}
for action in policy['actions']:
enqueue_action(action, ctx, policy['priority'])เส้นทางการยกระดับปัญหา
บางสถานการณ์จำเป็นต้องยกระดับให้มนุษย์เข้ามาดำเนินการ เช่น ไม่สามารถยืนยันคำสั่งได้ซ้ำหลายครั้ง สภาวะวิกฤตที่เกิดขึ้นต่อเนื่อง หรือนโยบายหลายรายการขัดแย้งกัน ให้กำหนดเส้นทางการยกระดับปัญหาที่ส่งการแจ้งเตือนแบบพุชหรือสร้างรายการแจ้งปัญหา
import requests
def escalate_to_human(
reason: str,
sensor_data: dict,
webhook_url: str = 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
):
message = {
'text': (
f'*IoT Agent Escalation* \n'
f'Reason: {reason}\n'
f'Sensor data: {sensor_data}\n'
f'Time: {datetime.utcnow().isoformat()}'
)
}
try:
response = requests.post(webhook_url, json=message, timeout=5)
response.raise_for_status()
print(f'Escalation sent: {reason}')
except requests.RequestException as e:
print(f'Escalation failed: {e}')
# Fall back: log to file
with open('escalations.log', 'a') as f:
import json
f.write(json.dumps({'reason': reason, 'data': sensor_data}) + '\n')การทดสอบกระบวนการทำงานของ Event
ก่อนนำกระบวนการทำงานของเหตุการณ์ไปใช้ในระบบจริง ให้เขียนการทดสอบอัตโนมัติที่จำลองเหตุการณ์จากเซนเซอร์และตรวจสอบว่าการดำเนินการที่ถูกต้องถูกใส่ลงในคิว ทดสอบแต่ละนโยบายแยกกัน พฤติกรรมการขจัดรายการซ้ำ และการหมดอายุของระยะพัก
import time
def test_high_temp_policy_fires_once():
dedup = EventDeduplicator()
cooldown = CoolDownManager(cool_down_minutes=0) # disable cooldown for test
pipeline = IoTAgentPipeline(None)
pipeline.dedup = dedup
pipeline.cooldown = cooldown
actions_fired = []
action_queue.queue.clear()
# Fire same event 5 times in a row
for _ in range(5):
pipeline.on_sensor_message('sensors/temperature', 40.0)
# Only 1 set of actions should have been enqueued
actions = list(action_queue.queue)
assert len(actions) > 0, 'At least one action should fire'
print(f'Actions enqueued: {len(actions)} (expected: just 1 event worth)')
return True
result = test_high_temp_policy_fires_once()
print('Test passed:', result)ตรวจสอบความรู้
จุดประสงค์หลักของการขจัดเหตุการณ์ซ้ำในกระบวนการทำงานของเหตุการณ์จากเซนเซอร์คืออะไร?
ทบทวน: การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์
ยอดเยี่ยม! สิ่งที่คุณได้เรียนรู้:
- นโยบายการดำเนินการ: การจับคู่เงื่อนไขกับการดำเนินการในรูปแบบประกาศ พร้อมลำดับความสำคัญ
- คิวการดำเนินการ: แยกการตรวจจับออกจากการดำเนินการ โดย Thread ผู้ปฏิบัติงานจะประมวลผลการดำเนินการ
- การขจัดรายการซ้ำ: เรียกใช้ครั้งเดียวต่อเหตุการณ์ ไม่ใช่ครั้งเดียวต่อค่าที่อ่านได้
- ระยะพัก: ป้องกันการเรียกใช้ซ้ำทันทีหลังเงื่อนไขสิ้นสุดลง
- การยกระดับให้ LLM: มอบหมายสถานการณ์ที่มีหลายนโยบายซับซ้อนให้ LLM ใช้เหตุผล
- การติดตามการยืนยัน: ตรวจจับและลองส่งคำสั่งที่ยังไม่ได้รับการยืนยันใหม่
ถัดไป: การนำ Agent น้ำหนักเบาไปใช้งานบนอุปกรณ์ขอบเครือข่าย เช่น Raspberry Pi
คำถามที่พบบ่อย
บทเรียน “การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์”
หากอุณหภูมิ > ค่าเกณฑ์ → แจ้งเตือน → สั่งงาน: วงจรควบคุม IoT ที่ขับเคลื่อนด้วยเอเจนต์ คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม
ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- โพรโทคอล MQTT สำหรับการผสานเอเจนต์
- การประมวลผลข้อมูลอนุกรมเวลาในเอเจนต์
- การตอบสนองอัตโนมัติต่อเหตุการณ์จากเซนเซอร์
- การนำเอเจนต์น้ำหนักเบาไปใช้งานที่เอดจ์