Respons Otomatis terhadap Peristiwa Sensor
Jika suhu > ambang batas → peringatan → aktuasi: putaran kendali IoT yang digerakkan agen.
Respons Otomatis terhadap Peristiwa Sensor adalah pelajaran AI Agents gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Agents, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Agents mencakup 4 pelajaran total.
Respons Otomatis Agen Berbasis Peristiwa
Saat sensor melewati ambang batas, agen harus merespons secara otomatis tanpa campur tangan manusia. Tantangan utamanya adalah menentukan tindakan yang harus diambil, memastikan peristiwa yang sama tidak memicu tindakan duplikat, serta mematuhi masa jeda agar agen tidak membanjiri aktuator dengan perintah.
Menentukan Kebijakan Tindakan
Kebijakan tindakan memetakan kondisi sensor ke respons agen. Tetapkan kebijakan secara deklaratif agar mudah dibaca dan diubah tanpa menyentuh kode logika. Setiap kebijakan memiliki kondisi, prioritas, dan satu tindakan atau lebih.
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']}")
Antrean Tindakan
Antrean tindakan memisahkan deteksi peristiwa dari pelaksanaan tindakan. Peristiwa dimasukkan ke antrean; pekerja mengambil dan menjalankannya. Cara ini mencegah pemblokiran loop penerimaan MQTT dan memungkinkan percobaan ulang jika suatu tindakan gagal.
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())
Deduplikasi Peristiwa
Tanpa deduplikasi, suhu yang tetap di atas 38°C selama 10 menit dengan laju 1 pembacaan per detik menghasilkan 600 peristiwa identik. Deduplikasi memastikan bahwa kombinasi (topik, kondisi, tindakan) yang sama hanya dipicu sekali per peristiwa, lalu diatur ulang saat kondisi terhapus.
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'))Masa Jeda
Bahkan setelah suatu peristiwa terhapus dan terpicu kembali, masa jeda mencegah pemicuan ulang secara cepat. Gabungkan deduplikasi (dipicu sekali selama kondisi berlangsung) dengan masa jeda (tunggu N menit setelah kondisi terhapus sebelum mengizinkan peringatan yang sama dipicu lagi).
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'))Keputusan Tindakan dengan Bantuan LLM
Untuk situasi yang kompleks — beberapa peringatan yang muncul bersamaan, kebijakan yang bertentangan, atau kombinasi pembacaan yang tidak biasa — serahkan keputusan kepada LLM. LLM menerima seluruh konteks sensor dan merekomendasikan rencana tindakan berdasarkan prioritas.
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)Menjalankan Tindakan melalui MQTT
Tindakan dijalankan dengan menerbitkan pesan perintah ke topik MQTT khusus perangkat. Muatan perintah mengikuti skema standar: nama tindakan, parameter, ID permintaan untuk konfirmasi, dan TTL (perintah kedaluwarsa jika perangkat terlalu lama luring).
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())
Konfirmasi Tindakan
Perangkat sebaiknya mengonfirmasi perintah yang diterima dengan menerbitkan pesan ke topik konfirmasi. Agen berlangganan ke topik konfirmasi dan dapat mencoba lagi jika tidak menerima konfirmasi dalam batas waktu.
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?')Alur Pemrosesan Peristiwa Sensor Lengkap
Merangkai semua komponen: penerimaan MQTT → pencocokan kebijakan → pemeriksaan deduplikasi + masa jeda → memasukkan tindakan ke antrean → pekerja menjalankan tindakan melalui penerbitan MQTT → pelacakan konfirmasi. Arsitektur ini menangani ribuan peristiwa sensor per menit tanpa pemblokiran.
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'])Jalur Eskalasi
Beberapa situasi memerlukan eskalasi kepada manusia: kegagalan berulang dalam mengonfirmasi perintah, kondisi kritis yang berlangsung lama, atau beberapa kebijakan yang saling bertentangan. Tentukan jalur eskalasi yang mengirim notifikasi push atau membuat tiket.
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')Menguji Alur Pemrosesan Peristiwa
Sebelum menerapkan alur pemrosesan peristiwa ke lingkungan produksi, tulislah pengujian otomatis yang menyimulasikan peristiwa sensor dan memverifikasi bahwa tindakan yang benar dimasukkan ke antrean. Uji setiap kebijakan secara terpisah, perilaku deduplikasi, dan kedaluwarsa masa jeda.
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)Uji Pemahaman
Apa tujuan utama deduplikasi peristiwa dalam alur pemrosesan peristiwa sensor?
Ringkasan: Respons Otomatis terhadap Peristiwa Sensor
Sangat baik! Hal-hal yang telah Anda pelajari:
- Kebijakan tindakan: pemetaan kondisi ke tindakan secara deklaratif beserta prioritasnya
- Antrean tindakan: memisahkan deteksi dari pelaksanaan; utas pekerja memproses tindakan
- Deduplikasi: dipicu sekali per peristiwa, bukan sekali per pembacaan
- Masa jeda: mencegah pemicuan ulang segera setelah kondisi terhapus
- Eskalasi LLM: situasi kompleks dengan banyak kebijakan diserahkan kepada penalaran LLM
- Pelacakan konfirmasi: mendeteksi dan mencoba ulang perintah yang belum dikonfirmasi
Berikutnya: menerapkan agen ringan pada perangkat tepi seperti Raspberry Pi.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Respons Otomatis terhadap Peristiwa Sensor” gratis?
Ya — teks lengkap “Respons Otomatis terhadap Peristiwa Sensor” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Agents, upgrade ke CoddyKit PRO. Kursus AI Agents mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Respons Otomatis terhadap Peristiwa Sensor”?
Jika suhu > ambang batas → peringatan → aktuasi: putaran kendali IoT yang digerakkan agen. Kamu berlatih AI Agents dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai AI Agents?
Tidak diperlukan pengalaman sebelumnya. AI Agents di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.
Berapa lama pelajaran “Respons Otomatis terhadap Peristiwa Sensor” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Agents ini?
Ya. Setiap pelajaran AI Agents menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Protokol MQTT untuk Integrasi Agen
- Pemrosesan Data Deret Waktu dalam Agen
- Respons Otomatis terhadap Peristiwa Sensor
- Penerapan Agen Ringan di Perangkat Tepi