PABLO.ENGINEERING
// WRÓĆ DO WSZYSTKICH WPISÓW
// 5 min

Odporność floty agentów: Asynchroniczne samoleczenie, eliminacja samobójczych restartów i diagnostyka live Telegrama

#ai-agents#openclaw#telegram#sre#resilience#systemd#sqlite

Zarządzanie flotą botów AI bezpośrednio z poziomu komunikatora Telegram daje ogromną wygodę, ale niesie ze sobą istotną pułapkę architektoniczną. Gdy zlecamy botowi zadania systemowe, łatwo zapomnieć, że jest on procesem sieciowym, który nie może zrestartować samego siebie w trakcie generowania wypowiedzi.

Problem: Przecięcie gałęzi, na której się siedzi (Hot-reload w aktywnej turze)

Wyobraźmy sobie mechanika, który próbuje wymienić skrzynię biegów w samochodzie pędzącym 120 km/h po autostradzie. Dokładnie to wydarzyło się dzisiaj w naszym środowisku.

Podczas rozmowy na Telegramie główny administrator zatwierdził odpięcie nieużywanej wtyczki codex. Bot podjął działanie i wykonał polecenie synchronicznie w trwającej turze czatu:

openclaw plugins uninstall codex --keep-files --force

W odpowiedzi na usunięcie wtyczki bramka OpenClaw natychmiast przeprowadziła dynamiczny hot-reload rejestru. Przeładowanie wyczyściło i zresetowało działającą instancję wtyczki telegram. Gdy bot ułożył odpowiedź i chciał odesłać potwierdzenie na czat, gniazdo transmisyjne już nie istniało:

PluginInstanceUnavailableError: Plugin telegram was reloaded or disabled; use its current tools.
prepared reply dispatch runtime owner was not published for main

Skutek był natychmiastowy: dispatcher sesji zablokował się w stanie zombie. Bot przestał odpowiadać na wiadomości, a administrator musiał łączyć się przez konsolę SSH, aby zdiagnozować powód zacięcia.

Dodatkowo audyt ujawnił dwa inne mankamenty:

  • Ryzykowne zapytania diagnostyczne: Dotychczasowy skrypt statusu pytał bezpośrednio pliki baz danych SQLite pracujących botów, co groziło zakleszczeniem bazy podczas aktywnego zapisu.
  • Brak samoleczenia strażnika: Strażnik floty (fleet_watchdog.py) w przypadku padu usługi biernie odnotowywał brak aktywności, zamiast podjąć natychmiastową, kontrolowaną próbę restartu.

Co zrobiłem: Architektura asynchronicznej samonaprawy i pełna telemetria

Rozwiązanie problemu wymagało wdrożenia wzorca odroczonego wykonania, automatycznego samoleczenia w strażniku oraz bezpiecznej sondy telemetrycznej.

1. Asynchroniczny zarządca restartów (safe_admin_exec.sh)

Zbudowałem dedykowany moduł powłoki safe_admin_exec.sh, który rozdziela interakcję z użytkownikiem od fizycznego restartu procesu:

  # safe_admin_exec.sh - wzorzec out-of-turn execution
do_restart_async() {
    local source="${1:-cli}"
    local reason="${2:-Ręczne zlecenie}"

    log_action "$source" "restart_requested" "pending" "$reason"

    (
        exec 9>"$LOCK_FILE"
        flock -n 9 || exit 0

        # Dajemy czas na zamknięcie tury czatu i wysłanie pakietu HTTP do Telegrama
        sleep 2

        systemctl --user restart openclaw-gateway.service

        # Weryfikacja powrotu usługi (health probe)
        for i in $(seq 1 35); do
            if curl -s -m 3 http://127.0.0.1:18789/healthz 2>/dev/null | grep -q '"ok":true'; then
                tg_notify "✅ [ADMIN] Gateway wstał pomyślnie w ${i}s. Flota operacyjna."
                exit 0
            fi
            sleep 1
        done
        tg_notify "❌ [ADMIN] Błąd restartu gateway: brak odpowiedzi w 35s."
    ) >/dev/null 2>&1 &

    echo "Zlecenie restartu przyjęte. Usługa zostanie przeładowana za 2 sekundy w tle."
}

Gdy administrator prosi o restart lub zmianę wtyczek, bot natychmiast odsyła potwierdzenie na czat, a bieżąca tura kończy się sukcesem. Dopiero 2 sekundy później podproces w tle bezpiecznie restartuje usługę, a po jej wstaniu wysyła bezpośredni meldunek na Telegram przez Bot API (z pominięciem modelu LLM).

2. Autonomiczne samoleczenie w strażniku floty (fleet_watchdog.py)

Zaktualizowałem skrypt strażnika uruchamiany co 5 minut z crona. Strażnik potrafi teraz natychmiast rozpoznać zablokowany dispatcher oraz brak aktywności systemd:

  # fleet_watchdog.py - wykrywanie zamrożenia i kontrolowana samonaprawa
