AI Agents · Lekcja

Wzorce projektowe agentów działających stale

Procesy działające w tle, agenci typu daemon i zarządzanie trwałymi połączeniami.

Lekcja 1 z 413 kroki

Wzorce projektowe agentów działających stale to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.

Czym jest agent działający stale?

Agent działający stale pracuje nieprzerwanie jako usługa w tle, czekając na zdarzenia i proaktywnie podejmując działania. W przeciwieństwie do agentów działających w modelu żądanie–odpowiedź zachowuje działanie między interakcjami i utrzymuje stan w czasie.

Wzorzec procesu demona

Proces demona działa w tle, niezależnie od jakiejkolwiek sesji terminala. Należy użyć wątków daemon języka Python albo menedżera procesów, aby agent działał po zamknięciu terminala.

import threading
import time
import signal
import sys

shutdown_flag = threading.Event()

def agent_main_loop():
    print('Agent daemon started')
    while not shutdown_flag.is_set():
        try:
            # Agent work: check for events, process tasks
            perform_agent_cycle()
            shutdown_flag.wait(timeout=60)  # Sleep 60s, wakes on shutdown
        except Exception as e:
            print(f'Agent loop error: {e}')
            shutdown_flag.wait(timeout=5)  # Brief pause on error
    print('Agent daemon stopped')

def perform_agent_cycle():
    print(f'Agent cycle at {time.strftime("%H:%M:%S")}')
    # Check emails, process queue, run scheduled tasks

def handle_signal(signum, frame):
    print(f'Signal {signum} received, shutting down...')
    shutdown_flag.set()

# Register signal handlers for graceful shutdown
signal.signal(signal.SIGTERM, handle_signal)
signal.signal(signal.SIGINT, handle_signal)

# Start as daemon thread
thread = threading.Thread(target=agent_main_loop, daemon=True)
thread.start()
print('Agent running in background')

Watchdog: automatyczny restart po awarii

Watchdog monitoruje proces agenta i uruchamia go ponownie, jeśli ulegnie awarii. Jest to niezbędne w środowisku produkcyjnym: agenci nieuchronnie napotykają nieoczekiwane błędy i muszą automatycznie odzyskiwać sprawność.

import subprocess
import time
import logging

logger = logging.getLogger('watchdog')

class AgentWatchdog:
    def __init__(self, agent_script: str, max_restarts: int = 10, restart_delay: float = 5.0):
        self.agent_script = agent_script
        self.max_restarts = max_restarts
        self.restart_delay = restart_delay
        self.restart_count = 0
        self.process = None
    
    def start(self):
        self.process = subprocess.Popen(
            ['python', self.agent_script],
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT
        )
        logger.info(f'Agent started (PID: {self.process.pid})')
    
    def run_forever(self):
        self.start()
        while True:
            return_code = self.process.wait()
            logger.warning(f'Agent exited with code {return_code}')
            
            if self.restart_count >= self.max_restarts:
                logger.error(f'Max restarts ({self.max_restarts}) reached. Stopping watchdog.')
                break
            
            self.restart_count += 1
            logger.info(f'Restarting agent (attempt {self.restart_count})...')
            time.sleep(self.restart_delay * self.restart_count)  # Backoff
            self.start()

print('AgentWatchdog defined')

Trwałe połączenie WebSocket

Aby dostarczać zdarzenia w czasie rzeczywistym, należy utrzymywać trwałe połączenie WebSocket. W przypadku zerwania połączenia trzeba automatycznie nawiązać je ponownie — to główne wyzwanie związane z agentami działającymi stale.

import asyncio
import websockets
import json

