← Blog

AI agents

Post-mortem AI-agent: een vendor-flag at de output op

Een Antwerpse legaltech van 38 mensen zag haar contract-review-agent samenvattingen van twee zinnen leveren in plaats van twaalf. De oorzaak: een workspace-flag die niemand beheerde.

Jacob Molkenboer· Oprichter · A Brand New Company· 12 jun 2026· 9 min
Messing relaisschakelaar op ivoorkleurig linnen naast gevouwen memo met groene tab, potloodstompje en paperclip.

Om 14:08 Brusselse tijd, op een woensdag eind mei, dropte een paralegal bij Clausemark, een legaltech van 38 mensen in Antwerpen, een distributieovereenkomst van 14 pagina's in de contract-review-queue. De agent leverde een samenvatting. Twee zinnen. Het hadden er twaalf moeten zijn.

Haar eerste reactie was de juiste: ze runde het contract opnieuw. Zelfde resultaat. Ze probeerde een kortere NDA. Twee zinnen. Ze liep naar de engineeringkamer.

Wat hierna komt is de post-mortem van de zes uur die volgden, geschreven met toestemming en met een paar namen veranderd. De trigger was een wijziging in het retention-beleid van de vendor die die ochtend in productie-workspaces landde. De schade was geen dataverlies. De schade was stille kwaliteitsregressie op elk contract dat de agent sinds 06:00 had aangeraakt.

Hoe de storing aan het licht kwam

Clausemark draait een contract-review-agent op een frontier model, gerouteerd via een dunne interne wrapper die ze cm-review noemen. Juristen plakken clausules, de agent extraheert verplichtingen, signaleert non-standaard taal en produceert een samenvatting per clausule. Gemiddelde output: rond de 1.800 tokens.

Tussen 06:14 en 13:52 gingen 117 contracten door de pipeline. Geen één faalde. Allemaal kwam er een samenvatting uit die middenin een clausule stopte. De QA-loop op het dashboard checkt token-count, schema-validiteit en weigeringstaal en markeerde alle 117 als groen. De output was valide JSON. De samenvattingen waren alleen kort.

Dit is de canonieke AI-agent-bug: niets crashte, geen exception kwam naar boven, geen alert ging af. Een senior associate las toevallig een van de samenvattingen voor hij die naar een klant stuurde, en zag dat ze eindigde bij clausule 4 van 11. Hij flagde het op Slack. De paralegal aan het begin van de queue bevestigde dat ze de hele ochtend al output had gezien die raar aanvoelde, maar ze had aangenomen dat de contracten simpeler waren dan normaal.

Waarschuwing

Als de enige health check op je agent “kwam er valide JSON terug” is, monitor je de agent niet. Dan monitor je de serializer.

De eerste 90 minuten

Het engineeringteam had binnen tien minuten drie theorieën:

  • De vendor heeft 's nachts een nieuwe model-snapshot gepusht en de system prompt brengt het volledige schema niet meer betrouwbaar naar boven.
  • Hun orchestrator (een zelfgebouwde laag bovenop LangGraph) loopt tegen een recursielimiet aan en stopt te vroeg.
  • Iemand heeft de prompt template aangepast en de migratie niet gepusht.

Alle drie waren fout, maar alle drie werden eerst gecheckt omdat alle drie vertrouwd waren. De echte oorzaak zat op een plek waar niemand sinds de eerste oplevering naar had gekeken: de workspace-settings-pagina aan de kant van de model-vendor.

De vorm van de regressie

Om 15:30 had de lead engineer (Joren) een schone reproductie. Hij haalde de ruwe API request voor een bekend-goed contract uit de logs van de vorige dag en runde 'm byte voor byte tegen productie. De response kwam terug op 312 output-tokens. Zelfde request, zelfde model, zelfde prompt, elf uur ertussen. Afgekapt.

Daarna runde hij dezelfde request tegen zijn eigen console met een andere API key. Volledige output. 1.847 tokens.

De variabele was de workspace, niet de request. Die ene observatie sneed de zoekruimte doormidden.

Root cause: een flag die niet van ons was

Ergens in de ochtend was de zero_data_retention-flag van de workspace stilletjes omgeklapt van true naar false. De reden was structureel. De model-vendor had een policy-wijziging gepubliceerd die een retention-floor van 30 dagen verplicht stelde voor de modelfamilie die Clausemark gebruikte. Workspaces die eerder voor zero retention hadden gekozen, kregen die flag uitgezet toen de policy van kracht werd, omdat zero retention voor die modellen niet meer toegestaan was. De wijziging was aangekondigd. De wijziging was niet luid.

