Trigger-Probe
Was das ist
Ein Agent, der sich selbst durch Ereignisse weckt, ist nur so sicher wie seine Trigger-Regeln. Der Agent selbst kann vorsichtig sein und trotzdem das Tagesbudget verbrennen, denn das Problem sitzt meist eine Ebene höher:
- Eine Regel reagiert auf ein Ereignis, das ihre eigene Arbeit erzeugt, und feuert deshalb immer wieder;
- Zwei Regeln speisen sich gegenseitig über ein zweites System (der Agent kommentiert, der Kommentar wird synchronisiert, die Synchronisation löst einen Webhook aus);
- Eine laute Quelle sendet zehn Bearbeitungen in zwei Minuten und jede startet einen neuen Durchlauf;
- Eine Cron-Zeile, die als „ab und zu prüfen“ gedacht war, feuert 288 Mal am Tag;
- Zwei Regeln behandeln dasselbe Ereignis und erledigen die Arbeit doppelt;
- Eine Regel, die Geld erstattet, löscht oder Nachrichten an Personen sendet, läuft, ohne jemanden zu fragen.
Nichts davon zeigt sich, wenn du den Agenten an einem Beispiel testest. Es zeigt sich, wenn echter Datenverkehr eintrifft. Trigger-Probe lässt echten Datenverkehr frühzeitig auf dem Papier eintreffen: Du schreibst die Regeln in einem einheitlichen Format auf, spielst einen Tag oder eine Woche Ereignisse durch und liest die Zeitleiste, bevor irgendetwas scharfgeschaltet ist.
Wann verwenden
- Bevor du eine Regel aktivierst, die es einem Agenten erlaubt, Arbeit zu starten, ohne dass eine Person eine Eingabe tippt.
- Beim Hinzufügen einer Regel zu einer bestehenden Menge (eine neue Regel kann einen Kreislauf schließen, der vorher offen war).
- Wenn ein Hintergrund-Agent viel häufiger oder viel seltener als erwartet lief.
- Bei der Wahl von Entprellfenstern, Parallelitätslimits und Tagesobergrenzen, wenn du Zahlen statt Schätzungen möchtest.
- Wenn ein Prüfer fragt: „Was passiert, wenn dieses Repository 50 Issues in einer Stunde erhält?“
Nicht für: die Überprüfung, was der Agent innerhalb eines Durchlaufs tut (das ist eine Frage zu Prompt oder Skill) oder den Lasttest der Ereignisquelle selbst.
Das Regelblatt
Alles wird in rules.json übersetzt. Halte die Übersetzung ehrlich: eine Blatt-Regel pro echter Regel, und notiere die Schutzmaßnahmen, die wirklich existieren, nicht die, die du hinzufügen möchtest.
{
"tz": "Europe/Berlin",
"run_s": 180,
"external_sources": ["github"],
"budget": {"runs_per_day": 40, "cost_per_run": 0.15, "cost_per_day": 6},
"rules": [
{
"name": "triage-neues-issue",
"on": {"source": "github", "types": ["issues.opened", "issues.edited"]},
"where": {"repo": "me/app"},
"key": "subject",
"debounce_s": 300,
"parallel": 1,
"max_per_day": 20,
"unless_actor": ["my-bot"],
"unless_label": "triaged",
"emits": [{"source": "github", "types": ["issues.labeled"], "actor": "my-bot", "labels": ["triaged"]}],
"sensitive": [],
"asks": false
},
{"name": "morgen-zusammenfassung", "on": {"cron": "0 9 * * 1-5"}, "max_per_day": 1}
]
}
| Feld | Bedeutung |
|---|---|
on.source + on.types | Welche Ereignisse starten die Regel. Lasse types weg, um jeden Typ von dieser Quelle zu treffen. |
on.cron | Eine fünfstellige Cron-Zeile in der Zeitzone des Blattes. Eine Regel hat entweder source oder cron. |
where | Ereignisfelder, die einem Wert entsprechen (oder in einer Liste sein) müssen. |
key | Ereignisfeld, das das Subjekt benennt (Issue-Nummer, Ticket-ID, URL). Wird verwendet, um Wiederholungen zusammenzuführen. |
debounce_s | Warte nach dem letzten Ereignis für ein Subjekt diese Zeit, dann führe einmal aus. |
parallel | Wie viele Läufe dieser Regel gleichzeitig aktiv sein dürfen. Zusätzliche Läufe warten. |
max_per_day | Harte Obergrenze pro lokalem Tag. Zusätzliche Läufe werden verworfen. |
run_s | Typische Lauflänge, für die Warteschlange verwendet. Fallback auf den Blattwert, dann 120. |
unless_actor, unless_label | Schutzmaßnahmen: Überspringe Ereignisse, die von diesen Akteuren erstellt wurden oder dieses Label tragen. |
emits | Ereignisse, die der Lauf selbst erzeugt (Kommentare, Labels, Statusänderungen, Folgeereignisse). Gib den Akteur und die Labels an, die sie tragen werden. |
sensitive + asks | Aktionen, die eine Person benötigen (löschen, ausgeben, neu starten, senden). asks: true bedeutet, der Lauf stoppt und fragt zuerst. |
external_sources | Quellen, die von der Außenwelt gespeist werden. Alles andere muss von einer Regel emittiert werden. |
budget | Optionale Läufe pro Tag, Kosten pro Lauf und Kosten pro Tag, die für das Urteil verwendet werden. |
Schritt für Schritt
- Sammle die echten Regeln. Lies die tatsächliche Konfiguration, nicht die Beschreibung davon. Agent-Hub-Regeldateien, gespeicherte Routinen, CI-Workflow-
on:-Blöcke, Crontab-Zeilen, No-Code-Automatisierungsbildschirme. Notiere alles, was zurück in eine Quelle schreibt, die eine andere Regel beobachtet. - Übersetze in
rules.json. Starte mitpython3 scripts/rehearse.py new rehearsal/. Frage für jede Regel drei Fragen und fülle die Felder aus: Was weckt sie, was schreibt ein Lauf zurück, und was stoppt sie, sich durch eigene Schreibvorgänge selbst zu wecken? Wenn die Antwort auf die dritte Frage „nichts“ ist, lasse die Schutzmaßnahme weg. Die Probe soll das ja einfangen. - Linten.
python3 scripts/rehearse.py lint rehearsal/rules.json. Behebe jedes HOLD vor dem Abspielen; REVIEW-Elemente können bis zu den Zahlen warten. - Ereignisse sammeln. Am besten: Exportiere einen echten, geschäftigen Tag aus der Quelle (Webhook-Zustellungslog, Issue-Zeitleiste, Helpdesk-Audit-Log) in
events.jsonl, ein JSON-Objekt pro Zeile mit mindestenst,source,type, plus die Felder, die deinkeyundwhereverwenden. Wenn keine Historie existiert, schreibe absichtlich einen kurzen synthetischen Ausbruch: zehn Bearbeitungen in drei Minuten, zwei Subjekte gleichzeitig, ein Ereignis, das von deinem eigenen Bot erstellt wurde. - Über ganze Tage abspielen.
python3 scripts/rehearse.py replay rehearsal/rules.json rehearsal/events.jsonl --from 2026-11-02T00:00:00Z --to 2026-11-03T00:00:00Z --md rehearsal/report.md. Übergib--from/--to, damit die Tageszahlen nicht durch ein kurzes Fenster aufgebläht werden. - Lies die Zeitleiste, nicht nur das Urteil. Suche nach Läufen zum gleichen Subjekt Minuten auseinander (fehlende oder zu kurze Entprellung), Ketten mit wachsender Tiefe (Schleifen), langen Wartezeiten (Parallelität zu niedrig), Verwerfungen (Obergrenze zu niedrig für echte Arbeit, oder ein Zeichen, dass die Regel auf Rauschen feuert).
- Anpassen und wiederholen. Ändere einen Knopf nach dem anderen und spiele die gleichen Ereignisse erneut ab, damit der Unterschied klar von dieser Änderung herrührt.
- Entscheiden. AKTIVIEREN nur, wenn die letzte Wiedergabe bei einem realistischen Tag AKTIVIEREN ergibt. Bewahre das finale
rules.json, die Ereignisdatei undreport.mdgemeinsam als Aufzeichnung darüber auf, warum die Regeln aktiviert wurden.
Urteile
- AKTIVIEREN: Keine HOLD- oder REVIEW-Funde. Sicher, wie geschrieben aktiviert zu werden.
- TRIM: Etwas verschwendet Läufe oder kann Arbeit überspringen (kein key, keine Obergrenze, Überschneidung, Obergrenze hat echte Ereignisse verworfen, knapp am Budget). Nur nach dem Trimmen aktivieren, oder mit einem schriftlichen Grund.
- HOLD: Nicht aktivieren. Selbstschleife, ungeschützter Kreislauf, eine Kette, die tiefer als Stufe 5 gelaufen ist, eine sensible Aktion ohne Nachfrage, über Budget oder eine defekte Cron-Zeile.
Der Exit-Code folgt dem Urteil (0, 1, 2; 3 bedeutet fehlerhafte Eingabe), sodass eine Probe eine Konfigurationsänderung in CI absichern kann.
Beispiel
Ein Support-Team möchte einen Agenten, der Helpdesk-Tickets beantwortet. Ihr Blatt (in examples/loopy/) hat eine Antwort-Regel, die eine Antwort postet, die das Ticket aktualisiert, was dieselbe Regel triggert. Eine Wiedergabe eines normalen Tages ergibt:
HOLD self-loop antworte-ticket: Eigene Ausgabe der Regel stimmt mit eigenem Trigger überein, ohne unless_actor/unless_label-Schutz
HOLD runaway antworte-ticket: Kette wurde immer wieder ausgelöst und überschritt Stufe 5 (3 Kürzungen)
HOLD sensitive-unasked rückerstattung-prüfen: Deklariert sensible Aktionen ['refund'], aber 'asks' ist nicht wahr
HOLD over-runs-budget -: 100.0/Tag bei einem Budget von 30/Tag
REVIEW cron-tight posteingang-abfrage: bis zu 720 Auslösungen/Tag
VERDICT HOLD
Drei Ticket-Aktualisierungen wurden zu 18 Antwort-Läufen. Die Behebung ist klein: füge "unless_actor": ["support-bot"] zur Antwort-Regel hinzu, setze "asks": true für die Rückerstattungs-Regel und ersetze die Zwei-Minuten-Abfrage durch ein Skript, das inbox.changed nur dann emittiert, wenn sich der Posteingang wirklich geändert hat (siehe examples/loopy/rules.fixed.json). Die nächste Wiedergabe desselben Tages reduziert die Läufe von 100 auf 4 und das Urteil wird zu TRIM. Der eine verbleibende Fund ist, dass Antworten und Rückerstattungsprüfung beide ein Rückerstattungs-Ticket sehen, was hier beabsichtigt ist, also wird es mit einem einzeiligen schriftlichen Grund neben dem Bericht akzeptiert.
Tipps
- Die beste einzelne Schleifenschutzmaßnahme ist eine Bedingung, die nach dem Lauf falsch wird: ein Label, das der Lauf hinzufügt und das die Regel ausschließt, oder ein Status, den die Regel nicht mehr matcht.
- Systemübergreifende Schleifen sind diejenigen, die leicht übersehen werden. Wenn der Agent in System A schreibt und System A synchronisiert sich nach B, liste das synchronisierte Ereignis unter
emitsmit dem Akteur, den es zeigen wird. - Entprellung fügt absichtlich Verzögerung hinzu. Wähle das Fenster basierend darauf, wie lange eine Person braucht, um einen Bearbeitungsschub abzuschließen, und halte es kurz, wo jemand auf eine Antwort wartet.
- Entprellung ist keine Deduplizierung von Aktionen. Für alles Unumkehrbare sollte der Lauf selbst vor dem Handeln immer noch prüfen: „Bereits erledigt?“.
- Eine Tagesobergrenze ist eine Sicherung, kein Plan. Wenn die Wiedergabe eines normalen Tages sie erreicht, ist die Regel zu weit gefasst.
- Pollen alle paar Minuten ist normalerweise günstiger als ein winziges Skript, das den Zustand vergleicht und bei Änderung ein Ereignis emittiert; der Agent wird nur geweckt, wenn es etwas Neues gibt.
- Führe die Probe erneut durch, wann immer eine Regel hinzugefügt wird. Die meisten Schleifen werden durch die zweite Regel erzeugt, nicht durch die erste.