Masterclass / Modul 2

Self-Healing und Self-Learning

5 Lektionen: Systeme, die sich nachts selbst reparieren und aus Fehlern lernen.

Lektion 06

Self-Healing-Philosophie: Begrenzte Retries, keine Endlosschleifen

Guards verhindern, dass ein Agent Schaden anrichtet. Aber Schaden, der von allein entsteht, um 3 Uhr nachts, ohne Zuschauer, dagegen tun Guards nichts. Ein Container crashed, ein Connection Pool ist erschöpft, eine Queue blockiert. Das Erste, was du davon erfährst, ist ein Kunde, der es bemerkt.

Self-Healing schließt diese Lücke. Eine Reihe von Skripten bemerkt, dass etwas nicht stimmt, und behebt es, ohne auf einen wachen Menschen zu warten.

Der kritische Fehlermodus

Der offensichtliche Fehlermodus jedes Self-Healing-Systems: Der „Fix" verschlimmert die Lage und das System versucht denselben kaputten Fix immer erneut. Eine Restart-Schleife, die nie etwas erfolgreich neu startet, ist schlimmer als gar keine Automatisierung. Sie verbraucht Ressourcen und erzeugt Rauschen, während das eigentliche Problem unangetastet bleibt.

Deshalb gilt die harte Regel: Retries sind begrenzt. Wenn die Grenze erreicht ist, stoppt das System und eskaliert an einen Menschen, statt weiterzurotieren. Ein Emergency-Fix bleibt immer explizit temporär.

Kernprinzip: Erkennen, gegen parallele Reparaturen sperren, begrenzt reparieren, verifizieren und bei ausgeschöpftem Limit mit gesichertem Zustand eskalieren. Nie endlos retrien.

Drei Skripte, drei Scopes

Die Übergabe zwischen ihnen ist eine Übergabe im Scope. Enge, häufige Prüfungen fangen offensichtliche Ausfälle schnell. Breitere, langsamere Prüfungen fangen die subtileren, die sich erst über ein längeres Beobachtungsfenster zeigen.

Übung 6: Self-Healing-Philosophie verstehen
  1. Schreibe die drei häufigsten Ausfallszenarien deiner eigenen Infrastruktur auf (Container-Crash, Deploy-Fehler, DB-Verbindung, etc.)
  2. Ordne jedem Szenario zu: Wann würdest du es bemerken? Wer bemerkt es zuerst (du oder der Kunde)?
  3. Definiere für jedes Szenario eine maximale Retry-Anzahl und begründe, warum diese Zahl
Lektion 07

Dein erster Watchdog: Container-Wiederbelebung

live-app-watchdog.sh hat einen spezifischen, engen Job: Live-Apps, die Kunden bedienen, vor dem Ausfall „der Container, der laufen soll, läuft nicht" schützen. Das Skript prüft Container-Status, Domain-Health und Soll-Zustand.

Die Regeln des Emergency-Containers

Ein Emergency-Container darf nur starten, wenn:

Ein exklusives Lock und eindeutige Instanzrollen verhindern parallele Starts, doppelte Worker und Split-Brain.

Kernprinzip: Das Wort „Emergency" leistet echte Arbeit. Ein Emergency-Container ist eine Brücke, kein Ziel. Er stellt den Dienst sofort wieder her, während das eigentliche Deployment-Problem während der Geschäftszeiten diagnostiziert wird. Einen Emergency-Container als permanenten Fix zu behandeln ist genau die Art von Abkürzung, die aus einer schlechten Nacht eine wiederkehrende macht.

Was Self-Healing nicht anfasst

Keines der drei Skripte berührt Kundendaten, keines modifiziert ein Datenbankschema und keines sendet Kommunikation an echte Nutzer. Das Behebungsvokabular bleibt gezielt eng und infrastrukturell: reversible Aktionen wie Prozess neustarten, Container aus bekanntem guten Image starten, Cache leeren oder einen Deploy-Schritt erneut ausführen.

Template: Minimaler Watchdog
#!/bin/bash
CONTAINER="deine-app"
DOMAIN="deine-app.example.de"
MAX_RETRIES=2

check_health() {
  local status
  status=$(curl -sS -o /dev/null -w "%{http_code}" \
    "https://${DOMAIN}/api/health" 2>/dev/null)
  [ "$status" = "200" ]
}

attempt=0
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
  while [ "$attempt" -lt "$MAX_RETRIES" ]; do
    attempt=$((attempt + 1))
    echo "[$(date)] Container ${CONTAINER} nicht gefunden. Restart $attempt/$MAX_RETRIES"
    docker start "$CONTAINER" 2>/dev/null || true
    sleep 10
    if check_health; then
      echo "[$(date)] Health OK nach Restart."
      exit 0
    fi
  done
  echo "[$(date)] ESKALATION: ${CONTAINER} nach $MAX_RETRIES Versuchen nicht healthy."
  # Hier: Push-Notification, Slack, E-Mail
  exit 1
