Notities uit de Release Cycle — Deel 8 (slot)
Je levert de regressiesuite uit post 7. Hij werkt. De slice-bewuste gates vangen echte bugs. De gekalibreerde judge houdt stand.
Dan vraagt je engineering lead hoeveel het kost om hem op elke PR te draaien. Je doet de vermenigvuldiging: ~12 minuten judge-inferentie per PR, 60 PR’s per dag, vier dimensies × zeventien slices, en de rekening is echt geld. Erger: elke developer wacht nu 12 minuten op een groen vinkje voor een typo van één regel in een prompt. De velocity zakt[1], het team mort, iemand stelt voor om “de gates dan maar ’s nachts te draaien” — wat precies de manier is om alles wat de gates moesten doen overboord te gooien.
De oplossing is niet minder testen. De oplossing is gelaagd testen, waarbij het meeste signaal in de eerste negentig seconden binnenkomt. Deze post gaat over wat onder de gate-suite draait: sub-seconde contracttests, een strakke smoke-laag, een kostenbewuste fleet, en een shadow-venster van twee weken voordat een nieuwe gate iemand blokkeert.
Dit is post 8, de laatste van deze serie. Aan het eind heb je het volledige plaatje — van de pipeline met vier fasen tot aan de contracttest-fixture die op elke commit draait.
Wat betekent CI voor een custom taalmodel?
CI voor een custom LLM is het werk dat de gate-suite niet hoeft te herhalen. De gate scoort semantische kwaliteit; CI vangt alles wat de score van de gate betekenisloos zou maken vóórdat de gate ook maar één judge-token uitgeeft.
Contracttests draaien in milliseconden en verifiëren dat prompttemplates nog renderen, dat tool-call-schema’s nog parsen, dat retrieval-indices nog antwoorden, dat het manifest nog verwijst naar hashes die daadwerkelijk bestaan. Ze zijn deterministisch, gratis, en de enige reden dat de rest van de pipeline zich kan veroorloven te bestaan. Een pull request die het prompttemplate breekt moet in 200 ms falen, niet pas na 12 minuten judge-inferentie die onzin scoort.
De contractlaag is het verschil tussen een CI-rekening die lineair schaalt met PR-volume en een die dat niet doet. De CI-runner van Divinci besteedt > 90% van zijn judge-budget aan echte semantische evaluatie, niet aan PR’s die een schemacheck hadden moeten falen. Die ratio is het kerngetal.
Waarom traditionele CI breekt voor LLM’s — door de kostenlens
Posts 1 en 7 behandelden waarom deterministische CI faalt voor een generatief model. De versie van dat verhaal waar deze post over gaat, is de kosten van die vier eigenschappen, niet het bestaan ervan.
| Eigenschap van LLM’s | Falen van traditionele CI | Kostenvorm |
|---|---|---|
| Niet-deterministische outputs | Exact-match-asserties zijn flaky | Reruns vergroten kosten lineair met flake-rate |
| Multidimensionale kwaliteit | Een enkele booleaanse waarde is oninformatief | Elke dimensie is een aparte (betaalde) judge-call |
| Provider-drift | Vastgepinde gpt-4-2024-01-01 wordt stilletjes afgevoerd | Hercalibratie-piek wanneer een provider een checkpoint uitfaseert |
| Niet-lokale prompteffecten | Een lokale unittest kan het effect niet vangen | Distributievorm verandert tussen PR’s, niet erbinnen — vereist herloop van de hele suite, niet alleen delta |
De CI-architectuur moet elk van deze betaalbaar maken. Contracttests behandelen eigenschap 1 en 3 goedkoop. Smoke-tests behandelen eigenschap 4 deels. Alleen de volledige suite behandelt eigenschap 2 — en alleen op de PR’s die het daadwerkelijk nodig hebben.
De CI-laagcake — sub-seconde tot vijfentwintig minuten
De architectuur die we leveren bestaat uit vier lagen, waarbij elke laag haar compute verdient door te vangen wat de goedkopere lagen eronder niet kunnen. De slice-bewuste framing van elke laag volgt dezelfde les die de Tianpan Semver Lie-postmortem expliciet maakte[4]: aggregaatsignalen liegen; per-slice-signalen vangen wat aggregaten verbergen.
De kostenvorm is het ontwerp. ~74% van de PR’s geeft nooit een judge-token uit — contract of smoke is genoeg. De PR’s die wel de volledige suite bereiken, zijn die welke een prompt, een modelconfig, een retrieval-index of evaluatiecode hebben aangeraakt — precies de wijzigingen waarbij de gate-suite het enige signaal is dat het vertrouwen waard is. Release-kandidaten vormen het kleine aandeel dat laag 4 bereikt.
Contracttests — het oneerlijke voordeel
Contracttests zijn de eerste linie, de goedkoopste linie, en de linie die de meeste teams overslaan omdat ze beneden de waardigheid van een “AI-evaluatiepipeline” lijken. Het is ook waar 30–40% van de zou-zijn-regressies daadwerkelijk faalt in de suites van onze klanten, voordat een enkele judge is aangeroepen.
De contractlaag toetst vijf dingen en niets anders:
- Prompttemplate-render. Elk template rendert tegen een canonieke fixture zonder ongebonden variabelen, op hol geslagen loops of kapotte Jinja-achtige includes.
- Tool-call-schema. Het argumentschema van elke gedeclareerde tool parset, de JSONSchema is geldig, en de gerenderde prompt verwijst daadwerkelijk naar alle vereiste slots.
- Manifest-integriteit. Elke SHA in het release-manifest — model, prompt, retrieval-index, judge, dataset — komt overeen met een artefact dat in het register bestaat. Dangling pointers falen hier, niet drie lagen verder.
- Index liveness. De retrieval-index reageert binnen het budget op een bekende query. Een herbouwde index die retrieval stilletjes heeft gebroken, komt hier aan het licht, niet in productie.
- Denylist & token-budget. Elk prompttemplate dat een verboden token introduceerde, het token-budget per call oversteeg, of voorbij het contextvenster rendeerde, faalt hier. Heuristische semantische-similariteitsscoring[6] is ook goedkoop genoeg om op de contractlaag te draaien voor fuzzy-match-denylist-dekking waar letterlijke string-matching tekortschiet.
# Een representatieve contracttest-aanroep — draait in ruwweg 600 ms
divinci ci contract \
--manifest release/staging.yaml \
--check schema,template,manifest,index,denylist \
--fail-fast \
--json-out /tmp/contract-report.jsonGeen van deze roept een judge aan. Geen ervan is niet-deterministisch. Geen ervan kost meetbaar geld. En elk van hen sluit een hele klasse van “de gate-suite zei dat de medische slice regresseerde”-alerts uit die anders volle 12 minuten judge-inferentie zou hebben verspild aan het scoren van output die het model überhaupt niet correct had kunnen produceren.
De smoke-laag — 90 seconden, ~$0,05 per PR
Als de contractlaag het goedkope oneerlijke voordeel is, dan is de smoke-laag de laag die regressies daadwerkelijk vangt voor minder dan de prijs van een koffie. Twintig tot dertig cases getrokken uit de slices met het hoogste volume, gescoord op taakvoltooiing en veiligheid alleen, geen faithfulness, geen latentie, geen retrieval-grounded checks. Elke PR draait deze. Het duurt ongeveer 90 seconden omdat de cases in één judge-call worden gebatcht met een gestructureerd outputschema, en omdat de judge de goedkope gekalibreerde judge is — niet de volwaardige die voor release-kandidaten wordt gebruikt.
We registreren welke laag elke verzonden fix heeft gevangen in een regressielog, en het histogram is de afgelopen zes maanden consistent geweest in klantdeployments:
De 3% die ontsnapt is waarom de instant rollback van post 5 bestaat. De gates beloven geen nul ontsnappingen; ze beloven een strakke bovengrens en een snel herstel voor wat erdoor komt.
CI-fleet-sizing — hoe de 12-minuten-suite goedkoop blijft
De volledige-suite-laag is waar de rekensom moet kloppen. Een naïeve implementatie roept de judge één keer aan per case-per-dimensie, draait ze sequentieel, en de rekening schaalt lineair met het aantal cases. Drie optimalisaties doen het meeste werk om hem hanteerbaar te houden:
Embedding-cache. De retrieval-context-fingerprint voor elke case in de golden dataset wordt gehasht; als de case niet is veranderd en de retrieval-index niet is veranderd, blijft de gecachte embedding staan en wordt de retrieval-stap overgeslagen. De hit-rate na de eerste stabiele week ligt in onze klantdeployments consistent boven de 90%.
Judge-batching. De gekalibreerde judge wordt aangeroepen met gestructureerde output, waarbij 8–16 cases per call worden gebatcht. De kosten per token van de judge blijven gelijk; de overhead per case daalt omdat de systeemprompt over de batch wordt geamortiseerd. De drempel voor veilig batchen wordt bepaald door de eigen gekalibreerde overeenstemming van de judge op die batchgrootte[2] — we meten dit tijdens de wekelijkse judge-kalibratiepass (post 7).
KV-cache-hergebruik over cases. Voor modellen waar dezelfde systeemprompt en tooldefinities aan elke call vooraf gaan, wordt de KV-cache voor dat prefix één keer per suite-run berekend, niet één keer per case[3]. Op open-weights-deployments is dit eenvoudig; op closed-API-modellen hangt het af van de prefix-caching-ondersteuning van de provider.
Het gecombineerde effect brengt de volledige suite ongeveer op de kostencijfers uit het laagcake-diagram hierboven. De exacte cijfers zijn intern, maar de ratio is de publieke claim: ~74% van de PR’s besteedt nul judge-dollars; ~22% besteedt centen; de resterende 4% besteedt een paar dollar voor het signaal met de hoogste betrouwbaarheid vóór uitrol dat we weten te produceren.
Shadow CI — zet hem aan zonder het team te breken
De enige fout die we teams het vaakst hebben zien maken, is een nieuwe gate van “uit” naar “blokkerend” zetten op dag één. De drempels zijn afgesteld op data van gisteren, het percentage valse positieven is onbekend, en de eerste keer dat de gate afgaat heeft het team geen kalibratie om te beoordelen of het echt is of vals alarm. De dienstdoende eval-engineer wordt opgepiept, de gate wordt uitgeschakeld, vertrouwen is weg, het project is dood.
De oplossing is shadow CI: draai de nieuwe gate niet-blokkerend gedurende twee weken, post het resultaat als bot-comment op elke PR, en bekijk wekelijks het percentage valse positieven voordat je hem op blokkerend zet. De Divinci-CI-runner heeft daarvoor een --shadow-flag. De PR-comment ziet er hetzelfde uit als de uiteindelijke blokkerende versie — zelfde diff-weergave, zelfde per-slice-uitsplitsing — alleen blokkeert hij de merge niet.
divinci ci run --layer=full --shadow --duration=14d --report-as=bot-commentAls het percentage valse positieven aanhoudend onder de 5% ligt over het venster, zetten we hem aan. Zo niet, dan scherpen we de per-slice-drempels aan, herkalibreren de judge en draaien opnieuw shadow. Hoe dan ook is het team niet overvallen door een nieuwe gate die op dag één afgaat.
Een GitHub Actions-workflow die echt componeert
Het stuk dat de laagcake aan je bestaande CI vastknoopt, draait in .github/workflows/llm-ci.yaml. De lagen zijn zo bedraad dat de goedkope snel falen en de dure alleen draaien wanneer ze nodig zijn — needs:-ketens en path-filtered triggers doen het werk[5].
name: LLM CI
on:
pull_request:
paths:
- 'prompts/**'
- 'config/models.yaml'
- 'eval/**'
- 'retrieval/**'
- 'manifests/**'
jobs:
contract:
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: actions/checkout@v4
- run: divinci ci contract --manifest manifests/staging.yaml --fail-fast
smoke:
needs: contract
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- run: divinci ci run --layer=smoke --post-pr-comment
env:
DIVINCI_API_KEY: ${{ secrets.DIVINCI_API_KEY }}
full:
needs: smoke
if: contains(steps.changes.outputs.paths, 'prompts/') || contains(steps.changes.outputs.paths, 'config/models.yaml')
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- run: divinci ci run --layer=full --post-pr-comment --gate
env:
DIVINCI_API_KEY: ${{ secrets.DIVINCI_API_KEY }}Drie dingen om op te merken. Lagen ketenen via needs:, dus smoke draait niet op een kapot contract en full draait niet op kapotte smoke. De full-job is path-filtered op de wijzigingen die daadwerkelijk een 12-minuten-run rechtvaardigen — een typo-fix in de README triggert de gate-suite niet. De --post-pr-comment-flag is wat de per-slice-diff zichtbaar maakt zonder GitHub te verlaten.
De debug-loop voor gefaalde PR’s
De andere helft van “de gate is afgegaan” is “laat me zien waarom.” Een regressiesuite-output van medical slice task-completion daalde 0,04 is niet actionable zonder de cases die het veroorzaakten. We tonen de vijf ergste per-slice-diffs in de PR-comment, met de oorspronkelijke input, de baseline-output, de kandidaat-output en de redeneer-trace van de judge. De debug-loop hoort seconden te duren, geen minuten:
# Haal de 5 ergste cases op die de medical-slice-gate op deze PR lieten afgaan
divinci ci diffs --pr 1247 --slice medical --dimension task_completion --top 5Dit is hetzelfde diagnostisch oppervlak als de zeven-stappenboom uit post 6, aangesloten op de CI-feedback-loop. De engineer die de PR opende, ziet het bewijs op case-niveau op de PR zelf; ze hoeven geen apart eval-dashboard te openen.
Versiebeheer-discipline — prompts, datasets, judges as code
Prompttemplates, golden datasets en judge-prompts leven allemaal in de repo, hash-gepind in het release-manifest. Het manifest is het enige object dat de suite verbindt met een specifieke reproduceerbare toestand:
# manifests/staging.yaml — elke CI-run hasht dit
release_id: rel-staging
model: { sha: 0c1f9…, weights: r2://models/custom-v7.2, open_weights: true }
prompt: { sha: c4a8e…, template: prompts/support/v3.4.j2 }
retrieval: { sha: b21f0…, index: r2://indices/kb-2026-04 }
judge: { sha: d8e21…, rubric: eval/rubrics/v7.yaml }
dataset: { sha: a90b1…, file: eval/datasets/golden-2026-04.jsonl }Als een CI-run een score post, wordt de score getagd met die manifest-hash. Wanneer een score beweegt, heeft de vraag “welke input is bewogen” een direct antwoord: diff het manifest, en de laag die afging vertelt je welke dimensie je het eerst moet bekijken. Dit is de lus die de pipeline met vier fasen uit post 1 en de vindex-receipt uit post 4 samen sluiten: het manifest is het audit-primitief waar al deze acht posts, in verschillende framings, naartoe hebben gewerkt.
Wat dit niet oplost
Dezelfde drie eerlijke beperkingen die we in elke post van deze serie hebben geschreven.
- CI test niet wat niet in de suite zit. Hoe slim de laagcake ook is, de enige regressies die hij vangt zijn die welke een case in de golden dataset zou hebben gemarkeerd. De replay-laag verzacht dit voor gedragsdrift, maar nieuwe queries die nog nooit zijn gezien ontsnappen nog steeds totdat ze in productie verschijnen. Het systeem moet worden gekoppeld aan productiemonitoring.
- Kostencijfers verschuiven met modelprijzen. Elk kostencijfer in deze post hangt af van judge-tokentarieven, embedding-tarieven en inferentietarieven die per kwartaal verschuiven. De ratio’s — 74% / 22% / 4%, 31% / 27% / 28% / 11% / 3% — zijn de dragende claims; de dollarcijfers zijn illustratief voor een moment in de tijd.
- Provider-side-checkpointwijzigingen blijven lastig. Wanneer een closed-API-provider stilletjes het model achter een stabiele naam bijwerkt, kan de contractlaag het niet vangen; alleen de gate-suite kan dat, en pas na het feit. We verzachten dit door expliciete checkpoint-identifiers vast te pinnen waar de provider dat ondersteunt, en door de dag waarop een checkpoint wordt aangekondigd te behandelen als een triggerend event voor een volledige-suite-rebaseline. We kunnen het onderliggende probleem niet voorkomen.
De serie afsluiten
Dit is post 8 van 8. De volledige boog:
- Hoe je een LLM CI/CD-pipeline bouwt met Divinci AI — de pipeline met vier fasen (Register / Gate / Roll / Observe) waarbinnen alles sindsdien heeft geleefd.
- 10 CI/CD-release-fouten in custom taalmodellen — de benoemde 2026-faalmodi, elk gekoppeld aan de fase die ze had moeten vangen.
- 12 QA- en release-management-capaciteiten voor LLM’s — de capaciteitenmatrix en het drie-kampen-Venn dat Divinci tegenover de alternatieven plaatst.
- Custom LM’s valideren en uitrollen in gereguleerde domeinen — de compliance-diepteduik, regulator-naar-fase-mapping, vindex-receipts.
- Geautomatiseerde LLM CI/CD-pipelines met instant rollback — de operationele laag, automatiseringsspectrum, auto-rollback-receipt.
- Hoe je custom-LLM QA-fouten in 7 stappen diagnosticeert — de diagnostische beslisboom; het model is ongeveer één keer op de zeven alerts het juiste antwoord.
- Geautomatiseerd regressietesten voor custom LLM’s in 2026 — slice-bewuste Spearman-gates, gekalibreerde judges, closed-loop productie-trace-replay.
- Deze post. De CI-infrastructuur die al het bovenstaande hanteerbaar maakt op elke PR.
De stukken componeren: het manifest is het audit-primitief, de gates vormen de veiligheidslaag, de diagnostische boom is de herstellus, de vindex-receipt is het externe anker, en de laagcake is wat het geheel betaalbaar maakt om op elke commit te draaien. Als jouw custom-LLM-release-proces deze vijf niet samen heeft, dan is dat het gat waar deze acht posts over gingen.
FAQ
Wat is de goedkoopste test die ik op elke commit kan draaien?
Een prompttemplate-render-check. Hij draait in milliseconden, vereist geen judge, vangt een verrassend deel van de breakages, en kost nooit een meetbare cent. Als je hem nog niet draait, is het het stukje CI met de hoogste ROI dat we kunnen aanbevelen.
Hoeveel mag ik verwachten dat een custom-LLM-CI-pipeline kost?
Centen per typische PR, lage eenpersoons-dollars per release-kandidaat-PR. De ratio hangt af van judge-prijsstelling en van welke fractie van je PR’s prompts of modelconfig aanraakt. Het 4%-aandeel release-kandidaten hierboven is typisch; voor producten met frequente prompt-iteratie stijgt het aandeel en klimt het gemiddelde dienovereenkomstig.
Moet ik de volledige suite op elke commit draaien?
Nee. Path-filter op PR’s die prompts, modelconfig, retrieval of eval-code aanraken. Voor alle andere wijzigingen is contract + smoke voldoende, en een wachttijd van 12 minuten op een README-typo verliest je het vertrouwen van het team binnen een sprint. De volledige suite is kostbaar; besteed hem aan wijzigingen die plausibel een kwaliteitsdimensie kunnen verschuiven.
Hoe introduceer ik een nieuwe gate zonder iedereen te breken?
Tweeweekse shadow-venster, niet-blokkerend. Stem drempels af op het percentage valse positieven dat tijdens de shadow wordt waargenomen. Zet pas op blokkerend wanneer het aanhoudende percentage valse positieven onder jouw tolerantie ligt (wij gebruiken 5%). Alles anders is hoe je een gate krijgt die iedereen heeft geleerd te negeren.
Wat is het enige getal dat ik moet volgen als ik er maar één volg?
De fractie bevestigde regressies die vóór productie wordt gevangen. Het histogram in deze post zet dat op ~97% in volwassen Divinci-deployments. De 3% die ontsnapt is waarom instant rollback bestaat. De 97% is waar de suite voor dient.
Referenties
- DORA / Google Cloud. "Accelerate State of DevOps — CI velocity, change-failure-rate and time-to-restore-service." De cross-industry-baselines die "12 minuten per PR is te traag" tot een verdedigbare claim maken in plaats van een mening.
- Zheng et al. "Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena." arXiv:2306.05685. Het empirische bewijs dat gebatchte LLM-als-judge-calls de kalibratie kunnen behouden op de batchgroottes die in de smoke- en full-lagen worden gebruikt — de reden dat de kostencijfers in deze post haalbaar zijn.
- Pope et al. "Efficiently Scaling Transformer Inference." arXiv:2211.05102. De KV-cache-hergebruik- en prefix-sharing-technieken die in de CI-fleet-sizing-sectie worden aangehaald.
- Pan, Tianpan. "The Semver Lie: how a minor LLM update broke production." 29 april 2026. De 2026 benoemde faalmodus voor aggregaat-only regressiesuites; de reden dat de CI-laagcake helemaal door slice-bewust is.
- GitHub. "GitHub Actions — chaining jobs with `needs:` and conditional execution." Het primitief waartegen de .yaml in deze post componeert.
- Zhang et al. "BERTScore: Evaluating Text Generation with BERT." arXiv:1904.09675. De heuristische semantische-similariteitsmetriek die wordt aangehaald als alternatief voor LLM-als-judge voor de goedkopere lagen; niet wat we draaien tijdens gate-tijd, maar nuttig in de contractlaag voor detectie van verboden frasen op schaal.
Ready to Build Your Custom AI Solution?
Discover how Divinci AI can help you implement RAG systems, automate quality assurance, and streamline your AI development process.
Get Started Today