async def persistent_websocket_connection(uri: str, on_message):
    backoff = 1
    max_backoff = 60
    
    while True:  # Reconnect forever
        try:
            print(f'Connecting to {uri}')
            async with websockets.connect(uri, ping_interval=30, ping_timeout=10) as ws:
                print('WebSocket connected')
                backoff = 1  # Reset backoff on successful connection
                
                async for raw_message in ws:
                    try:
                        message = json.loads(raw_message)
                        await on_message(message)
                    except json.JSONDecodeError:
                        print(f'Invalid JSON message: {raw_message[:100]}')
        
        except websockets.exceptions.ConnectionClosed as e:
            print(f'WebSocket closed: {e}. Reconnecting in {backoff}s')
        except Exception as e:
            print(f'WebSocket error: {e}. Reconnecting in {backoff}s')
        
        await asyncio.sleep(backoff)
        backoff = min(backoff * 2, max_backoff)  # Exponential backoff

async def handle_ws_message(message: dict):
    print(f'Received: {message}')

print('Persistent WebSocket connection function defined')

Kontrole heartbeat

Heartbeat potwierdza, że agent działa i przetwarza dane. Należy wysyłać sygnał heartbeat co N sekund; jeśli sygnały przestaną przychodzić, watchdog będzie wiedzieć, że agent utknął lub zakończył działanie.

import threading
import time
from datetime import datetime

class HeartbeatMonitor:
    def __init__(self, max_silence_seconds: int = 300):
        self.last_heartbeat = datetime.utcnow()
        self.max_silence = max_silence_seconds
        self.lock = threading.Lock()
    
    def beat(self):
        with self.lock:
            self.last_heartbeat = datetime.utcnow()
    
    def is_alive(self) -> bool:
        with self.lock:
            silence = (datetime.utcnow() - self.last_heartbeat).total_seconds()
            return silence < self.max_silence
    
    def silence_seconds(self) -> float:
        with self.lock:
            return (datetime.utcnow() - self.last_heartbeat).total_seconds()

monitor = HeartbeatMonitor(max_silence_seconds=60)

def agent_with_heartbeat():
    while not shutdown_flag.is_set():
        # Send heartbeat at start of each cycle
        monitor.beat()
        
        # Do agent work
        perform_agent_cycle()
        time.sleep(30)

# External watchdog checks the monitor
def watchdog_check():
    while True:
        if not monitor.is_alive():
            print(f'ALERT: Agent silent for {monitor.silence_seconds():.0f}s')
            # Restart agent here
        time.sleep(30)

print('Heartbeat monitor defined')

Kontrolowane zamykanie

Kontrolowane zamykanie kończy trwające zadania przed zatrzymaniem procesu. Agent odbiera sygnał zatrzymania, nie pozwala rozpoczynać nowych zadań, kończy bieżące zadania, zapisuje stan i poprawnie kończy działanie.

import signal
import threading
from contextlib import contextmanager

class GracefulShutdown:
    def __init__(self, timeout: float = 30.0):
        self.should_stop = threading.Event()
        self.active_tasks = 0
        self.lock = threading.Lock()
        self.timeout = timeout
        signal.signal(signal.SIGTERM, self._handle_signal)
        signal.signal(signal.SIGINT, self._handle_signal)
    
    def _handle_signal(self, signum, frame):
        print(f'Shutdown signal received. Waiting for {self.active_tasks} active tasks...')
        self.should_stop.set()
    
    @contextmanager
    def task(self):
        if self.should_stop.is_set():
            raise RuntimeError('Shutdown in progress, not accepting new tasks')
        with self.lock:
            self.active_tasks += 1
        try:
            yield
        finally:
            with self.lock:
                self.active_tasks -= 1
    
    def wait_for_all_tasks(self):
        self.should_stop.wait()
        deadline = time.time() + self.timeout
        while self.active_tasks > 0 and time.time() < deadline:
            time.sleep(0.1)
        if self.active_tasks > 0:
            print(f'WARNING: Forced shutdown with {self.active_tasks} tasks still active')

shutdown = GracefulShutdown(timeout=30)
print('Graceful shutdown manager created')

Obsługa rozłączeń i ponownych połączeń

Agenci działający stale potrzebują strategii obsługi rozłączeń z usługami: buforowania zdarzeń podczas przestoju, odtwarzania pominiętych zdarzeń po ponownym połączeniu oraz zapobiegania utracie zdarzeń, które nadeszły w czasie rozłączenia.