fi
Übung 7: Watchdog implementieren
  1. Wähle deine wichtigste App und notiere: Container-Name, Domain, Health-Endpoint
  2. Passe das Template oben an deine Werte an
  3. Setze das Skript als Cron-Job auf (z.B. alle 5 Minuten): */5 * * * * /opt/scripts/watchdog.sh >> /var/log/watchdog.log 2>&1
  4. Teste manuell: Stoppe den Container, warte auf den nächsten Cron-Lauf, prüfe das Log
Lektion 08

Die Post-Deploy-Reparaturschleife

post-deploy-repair-loop.sh mit 187 Zeilen ist das straffste der drei Self-Healing-Skripte. Es läuft unmittelbar nach jedem Deploy und folgt einem einfachen Ablauf:

  1. HTTP-Check: Antwortet die deployete App korrekt?
  2. Visueller Check: Sieht die gerenderte Seite richtig aus, nicht nur Status 200?
  3. Fix-Versuch: Wenn ein Check fehlschlägt, eine bekannte Behebung versuchen.
  4. Retry: Checks erneut ausführen.

Die Schleife ist auf drei Iterationen begrenzt. Diese Begrenzung ist die wichtigste Zeile im gesamten Skript. Der Unterschied zwischen einem System, das sagt „Ich konnte das nicht reparieren, bitte schau hin" und einem System, das still Compute verbrennt.

Kernprinzip: „Bekannte Behebung" bedeutet nicht „lass den Agenten einen Fix improvisieren." Ein kleiner, gezielt begrenzter Satz vorab geprüfter Aktionen: Container neustarten, bestimmten Cache leeren, bestimmten Migrationsschritt erneut ausführen. Nicht um 2 Uhr nachts unter Druck kreativ werden.

Warum das nur wegen der Guards funktioniert

Self-Healing ohne eine eigene Policy-Schicht wäre rücksichtslos. Ein Skript, das Container neustarten, Caches leeren und Behebungen automatisch ausführen darf, hat echtes destruktives Potenzial. Deshalb besitzt der Watchdog eine eigene enge Allowlist, Locking, begrenzte Versuche, Zustandsprüfungen und unveränderbare Logs.

Template: Post-Deploy-Verification
#!/bin/bash
APP_URL="https://deine-app.example.de"
MAX_ATTEMPTS=3
attempt=0

while [ "$attempt" -lt "$MAX_ATTEMPTS" ]; do
  http_code=$(curl -sS -o /dev/null -w "%{http_code}" "$APP_URL")

  if [ "$http_code" = "200" ]; then
    echo "Deploy verified healthy nach $attempt Repair-Versuch(en)."
    exit 0
  fi

  attempt=$((attempt + 1))
  echo "Health check failed (HTTP $http_code). Repair $attempt/$MAX_ATTEMPTS."

  # Bekannte Behebungen (anpassen!)
  docker restart "$(docker ps -q --filter name=deine-app)" 2>/dev/null
  sleep 15
done

echo "ESKALATION: Deploy nicht healthy nach $MAX_ATTEMPTS Versuchen."
# Push-Notification senden
exit 1
Übung 8: Deploy-Verification einrichten
  1. Passe das Template an deine App an (URL, Container-Name)
  2. Definiere 2-3 „bekannte Behebungen" für deine häufigsten Deploy-Probleme
  3. Integriere das Skript in deinen Deploy-Prozess (Coolify Webhook, CI/CD Post-Step, etc.)
  4. Teste mit einem absichtlich kaputten Deploy (z.B. falscher Port)
Lektion 09

Die Self-Learning-Kette: Wie dein System lernt

Guards stoppen bekannte schlechte Aktionen und Self-Healing stellt bekannte Fehlzustände wieder her, doch keines von beiden wird von allein besser. Ein Guard bleibt exakt so intelligent wie am Tag seiner Niederschrift. Hörst du hier auf, ist das System stabil, aber statisch.

Das fehlende Stück ist die Self-Learning-Kette: Skripte, die bemerken, was passiert ist, entscheiden, ob es sich lohnt, die Erkenntnis zu behalten, und sie bei wiederholter Bewährung in eine permanente Regel umwandeln.

Vier Skripte, nacheinander

Kernprinzip: Die ehrliche Unterscheidung zwischen „das System hat Guards" und „das System lernt": Guards sind ein Snapshot von allem, was du bereits herausgefunden hast. Die Learning-Kette ist der Mechanismus, durch den sich der Snapshot selbst aktualisiert.

Warum dreaming.sh das größte Skript ist

