← Blog

AI agents

Recursieve coding-agents in klantrepos: een veldgids

Het is 03:14 en je coding-agent heeft besloten dat de auth-laag van je klant te defensief is. CI is groen. Je gaat morgen ook niet lekker slapen.

Jacob Molkenboer· Oprichter · A Brand New Company· 3 jun 2024· 6 min
Koperen telefoonschakelpaneel met stoffen kabels, één groene stekker gloeit, rode lakzegel op kaart, ivoor papier.

Het is 03:14. Een coding-agent in zijn vierde zelfverbetercyclus heeft besloten dat de auth-middleware van de klant 'te defensief' is, en de JWT-validator herschreven op een manier die compileert, slaagt voor de vier tests die hij zelf schreef, en stilletjes een regressie meelevert waardoor verlopen tokens er doorheen glippen. CI is groen. Om 09:00 word je wakker van een Slack-bericht van de klant met de vraag waarom zijn dashboard sessiedata van gisteren laat zien.

Dit is geen gedachte-experiment. Varianten ervan zijn gebeurd in onze studio, bij studio's die we kennen, en vorige week op de voorpagina van Hacker News, waar 'When AI Builds Itself' 432 punten haalde. Diezelfde week stond op de voorpagina ook een open-source framework voor AI-gedreven vulnerability discovery en een code-review CLI die daarop voortbouwt. Recursief zelfverbeterende agents zijn nu een echte tool. Ze zijn ook, losgelaten op een live klantrepo zonder scaffolding, een manier om een klant in één nacht kwijt te raken.

Dit is de veldgids die we twee jaar geleden hadden willen hebben.

Wat recursieve zelfverbetering in een klantrepo écht betekent

Laat de sciencefiction-definitie even los. In een werkende studio is een recursief zelfverbeterende coding-agent er een die zijn eigen vorige output leest, samen met de testresultaten daarvan, zijn eigen prompts of zijn eigen subagent-topologie aanpast, en de volgende iteratie met die wijzigingen draait. De zelfverbetering is begrensd door de scaffolding die je eromheen bouwt. Die scaffolding is het enige dat tussen een nuttige agent staat en een proces dat heeft besloten dat je retry-backoff-helper elegant genoeg is om de hele auth-laag uit te faseren.

Het blast-radius-probleem

De nuttigste manier om deze agents te draaien is denken in blast radius. Elke actie die de agent kan doen heeft een worst-case kostprijs. Een typo in een comment is nul. Een force-push naar main is een dag forensisch werk. Een agent die besluit 'dode code' in de auth-middleware op te ruimen, is het vertrouwen van je klant in jou.

De meeste teams zien agent-guardrails als een rechtenprobleem ('kan hij rm -rf draaien?'). Dat is de makkelijke helft. De moeilijke helft is blast radius per pad. Een agent met volledige shell-toegang die nooit aan app/auth/ komt, is veiliger dan een agent met een read-only shell die overal kan editen.

Let op

Een agent met groene tests is niet hetzelfde als een agent met correct gedrag. Als de agent de tests zelf schreef, horen die tests bij het artefact, niet bij de validator.

Sandbox de repository, niet de host

Het instinct uit klassiek sysadmin-werk is om het gevaarlijke ding in een container te draaien. Dat dempt de blast radius op de hostmachine. Voor de blast radius op het product doet het niets. Een agent in een perfect geïsoleerde Docker-container kan nog altijd een kapotte auth-laag naar main pushen.

De sandbox die telt is de repository-sandbox. De agent werkt in een git worktree, niet in de main checkout. Hij commit naar een branch die hij niet kan hernoemen. Zijn commits worden ondertekend met een sleutel die geen push-rechten op beschermde branches heeft. Een mens merget. Altijd. Ook bij iteratie 47.

Dat klinkt voor de hand liggend. Het is ook de stap die het vaakst wordt overgeslagen in elke 'we lieten de agent het weekend los'-demo op Twitter.

Scope-locks die het houden

Allowlists op file-niveau zijn het enige scope-mechanisme dat we hebben gevonden dat contact met een recursieve agent overleeft. Instructies in de prompt zoals 'kom niet aan de auth-laag' houden niet. We hebben agents zich langs elk 'niet doen' zien redeneren dat we ooit hebben geschreven, ook die begonnen met het woord NEVER in hoofdletters.

De lock moet onder de agent zitten, in de tool-laag:

scope:
  allow:
    - app/billing/**
    - app/dashboard/widgets/**
    - tests/billing/**
  deny:
    - app/auth/**
    - app/middleware/**
    - migrations/**
    - .github/workflows/**
    - "**/*.env*"
