Warum Hooks existieren
Anweisungen in einem System-Prompt sind Vorschläge. Ein ausreichend eigensinniges oder verwirrtes LLM kann sie ignorieren. Für die meisten Agent-Verhaltensweisen ist das genau das, was Sie möchten — Anweisungen geben dem Modell Raum zur Anpassung. Aber einige Anforderungen sind keine Vorschläge. „Jeder sensible Tool-Aufruf muss protokolliert werden.” „Schreibvorgänge sind blockiert, wenn die Organisation im Nur-Lese-Modus ist.” „Zahlungen über ¥50k erfordern eine menschliche Bestätigung vor der Ausführung.” Das sind Invarianten — Fakten über das System, die unabhängig davon gelten müssen, was das Modell in einem bestimmten Durchlauf entscheidet. Ein Hook ist Code, der außerhalb der LLM-Schleife an einem klar definierten Punkt im Ausführungs-Lebenszyklus des Agenten läuft. Das LLM kann den Hook nicht sehen. Das LLM kann mit dem Hook nicht argumentieren. Das LLM kann den Hook nicht überreden, einen Schritt zu überspringen. Wenn einPreToolUse Hook allow=False zurückgibt, findet der Tool-Aufruf nicht statt — egal wie hartnäckig die Reasoning-Spur war.
Das ist die kritische architektonische Unterscheidung:
Hooks sind, wie FIM One „der Agent soll…” in „der Agent kann nicht umgehen…” umwandelt.
Wo Hooks angebunden werden
Heute sind drei Hook-Punkte definiert. Jeder markiert eine Grenze, die der Agent während einer Schleifenwiederholung überschreitet:
Mehrere Hooks am gleichen Punkt werden in Prioritätsreihenfolge ausgeführt. Die umgeschriebenen Args eines früheren
PreToolUse-Hooks werden an spätere Hooks weitergeleitet, sodass sich Middleware zusammensetzt.
Wann ein Hook vs. eine Anweisung
Die Entscheidung, ob eine Anforderung mit einer Prompt-Anweisung oder einem Hook gelöst werden soll, ist dieselbe Berechnung wie “Runtime-Assertion vs. Code-Kommentar”:
Faustregel: Wenn das falsche Verhalten ein Incident ist, verwenden Sie einen Hook. Wenn das falsche Verhalten eine kleine Unannehmlichkeit ist, ist eine Anweisung ausreichend.
Der Hook-Vertrag
Ein Hook ist eine Unterklasse vonPreToolUseHook, PostToolUseHook oder SessionStartHook mit einer erforderlichen Methode:
HookContext enthält tool_name, tool_args, agent_id, user_id und ein flexibles metadata-Wörterbuch, das die Engine mit anfragespezifischen Fakten füllt (Organisations-ID, Konversations-ID, das requires_confirmation-Flag der Connector-Aktion, …).
Das zurückgegebene HookResult steuert das Ergebnis:
allow: bool = True— ob der Tool-Aufruf fortgesetzt wird (wird fürPostToolUse/SessionStartignoriert)error: str | None— benutzerfreundliche Begründung, die dem LLM als Beobachtung angezeigt wird, wenn blockiertmodified_args: dict | None— falls gesetzt, ersetzt die Tool-Argumente vor der Ausführungmodified_result: Any | None— falls gesetzt (PostToolUse), ersetzt die Beobachtung, bevor sie an das LLM zurückgegeben wirdside_effects: list[str]— Audit-Trail der Hook-Aktionen, zusammengeführt in die Agent-Trace
Fallstudie: FeishuGateHook
Der erste Hook, der auf diesem System implementiert wurde, ist FeishuGateHook — ein PreToolUse Hook, der jedes Tool mit dem Flag requires_confirmation=True in eine Genehmigungskarte mit menschlicher Kontrolle umwandelt, die in der Feishu-Gruppe der Organisation gepostet wird.
Dieser Hook durchläuft den vollständigen Lebenszyklus:
Das bringt dieses Design mit sich:
- Der Tool-Aufruf wird wirklich unterbrochen. Der SSE-Stream des Agenten pausiert zwischen „Ich werde
oa__purchase_payaufrufen” und der Observation. Der Benutzer sieht den wartenden Agenten, was dem entspricht, was unter der Haube passiert. - Die Genehmigung übersteht einen Prozessneustart. Die ausstehende Zeile befindet sich in der Datenbank, nicht im Speicher. Wenn das Backend neu startet, während eine Karte ausstehend ist, nimmt die nächste Abfrage dort auf, wo sie aufgehört hat.
- Die Entscheidung wird geprüft.
ConfirmationRequestspeichertpayload,responded_at,responded_by_open_idund den endgültigen Status — ein überprüfbarer Datensatz darüber, wer was und wann genehmigt hat. - Kein LLM in der Entscheidungsschleife. Das Modell erzeugt den Tool-Aufruf. Menschen erzeugen das Urteil. Der Hook ist die deterministische Brücke.
FeishuGateHook hängt von einem konfigurierten Feishu Channel ab — der Hook sendet die Karte über die send_interactive_card()-Methode des Channels und wartet auf Callback-Events, die der Channel geparst hat. Die Trennung ist beabsichtigt: Der Hook besitzt die „Genehmigungszustandsmaschine”, der Channel besitzt die „IM-Plattformmechanik”. Der gleiche Hook könnte morgen auf Slack oder WeCom abzielen, ohne seine Logik zu ändern — nur die Channel-Implementierung.
Mögliche Hooks (nicht geplant)
Vier Hook-Muster passen in denselben Lebenszyklus. Keines davon steht auf der aktuellen Roadmap; sie bleiben zurückgestellt, bis ein Deployment eines davon benötigt:
Eine benutzerdefinierte Hook-Ebene bleibt unter denselben Bedingungen zurückgestellt: eine YAML-Konfiguration pro Agent (
hooks: [...]), in der Shell-Befehle oder Python-Aufrufobjekte festgelegt werden, die bei passenden Tool-Ereignissen ausgeführt werden. Dieser Ansatz folgt dem Muster, auf das sich moderne Agent-Frameworks (Claude Code, OpenDevin) verständigt haben — eine Durchsetzung per Hook hält die Logik für Dinge, die „immer geschehen müssen“, aus den Prompts heraus.
Hooks vs. Channels
Die beiden Abstraktionen lösen orthogonale Probleme:
Hooks nutzen Channels — ein Hook, der mit der Außenwelt kommunizieren muss (eine Karte senden, eine Benachrichtigung posten, an eine Gruppe eskalieren), ruft den Channel der Organisation auf. Ein Channel ohne einen Hook, der ihn nutzt, ist immer noch nützlich (z. B. können Agenten proaktiv Benachrichtigungen über ein Tool senden), aber das Approval-Gate-Muster erfordert speziell, dass beide Teile vorhanden sind.
Anders ausgedrückt: Channels sind die „Wie kommuniziere ich mit Menschen”-Infrastruktur, Hooks sind die „Wann muss ich mit Menschen kommunizieren”-Richtlinie. Produktive Human-in-the-Loop-Workflows benötigen beides.
Aktueller Stand (v0.8.4)
Zusammenfassung dessen, was ausgeliefert wurde und was noch aussteht:-
✅ Die Grundbausteine
HookRegistry,HookContextundHookResultsind in ReAct und DAG integriert -
✅ Abstrakte Basisklassen
PreToolUseHook/PostToolUseHook/SessionStartHook -
✅
FeishuGateHook– vollständig implementiert, einschließlich der TabelleConfirmationRequest, der Polling-Schleife, Timeout und Ablauf sowie callback-gesteuerter Zustandsänderungen -
✅ Feishu-Kanal-Callback-Endpunkt, der
card.action.triggerdekodiert und den ausstehenden Datensatz aktualisiert -
✅ Hook-Deklarationen auf Agent-Ebene:
agent.model_config_json.hooks.class_hookswird für jede ReAct-/DAG-Sitzung zu einer instanziiertenHookRegistryaufgelöst -
✅ Hooks werden bei jeder Ausführungsart ausgelöst: im Haupt-Chatpfad (Portal, API, DAG), bei delegierten Sub-Agenten (
CallAgentTool) und in Workflow-AGENT-Knoten. Das Eval Center umgeht Hooks absichtlich (automatisierte Evaluationen dürfen nicht durch eine menschliche Genehmigung blockiert werden). Ein delegierter Agent übernimmt nicht die Registry seines aufrufenden Agents, sondern erstellt eine eigene anhand seiner eigenenmodel_config_json. Da das Bestätigungsgate automatisch jedem Agent hinzugefügt wird, ist der Neuaufbau umfassender als eine Übernahme: Der delegierte Agent erhält das Gate sowie alle Hooks, die er selbst deklariert, und das Gate wertetrequire_confirmation_for_alldes delegierten Agents aus, nicht das seines Aufrufers. Beide Ausführungsarten schlagen geschlossen fehl: Ein Agent, dessen Hooks nicht erstellt werden können, wird nicht ausgeführt. -
❌
AuditLogHook,ReadOnlyGuard,ResultTruncateHook,ConnectorRateLimitHook(nicht geplant) - ❌ YAML-Deklarationen für benutzerdefinierte Hooks (nicht geplant)
FeishuGateHook) ist zugleich ein eigenständiges Produktivfeature. Deshalb wurde das Grundgerüst bereits vorab für den Roadshow-Termin am 24.04.2026 ausgeliefert, statt auf den vollständigen Hook-Katalog zu warten.