Konsolidierung wiegt schwerer als Extraktion. Eine Lektion aus einer einzelnen Session zu extrahieren ist ein begrenztes Problem. Konsolidierung muss über alles bisher Angesammelte schauen: Sagt diese neue Notiz dasselbe wie eine bestehende? Stimmt die alte Notiz noch mit dem realen Systemverhalten überein? Verbirgt sich ein Muster über fünf separate Notizen hinweg?

dreaming.sh nächtlich laufen zu lassen statt nach jeder Session ist kein Zufall. Konsolidierung braucht einen Stapel angesammelten Materials und muss laufen, wenn nichts anderes aktiv in den Speicher schreibt.

Übung 9: Learning-Kette starten
  1. Erstelle eine Datei learnings.md in deinem Projekt
  2. Nach jeder Session: Notiere 1-3 Korrekturen, die du manuell vorgenommen hast (Format: Kontext, Problem, Lösung)
  3. Mache das eine Woche lang. Dann suche nach derselben Korrektur, die mehr als einmal erscheint
  4. Dreimal dieselbe Korrektur vermerkt? Das ist dein erster Regelvorschlag
Lektion 10

Der Kristallisierungs-Loop: Von Beobachtung zu Regel

Ein Lernkandidat wird nicht beim ersten Auftreten zur permanenten Regel. Er wird gespeichert mit Kontext, vorgeschlagener Regel, Nützlichkeits-Score und Run-Count.

Die Beförderungsregel

Jedes Mal, wenn sich das Learning in einer späteren Session als nützlich erweist, steigt der Run-Count. Die Schwelle:

Template: State Machine eines Learnings
learning captured (session-learner.sh)
  -> stored: { context, proposed_rule, score, runs: 0 }
  -> each session where it helps again: runs += 1
  -> human or system feedback: score updated
  -> if score >= 4 AND runs >= 3: READY FOR REVIEW
  -> provenance check + conflict scan + isolated tests + human approval
  -> signed and versioned: CRYSTALLIZED -> written into permanent rules

Warum drei Bestätigungen, nicht eine

Eine einzelne Beobachtung kann nicht zwischen einem echten Muster und einem einmaligen Zufall unterscheiden. Ein Fix, der einmal funktionierte, könnte aus einem Grund funktioniert haben, der nichts mit der vorgeschlagenen Regel zu tun hat. Drei separate, unabhängige Sessions filtern dieses Rauschen heraus.

Ein Regelsatz voller einmaliger Zufälle wäre über die Zeit genauso belastend wie gar keine Regeln. Anleitung, die mit Überzeugung falsch ist, schadet mehr als fehlende Anleitung, weil man ihr standardmäßig vertraut.

Das Risiko eines Regelsatzes, der nie vergisst

Ein Lernsystem, das immer nur Regeln hinzufügt, hat seinen eigenen Fehlermodus. Code ändert sich. Eine Regel, kristallisiert vor sechs Monaten, kann im Moment des Refactorings still falsch werden. Das ist die zweite Aufgabe von dreaming.sh: veraltete Einträge finden und zur Überprüfung markieren.

Kernprinzip: Ein Memory-System, das nur akkumuliert und nie bereinigt, wird irgendwann weniger vertrauenswürdig als gar kein Memory-System. Eine falsche Regel, formuliert mit Überzeugung, schadet mehr als eine fehlende. Die fehlende provoziert zumindest einen frischen Blick.

Memory Agent V2: Das Substrat

16.756 Vault-Dateien zu speichern ist nutzlos, wenn der Abruf der relevanten Datei länger dauert als die Antwort einfach neu abzuleiten. Memory Agent V2 löst das mit einem hybriden Ansatz: TF-IDF-Keyword-Matching kombiniert mit Embedding-basierter semantischer Suche. Keyword-Matching findet exakte Begriffsabgleiche. Embedding-Suche findet konzeptionell verwandte Notizen ohne ein einziges gemeinsames Wort. Zusammen decken sie gegenseitig ihre blinden Flecken ab.

Übung 10: Kristallisierung einrichten
  1. Nimm deine learnings.md aus Übung 9 und füge jedem Eintrag hinzu: score: [1-5] und runs: [Anzahl]
  2. Identifiziere alle Einträge mit Score >= 4 und Runs >= 3
  3. Für jeden Kandidaten: Prüfe, ob die vorgeschlagene Regel noch zum aktuellen Code passt
  4. Wandle einen Kandidaten in einen echten Guard oder eine Hook-Regel um
  5. Bonus: Schreibe ein Skript, das learnings.md nach Beförderungskandidaten filtert

Modul 2 abgeschlossen

Du hast jetzt: Self-Healing-Philosophie verstanden, einen Watchdog und eine Deploy-Verification gebaut, die Learning-Kette und den Kristallisierungs-Loop eingerichtet.

Weiter mit Modul 3: Die Intelligenzschicht: Vault, RAG, MCP

Modul 1 Modul 3 Modul 4 Modul 5 Modul 6