Output afdwingen: promptregels of API-schema — wanneer welke
Wie een taalmodel integreert in softwarearchitectuur stuit vroeg of laat op hetzelfde fundamentele probleem: hoe dwing je af dat het model valide JSON, strikte veldtypen of een vast syntaxformaat teruggeeft? Binnen softwareontwikkeling zijn er grofweg twee stromingen om dit te bereiken. Enerzijds is er de declaratieve route via puur promptontwerp, waarbij je context, expliciete syntaxregels en few-shot voorbeelden aanbiedt. Anderzijds is er de programmatische route via API-schema's, JSON Schema-definities en logit-masking op token-niveau.
Beide benaderingen hebben hun eigen bestaansrecht, maar worden in de praktijk regelmatig verkeerd ingezet. Ontwikkelaars proberen soms met tientallen regels promptinstructies te voorkomen dat een model afdwaalt, terwijl een strikt API-schema 100% syntactische garantie biedt. Omgekeerd worden dynamische, polymorfe datastructuren soms krampachtig in starre API-schema's geperst, waardoor redeneerkracht en flexibiliteit verloren gaan. In dit artikel vergelijken we beide technieken grondig op betrouwbaarheid, latency, tokenkosten, implementatiecomplexiteit en onderhoudbaarheid.
Het fundamentele onderscheid: semantische suggestie versus deterministische validatie
Het principiële verschil tussen promptregels en API-schema's zit in de plek waar de validatie plaatsvindt. Wanneer je output probeert af te dwingen via promptregels, vertrouw je volledig op het probabilistische taalgevoel van het model. Je vraagt het model om tijdens het decoderen van elk volgend token rekening te houden met de instructie in de systeemprompt. Dat blijft in de kern een semantisch verzoek: het model berekent per token de meest logische voortzetting op basis van zijn trainingsdata en de meegegeven context.
Bij een API-schema (zoals OpenAI Structured Outputs of Anthropic Tool Use / Tool Calling) grijpt de inferentie-engine direct in op het decoderingsproces. Via technieken zoals context-free grammars (CFG) of finite state machines (FSM) filtert de engine ongeldige tokens weg voordat de sampling plaatsvindt. Tokens die op dat specifieke moment in de JSON-structuur grammaticaal onmogelijk zijn — zoals een letterteken wanneer een integer wordt verwacht — krijgen een logit-waarde van min oneindig. Hierdoor kan het model simpelweg geen syntaxfouten produceren.
In het artikel over hoe je een LLM in een vast outputformaat laat antwoorden bespreken we de algemene strategieën om parsable antwoorden te forceren. Waar algemene outputsturing het speelveld verkent, zoomt dit artikel in op de afweging tussen sturing via instructies versus sturing via schema-gebaseerde runtime-engines.
Hoe promptregels werken: sturing via context, voorbeelden en negatieve constraints
Outputsturing via prompts berust op drie mechanismen: expliciete formaatdeclaratie, type-aanduidingen en boundary markers. Een typische promptgebaseerde implementatie specificeert exact welke velden aanwezig moeten zijn, welk datatype wordt verwacht en in welke JSON-structuur het resultaat moet worden gevat. Om het model te weerhouden van markdown-opmaak (zoals ```json codeblokken) of inleidende beleefdheidsfrasen, worden vaak expliciete negatieve instructies toegevoegd.
Voor wie uitsluitend via prompttekst wil sturen zonder afhankelijkheid van specifieke API-features, biedt de gids over vorm afdwingen vanuit de prompt zelf een overzicht van sjablonen en schematechnieken. Een basisvoorbeeld van pure promptsturing ziet er als volgt uit:
Je bent een data-extractie parser.
Zet de invoertekst om naar een JSON-object met exact deze structuur:
{
"klant_id": "string (formaat: KLT-XXXX)",
"factuurbedrag": "number (in euro's)",
"betaald": "boolean",
"posten": ["string"]
}
Regels:
1. Geef uitsluitend het JSON-object terug.
2. Geen markdown backticks, geen inleidende tekst, geen afsluiting.
3. Ontbreekt een veld, gebruik dan null.
Om ongewenste elementen zoals inleidende beleefdheidsfrasen te elimineren bij pure promptsturing, kun je technieken inzetten uit het artikel over negative prompting en constraint enforcement. Het nadeel van deze pure promptbenadering blijft echter evident: bij hoge verwerkingsvolumes of complexe geneste structuren zal het model statistisch gezien in een fractie van de gevallen alsnog hallucineren, komma's vergeten of ongeldige karakters invoegen.
Hoe API-schema's en grammar-constrained decoding werken
API-gebaseerde structured output verplaatst de verantwoordelijkheid van het taalmodel naar de inferentie-runtime. Ontwikkelaars leveren een formeel JSON Schema (vaak gegenereerd via Pydantic of Zod) mee met de API-call. De runtime compileert dit schema naar een grammatica. Tijdens het genereren van elk token berekent de engine welke tokens syntactisch geldig zijn volgens de grammatica en blokkeert alle overige opties in het vocabulaire.
Wie wil zien hoe API-providers dit op endpoint-niveau afdwingen via JSON Schema en tool-parameters, leest de technische specificaties in het overzicht over betrouwbare structured output uit API's halen. Het generatieproces verloopt daarmee deterministisch volgens de onderstaande stappen:
- Schema compilatie: Het JSON Schema wordt omgezet naar een interne representatie (zoals een regex-parser of pushdown-automaat).
- Context-evaluatie: Het model verwerkt de input-tokens en berekent logits voor de gehele token-vocabulaire.
- Logit masking: De runtime controleert welke tokens grammaticaal toegestaan zijn op basis van de huidige JSON-staat en maskeert alle ongeldige tokens.
- Sampling: Het model kiest het meest waarschijnlijke token uit de gefilterde subset van geldige tokens.
- Staat-update: De parser schuift op naar de volgende positie in het schema totdat het sluitende accolade-token is gegenereerd.
Voor het toepassen van grammars op opensource runtime-engines zoals llama.cpp en vLLM verwijzen we naar het stappenplan over gevormde JSON outputs afdwingen bij lokale LLM's, waar formaten zoals GBNF (GGML BNF) worden uitgelegd.
Betrouwbaarheid en foutpercentages: willekeur versus wiskundige garantie
Het belangrijkste selectiecriterium tussen beide benaderingen is het acceptabele foutpercentage van het subsysteem. In productiesystemen waar output direct doorgegeven wordt aan downstream API's, databases of bedrijfskritische pipelines, leidt een syntaxfout direct tot een ongeïnterpreteerde runtime exception.
Bij pure promptregels ligt het syntaxfoutpercentage bij state-of-the-art modellen (zoals GPT-4o of Claude 3.5 Sonnet) bij eenvoudige schema's rond de 0,5% tot 3%. Bij kleinere opensource modellen (7B tot 14B parameters) of bij diepe geneste arrays kan het uitvalpercentage via prompts oplopen tot boven de 12%. Fouten manifesteren zich typisch als:
- Vergeten aanhalingstekens rond object keys.
- Niet-ontsnapte newlines of dubbele aanhalingstekens binnen stringwaarden.
- Ongewenste toevoegingen zoals
```json ... ```markdown fences. - Trailing commas aan het einde van een array of object, wat ongeldig is in strikte JSON parsers.
- Typeverwarring, waarbij een boolean als
"true"(string) of een getal als"12.50"wordt weergegeven.
Met een API-schema is het syntaxfoutpercentage letterlijk 0,0%. De parser kan wiskundig gezien geen byte produceren die strijdig is met het gecompileerde JSON Schema. Let wel: dit betreft uitsluitend syntactische correctheid. Semantische fouten — zoals een feitelijk onjuist berekend factuurbedrag of een gehallucineerde productnaam — worden niet door een schema voorkomen.
Latency, tokenverbruik en verwerkingskosten vergeleken
Een veelgehoord misverstand is dat API-schema's altijd sneller en goedkoper zijn dan promptregels. De realiteit is genuanceerder en hangt sterk af van caching, schema-omvang en de initiële compilatiestap.
Wat betreft input-tokens: bij promptregels moet je de volledige structuurbeschrijving in de prompttekst opnemen. Bij API-schema's stuur je het schema mee via de API-payload. Bij providers die structured outputs ondersteunen, telt het schema mee voor de input-tokentelling. Een uitgebreid JSON Schema met type-definities, veldbeschrijvingen en required-arrays kan gemakkelijk 300 tot 800 tokens consumeren.
Wat betreft output-tokens en latency: API-schema's genereren vaak minder overbodige tokens omdat het model niet hoeft te worden geïnstrueerd om markdown te vermijden. Het model begint direct met het openings-accolade {. Echter, bij lokale implementaties en sommige cloud-API's brengt de eerste aanroep met een nieuw schema een kleine compilatie-overhead (pre-processing latency) met zich mee. Zodra het schema gecachet is, verloopt de generatiesnelheid identiek aan ongefilterde sampling.
| Eigenschap | Promptregels | API-schema (Constrained Decoding) |
|---|---|---|
| Syntaxgarantie | Probabilistisch (97% – 99,5%) | Deterministisch (100% syntactisch valide) |
| Veldvalidatie | Geen strikte type-handhaving | Strikte enum-, string- en type-controle |
| Setup complexiteit | Zeer laag (enkel prompttekst) | Gemiddeld (JSON Schema / Pydantic models) |
| Flexibiliteit | Zeer hoog (vrije vorm, Markdown, CSV) | Beperkt tot formele dataspecificaties |
| Token overhead | Vastgelegd in prompttekst | Vastgelegd in API schema-payload |
| Retry-noodzaak | Regelmatig try-catch + fallback nodig | Zelden retry nodig voor syntaxfouten |
Flexibiliteit versus starheid: dynamische structuren en polymorfe data
Hoewel API-schema's superieur zijn in syntactische betrouwbaarheid, kent de techniek een duidelijke beperking: starheid. De meeste runtime-engines vereisen dat alle velden vooraf expliciet gedefinieerd zijn (inclusief additionalProperties: false bij OpenAI Structured Outputs). Dit maakt het afdwingen van dynamische of polymorfe datastructuren aanzienlijk complexer.
Stel dat je een model vraagt om een willekeurige tabel uit een juridisch contract te extraheren, waarbij het aantal kolommen en de kolomnamen per document verschillen. In een zuiver API-schema kun je geen arbitraire keys toestaan zonder terug te vallen op een generieke sleutel-waarde arraystructuur. Dat dwingt het model tot een minder natuurlijke datarepresentatie, wat ten koste kan gaan van de semantische nauwkeurigheid.
Promptregels blinken juist uit in scenario's waarin de outputstructuur deels organisch moet meebewegen met de invoer. Denk aan geneste documentbomen met variërende diepte, geannoteerde markdown-tabellen of situaties waarin het model zelf mag bepalen of een toelichtend veld relevant is om toe te voegen.
Redeneerruimte: het gevaar van overhaaste JSON-generatie
Een technisch knelpunt bij strikte schema-afdwinging is het verlies van redeneerruimte (scratchpad / chain-of-thought). Een taalmodel denkt na via de tokens die het genereert. Als het schema voorschrijft dat het allereerste output-token een openingsaccolade moet zijn, direct gevolgd door de eindconclusie, ontneem je het model de mogelijkheid om een tussenberekening of analyse uit te voeren.
Als een model bijvoorbeeld een complexe classificatie moet uitvoeren op basis van tegenstrijdige criteria, en het schema dwingt direct {"classificatie": "..."} af, dan moet het model die beslissing nemen in één enkele forward pass zonder tussenliggende redeneerstappen. Dit leidt in de praktijk tot een aantoonbare daling van de inhoudelijke kwaliteit.
Bij het ontwerpen van API-schema's voor analytische taken is het daarom cruciaal om een redeneerveld vooraan in het schema te plaatsen:
{
"type": "object",
"properties": {
"redenering": {
"type": "string",
"description": "Stapsgewijze analyse van de context voordat de conclusie wordt getrokken"
},
"classificatie": {
"type": "string",
"enum": ["laag", "gemiddeld", "hoog", "kritiek"]
},
"betrouwbaarheidsscore": {
"type": "number"
}
},
"required": ["redenering", "classificatie", "betrouwbaarheidsscore"],
"additionalProperties": false
}
Door het veld redenering als eerste verplichte veld te definiëren, genereert het model eerst zijn denkstappen. Pas daarna sampelt het de finale categorie en score, wat de accuraatheid aanzienlijk verhoogt.
Lokale modellen versus externe cloud-API's: ondersteuning in de praktijk
De keuze tussen promptregels en schema's wordt mede bepaald door de hostingomgeving van het model. Bij grote commerciële cloud-aanbieders is structured output via API-parameters inmiddels de standaard. De implementatie is daar volledig geabstraheerd: je definieert een modelklasse of JSON Schema, en de provider handelt de logit-masking af.
In lokale en private cloud-omgevingen (zoals self-hosted vLLM, Ollama, TGI of llama.cpp) vereist schema-afdwinging specifieke configuratie. Hoewel frameworks zoals Outlines, Guidance en SGLang geavanceerde grammar-decoding ondersteunen, brengt dit operationele aandachtspunten met zich mee:
- Geheugengebruik en compilatietijd: Complexe regex- en CFG-grammatica's kunnen bij de initiële opstart merkbare CPU/GPU-overhead veroorzaken.
- Compatibiliteit met speculatieve decoding: Sommige geavanceerde optimalisaties voor versnelde token-generatie werken minder efficiënt in combinatie met strikte logit-maskers.
- Tokenizer-afhankelijkheden: Grammatica-parsers moeten exact zijn afgestemd op de byte-level byte-pair encoding (BPE) van het specifieke model om edge-case fouten te voorkomen.
Hybride architecturen: het schema bewaakt de structuur, de prompt vult de inhoud
In professionele software-ontwikkeling is de tegenstelling tussen promptregels en API-schema's vaak een schijntegenstelling. De meest robuuste productiesystemen combineren beide technieken in een gelaagde aanpak:
1. Het API-schema definieert het contract. Het schema bewaakt de syntax, garandeert types (strings, integers, arrays, booleans), blokkeert ongewenste velden en zorgt dat de response direct zonder parsing-fouten kan worden gedeserialiseerd in applicatiecode.
2. De promptregels sturen de semantiek en toonzetting. Binnen de veldbeschrijvingen van het schema en in de systeemprompt specificeer je de inhoudelijke randvoorwaarden: welke criteria gelden voor een samenvatting, welke extractieregels gelden voor datums en hoe moet worden omgegaan met ontbrekende gegevens.
import { z } from "zod";
// 1. Het schema bewaakt het syntactische contract
export const KlantEvaluatieSchema = z.object({
analyse: z.string().describe("Korte analyse van het klantsignaal"),
risicoNiveau: z.enum(["laag", "gemiddeld", "hoog"]),
actiepunten: z.array(z.string()).min(1).max(5),
opvolgingVereist: z.boolean()
});
// 2. De prompt stuurt de inhoudelijke interpretatie
export const SYSTEEM_PROMPT = `
Beoordeel het binnenkomende klantbericht strikt op basis van ons servicebeleid.
Markeer een risico alleen als 'hoog' wanneer er sprake is van juridische dreiging of dataverlies.
Formuleer actiepunten altijd als concrete werkwoorden gericht aan het supportteam.
`;
Beslismatrix: wanneer kies je welke techniek?
Om snel de juiste architectuurbeslissing te nemen voor een specifieke use case, kan de onderstaande beslisstructuur worden aangehouden:
| Use Case / Vereiste | Aanbevolen Methode | Primaire Reden |
|---|---|---|
| Directe database-insert of API-koppeling | API-schema (Structured Output) | 100% syntaxgarantie vereist om runtime crashes te voorkomen. |
| Agent Tool Calling / Functie-aanroepen | API-schema (Tools/Functions) | De orchestrator moet argumenten zonder parsing-laag kunnen aanroepen. |
| Genereren van Markdown rapporten met tabellen | Promptregels | Markdown is flexibel en vereist geen starre JSON object-structuur. |
| Complexe data-extractie met onbekende structuur | Promptregels met Pydantic fallback | Dynamische veldnamen laten zich lastig vangen in statische schema's. |
| Lokaal draaiende LLM's met beperkte rekenkracht | Grammar-constrained decoding (GBNF/Outlines) | Kleine modellen falen vaker op promptsturing; grammars lossen dit op. |
| Snelle prototyping en experimenten | Promptregels | Geen overhead van schema-definities tijdens verkennende fases. |
Conclusie en migratiestrategie
Promptregels en API-schema's zijn geen concurrerende methoden, maar complementaire gereedschappen in de toolbox van een AI-ontwikkelaar. Promptregels zijn ongeëvenaard voor flexibele, vormvrije of snel veranderende interfaces waarbij menselijke lezers de output beoordelen. Zodra data echter programmatisch door software moet worden verwerkt, zijn API-schema's en constrained decoding de enige verantwoorde keuze om robuustheid in productie te garanderen.
Voor bestaande codebases die nog leunen op kwetsbare regex-parsers of retry-loops rondom prompt-gebaseerde JSON, is de migratieroute helder: vertaal de gewenste outputstructuren naar formele JSON Schema's of Pydantic-modellen, activeer structured outputs op het API-koppelvlak, en verplaats semantische instructies naar de veldbeschrijvingen en systeemprompt. Zo ontstaat een architectuur die zowel inhoudelijk flexibel als technisch onbreekbaar is.


