Prompts zijn suggesties. Hooks zijn harde stops.
Maandenlang behandelde ik Claude Code als een junior developer die simpelweg duidelijke spelregels nodig had. Mijn instructies voor het project waren expliciet: nooit force-pushen, nooit branches verwijderen, nooit destructieve commando's uitvoeren. De meeste avonden werkte dat. De agent schreef tests, refactorde functies en liet de git-historie met rust. Toen ging er een rebase mis.
Het contextvenster raakte gevuld met git-foutmeldingen. Conflictmarkers, berichten over een 'detached HEAD' en waarschuwingen over afwijkende branches stapelden zich op, token voor token. Begraven onder die ruis lag mijn beleefde instructie om force-pushen te vermijden. Voor het model was de meest recente en opvallende tekst in de thread de foutstroom. Statistische aandacht won het van de instructies. De agent voerde een commando uit dat twee uur aan niet-gecommitteerde lokale wijzigingen wist. Het was niet kwaadwillend; het was afgeleid. Dat onderscheid is belangrijk. Een LLM breekt geen regels uit wrok. Het breekt ze omdat een luider patroon in het contextvenster een eerdere instructie tijdelijk overschrijft.
Dat incident veranderde mijn kijk op agent-veiligheid. Een guardrail die negentignegen procent van de tijd werkt, is een risico. Als de foutmodus je tijd, geld of productiedata kost, kun je het niet binnen de prompt laten. Je hebt handhaving nodig buiten de redeneercyclus van het model om.
Claude Code hooks lossen precies dit op. Het zijn kleine scripts die tool-aanroepen onderscheppen op drie specifieke momenten: voordat een tool wordt uitgevoerd (PreToolUse), nadat een tool is voltooid (PostToolUse), en wanneer de agent besluit dat hij klaar is (Stop). Omdat ze als externe code draaien, zijn ze niet afhankelijk van het geheugen, de stemming of de contextdruk van het model. Het model kan elke instructie die je ooit hebt gegeven vergeten; de hook zal nog steeds "nee" zeggen.
Hier is de constructie die ik bouwde na die verloren avond.
De Guard Hook: onderschep voordat er schade ontstaat
Mijn PreToolUse hook inspecteert elk Bash-commando voordat de shell het aanraakt. Ik houd een strikte denylist bij van destructieve patronen. Als de commando-string overeenkomt met iets gevaarlijks, breekt de hook de uitvoering af en stuurt een foutmelding rechtstreeks terug naar de agent.
De patronen die ik blokkeer zijn eenvoudig en ondubbelzinnig:
git push --forceof elke force-with-lease variant die ik nog niet vertrouwgit reset --hardrm -rf
Dit is geen geavanceerd beveiligingsonderzoek. Het is een veiligheidsgordel. Maar het cruciale detail is wat er gebeurt na de blokkade.
Ik geef nooit een bot “Geblokkeerd” terug. Een platte weigering verwart de agent en kan hem in een loop terecht laten komen waarin hij variaties van hetzelfde destructieve commando probeert. In plaats daarvan bevat de foutmelding een ontsnappingsroute. Wanneer de hook een harde reset detecteert, vertelt hij de agent: “Dit commando is geblokkeerd om ongecommitteerd werk te beschermen. Commit eerst een checkpoint en beoordeel de situatie dan opnieuw.” Die extra zin verandert het gedrag van de agent volledig. Hij schakelt over van het proberen van schadebeperking naar het creëren van veiligheid. De hook is niet alleen een muur; het is verkeersregeling.
Ik koos ook voor een denylist in plaats van een allowlist voor shell-commando's. In het begin overwoog ik om alleen een expliciete set veilige git-subcommando's toe te staan. Dat mislukte snel. Agents zijn creatief letterlijk. Ze voeren legitieme maar onverwachte commando's uit zoals git stash push -m "wip" of git branch --show-current om de status te controleren. Een allowlist verbreekt de normale workflow op het moment dat het model een geldige maar niet-vermelde opdracht verzint. Een korte, gecureerde denylist van echt destructieve patronen geeft de agent de ruimte om te bewegen, terwijl de grenzen worden bewaakt.
De Formatter Hook: automatiseer het routinewerk
Vroeger verspilde ik prompt-tokens door de agent te vertellen dat hij "altijd de formatter moet draaien na het bewerken van een bestand". De helft van de tijd vergat hij het. De andere helft pauzeerde hij en vroeg hij of hij moest formatteren, waarbij hij een tool-aanroep verspilde aan een beslissing die maar één juist antwoord had.
Nu regel ik dat met een PostToolUse hook. Nadat de agent een bestand heeft bewerkt, controleert de hook de bestandsextensie. Als het Python is, draait hij Ruff. Als het JavaScript of TypeScript is, draait hij Prettier. Als het Go is, draait hij gofmt. De agent weet niet eens dat de formatter bestaat. Dat hoeft ook niet.
Het verplaatsen hiervan uit de prompt had twee effecten. Ten eerste is de code consistent schoon zonder de cognitieve belasting voor het model te verhogen. Ten tweede werden mijn projectinstructies korter. Elke "altijd" en "nooit" die je uit een prompt verwijdert, is een token dat het model kan besteden aan daadwerkelijke probleemoplossing. De hook beheert de invariant; de prompt beheert de intentie.
De Quality Gate: "Klaar" opnieuw definiëren
De Stop-hook wordt uitgevoerd wanneer de agent besluit dat hij de taak heeft voltooid en probeert de sessie te beëindigen. Dat laat ik niet toe. In plaats daarvan voert de hook de volledige testsuite uit. Als een test faalt, blokkeert de hook het stopcommando en stuurt de foutmelding terug naar de agent.
Dit verandert de definitie van voltooiing. “Klaar” is niet langer een gevoel dat het model heeft. Het is een meetbare poort. De agent kan pas klaar zijn wanneer de harness bevestigt dat de code werkt. In de praktijk creëert dit een strakke feedbackloop. De agent schrijft code, denkt dat hij klaar is, drukt op de stopknop en ziet onmiddellijk een pytest-traceback. Vervolgens corrigeert hij zichzelf, herstelt de importfout of de defecte assertion, en probeert opnieuw te stoppen. Ik heb gezien hoe agents drie of vier keer binnen deze loop itereren zonder menselijke tussenkomst. De harness handhaaft de kwaliteit; het model levert de patches.
Wat dit leert over Agent Engineering
Het bouwen van betrouwbare autonome systemen vereist een verschuiving in mindset. Je gaat van het schrijven van langere prompts naar het bouwen van strakkere harnesses.
Gebruik hooks voor handhaving en prompts voor beleid. Als een regel honderd procent van de tijd moet gelden, hoort deze in de code thuis, niet in natuurlijke taal. Prompts blinken uit in ambiguïteit, smaak en architectuur. Ze zijn verschrikkelijk in invarianten. Als een fout je een middag hersteltijd zou kosten of, erger nog, de uptime van de productie, schrijf dan een hook.
Kortere prompts leveren betere resultaten op. Wanneer je mechanische regels verplaatst naar scripts, heeft het model minder om te onthouden en minder om tegen te spreken. Het contextvenster van de agent is een schaarse hulpbron. Vul het niet met herinneringen aan de opmaak.
Accepteer tot slot dat je rol verandert. Naarmate agents meer autonomie krijgen, verschuift de taak van de mens van het genereren van content naar het ontwerpen van de guardrails. Je bouwt de harness die bepaalt waar het model bij kan, wanneer het klaar kan zijn en hoe het zich moet gedragen als er iets misgaat. Dat is engineering, geen prompting.
De bron die deze aanpak heeft geïnspireerd en aanvullende implementatiedetails zijn hier te vinden.
Als je bouwt met AI-agents en van gedachten wilt wisselen met andere professionals, dan vind je de GyaanSetu leercommunity hier.