import asyncio
import redis
from datetime import datetime

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

class DisconnectHandler:
    def __init__(self, buffer_key: str = 'agent:offline_buffer'):
        self.buffer_key = buffer_key
        self.connected = True
    
    def on_disconnect(self):
        self.connected = False
        print(f'Disconnected at {datetime.utcnow()}')
    
    def on_reconnect(self):
        self.connected = True
        print(f'Reconnected at {datetime.utcnow()}')
        self.replay_buffered_events()
    
    def handle_event(self, event: dict):
        if not self.connected:
            # Buffer events for later replay
            import json
            r.lpush(self.buffer_key, json.dumps(event))
            print(f'Event buffered (offline): {event["type"]}')
            return
        self.process_event(event)
    
    def replay_buffered_events(self):
        import json
        replayed = 0
        while True:
            raw = r.rpop(self.buffer_key)
            if not raw:
                break
            event = json.loads(raw)
            self.process_event(event)
            replayed += 1
        if replayed:
            print(f'Replayed {replayed} buffered events')
    
    def process_event(self, event: dict):
        print(f'Processing event: {event["type"]}')

handler = DisconnectHandler()
print('Disconnect handler created')

Nadzorowanie procesów za pomocą systemd

W przypadku produkcyjnych wdrożeń w systemie Linux należy użyć systemd do zarządzania procesem agenta. systemd obsługuje automatyczny restart, rejestrowanie w journald oraz uruchamianie agenta podczas startu systemu.

# /etc/systemd/system/my-agent.service
SERVICE_FILE = '''
[Unit]
Description=My AI Agent Service
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/agent
ExecStart=/home/ubuntu/venv/bin/python agent.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

# Environment variables
EnvironmentFile=/home/ubuntu/agent/.env

# Resource limits
MemoryLimit=1G
CPUQuota=50%

[Install]
WantedBy=multi-user.target
'''

# Deploy commands:
# sudo cp my-agent.service /etc/systemd/system/
# sudo systemctl daemon-reload
# sudo systemctl enable my-agent
# sudo systemctl start my-agent
# sudo systemctl status my-agent
# sudo journalctl -u my-agent -f  # Follow logs

print('Systemd service configuration defined')
print('Enables: auto-start on boot, auto-restart on crash, centralized logging')

Utrwalanie stanu między restartami

Agent działający stale musi zapisywać swój stan, aby po restarcie móc wznowić działanie od miejsca, w którym je przerwał. Należy regularnie zapisywać dane punktu kontrolnego na dysku lub w Redisie, a także po istotnych zmianach stanu.

import json
import os
from datetime import datetime

CHECKPOINT_FILE = '/tmp/agent_checkpoint.json'

def save_checkpoint(state: dict):
    state['last_saved'] = datetime.utcnow().isoformat()
    with open(CHECKPOINT_FILE, 'w') as f:
        json.dump(state, f, indent=2)
    print(f'Checkpoint saved at {state["last_saved"]}')

def load_checkpoint() -> dict:
    if not os.path.exists(CHECKPOINT_FILE):
        print('No checkpoint found, starting fresh')
        return {}
    with open(CHECKPOINT_FILE) as f:
        state = json.load(f)
    print(f'Checkpoint loaded from {state.get("last_saved", "unknown")}')
    return state

# Agent startup
agent_state = load_checkpoint()
last_processed_id = agent_state.get('last_processed_email_id', 0)
print(f'Resuming from email ID: {last_processed_id}')

# After processing each email
agent_state['last_processed_email_id'] = last_processed_id + 1
if agent_state['last_processed_email_id'] % 10 == 0:  # Checkpoint every 10 items
    save_checkpoint(agent_state)

Monitorowanie agentów działających stale

Należy śledzić kluczowe metryki agentów działających stale: czas działania, liczbę przetworzonych zdarzeń na godzinę, współczynnik błędów, zużycie pamięci oraz czas ostatniej aktywności. Metryki te należy udostępniać za pomocą endpointu health albo przesyłać do usługi monitorującej.

