Automation
Durable execution voor juridische AI: welke stack wint?
De pager ging om 03:14. Een worker pod die de nachtelijke contract-review queue draaide, ging OOM. Drie durable execution stacks, één auditklok. Welke overleeft de ochtend?

De pager ging af om 03:14 op een dinsdag. Een worker pod die de nachtelijke contract-review queue verwerkte voor een 27-koppige juridische dienstverlener in Den Haag, ging halverwege een clausule OOM. De partner die om 08:30 binnen zou lopen, verwachtte 380 dossiers getrieerd en een verdedigbaar log van elke clausule die de agent had aangemerkt voor menselijke review. De on-call engineer, die ook de enige engineer was, moest beslissen: de queue opnieuw afspelen vanaf het punt waar hij stierf, of accepteren dat 41 dossiers in een onbekende staat waren.
Dit is de vraag die durable execution moet beantwoorden. De agent-laag is interessant, maar de laag eronder, die zegt deze workflow heeft gedraaid, hier is elke input en elke output, en ik kan bewijzen dat hij exact één keer is uitgevoerd, is de laag die een AVG-audit overleeft en de laag die de engineer laat slapen.
We hebben deze laag in de afgelopen achttien maanden op drie verschillende manieren uitgerold. Hier is wat elke optie kost, wat elke optie aan bewijs oplevert, en wie je om 03:00 belt.
De vorm van de workload
Eerst de workload, voordat de vergelijking begint. Het kantoor verwerkt rond de 380 dossier-runs per werkdag, ongeveer 1.920 per week. Elke run ziet er zo uit:
- Dossier inlezen (meestal een PDF, soms een stapel)
- Chunken en routeren naar een specialistisch model
- Clausules extraheren, risico classificeren
- Als het risico over een drempel komt, escaleren naar een partner met een samenvatting van één alinea
- De audit-record wegschrijven
Stappen 1 tot en met 4 duren tussen de 18 seconden en 9 minuten. Stap 5 is de enige waar een toezichthouder om geeft. De AVG kan het niet schelen dat je een LLM hebt gebruikt. De AVG geeft erom dat je op verzoek kunt aantonen welke data is verwerkt, welk besluit is genomen, en dat je het besluit kunt reproduceren als het wordt aangevochten. De Autoriteit Persoonsgegevens is duidelijk geweest dat geautomatiseerde besluitvorming in cliëntdossiers een schoon bewijsspoor nodig heeft.
De lat ligt dus niet bij "heeft het gedraaid". De lat ligt bij "kun je het over veertien maanden opnieuw afspelen".
Optie 1: Temporal
Temporal is het zwaargewicht. Workflow-code is de bron van waarheid, event history wordt op elke recovery opnieuw afgespeeld, en het framework garandeert dat een functie die voltooid is, voltooid blijft, zelfs over deploys heen. Als je worker sterft, pakt een nieuwe de workflow op bij de volgende stap.
import { proxyActivities } from '@temporalio/workflow';
import type * as activities from './activities';
const { extractClauses, classifyRisk, escalateToPartner, writeAuditRecord } =
proxyActivities<typeof activities>({
startToCloseTimeout: '10 minutes',
retry: { maximumAttempts: 3 },
});
export async function reviewDossier(dossierId: string): Promise<void> {
const clauses = await extractClauses(dossierId);
const scored = await classifyRisk(clauses);
for (const c of scored.filter(c => c.risk >= 0.7)) {
await escalateToPartner(dossierId, c);
}
await writeAuditRecord({ dossierId, scored, ts: clauses.processedAt });
}
Wat het kost. Temporal Cloud rekent per action, waarbij een action een stap is in de opgenomen event history. De workflow van ons kantoor logt ongeveer 22 actions per dossier. 1.920 dossiers per week is ongeveer 169.000 actions per maand. Tegen de huidige gepubliceerde tarieven is dat in pure action-termen klein, maar storage- en active-workflow-kosten duwen de echte Temporal Cloud-rekening richting €200 tot €400 per maand op deze schaal. Zelf-hosten op een klein Kubernetes-cluster ruilt de licentie in voor engineer-tijd. De Temporal pricing docs zijn het waard om langzaam te lezen voordat je het sizet.
Wat je de toezichthouder schuldig bent. De event history van Temporal is het replay-log. Elke input, elke output, elke retry-poging is verzegeld en queryable. Een workflow die op 12 april 2026 draaide, kan op 30 september 2027 worden afgespeeld tegen dezelfde activity-code en hetzelfde waarneembare resultaat opleveren, mits je ook modelversies hebt vastgepind. Dat is precies wat een AVG-auditor wil.
Wie je om 03:00 belt. Jezelf. De failure modes van Temporal liggen diep. Een worker gaat OOM en jij moet herkennen dat Workflow Task Failure plus een non-determinism-code betekent dat je worker-versie is afgedreven van de opgenomen workflow-history. De foutmeldingen zijn prima, maar ze veronderstellen dat de lezer twee rustige weken in de docs heeft gestopt. Heeft niemand in je team dat gedaan, dan is het runbook "page de consultant".
Optie 2: Inngest
Inngest is het middengewicht. Het is event-driven, draait functies in stappen, en bewaart elke step output. Het voelt meer als serverless functies schrijven met magische retries dan als workflow-code schrijven.
import { inngest } from './client';
export const reviewDossier = inngest.createFunction(
{ id: 'review-dossier', retries: 3 },
{ event: 'dossier/uploaded' },
async ({ event, step }) => {
const clauses = await step.run('extract', () => extractClauses(event.data.id));
const scored = await step.run('classify', () => classifyRisk(clauses));
const high = scored.filter(c => c.risk >= 0.7);
if (high.length) {
await step.run('escalate', () => escalateToPartner(event.data.id, high));
}
await step.run('audit', () => writeAuditRecord({
dossierId: event.data.id, scored, modelVersion: event.data.modelVersion,
}));
},
);
Wat het kost. Inngest rekent per stap. Met acht tot tien logische stappen per dossier komen 1.920 runs per week uit op rond de 80.000 stappen per maand. Het Pro-plan dekt het meeste en de echte rekening is €50 tot €80 per maand op deze schaal (huidige prijzen). Zelf-hosten kan, maar is niet het standaardpad.
Wat je de toezichthouder schuldig bent. Step outputs worden opgeslagen en zijn vanuit het dashboard queryable, met een retentie die je zelf instelt. Replay werkt voor losse runs. De eerlijke kanttekening: "replay" in Inngest betekent je functie opnieuw draaien tegen je code, geen bit-voor-bit deterministische playback van de originele uitvoering. Voor de meeste AVG-cases is dat genoeg, want de juridische vraag is "kun je laten zien wat er is gebeurd en hetzelfde antwoord produceren", niet "zijn dit dezelfde bytes". Pin je modellen, of je komt het tweede deel van die vraag niet door.
Wie je om 03:00 belt. Vaak niemand. Het dashboard laat zien dat de functie faalde, op welke stap, op welke input, en geeft je een replay-knop. Het OOM-scenario is meestal een non-event: de functie wordt gepauzeerd, herstart op een gezonde worker, en pakt op bij de gefaalde stap.
Optie 3: BullMQ + Redis
De zelfgebouwde optie. BullMQ is een volwassen Node job queue op Redis, snel en gratis. De durability-laag schrijf je zelf.
import { Queue, Worker } from 'bullmq';
const connection = { host: 'redis', port: 6379 };
export const reviewQueue = new Queue('dossier-review', { connection });
new Worker('dossier-review', async (job) => {
const { dossierId } = job.data;
await job.updateProgress({ step: 'extract' });
const clauses = await extractClauses(dossierId);
await job.updateProgress({ step: 'classify' });
const scored = await classifyRisk(clauses);
const high = scored.filter(c => c.risk >= 0.7);
if (high.length) {
await job.updateProgress({ step: 'escalate' });
await escalateToPartner(dossierId, high);
}
await writeAuditRecord({ dossierId, scored, jobId: job.id });
}, { connection, concurrency: 8 });
Wat het kost. Een managed Redis-instance van Upstash of Redis Cloud met genoeg geheugen voor een audit-venster van veertien maanden kost €30 tot €80 per maand. Worker-compute betaal je toch al. De cash-rekening is de kleinste van de drie.
Wat je de toezichthouder schuldig bent. Hier wordt BullMQ duur in een andere valuta. Out of the box verwijdert BullMQ voltooide jobs na een instelbaar venster. AVG-verdedigbare logging betekent dat je een eigen append-only audit-tabel schrijft (Postgres met WAL-retentie, of S3 met object lock) en daar transactioneel naar wegschrijft bij elke stap. Sterft de worker tussen stap 3 en de audit-write in, dan moet jij dat detecteren en reconciliëren. Dat is een project, geen config flag.
Wie je om 03:00 belt. Je hebt de durability-laag zelf geschreven, dus elke failure mode is van jou. OOM om 03:14 is jouw probleem om te detecteren (Sentry, Prometheus, een heartbeat watchdog), jouw probleem om te herstellen (job-retry-semantiek die jij hebt geconfigureerd), en jouw probleem om aan te tonen dat het de audit-log niet heeft beschadigd. Er is geen dashboard dat je niet zelf hebt gebouwd.
De score bij 1.920 runs per week
De drie maatstaven in één plaatje:
- Kosten per workflow. BullMQ wint op cash (€30 tot €80), Inngest zit er dicht achter zodra je het dashboard meerekent dat je anders zelf zou bouwen (€50 tot €80), Temporal Cloud is significant duurder (€200 tot €400). Self-hosted Temporal kantelt de kosten naar engineer-tijd, en op deze grootte is dat de duurdere valuta.
- AVG-verdedigbare replay. Temporal is de enige waar replay het product is. Inngest brengt je het grootste deel van de weg en is genoeg voor een partner-escalatie-audit als je modellen pint. BullMQ levert je gratis niets en levert je alles als je het bouwt.
- Eigenaarschap van het 03:00-runbook. Inngest geeft je een dashboard en een replay-knop. Temporal geeft je een krachtige CLI en de aanname dat je hem hebt gelezen. BullMQ geeft je een Redis CLI en de sleutels van je eigen koninkrijk.
Geen van deze opties lost "de LLM gaf deze keer een ander antwoord" op. Hangt je replay-verhaal af van modeldeterminisme, pin dan de modelversie expliciet in elke activity, log hem in het audit-record, en laat latest nooit bij de workflow-code komen. De durable laag kan alleen afspelen wat je hem hebt verteld.
Wat we daadwerkelijk zouden kiezen
Voor 27 mensen, geen fulltime platform engineer, en AVG-blootstelling die meer kost dan de hosting-rekening: Inngest. Het dashboard om 03:00 is meer waard dan de €200 per maand die je zou besparen op Temporal Cloud, en het audit-verhaal komt dichter bij regulator-niveau dan dat van BullMQ uit de doos.
Voor 200-plus mensen, een toegewijd platform team, en contracten onder zes retentieregimes: self-hosted Temporal. De complexiteit doet er niet meer toe als drie mensen in het team het al kennen.
Voor een prototype waar de AVG-blootstelling nog niet live is en je nog uitzoekt wat de workflow überhaupt is: BullMQ. Stap over op Inngest in de week dat je je eerste gereguleerde klant tekent. Sla die stap niet over.
Er loopt deze maand een zijdelings verwante thread op Hacker News met de vraag of iemand hosted modellen heeft vervangen door een lokaal model voor dagelijks coderen. Hetzelfde instinct, andere laag in de stack: als de toezichthouder je publiek is, wordt "we draaien het zelf" een verdedigbaar antwoord, zelfs op MKB-schaal. Het bijhouden waard als de risicobereidheid van je kantoor verschuift.
Het audit-record is het product
De fout die we teams nu twee keer hebben zien maken, is de durable execution-laag behandelen als loodgieterswerk en het audit-record als een logging-issue. Het is hetzelfde artefact. De workflow engine schrijft het audit-record door simpelweg te draaien. Kies je een tool die het record dat jij schuldig bent niet schrijft, dan heb je geen geld bespaard. Je hebt het werk verplaatst naar een tweede systeem dat zal gaan afdrijven.
Toen we de contract-review AI-agent bouwden voor dit kantoor in Den Haag, was het probleem niet welke durable engine we moesten kiezen. Het was dat het audit-record dat de AVG eigenlijk wilde, rijker was dan wat deze tools standaard loggen. We hebben uiteindelijk een dunne transactionele audit-laag bovenop de step outputs van Inngest geschreven en elke modelversie binnen de activity zelf vastgepind.
Het kleinste wat je vandaag kunt doen: trek de worker-logs van de afgelopen 30 dagen en grep op OOM en ECONNRESET. Zijn er meer dan twee van elk, dan is je durable execution-verhaal niet af, ongeacht op welke van deze drie je zit.
Kern
Kies durable execution op wie de pager om 03:00 beantwoordt, niet op de mooiste pricing-pagina. De vraag over audit-replay komt op de tweede plek, nooit eerst.
FAQ
Is Temporal overkill voor een kantoor van 27 mensen?
Meestal wel. Op Temporal Cloud is de rekening prima, maar het runbook gaat ervan uit dat iemand twee weken in de docs heeft gestopt. Self-hosted is nog zwaarder. Inngest past eerlijker bij deze teamgrootte.
Kan BullMQ een AVG-audit doorstaan?
Ja, als je een append-only audit-tabel bouwt die transactioneel met elke stap wordt geschreven en je hem bewaart voor het vereiste venster. BullMQ zelf doet dit niet. Dat ga jij doen.
Wat telt als verdedigbare replay onder de AVG?
Kunnen aantonen welke data is verwerkt, welk besluit is genomen, en het besluit kunnen reproduceren als het wordt aangevochten. Pin modelversies, of replay valt om op het tweede punt.
Ondersteunt Inngest self-hosting?
Ja, maar het standaardpad is hosted en daar is het dashboard-verhaal scherper. Voor een MKB zonder platform team is de hosted tier de realistische keuze.
Hoe handel je de OOM van 03:00 in de praktijk af?
Op Inngest pauzeert de functie en hervat op een gezonde worker. Op Temporal hervat de workflow zodra een worker terug is, maar je hebt een non-determinism-check nodig. Op BullMQ heb je de recovery zelf geschreven, dus hij werkt zoals jij hem hebt geschreven.