Dat alleen is nog geen bug. De bug zat in de wrapper-code van Clausemark.

def build_request(contract: str, ws: Workspace) -> dict:
    if ws.privacy_mode:
        return {
            "model": "frontier-3.7",
            "max_tokens": 4096,
            "system": HIGH_PRIVACY_PROMPT,
            "messages": [{"role": "user", "content": contract}],
        }
    # Fallback for non-privacy workspaces. Predates the current model.
    return {
        "model": "frontier-3.7",
        "max_tokens": 512,
        "system": STANDARD_PROMPT,
        "messages": [{"role": "user", "content": contract}],
    }

Twee jaar eerder, toen Clausemark voor het eerst werd opgetuigd, ging de engineer die build_request schreef ervan uit dat privacy-modus altijd aan zou staan. Het was een harde eis van een van hun drie vlaggenschipklanten. De non-privacy branch was een “voor de zekerheid”-fallback met een cap van 512 tokens, gekopieerd uit een ouder proof-of-concept. Hij was in productie nog nooit geraakt. Tot 06:14 die ochtend.

Toen de workspace-flag omklapte, ging elke request via de fallback branch. 512 output-tokens is genoeg voor een valide JSON-envelope en de eerste paar clausules van een samenvatting. Dus het schema klopte. De inhoud was een kwart van wat de juristen verwachtten.

De fix, in twee delen

De directe fix kostte elf minuten:

def build_request(contract: str, ws: Workspace) -> dict:
    return {
        "model": "frontier-3.7",
        "max_tokens": 4096,
        "system": HIGH_PRIVACY_PROMPT if ws.privacy_mode else STANDARD_PROMPT,
        "messages": [{"role": "user", "content": contract}],
    }

De structurele fix kostte de twee weken erna en is de reden dat deze post bestaat. Elf minuten herstelden de output-kwaliteit. Die twee weken zorgden ervoor dat de volgende vendor-verrassing opgemerkt zou worden voordat juristen de kanarie waren.

Wat we hebben veranderd aan hoe de agent draait

We hebben dit samen met het team van Clausemark doorgelopen en hun reliability-laag opnieuw opgebouwd. Vier wijzigingen telden echt.

Output-shape-checks, geen output-validity-checks

“De JSON parste” is geen kwaliteitssignaal. De nieuwe health check telt de clausules in het input-contract en vergelijkt met de clausules die in de output samengevat worden. Een samenvatting die 4 van de 11 clausules dekt is nu een P1-alert, geen groen vinkje. We samplen ook 2% van de output naar een tweede model dat volledigheid scoort op een schaal van 1 tot 5. Een aanhoudende daling in het rollende 30-minuten-gemiddelde triggert een alert.

Maak vendor-side state observable

De workspace-settings waren een black box. We hebben een dagelijkse job toegevoegd die de workspace admin API van de vendor aanroept, de workspace-configuratie ophaalt (model-toegang, retention-setting, rate limits, default headers), die diffe-t tegen een ingecheckte YAML, en een pull request opent als er iets gedrift is. De diff die dit incident op dag één had gevangen ziet er zo uit:

 workspace:
   name: clausemark-prod
   models:
     - frontier-3.7
-  zero_data_retention: true
+  zero_data_retention: false

Hetzelfde patroon werkt voor elke vendor met een leesbare admin-API. Voor vendors zonder API: maak wekelijks een screenshot van de settings-pagina in een versioned folder. Niet perfect, maar beter dan niets.

Behandel vendor-policy-wijzigingen als deploys

De retention-wijziging stond al weken op de changelog van de vendor voordat hij landde. Niemand bij Clausemark had het gelezen, omdat niemand verantwoordelijk was om het te lezen. Er staat nu wekelijks op vrijdag een uur in de agenda van één engineer wiens enige taak in dat uur is: vendor-changelogs lezen (model-provider, auth-provider, database-vendor) en tickets aanmaken voor alles wat gedrag, prijzen of data-residency raakt. Een policy-wijziging die je vier weken vooraf kunt lezen, is een policy-wijziging waarop je kunt deployen, in plaats van een policy-wijziging die jou deployt.

Dode branches opruimen