from fastapi import FastAPI
from datetime import datetime
import psutil
import os

app = FastAPI()
start_time = datetime.utcnow()
events_processed = 0
last_event_time = None

@app.get('/health')
def health_check():
    process = psutil.Process(os.getpid())
    uptime_seconds = (datetime.utcnow() - start_time).total_seconds()
    
    last_active = None
    if last_event_time:
        last_active = (datetime.utcnow() - last_event_time).total_seconds()
    
    return {
        'status': 'ok',
        'uptime_seconds': round(uptime_seconds),
        'events_processed': events_processed,
        'memory_mb': round(process.memory_info().rss / 1024 / 1024, 1),
        'cpu_percent': process.cpu_percent(interval=1),
        'last_event_seconds_ago': round(last_active) if last_active else None,
        'timestamp': datetime.utcnow().isoformat()
    }

Circuit breaker dla zależności zewnętrznych

Agent działający stale korzysta z usług zewnętrznych, które mogą ulec awarii. Circuit breaker na pewien czas wstrzymuje wywołania zawodnej usługi, zapobiegając awariom kaskadowym.

import time
from enum import Enum

class CircuitState(Enum):
    CLOSED = 'closed'      # Normal operation
    OPEN = 'open'          # Service down, not calling
    HALF_OPEN = 'half_open'  # Testing if service recovered

class CircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=60):
        self.state = CircuitState.CLOSED
        self.failure_count = 0
        self.threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.last_failure_time = None
    
    def call(self, fn, *args):
        if self.state == CircuitState.OPEN:
            if time.time() - self.last_failure_time > self.recovery_timeout:
                self.state = CircuitState.HALF_OPEN
            else:
                raise RuntimeError('Circuit open: service unavailable')
        
        try:
            result = fn(*args)
            if self.state == CircuitState.HALF_OPEN:
                self.state = CircuitState.CLOSED
                self.failure_count = 0
                print('Circuit closed: service recovered')
            return result
        except Exception as e:
            self.failure_count += 1
            self.last_failure_time = time.time()
            if self.failure_count >= self.threshold:
                self.state = CircuitState.OPEN
                print(f'Circuit opened after {self.failure_count} failures')
            raise

circuit = CircuitBreaker(failure_threshold=3, recovery_timeout=30)
print('Circuit breaker created')

Sprawdzenie wiedzy: agenci działający stale

Sprawdź swoją wiedzę na temat wzorców projektowych agentów działających stale.

Podsumowanie wzorców projektowych agentów działających stale

Niezawodni agenci działający stale łączą procesy demona z obsługą sygnałów na potrzeby kontrolowanego zamykania, mechanizmy watchdog zapewniające automatyczny restart po awarii, trwałe połączenia WebSocket z ponownym łączeniem i wykładniczym zwiększaniem opóźnienia, kontrole heartbeat wykrywające zablokowane procesy, kontrolowane zamykanie kończące zadania będące w toku oraz zapisywanie punktów kontrolnych stanu, aby wznowić działanie po restarcie. W środowisku produkcyjnym należy używać systemd lub supervisord do zarządzania procesami.

Bezpłatny start

Ucz się AI Agents dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
60
Lekcje
239

Często zadawane pytania

Czy lekcja „Wzorce projektowe agentów działających stale” jest bezpłatna?

Tak — pełny tekst „Wzorce projektowe agentów działających stale” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.

Co nauczysz się w „Wzorce projektowe agentów działających stale”?

Procesy działające w tle, agenci typu daemon i zarządzanie trwałymi połączeniami. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Agents?

Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Wzorce projektowe agentów działających stale”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?

Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Wzorce projektowe agentów działających stale
  2. Proaktywne systemy powiadomień i alertów
  3. Zachowywanie kontekstu między sesjami
  4. Budowanie agenta codziennego briefingu
← Powrót do AI Agents