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:
| Mechanismus | Wo es läuft | Wer kontrolliert es | Garantie |
|---|---|---|---|
| System-Prompt-Anweisung | Innerhalb von LLM-Inferenz | Modell | „Folgt wahrscheinlich” |
| Tool-Beschreibung / Schema | Innerhalb von LLM-Inferenz | Modell | „Folgt wahrscheinlich” |
| Hook | Um LLM-Inferenz herum | Platform-Code | Läuft immer |
Wo Hooks angebunden werden
Heute sind drei Hook-Punkte definiert. Jeder markiert eine Grenze, die der Agent während einer Schleifenwiederholung überschreitet:| Hook-Punkt | Wird ausgelöst, wenn | Kann es blockieren? | Kann es Daten mutieren? |
|---|---|---|---|
SessionStart | Vor dem ersten LLM-Aufruf einer Sitzung | Nein | Ja — injiziert Kontext in die anfängliche Eingabeaufforderung |
PreToolUse | Nachdem das LLM ein Tool ausgewählt hat, bevor das Tool ausgeführt wird | Ja (über allow=False) | Ja — kann tool_args vor der Ausführung umschreiben |
PostToolUse | Nachdem das Tool zurückgekehrt ist, bevor die Beobachtung zum LLM geht | Nein | Ja — kann die Beobachtung umschreiben |
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”:| Symptom | Lösung |
|---|---|
| ”Der Agent sollte X bevorzugen, wenn Y” | Anweisung — sanfte Anleitung, Modell hat Spielraum |
| ”Der Agent muss jeden Aufruf an Connector Z protokollieren” | PostToolUse Hook — kann sich nicht auf das Modell verlassen, um sich zu erinnern |
| ”Zahlungen über ¥50k benötigen menschliche Genehmigung” | PreToolUse Hook — kann sich nicht auf das Modell verlassen zu fragen |
| ”Der Agent sollte sich auf Chinesisch vorstellen” | Anweisung — stilistisch, geringe Kosten bei Fehlschlag |
| ”Der Agent kann nicht in die Produktionsdatenbank im Nur-Lese-Modus schreiben” | PreToolUse Hook — Sicherheitsinvariante, Null-Toleranz |
| ”Der Agent sollte lange DB-Abfrageergebnisse zusammenfassen” | Könnte beides sein, aber ein Hook ist robuster — siehe PostToolUse truncate |
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.
Geplante Hooks (v0.9)
Vier Hook-Muster sind auf der v0.9-Roadmap geplant und verwenden alle denselben Lebenszyklus:| Hook | Punkt | Zweck |
|---|---|---|
AuditLogHook | PostToolUse | Automatisches Schreiben von ConnectorCallLog bei jedem Connector-Aufruf. Heute ist dies manuell; ein Hook stellt Abdeckung sicher. |
ReadOnlyGuard | PreToolUse | Blockiert Schreibvorgänge, wenn die Organisation im Read-Only-Modus ist. |
ResultTruncateHook | PostToolUse | Kürzt übergroße Tool-Beobachtungen (>8k Zeichen), bevor sie den LLM-Kontext erreichen. |
ConnectorRateLimitHook | PreToolUse | Pro-Connector pro-Benutzer Aufruffrequenz-Obergrenze, unabhängig von LLM-Ratenlimits. |
hooks: [...]), die Shell-Befehle oder Python-Callables deklariert, um sie bei passenden Tool-Events auszuführen. Dies folgt dem gleichen Muster, auf das moderne Agent-Frameworks (Claude Code, OpenDevin) konvergiert haben — Hook-basierte Durchsetzung hält die „muss immer passieren”-Logik aus Prompts heraus.
Hooks vs. Channels
Die beiden Abstraktionen lösen orthogonale Probleme:| Konzept | Was es modelliert | Lebensdauer | Beispiel |
|---|---|---|---|
| Hook | Ein Punkt in der Ausführung des Agenten, an dem Plattformcode ausgeführt wird | Pro Tool-Aufruf | FeishuGateHook, AuditLogHook |
| Channel | Ein austauschbarer Adapter zu einer externen Messaging-Plattform | Langlebig pro Organisation | FeishuChannel, geplanter SlackChannel |
Aktueller Stand (v0.8.4)
Übersicht über das bereits Ausgelieferte und die kommenden Funktionen:- ✅
HookRegistry,HookContext,HookResultPrimitive in ReAct und DAG integriert - ✅
PreToolUseHook/PostToolUseHook/SessionStartHookabstrakte Basisklassen - ✅
FeishuGateHook— vollständig implementiert, einschließlichConfirmationRequestTabelle, Polling-Schleife, Timeout/Ablauf und Callback-gesteuerte Zustandsübergänge - ✅ Feishu-Kanal-Callback-Endpunkt, der
card.action.triggerdekodiert und die ausstehende Zeile aktualisiert - ✅ Agent-Level Hook-Deklarationen:
agent.model_config_json.hooks.class_hookswird bei jeder ReAct/DAG-Sitzung zu einer instanziiertenHookRegistry - 🟡 Hook-Vererbung über Ausführungsoberflächen (v0.8.5): Der Haupt-Chat-Pfad (Portal, API, DAG) löst Hooks aus. Eval Center umgeht absichtlich Hooks (automatisierte Evaluierung darf nicht auf menschliche Genehmigung warten). Delegierte Sub-Agenten (
CallAgentTool) und WorkflowAGENTKnoten erben derzeit keine übergeordneten Hooks — die Vererbungsrichtlinie ist ein Entscheidungspunkt für v0.8.5. - ❌
AuditLogHook,ReadOnlyGuard,ResultTruncateHook,ConnectorRateLimitHook(v0.9) - ❌ Benutzerdefinierte YAML Hook-Deklarationen (v0.9)
FeishuGateHook) ist auch selbst ein Produktionsmerkmal, weshalb das Grundgerüst bereits für die Roadshow vom 24.04.2026 ausgeliefert wurde, anstatt auf den vollständigen Hook-Katalog zu warten.