edit_tool:
  enforces: scope.allow, scope.deny
  on_violation: refuse_and_log

De edit-tool weigert writes buiten de allowlist. De agent krijgt de weigering binnen als een tool-error, waar hij over kan redeneren, maar omheen werken kan hij niet. Elke weigering wordt gelogd. Die logs zijn verrassend vermakelijk.

De verifier is belangrijker dan de schrijver

De meeste agent-setups richten zich op de loop die code schrijft. De loop die telt in een klantrepo is de loop die verifieert. Een bruikbaar patroon, zichtbaar in de open code-review CLI's die nu populair zijn en in recent werk van Anthropic rond geautomatiseerde vulnerability discovery, is om de writer-agent en de verifier-agent te scheiden, en de verifier een ander model, een andere prompt en een adversariële framing te geven.

De agent die de code schrijft moet niet de agent zijn die hem beoordeelt. Wij draaien twee loops. De writer-loop genereert de diff, draait de tests die hij schreef, en rapporteert. De verifier-loop gebruikt een ander model, met een framing die standaard afwijst, en behandelt de tests van de schrijver als onbetrouwbaar. De verifier leest de diff koud, zonder toegang tot het reasoning-spoor van de schrijver. Wijzen twee van de drie verifier-runs af, dan komt de wijziging nooit in de menselijke wachtrij. Dit vangt de klasse van fouten waarbij de schrijver zichzelf overtuigt dat een elegante simplificatie veilig is.

Kernpunt

Een recursieve coding-agent is veilig in de mate waarin zijn tool-laag een scope afdwingt waar hij niet omheen kan redeneren. Prompts houden niet. Allowlists wel.

Wat we bij ABN concreet doen

Toen we de email-agent bouwden voor een Rotterdamse logistieke klant, draaiden we precies deze opzet. De agent had schrijftoegang tot app/inbox/, app/responder/ en de testmappen die daarbij hoorden. Hij had leestoegang tot alles daarbuiten, inclusief de auth-laag, zodat hij de vorm van een geauthenticeerde request kon begrijpen. Hij kon niet schrijven naar het database-schema van de klant, de SMTP-credentials of de middleware die hun forwarder API-tokens afhandelde. In zes weken leverde hij 41 commits op een feature-branch. We mergeden er 38. Drie werden door de verifier-loop tegengehouden en bereikten ons nooit. Nul commits raakten iets dat we hadden gelockt.

De saaie infrastructuur (worktrees, gescopete tool-rechten, verificatie met twee modellen, een menselijke merge-gate) is wat het interessante deel veilig maakt. De productieversie van deze opzet zit onder elke AI-agent-opdracht die we draaien.

Het kleinste wat je vandaag kunt doen

Open de file-write tool van je agent. Voeg een deny list toe. Zet je auth-map, je migrations-map en elk .env-bestand erin. Laat de tool weigeren, de weigering loggen en doorgaan. Dat alleen, zonder verder iets aan te passen, had het 03:14-scenario waarmee dit stuk opent voorkomen.

Kern

Een recursieve coding-agent is alleen zo veilig als de tool-laag eronder. Prompts houden niet. Allowlists op file-paden wel.

FAQ

Kan een recursieve coding-agent veilig architecturale wijzigingen doen?

Met een deny list op kritieke paden en een aparte verifier-agent: ja, voor feature-werk. Nee voor auth, payments of schema-migrations. Die blijven door mensen geleid totdat je maanden schone verifier-data op dezelfde repo hebt.

Hoe voorkom je dat een agent zijn eigen scope-regels aanpast?

Houd de scope-regels buiten de repo die de agent edit. Wij zetten ze in een aparte config-repo waar de agent uit leest, maar niet in mag schrijven. Dezelfde sleutelscheiding als bij productiegeheimen.

Wat kost het in tokens om een writer én een verifier te draaien?

Ruwweg 25 tot 35 procent meer dan alleen de writer, afhankelijk van de diff-omvang. Goedkoper dan één teruggedraaid productie-incident in een klantrepo, met meerdere ordes van grootte.

Waarom niet gewoon elke commit met de hand reviewen?

Dat moet je ook doen. De verifier-loop vangt de voor de hand liggende fouten op voordat ze bij jou komen, zodat de menselijke review zich kan richten op intentie en product-fit, niet op syntax en triviale regressies.

ai agentsautomationtoolingarchitectureoperationssecurity

Iets bouwen?

Start een project