De 512-token fallback was een branch die in twee jaar niet in productie had gedraaid. Hij kwam toch elke keer door code review heen als iemand het bestand aanraakte, omdat niemand een gegarde “voor de zekerheid”-pad in twijfel trok. We doen nu per kwartaal een review van elke conditionele branch in de agent-pipeline die in de logs van de afgelopen 90 dagen nul hits heeft. De meeste gaan weg. De branches die overleven krijgen een comment die uitlegt waarom ze bestaan en wat er moet veranderen voordat ze weg kunnen.

Takeaway

Een dode branch in een AI-agent-pipeline is een geladen pistool aan de muur. Vroeg of laat haalt een vendor-flag de trekker over.

De bredere context

Dit is geen geïsoleerde storingssoort. Het patroon herhaalt zich in elk agent-incident dat we hebben gelezen of meegemaakt. “De agent deed iets raars” is bijna nooit de juiste framing. De agent deed precies wat de omringende code, prompts, tool-definities en workspace-settings hem opdroegen. De omringende code was fout. De omringende code had een pad waar in twee jaar niemand over had nagedacht. De omringende code wist niet dat zijn eigen vendor de vloer verplaatst had.

Frameworks zoals Burr bestaan deels hierom. Hun pitch (bouw agents als inspecteerbare state machines, met elke transitie gelogd en replaybaar) gaat niet over beter modelleren. Hij gaat over het kunnen beantwoorden van de vraag “wat zag de agent, en wat besloot hij” twaalf uur na dato, als er een paralegal binnenkomt met een samenvatting van twee zinnen en vraagt waarom.

Wat dit incident werkelijk kostte

Niets ging naar een klant. De associate die de afkapping ving, ging terug door de 117 contracten, runde ze elk opnieuw door de gefixte pipeline en vergeleek. Elf zaten al in uitgaande e-mailconcepten; geen één was verstuurd. De voor de gebruiker zichtbare kosten waren nul. De interne kosten waren ongeveer 40 engineer-uren en een lastig gesprek over waarom een vendor-policy-flip bijna acht uur lang stilletjes de output-kwaliteit kon verlagen voordat iemand het opmerkte.

Dat gesprek is het werkelijke artefact. De fix van elf minuten heeft niets belangrijks veranderd. Het gesprek heeft veranderd hoe Clausemark denkt over de grens tussen code die zij bezitten en state die een vendor bezit.

Wat je vanmiddag kunt doen

Toen we de contract-review-pipeline voor Clausemark bouwden, was het ding waar we tegenaan liepen dat elke AI-agent uiteindelijk afhankelijk is van een stukje state dat wij niet beheren: een vendor-flag, een model-versie, een workspace-policy. We hebben dat opgelost door die state een first-class deploy-artifact te maken, gediffed en gereviewd zoals code. Als je AI-agents in productie draait, is het kleinste wat je vandaag kunt doen je vendor admin console openen, een screenshot maken van de workspace-settings, het bestand in een folder droppen en een agenda-herinnering zetten om het over een maand opnieuw te doen. Als er iets veranderd is zonder bijbehorend ticket in je tracker, heb je net je volgende P1 gevonden.

Kern

Een dode branch in je agent-code is een geladen pistool aan de muur. Vroeg of laat haalt een vendor-flag de trekker over.

FAQ

Wat veroorzaakte de afgekapte samenvattingen precies?

Een workspace-privacy-flag werd stilletjes uitgezet toen het nieuwe 30-daagse retention-beleid van de model-vendor van kracht werd. Het fallback-codepad gebruikte een output-cap van 512 tokens die al twee jaar dood lag.

Waarom ving de health check het niet op?

De check valideerde JSON-schema, token-count en weigeringstaal. Een valide JSON-envelope met één clausule erin kwam door elke test heen. Output-volledigheid werd nooit gemeten.

Hoe monitor je voor dit soort stille regressie?

Diff de vendor-workspace-settings tegen een ingecheckte YAML op een dagelijkse cron, en voeg een output-volledigheidscheck toe die input-structuur vergelijkt met output-dekking, niet alleen schema-validiteit.

Kan dit ook bij andere LLM-vendors gebeuren?

Ja. Elke vendor-flag die modelgedrag, output-limieten of routing buiten je deploy-pipeline om verandert, kan een stille regressie veroorzaken. Dit type bug is vendor-agnostisch.

Zijn er klantgegevens gelekt?

Nee. De flag-flip veranderde retention, geen toegang. De kosten waren kwaliteitsverlies op interne output, die werd opgevangen voordat één contract-samenvatting naar een klant ging.

ai agentscase studyoperationsarchitectureworkflowbusiness

Iets bouwen?

Start een project