if crit:
    freeze_match = any(re.search(
        r"prepared reply dispatch|prepared model runtime|PluginInstanceUnavailableError",
        c, re.I
    ) for c in crit)
    
    if freeze_match:
        last_heal = state.get("last_freeze_heal_at", 0)
        if now - last_heal > 1800:  # 30 min cooldown
            state["last_freeze_heal_at"] = now
            save_state(state)
            log("Wykryto zablokowanie dispatchera - uruchamiam bezpieczny restart samonaprawczy")
            tg_send("🚨 [FLEET] Wykryto zablokowanie dispatchera odpowiedzi. Uruchamiam automatyczny restart...")
            sh("/bin/bash", "/root/.openclaw/workspace/scripts/safe_admin_exec.sh",
               "restart", "watchdog-autoheal", "Zablokowany dispatcher odpowiedzi")

Dzięki 30-minutowemu cooldownowi system jest w 100% zabezpieczony przed wpadnięciem w pętlę ciągłych restartów (restart-loop), a administrator otrzymuje powiadomienie o incydencie bez konieczności manualnej interwencji.

3. Bezpieczna diagnostyka floty w czasie rzeczywistym (fleet_status.sh)

Przebudowałem skrypt diagnostyczny wywoływany przez bota na komendy /status i /diag. Zamiast niebezpiecznych zapytań do bazy SQLite, skrypt korzysta z natywnej sondy kanałów:

=== FLOTA OPENCLAW: RAPORT STATUSU ===

1. STATUS GATEWAY I ZASOBY:
 🟢 Gateway: aktywny (od: Tue 2026-09-22 19:26:14 UTC, PID: 3494657)
 📊 RAM: 9735MB wolne z 15936MB (użycie: 38%) | Swap: 3949MB użyte z 4095MB
 💾 /: wolne 378G (19%) | /tmp: użyte 979M (24%)
 📁 /var/lib/openclaw/tmp: 87G (19%)

2. STAN KANAŁÓW TELEGRAM (LIVE PROBE):
 🟢 [aneta] aneta: aktywny (74ms)
 🟢 [default] Pabloski: aktywny (81ms)
 🟢 [gym] gym: aktywny (83ms)
 🟢 [spanish] spanish: aktywny (56ms)

3. BAZA STANU I MIGRACJE:
 🟢 Wszystkie migracje zakończone sukcesem (zero pending)

4. OSTATNIE BŁĘDY SYSTEMOWE (Z 60 MINUT):
 🟢 Brak krytycznych błędów w dzienniku systemd.
======================================

Weryfikacja tabeli migracji SQLite (migration_runs) odbywa się teraz na ulotnej kopii wykonanej w ułamku sekundy w /tmp, co całkowicie eliminuje ryzyko zablokowania bazy produkcyjnej.

4. Żelazne reguły w AGENTS.md i dedykowany log audytu

Do instrukcji bota głównego (AGENTS.md) wprowadziłem bezwzględny zakaz synchronicznego przeładowywania wtyczek oraz restartowania bramki. Wszystkie akcje administracyjne trafiają do pliku /var/log/openclaw-admin-actions.log, co gwarantuje pełny audyt operacyjny.

Dlaczego to ważne (Use Case)

Wprowadzone usprawnienia rozwiązują trzy kluczowe wyzwania utrzymania systemów agentowych:

  • Zarządzanie z poziomu telefonu bez konsoli SSH: Administrator ma pełny podgląd zdrowia 4 botów, pamięci RAM, swapu i błędów systemowych bezpośrednio w oknie czatu Telegram. Wpisanie /status daje kompletny raport w 2 sekundy.
  • Bezpieczeństwo operacji krytycznych: Zastosowanie wzorca asynchronicznego wykonania (out-of-turn) uniemożliwia przypadkowe "odcięcie gałęzi", na której działa bot. Wiadomość z potwierdzeniem zawsze dotrze do użytkownika zanim usługa zostanie przeładowana.
  • Autonomiczna odporność 24/7: W przypadku awarii sieci, anomalii wtyczki lub wycieku w bibliotece zewnętrznej, strażnik samodzielnie zdiagnozuje zablokowany dispatcher i w bezpieczny sposób uzdrowi flotę, wysyłając czytelny raport na Telegram.

Czego się nauczyłem

  1. Aplikacja sieciowa nie może restartować się wewnątrz obsługiwanego żądania: Operacje restartu i przeładowania komponentów muszą być bezwzględnie odroczone w czasie (asynchroniczne) i wykonywane przez niezależny proces w tle.
  2. Strażnik musi mieć uprawnienia wykonawcze i mechanizm cooldownu: Samo logowanie awarii przez monitor to za mało. Prawdziwa odporność (resilience) polega na zdolności do automatycznego samoleczenia z rygorystycznym limitem powtórzeń zapobiegającym pętlom awaryjnym.
  3. Diagnostyka nie może generować ryzyka produkcyjnego: Nigdy nie wolno pytać pracującej bazy danych SQLite bezpośrednio ze skryptów monitorujących. Badanie stanu musi odbywać się na kopii tymczasowej lub za pośrednictwem dedykowanych sond API.