#!/usr/bin/env bash # stack-radar.sh — Job 3: "KI & Stack Radar" (21.08.2026). # # Ablauf: IST-Zustand holen (Fakten) -> Modell bewerten lassen (Prosa) # -> ueber notify.sh melden (Telegram + Lucys Stimme) # # KUGELSICHER heisst hier konkret: # * Die Fakten kommen aus stack-ist.sh, nicht aus dem Modell. Faellt das Modell # aus, werden die ROTEN Zeilen trotzdem gemeldet — roh statt gar nicht. # * Es meldet IMMER. "Alles gruen" ist ein Ergebnis, kein Grund zu schweigen. # * Kein set -e: ein einzelner Fehlschlag darf den Bericht nicht abschneiden. # # Aufruf ueber Hermes-Cron: # hermes cron create '0 8 * * 6' --name 'KI & Stack Radar' \ # --no-agent --script stack-radar.sh --deliver local # (--no-agent, weil dieses Skript selbst meldet. Leere Ausgabe = Hermes schweigt.) set -uo pipefail HIER="$(cd "$(dirname "$0")" && pwd)" IST_SKRIPT="${IST_SKRIPT:-$HOME/.hermes/scripts/stack-ist.sh}" NOTIFY="${NOTIFY:-$HOME/mission-control-v2/deploy/notify.sh}" GATEWAY="${GATEWAY_URL:-http://127.0.0.1:9010/v1}" MODELL="${RADAR_MODELL:-fast}" # bewusst NICHT heavy: das laedt 68 GB und der # Radar meldet danach seinen eigenen Speicherverbrauch als Befund [ -x "$IST_SKRIPT" ] || IST_SKRIPT="$HIER/stack-ist.sh" melde() { if [ -x "$NOTIFY" ] || [ -f "$NOTIFY" ]; then bash "$NOTIFY" -s "[Stack-Radar]" "$1" else # Letzte Rueckfallebene: wenigstens ins Log, damit nichts still verschwindet. echo "[stack-radar] MELDEWEG FEHLT — Bericht folgt roh:" >&2 echo "$1" >&2 fi } # ------------------------------------------------------------- 1. Fakten --- IST="$(bash "$IST_SKRIPT" 2>&1)" if [ -z "$IST" ]; then melde "Radar konnte den IST-Zustand nicht erheben — $IST_SKRIPT lieferte nichts. Das ist selbst ein Befund: bitte nachsehen." exit 1 fi # Der Blick nach DRAUSSEN (04.09.2026): Releases, PRs, neue Entwurfsmodelle — # als Skript-Fakten, gegen den letzten Lauf verglichen. Ohne das lag DFlash2 fuer # den Coder zwei Wochen ungenutzt auf der Platte. Faellt es aus, bleibt der # Innen-Bericht vollstaendig; das Ausbleiben steht dann selbst als ACHTUNG drin. UPSTREAM_SKRIPT="${UPSTREAM_SKRIPT:-$HIER/stack-upstream.py}" if [ -f "$UPSTREAM_SKRIPT" ]; then UPSTREAM="$(timeout 240 python3 "$UPSTREAM_SKRIPT" 2>&1)" \ || UPSTREAM="UPSTREAM (Watchlist) ACHTUNG Upstream-Pruefung abgebrochen (exit $?) — $UPSTREAM_SKRIPT nachsehen" # Nur Aenderungen ans Modell. Beim ersten Lauf (04.09.) bekam es auch die # "unveraendert"-Zeilen und erfand zu jeder einen "naechsten Schritt" — der # Prompt-Hinweis half nicht. Fakten filtert das Skript, nicht die Prosa. ANZ_QUELLEN="$(printf '%s\n' "$UPSTREAM" | grep -cE '^\s+(OK|ACHTUNG)' || true)" UPSTREAM_NEU="$(printf '%s\n' "$UPSTREAM" | grep -A1 -E '^\s+ACHTUNG' | grep -vE '^--$' || true)" [ -z "$UPSTREAM_NEU" ] && UPSTREAM_NEU=" OK nichts Neues draussen ($ANZ_QUELLEN Quellen geprueft, alle unveraendert)" IST="$IST UPSTREAM (was hat sich draussen getan?) $UPSTREAM_NEU" fi ROT="$(printf '%s\n' "$IST" | grep -E '^\s+(ROT|ACHTUNG)' || true)" ANZ_ROT="$(printf '%s\n' "$ROT" | grep -c 'ROT' || true)" # ------------------------------------------------------- 2. Bewertung ------ # Das Modell bekommt NUR die Fakten und darf nichts dazuerfinden. Es soll # einordnen, nicht ermitteln. PROMPT="Du bist der Stack-Radar einer selbstgehosteten KI-Box. Unten steht der gemessene IST-Zustand. Schreibe einen kurzen Bericht auf Deutsch, per Du. Regeln: - Erfinde NICHTS. Nur was unten steht. - Beginne mit einer Zeile Gesamturteil. - Dann: was ist ROT oder ACHTUNG, und was waere jeweils der naechste Schritt. - Verbesserungsvorschlaege NUR, wenn sich aus den Zahlen unten wirklich etwas Konkretes ergibt. Gibt es nichts, lass den Abschnitt ersatzlos weg. Schreibe lieber gar keinen Vorschlag als einen ausgedachten - ein Bericht mit Fuellmaterial wird nicht gelesen. - Was du an den Messwerten selbst merkwuerdig findest, gehoert unter ACHTUNG, nicht unter Verbesserungen. - Zeilen mit NEU im Abschnitt UPSTREAM sind Neuigkeiten von draussen. Nenne jede kurz und gib den Hinweis dahinter (der Pfeil-Satz) als naechsten Schritt wieder. 'erstmals erfasst' und 'unveraendert' sind KEINE Neuigkeiten. - Wenn alles gruen ist, sag das in einem Satz und hoere auf. - Keine Ueberschriften-Deko, keine Emojis, hoechstens 200 Woerter. IST-ZUSTAND: $IST" ANTWORT="" if NUTZLAST="$(python3 -c ' import json, sys print(json.dumps({ "model": sys.argv[1], "messages": [{"role": "user", "content": sys.argv[2]}], "max_tokens": 900, "temperature": 0.3, }))' "$MODELL" "$PROMPT" 2>/dev/null)"; then ANTWORT="$(curl -s -m 300 -X POST "$GATEWAY/chat/completions" \ -H 'Content-Type: application/json' -d "$NUTZLAST" 2>/dev/null \ | python3 -c ' import json, sys try: print(json.load(sys.stdin)["choices"][0]["message"]["content"].strip()) except Exception: pass' 2>/dev/null)" fi # ------------------------------------------------------------- 3. Melden --- if [ -n "$ANTWORT" ]; then melde "$ANTWORT" else # Modell stumm oder kaputt -> die Fakten gehen TROTZDEM raus. Genau dafuer # steht das Modell nicht im kritischen Pfad. if [ -n "$ROT" ]; then melde "Radar: Bewertung durch das Modell hat nicht geklappt — hier die Rohbefunde: $ROT" else melde "Radar: alles gruen (Bewertung durch das Modell hat nicht geklappt, die Messung schon)." fi fi # Hermes bekommt eine LEERE Ausgabe -> keine zweite Nachricht. Gemeldet wurde # bereits ueber notify.sh, und das erreicht Telegram UND Lucys Stimme. exit 0