|
| 1 | +--- |
| 2 | +draft: true |
| 3 | +authors: [dimitri-van-hees] |
| 4 | +tags: |
| 5 | + - ai |
| 6 | + - llm |
| 7 | + - cli |
| 8 | + - validator |
| 9 | + - oas |
| 10 | + - adr |
| 11 | + - publiccode |
| 12 | + - open-source |
| 13 | + - standaarden |
| 14 | +description: > |
| 15 | + Voor wie AI inzet om overheidssoftware te bouwen, houden we naast de mens ook |
| 16 | + de tools in de loop: generators die het zware werk doen en validators die de |
| 17 | + regels bewaken. Zo verschuift het naleven van standaarden van menselijke |
| 18 | + oplettendheid naar deterministische tooling die je kunt vertrouwen. |
| 19 | +image: ./img/tools-in-the-loop.png |
| 20 | +--- |
| 21 | + |
| 22 | +# Tools in the loop: "human in the loop" krijgt hulp |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | +"Human in the loop" is inmiddels het standaardantwoord op bijna elke zorg rond |
| 27 | +AI. Maar voor wie AI inzet om overheidssoftware te bouwen, is het verstandig om |
| 28 | +naast de mens ook _tools_ in de loop houden: generators die het zware werk doen |
| 29 | +en validators die de regels bewaken. Zo verschuift het naleven van standaarden |
| 30 | +van de oplettendheid van een mens en de welwillendheid van AI naar tooling die |
| 31 | +je kunt vertrouwen. Hoe dat samenwerkt, en aan welk arsenaal aan skills, |
| 32 | +generators, validators en andere tools we werken, lees je in deze post. |
| 33 | + |
| 34 | +<!-- truncate --> |
| 35 | + |
| 36 | +:::success[TL;DR] |
| 37 | + |
| 38 | +Als je AI gebruikt om software te bouwen, houd dan naast de mens ook onze |
| 39 | +_tools_ in de loop: |
| 40 | + |
| 41 | +- **Generators** doen het zware, repetitieve werk (boilerplate, |
| 42 | + projectstructuur) en zijn deterministisch. |
| 43 | +- **Validators** beantwoorden de waarheidsvraag "Voldoet dit aan de regels?". |
| 44 | + Niet het model, maar een tool met een vaste set regels. |
| 45 | +- De **command line** is de natuurlijke interface voor agents: de agent itereert |
| 46 | + op exit codes tot de checker `valid` teruggeeft. |
| 47 | +- AI is zo een hulpmiddel voor de input, niet het orakel dat de waarheid |
| 48 | + bepaalt. |
| 49 | + |
| 50 | +Dit valt of staat met **standaarden**: elke afspraak is een stukje waarheid dat |
| 51 | +je in betrouwbare tooling stopt zodat AI het niet zelf hoeft te verzinnen. |
| 52 | + |
| 53 | +::: |
| 54 | + |
| 55 | +## Validators en generators worden steeds belangrijker |
| 56 | + |
| 57 | +Je zou kunnen denken dat in een wereld waarin een AI-agent "gewoon de code |
| 58 | +schrijft" de behoefte aan generators en validators afneemt. Het |
| 59 | +tegenovergestelde is waar. Juist omdat een taalmodel plausibel-ogend werk |
| 60 | +produceert dat tóch fout kan zijn, wordt het werk dat je níet aan het model wilt |
| 61 | +overlaten belangrijker dan ooit. |
| 62 | + |
| 63 | +Dat werk bestaat uit twee soorten. Het zware, repetitieve werk, zoals het |
| 64 | +genereren van boilerplates en het opzetten van een projectstructuur, dat kun je |
| 65 | +een generator laten doen. Die is deterministisch en doet elke keer hetzelfde. En |
| 66 | +de waarheidsvraag, "Voldoet dit aan de regels die wij hanteren?", kun je door |
| 67 | +een validator laten beantwoorden. Niet AI, niet de mens, maar een tool met een |
| 68 | +vaste set regels. |
| 69 | + |
| 70 | +Bovendien hoeft AI zo minder zelf te "bedenken". Dat scheelt hallucinaties en |
| 71 | +het verbrandt geen onnodige tokens. Het model doet waar het goed in is, namelijk |
| 72 | +taal en intentie interpreteren, en de tools doen waar zij goed in zijn. |
| 73 | + |
| 74 | +## CLI als natuurlijke interface voor AI-agents |
| 75 | + |
| 76 | +Agents leven op de command line. Ze roepen tools aan, lezen de output, en |
| 77 | +bepalen op basis daarvan hun volgende stap. Dat maakt een CLI de meest |
| 78 | +natuurlijke interface die je een agent kunt aanbieden: deterministisch, |
| 79 | +scriptbaar, aan elkaar te knopen, en met een exit code en gestructureerde output |
| 80 | +waar een agent direct op kan itereren. |
| 81 | + |
| 82 | +Daarom hebben onze tools sinds kort allemaal een command line interface |
| 83 | +gekregen, van de generators tot de checkers. Een invalid output uit onze checker |
| 84 | +is daarmee een exit status: de agent leest het, ziet wat er mis is, past het aan |
| 85 | +en draait de checker opnieuw. Daar draait de hele validatieloop op, zonder dat |
| 86 | +er een mens tussen hoeft te zitten. |
| 87 | + |
| 88 | +## Hoe alles samenwerkt |
| 89 | + |
| 90 | +Vanuit developer.overheid.nl bieden we de volgende features (deels al, deels |
| 91 | +binnenkort) aan. De rolverdeling: |
| 92 | + |
| 93 | +- **Schema Register.** Een register van herbruikbare JSON schema's waar straks |
| 94 | + niet alleen mensen, maar ook agents uit kunnen putten. In plaats van schema's |
| 95 | + te verzinnen, verwijst de agent naar wat er al is. Met de komende upgrade naar |
| 96 | + [OpenAPI 3.1](/blog/2025/07/10/openapi-31-in-zicht) zijn deze schema's direct |
| 97 | + te gebruiken in OAS-documenten. Dit register is momenteel in ontwikkeling; we |
| 98 | + verwachten dit snel te kunnen lanceren. |
| 99 | +- **[Agent Skills](https://github.com/developer-overheid-nl/skills-marketplace).** |
| 100 | + Hiermee geven we agents instructies mee: welke tool, op welk moment, in welke |
| 101 | + volgorde. De skill is wat de agent dwingt om onze tooling te gebruiken in |
| 102 | + plaats van zelf te improviseren. Deze zijn in beta en nog volop in onderzoek, |
| 103 | + dus gebruiken op eigen risico! Eerder schreven we al over |
| 104 | + [hoe je standaarden in je AI-assistant laadt](/blog/2026/03/25/skills). |
| 105 | +- **[OAS Generator](https://developer-overheid-nl.github.io/oas-generator).** |
| 106 | + Genereert een boilerplate OpenAPI-document op basis van een `input.json`. Die |
| 107 | + boilerplate is al ADR-conform van opzet, dus de agent begint niet van scratch |
| 108 | + maar met een correct startpunt. |
| 109 | +- **[Checker](https://developer-overheid-nl.github.io/don-checker).** Onze |
| 110 | + linter/validator die een document toetst aan een ruleset, bijvoorbeeld de |
| 111 | + ADR-ruleset voor een OAS of de publiccode-ruleset voor |
| 112 | + [`publiccode.yml`](/kennisbank/open-source/standaarden/publiccode-yml). Bij |
| 113 | + `valid` mag de pijplijn door, bij `invalid` moet er opnieuw geïtereerd worden. |
| 114 | + Dit is de spil waar de hele kwaliteitsborging om draait. |
| 115 | +- **[Codegen Templates](https://github.com/developer-overheid-nl/codegen-templates).** |
| 116 | + Eigen [OpenAPI Generator](https://openapi-generator.tech/) templates, zodat de |
| 117 | + gegenereerde servercode voor API's aansluit op de API Design Rules en andere |
| 118 | + overheidsstandaarden. Momenteel hebben we een aantal smaken in de aanbieding, |
| 119 | + waaronder voor Java, Go, Node.js, Rust en Python. |
| 120 | +- **[Repo Docs Generator](/kennisbank/open-source/tutorials/tutorial-repo-docs-generator).** |
| 121 | + Genereert de standaard repo-documentatie: een `README.md`, `CONTRIBUTING.md`, |
| 122 | + `CODE_OF_CONDUCT.md`, `LICENSE`, `SECURITY.md`, `CHANGELOG.md` en een |
| 123 | + `publiccode.yml`. In één keer nette, consistente templates in plaats van |
| 124 | + handmatig samengeraapte bestanden. |
| 125 | + |
| 126 | +De AI doet in dit geheel maar twee dingen: de `input.json` vullen, een vast |
| 127 | +formaat dat het model kent, en via dialoog met de gebruiker die input compleet |
| 128 | +maken. Pas als de input volledig is, wordt er daadwerkelijk gegenereerd. |
| 129 | + |
| 130 | +## Generator en checker zijn complementair |
| 131 | + |
| 132 | +Een terechte vraag is: als de generator al een boilerplate maakt conform de |
| 133 | +[API Design Rules (ADR)](/kennisbank/api-ontwikkeling/standaarden/api-design-rules), |
| 134 | +en de agent borduurt voort op wat er al staat, heb je die checker dan überhaupt |
| 135 | +nog nodig? |
| 136 | + |
| 137 | +Het antwoord is ja, en het waarom is meteen het sterkste argument vóór deze |
| 138 | +opzet. Het klopt dat een agent geneigd is om voort te borduren op een bestaand, |
| 139 | +intern consistent document. Een schone boilerplate werkt als voorbeeld: de agent |
| 140 | +ziet hoe wij naar het register verwijzen, hoe we fouten modelleren, welke naming |
| 141 | +we hanteren, en trekt dat door. Dat is precies waarom je met een goede |
| 142 | +boilerplate begint. Het vernauwt de output, scheelt tokens en verkleint de kans |
| 143 | +op afwijkingen. |
| 144 | + |
| 145 | +Maar het is geen garantie. Naarmate een document groeit, valt de oorspronkelijke |
| 146 | +boilerplate buiten het effectieve aandachtsvenster van het model. Voeg je |
| 147 | +functionaliteit toe waar geen voorbeeld voor in het document staat, dan valt het |
| 148 | +model terug op zijn trainingsdata, en die is niet ADR-conform. En over meerdere |
| 149 | +bewerkingen heen kunnen kleine afwijkingen zich opstapelen. |
| 150 | + |
| 151 | +Daarom zijn de generator en de checker geen overlap maar complementair. De |
| 152 | +generator-boilerplate zorgt dat de checker bijna altijd meteen `valid` |
| 153 | +teruggeeft. De checker is er voor de keren dat dat niet zo is, of als er na de |
| 154 | +generatie nog wijzigingen aan de OAS doorgevoerd worden. Denk aan extra |
| 155 | +endpoints, filters, etc. De checker handhaaft de harde grens die een taalmodel |
| 156 | +statistisch nooit kan garanderen. |
| 157 | + |
| 158 | +## Een overheids-API bouwen |
| 159 | + |
| 160 | +Tijd om te laten zien hoe die loop er in de praktijk uitziet. De onderstaande |
| 161 | +flowchart toont het hele proces in één oogopslag; daaronder lopen we de stappen |
| 162 | +langs. |
| 163 | + |
| 164 | +```mermaid |
| 165 | +graph TD |
| 166 | + A([''Bouw een overheids-API''])-->SK[/Agent Skill/]-->B[Agent verzamelt input] |
| 167 | + U[/User input/]<-->|Feedback|B |
| 168 | + B<-->|API Search|T1[(Schema Register)] |
| 169 | + B-->IN[/input.json/]-->C[[OAS Generator]]-->D[/openapi.json/]-->E[[Checker]] |
| 170 | + ADR[/ADR Ruleset/]-->E |
| 171 | + E-->|Invalid?|FIX[Agent fixt openapi.json]-->E |
| 172 | + E-->|Valid?|F[[OpenAPI Generator]]-->G[/Servercode/] |
| 173 | + CG[/Codegen Template/]-->F |
| 174 | +``` |
| 175 | + |
| 176 | +De gebruiker geeft de prompt "bouw een overheids-API". De juiste skill wordt |
| 177 | +getriggerd en stuurt de agent door een vast proces. Eerst verzamelt de agent |
| 178 | +input: deels door vragen te stellen aan de gebruiker, deels door het |
| 179 | +schema-register te doorzoeken naar bruikbare, herbruikbare schema's. Het |
| 180 | +resultaat is een complete `input.json`. |
| 181 | + |
| 182 | +Die input gaat de OAS Generator in, die er een boilerplate `openapi.json` van |
| 183 | +maakt. Vervolgens komt de Checker om de hoek kijken. Deze toetst het document |
| 184 | +tegen de ADR-ruleset. Is het `invalid`, dan gaat het terug: de agent fixt de OAS |
| 185 | +en draait de checker opnieuw, net zo lang tot het klopt. Óók als het document na |
| 186 | +de generatie nog gewijzigd wordt dus. |
| 187 | + |
| 188 | +Het belangrijkste aan dit plaatje is de lus rond de checker. De agent _kan_ niet |
| 189 | +langs de regels. Hij mag itereren, hij mag fouten maken, maar hij komt pas |
| 190 | +verder als een deterministische tool groen licht geeft. De waarheid zit niet in |
| 191 | +het model, maar in de checker. |
| 192 | + |
| 193 | +## De code open source maken |
| 194 | + |
| 195 | +Precies hetzelfde patroon keert terug voor een ander prompt. De flowchart |
| 196 | +hieronder laat zien hoe: |
| 197 | + |
| 198 | +```mermaid |
| 199 | +graph TD |
| 200 | + H([''Maak dit open source''])-->AS[/Agent Skill/]-->I[Agent verzamelt input] |
| 201 | + U[/User input/]<-->|Feedback|I |
| 202 | + I-->T[[Repo Docs Generator]] |
| 203 | + T-->R[/README.md/]---C[/CONTRIBUTING.md, etc./] |
| 204 | + T-->P[/publiccode.yml/]-->L[[Checker]] |
| 205 | + PR[/Publiccode Ruleset/]-->L |
| 206 | + L--Invalid?-->M[Agent fixt publiccode.yml]-->L |
| 207 | +``` |
| 208 | + |
| 209 | +"Maak dit open source" trapt een vergelijkbaar proces af: de juiste skill wordt |
| 210 | +getriggerd, de agent verzamelt input en laat dat door de Repo Docs Generator |
| 211 | +lopen. Die genereert in één keer de complete set repo-documentatie, van |
| 212 | +`README.md` en `CONTRIBUTING.md` tot `CODE_OF_CONDUCT.md`, `LICENSE.md`, |
| 213 | +`SECURITY.md`, `CHANGELOG.md` en een `publiccode.yml`. Vervolgens komt dezelfde |
| 214 | +checker terug, nu met de publiccode-ruleset, die de `publiccode.yml` toetst. Is |
| 215 | +het `invalid`, dan fixt de agent het bestand en draait de checker opnieuw, exact |
| 216 | +dezelfde lus als bij het bouwen van de API. Saillant detail: als er reeds een |
| 217 | +`README.md` is, zoals het geval is nadat je de OpenAPI-generator hebt gebruikt, |
| 218 | +zal de agent deze aanpassen conform de template van de generator, welke de best |
| 219 | +practices voor een `README.md` bevat. |
| 220 | + |
| 221 | +In het geval van een bestaande codebase, instrueert de skill de agent om te |
| 222 | +kijken naar de git remote, bestanden als `openapi.json` en het projectmanifest |
| 223 | +(zoals een `package.json`, `pyproject.toml` of `pom.xml`) om daar de benodigde |
| 224 | +input uit te halen. Doe je dit prompt vanuit de bestaande context, dan weet de |
| 225 | +agent nog dingen uit de eerdere input. Denk hierbij bijvoorbeeld aan de |
| 226 | +contactgegevens uit de zojuist gegenereerde OAS. |
| 227 | + |
| 228 | +## Standaarden |
| 229 | + |
| 230 | +Er zit een randvoorwaarde onder dit hele verhaal, en die verdient het om |
| 231 | +expliciet genoemd te worden: standaarden. De templates, generators en validators |
| 232 | +uit deze blog bestaan alleen omdat er standaarden onder liggen waar we ze |
| 233 | +tegenaan kunnen bouwen, de ADR, de publiccode-standaard, herbruikbare schema's. |
| 234 | +Zonder een vaste set regels is er niets om een boilerplate op te baseren of een |
| 235 | +document tegenaan te toetsen. |
| 236 | + |
| 237 | +Hoe meer er gestandaardiseerd wordt, hoe nauwkeuriger we onze templates, |
| 238 | +generators en validators kunnen maken. Elke afspraak die we vastleggen is een |
| 239 | +stukje waarheid dat we uit het model kunnen halen en in deterministische tooling |
| 240 | +kunnen stoppen. Standaardisatie is de fundering die betrouwbaar bouwen met AI |
| 241 | +mogelijk maakt. Hoe steviger die fundering, hoe meer je met een gerust hart aan |
| 242 | +de tools kunt overlaten. |
| 243 | + |
| 244 | +## Conclusie |
| 245 | + |
| 246 | +Het resultaat is een proces dat reproduceerbaar en controleerbaar is. Dezelfde |
| 247 | +input levert dezelfde output, en wat eruit komt voldoet aantoonbaar aan onze |
| 248 | +regels, niet omdat een model toevallig goed gemutst was of een reviewer scherp |
| 249 | +oplette, maar omdat een tool het heeft afgedwongen. |
| 250 | + |
| 251 | +De rolverdeling is daarmee helder. Het taalmodel is de orkestrator die intentie |
| 252 | +vertaalt naar input en tools aanroept, niet het orakel dat de waarheid bepaalt. |
| 253 | +Generators doen het zware werk, validators bewaken de grenzen. En de mens blijft |
| 254 | +gewoon in de loop, maar op het niveau waar menselijk oordeel telt: keuzes, |
| 255 | +context, akkoord. Niet op het naleven van regels die een machine beter onthoudt. |
| 256 | + |
| 257 | +Of je AI inzet is aan jou. Maar áls je het doet, houd dan niet alleen de mens in |
| 258 | +de loop, maar óók beschikbare tools. |
0 commit comments