Versiebeheer voor Prompts in een Codebase
In veel softwareorganisaties start het gebruik van Large Language Models (LLM's) als een experiment. Een ontwikkelaar plakt een instructietekst als een hardcoded string in een API-call en levert de feature op. Zodra het systeem in productie draait, blijkt deze aanpak onhoudbaar. Een kleine wijziging in de promptinstructies verandert de structuur van het antwoord, breekt parsers stroomafwaarts of introduceert onvoorziene fouten bij randgevallen. Prompts beheren als losse tekstfragmenten zonder proces leidt direct tot regressies op de productieomgeving.
Om LLM-gebaseerde functionaliteit betrouwbaar te schalen, is een volwassen versiebeheerproces noodzakelijk. Dit artikel behandelt het ontwerp van prompt-versiebeheer binnen software-architecturen, van opslag- en reviewstrategieën tot regressietesten, koppeling aan specifieke modelversies en gefaseerde uitrol patronen.
Waarom een prompt zich als broncode gedraagt en niet als configuratie of content
Het is een veelvoorkomende misvatting om prompts te behandelen als statische applicatieconfiguratie of bewerkbare content. Een instelling zoals het maximaal aantal database-verbindingen is deterministisch: verander je het getal van 10 naar 20, dan schaalt het verwerkingsvermogen voorspelbaar. Redactionele content op een website verandert de presentatie, maar heeft geen invloed op de uitvoerbare logica van de software.
Een prompt daarentegen stelt het runtime gedrag van de LLM in en bepaalt de manier waarop de applicatie gegevens transformeert, beslissingen neemt en uitvoer genereert. Een verandering van één enkel woord of het toevoegen van een extra voorbeeld in de prompt kan de output-indeling radicaal veranderen. Wanneer je gebruikmaakt van structured output of function calling, fungeert de prompt als de interfacecontract-definitie tussen de ongestructureerde model-output en je deterministische software-code.
Omdat wijzigingen in een prompt rechtstreeks invloed hebben op de logica en stabiliteit van je software, moet een prompt exact dezelfde cyclus doorlopen als broncode: peer reviews, geautomatiseerde regressietesten, audit-sporen en gecontroleerde deployments.
Opslagstrategieën voor prompts
Er bestaan vier gangbare patronen voor de opslag van prompts, elk met specifieke voor- en nadelen voor het ontwikkelproces en de runtime-architectuur.
| Opslaglocatie | Voordelen | Nadelen | Geschiktheid |
|---|---|---|---|
| Inline in code | Maximale nabijheid bij uitvoerlogica; directe opslag in Git; geen externe afhankelijkheid. | Slechte leesbaarheid bij lange teksten; JSON-escaping vervuilt diffs; niet toegankelijk voor niet-ontwikkelaars. | Kleine prototypes en zeer korte instructies. |
| Losse bestanden in de repository | Schone Git-diffs; versiebeheer gekoppeld aan applicatierelease; goede scheiding van concerns. | Aanpassing vereist een nieuwe deployment van de applicatie. | Middelgrote tot grote bedrijfapplicaties met strenge CI/CD-eisen. |
| Database | Aanpasbaar via een beheerdashboard; updates mogelijk zonder code-deployments. | Ontkoppeld van de Git-historie; risico op inconsistentie tussen omgevingen (dev/prod). | Systemen waar niet-technische domeinexperts live instructies moeten bijsturen. |
| Externe Prompt-registry | Ingebouwde functies voor evaluatie, versiebeheer, dashboards en A/B-testen. | Extra externe netwerk-call; vendor lock-in; extra afhankelijkheid in de kritieke keten. | Teams met sterke focus op AI-productbeheer die loskoppeling van de release-cyclus wensen. |
Concrete implementatie: Losse bestanden in de repository
Voor de meeste softwareteams levert de opslag van prompts in losse bestanden binnen de Git-repository de optimale balans tussen onderhoudbaarheid, veiligheid en transparantie. Hierbij worden prompts opgeslagen in een dedicated map (bijvoorbeeld prompts/) in een helder formaat zoals Markdown of Text, vergezeld van een metadata-bestand (JSON of YAML).
prompts/
└── facturatie/
└── verwerk_factuur/
├── v1.0.0.md
├── v1.1.0.md
└── metadata.json
Het bestand metadata.json definieert welke versie momenteel als actief of standaard is aangemerkt en welke variabelen vereist zijn:
{
"prompt_name": "verwerk_factuur",
"active_version": "1.1.0",
"versions": {
"1.1.0": {
"file": "v1.1.0.md",
"target_model": "gpt-4o-2024-08-06",
"temperature": 0.0,
"max_tokens": 1000
}
}
}
Versienummering, identiteit en content-hashes
Een robuust versiebeheersysteem voor prompts combineert twee vormen van identificatie: menselijk leesbare versienummers (SemVer) en cryptografische content-hashes.
Semantische Versienummering (SemVer)
Pas het MAJOR.MINOR.PATCH principe toe op je promptbestanden:
- MAJOR: Wijzigingen in de prompt die de structuur of het schema van de verwachte output veranderen (bijv. toevoegen van verplichte JSON-velden), of die de intentie van de prompt fundamenteel aanpassen.
- MINOR: Toevoeging van aanvullende voorbeelden (few-shot learning), herstructurering van instructies voor een hogere nauwkeurigheid zonder de output-interface te breken.
- PATCH: Taalkundige correcties, verduidelijking van typefouten of kleine nuances die geen meetbare invloed hebben op de logica.
Content-hashes
Naast de SemVer-tag dient de runtime-code een SHA-256 hash van het ingevulde of on-ingevulde prompt-sjabloon te berekenen. Een versienummer in de code kan immers per ongeluk ongewijzigd blijven na een aanpassing, terwijl een hash gegarandeerd verandert bij elke wijziging in de string.
import hashlib
def calculate_prompt_hash(template_content: str) -> str:
return hashlib.sha256(template_content.encode('utf-8')).hexdigest()[:12]
Door bij elke API-call naar het LLM zowel de versienaam (bijv. verwerk_factuur:1.1.0) als de unieke content-hash toe te voegen aan de metadata, ontstaat een onweerlegbaar spoor in je observability-infrastructuur. Raadpleeg het artikel over observability-en-logging voor gedetailleerde patronen rondom het vastleggen van deze traces.
De unieke koppeling: Prompt-versie x Modelversie
Een veelgemaakte fout in AI-engineering is de aanname dat een prompt een universele, model-onafhankelijke instructie is. In de praktijk is het gedrag van een prompt strikt gekoppeld aan een specifieke modelversie en zelfs aan een specifieke snapshot van een provider.
Een prompt die optimaal werkt op gpt-4o-2024-05-13 kan afwijkende of slechtere resultaten geven wanneer dezelfde provider migreert naar gpt-4o-2024-08-06 of wanneer de call wordt omgeleid naar een alternatief model via model-routing. Modellen verschillen in hun gevoeligheid voor systeeminstructies, het verwerken van markdown-formatting en hun neiging tot verbositeit.
Leg daarom een promptversie nooit los vast, maar altijd als een onlosmakelijk koppel van [Prompt ID + Prompt Versie + Model ID + Provider Snapshot + Hyperparameters]. Wanneer het onderliggende model verandert, dient dit te worden behandeld als een wijziging in de runtime-omgeving. De bestaande prompt moet opnieuw geëvalueerd en eventueel geherversioneerd worden voor de nieuwe modelidentiteit.
Het reviewproces en leesbare code-diffs
Om een effectieve code review uit te voeren op prompt-wijzigingen, moeten de bestanden leesbaar zijn in tools zoals GitHub, GitLab of Bitbucket. Directe inline opslag van meerregelige teksten in talen zoals Java of Python leidt vaak tot nodeloze string-escapes (zoals \n of geïnterpoleerde quotes), wat reviews bemoeilijkt.
Regels voor schone prompt-diffs:
- Gebruik dedicated Markdown- of TXT-bestanden: Markdown biedt het voordeel dat je secties in de prompt kunt structureren met kopjes (zoals
# Context,# Regels,# Voorbeelden), wat zowel voor de menselijke reviewer als voor de LLM de verwerking verbetert. - Vermijd JSON-escaping in Git: Sla sjablonen op als platte tekst. Gebruik bekende templating-engines (zoals Jinja2, Mustache of native string templates) om variabelen in te voegen op runtime.
- Scheid instructies en data duidelijk: Zorg dat het sjabloon een duidelijke afbakening heeft voor dynamische runtime-invoer.
Voorbeeld van een schoon prompt-sjabloon in prompts/samenvatten/v1.0.0.md:
# Rol
Je bent een gespecialiseerde juridische assistent die Nederlandstalige contracten analyseert.
# Taak
Vat de onderstaande tekst samen in maximaal 3 opsommingstekens. Focus uitsluitend op de financiële aansprakelijkheid.
# Constraints
- Gebruik geen vaktermen zonder korte uitleg.
- Als er geen aansprakelijkheid wordt genoemd, antwoord dan exact met: "Geen aansprakelijkheid vermeld."
# Invoer
{{ contract_tekst }}
Testen bij release: Regressietesten op evaluatie-eigenschappen
Het testen van een prompt verschilt fundamenteel van traditionele unit tests. Omdat een LLM probabilistisch is, kan een test op een exacte string-match (zoals assert output == "expected") zelden worden toegepast, behalve bij zeer strikte formaten. Een robuuste CI/CD-pipeline voor prompts draait om regressietesten op vastgestelde testsets.
Opzet van een prompt-testset
Houd in de repository een dataset bij met representatieve invoervoorbeelden en verwachte eigenschappen (gouden standaard dataset). Dit kunnen randgevallen zijn uit de productiepraktijk, inclusief gevallen die eerder tot bugs leidden.
tests/prompts/facturatie_testset.json
[
{
"id": "tc_001",
"input": {"contract_tekst": "De totale aansprakelijkheid is beperkt tot EUR 10.000."},
"expected_checks": {
"contains_keywords": ["10.000", "aansprakelijkheid"],
"max_words": 50,
"forbidden_words": ["geen idee"]
}
}
]
Evaluatie-eigenschappen (Assertions)
Gebruik in plaats van exacte tekst-matches de volgende methoden om de geldigheid van het resultaat te bepalen:
- Schema-validatie: Controleer met behulp van JSON Schema of Pydantic of de uitvoer exact voldoet aan het verwachte formaat. Zie voor meer informatie invoervalidatie-en-outputfiltering.
- Heuristische regressietests: Valideer harde randvoorwaarden zoals maximale lengte, aanwezigheid van specifieke entiteiten, afwezigheid van verboden termen en taaldetectie.
- Model-as-a-Judge evaluaties: Voor complexe kwalitatieve eisen zet je een zwaarder, geëvalueerd model in dat de gegenereerde uitvoer beoordeelt aan de hand van een rubric (beoordelingsmatrix) op een schaal van 1 tot 5 op criteria zoals correctheid en toon. Wil je de uitkomsten afzetten tegen bredere modelvergelijkingen, kijk dan op benchmark.llmnet.nl.
In je CI-pipeline (bijvoorbeeld GitHub Actions) voer je de testsuite uit bij elke Pull Request waarin een promptbestand in de map prompts/ is gewijzigd. Als de score op de testset onder een vooraf ingestelde drempelwaarde valt, wordt het samenvoegen van de PR geblokkeerd.
Gefaseerd uitrollen van prompt-wijzigingen
Een gecodeerde en gecontroleerde prompt moet veilig naar productie worden gebracht. Omdat een prompt-update een onvoorziene impact kan hebben op echte gebruikers, mag een release nooit in één keer naar 100% van het verkeer worden uitgerold.
1. Feature flags en dynamische routing
Koppel de prompt-versieselectie in de code aan een feature-flag-systeem (zoals LaunchDarkly, Unleash of een interne configuratie-service). Hiermee ontkoppel je het deployment-moment van het activatie-moment.
def get_prompt_version(user_id: str) -> str:
if feature_flags.is_enabled("use_prompt_v1_1_0", context={"user_id": user_id}):
return "1.1.0"
return "1.0.0"
2. Canary releases en A/B-testen
Stuur eerst een klein percentage (bijvoorbeeld 5%) van het productieverkeer naar de nieuwe promptversie (v1.1.0) terwijl het overgrote deel op de stabiele versie (v1.0.0) blijft draaien. Vergelijk gedurende deze periode de belangrijkste metrieken: foutpercentages van parsers, latency, tokenverbruik en gebruikersfeedback.
3. Directe rollback zonder deployment
Indien er op de productieomgeving een verhoogd aantal fouten optreedt (bijvoorbeeld mislukte JSON-parsing), kan de feature flag direct worden teruggezet naar de vorige versie (v1.0.0). Omdat de oude prompt-versies in de codebase aanwezig blijven, is er geen noodzaak voor een snelle hotfix-deployment of het terugdraaien van de gehele applicatie-build.
Mocht de API-provider tijdens de uitrol te maken krijgen met storingen of verhoogde foutmarges, dan biedt een strategie voor graceful-degradation-bij-llm-uitval de nodige opvang om de continuïteit van de dienstverlening te waarborgen.
Wat je moet loggen voor incident-reconstructie
Wanneer een gebruiker een incorrect antwoord of een foutmelding rapporteert, is het noodzakelijk om exact te kunnen reconstrueren hoe het LLM tot die specifieke uitvoer is gekomen. Het achteraf aanpassen van een prompt zonder gedetailleerde audit-logging maakt het traceren van problemen onmogelijk.
Sla bij elke LLM-interactie een gestructureerd log-record op waarin minimaal de volgende velden zijn opgenomen:
request_id: Unieke ID van de transactie.prompt_name: De naam van de functionele prompt (bijv.analyseer_risico).prompt_version: Het exacte SemVer-nummer (bijv.1.2.0).prompt_hash: De SHA-256 hash van het gecompileerde sjabloon.model_provider: De leverancier van de API (bijv. OpenAI, Anthropic, lokaal).model_version: De exacte snapshot-versie van het model (bijv.claude-3-5-sonnet-20241022).hyperparameters: De gebruikte instellingen (zoalstemperature,top_p,presence_penalty).input_variables: De dynamische parameters die in de prompt zijn geïnjecteerd (geanonimiseerd conform privacy-richtlijnen).raw_response_hash: Hash of referentie naar de gegenereerde uitvoer.
Door deze informatie op te slaan in een centrale logging-voorziening, kun je bij een foutieve productie-call de exacte staat van de applicatie op het specifieke moment van uitvoering nabootsen in een ontwikkelomgeving.
Checklist: Versiebeheer voor Prompts
Gebruik deze nuchtere checklist om de verwerking van prompts binnen je softwareteam te toetsen:
- [ ] Opslag: Prompts staan in losse, versiebeheerde bestanden (bijv. Markdown) in de Git-repository, gescheiden van de applicatiecode.
- [ ] Identificatie: Elke prompt heeft een expliciet SemVer-nummer én de runtime berekent een content-hash van het sjabloon.
- [ ] Koppeling: In de configuratie is elke promptversie expliciet gekoppeld aan een specifieke modelnaam en snapshot-versie.
- [ ] Reviews: Prompt-wijzigingen verlopen via een standaard Pull Request proces inclusief peer review op leesbare tekstdiffs.
- [ ] CI/CD Testen: Wijzigingen worden automatisch getest tegen een gouden regressietestset. De PR wordt geblokkeerd bij daling van de validatiescores.
- [ ] Deployment: Nieuwe promptversies worden uitgerold achter feature flags en stapsgewijs ingezet via canary releases.
- [ ] Observability: Elke productie-call logt het prompt-ID, de prompt-versie, de content-hash en de exacte modelversie voor volledige traceerbaarheid.

