Case study
LLM-routering: de dag dat een demo 40 minuten vastliep
Een live demo liep veertig minuten vast toen de rate limits van onze klant krompen. Zo bouwden we de KYC-samenvatter om naar een LLM-fallback-keten met vier routes.

11:40 op een dinsdagochtend. Het Rotterdamse team zit veertig minuten in een inkoopgesprek met een Belgische retailbank. Drie mensen aan prospectkant, twee aan hun kant, en het product doet wat het altijd doet. Het trekt een paspoortscan, twee energierekeningen en een transactie-CSV samen tot een gestructureerde KYC-samenvatting in onder de negen seconden. Dan staat het scherm stil.
Hun CTO ververst. Niets. De voortgangsbalk blijft op 12% staan. De compliance officer van de prospect maakt een beleefde grap over de demogoden. Veertig minuten later eindigt het gesprek met "stuur ons later maar een opname". De CTO opent de statuspagina van Anthropic en ziet het groene bolletje. Hij opent zijn eigen logs en ziet de waarheid: elke call naar claude-sonnet-4 geeft sinds 11:38 een 429 terug.
Ze zijn een compliance-tech SaaS van zeventien mensen. Twaalfduizend KYC-samenvattingscalls per week. Eén LLM-provider. Ze zaten al veertien maanden op de directe Anthropic-API en hadden er nog nooit een demo door verloren. Tot nu.
De ochtend van de capaciteitsklif
Je mag een mening hebben over de oorzaak. Anthropic levert tegenwoordig een steeds groter deel van zijn capaciteit via cloudpartners, met AWS Bedrock dat cross-region inference draait voor Claude-modellen en Google Vertex AI dat zijn eigen variant draait. De rekensom op een direct-API-only-architectuur wordt sinds eind 2025 ongunstiger, wat je kunt aflezen aan de tier limits en de response headers, ook al heeft geen leverancier het zwart op wit gezet. We doen niet alsof we weten welke kant van de meeting de nieuwe getallen heeft bepaald. Wat we wel weten: een single-vendor LLM-afhankelijkheid, zelfs op het beste model in de markt, is een inkooprisico dat elk kwartaal dat je het negeert harder doortikt.
De volgende ochtend belden de oprichters ons. Ze hadden twee opties en dat wisten ze. Onderhandelen over hogere directe-API-limieten en hopen, of de afhankelijkheid splitsen over meerdere providers en routen. Ze kozen het tweede. We hadden drie weken voor de volgende democyclus.
De vorm van de router die we kozen
Vier routes. Op drie ervan dezelfde Claude-familie, op de vierde een ander model als harde fallback.
- Primair. Directe Anthropic-API, claude-sonnet-4. Het snelste pad als die gezond is.
- Secundair. AWS Bedrock, claude-sonnet-4 met cross-region inference over eu-central-1 en us-east-1. Onafhankelijke rate limits en een onafhankelijk control plane.
- Tertiair. Vertex AI in europe-west4, dezelfde Claude-familie, gefactureerd via het bestaande Google Workspace-contract van het team.
- Harde fallback. Zelf-gehoste Qwen3-32B op één H100 in een Hetzner-rack in Helsinki, geserveerd door vLLM. Lagere kwaliteit, lagere latency floor, nooit rate-limited.
De router zit tussen de applicatie en elk van de vier in. Hij draait een circuit breaker op iedere route, een budget van vijf seconden per poging, en een hard totaalbudget van tweeëntwintig seconden voor de hele keten. De vorm, in TypeScript:
// router.ts
type Route = "anthropic" | "bedrock" | "vertex" | "qwen3";
const CHAIN: Route[] = ["anthropic", "bedrock", "vertex", "qwen3"];
export async function summariseKyc(payload: KycPayload) {
const errors: Partial<Record<Route, unknown>> = {};
const started = performance.now();
for (const route of CHAIN) {
if (breaker[route].isOpen()) continue;
if (performance.now() - started > 22_000) break;
try {
const res = await withTimeout(5_000, callRoute(route, payload));
breaker[route].recordSuccess();
metrics.record({ route, ok: true, ms: res.elapsed });
return res.summary;
} catch (err) {
breaker[route].recordFailure(err);
errors[route] = err;
metrics.record({ route, ok: false, err: String(err) });
}
}
throw new AllRoutesFailed(errors);
}
De hele router is tweehonderd regels. Het zware werk zat nooit in de router.
Twaalfduizend calls per week scoren
Drie weken lang lieten we elke productiecall in shadow mode parallel meelopen op alle vier de routes. Productie gebruikte het bestaande directe-API-pad. De shadow runner registreerde latency, tokenkosten en een deterministische kwaliteitsscore op elke parallelle response.
KYC-output kun je niet door een model laten beoordelen. Een model dat door een model wordt beoordeeld is gewoon een recursie met extra stappen. Het team bouwde een strenge grader: tweehonderd handmatig gelabelde cases die elke nacht tegen iedere route draaien, plus een structurele validator die elke output afkeurt waarin een verplicht veld ontbreekt of waarvan een boolean buiten zijn toegestane enum valt. Alles wat meer dan twee standaarddeviaties onder de productie-baseline zakte, paged automatisch de on-call.
Over ongeveer zesendertigduizend shadow calls per route stabiliseerden de cijfers naar een herkenbare vorm:
| Route | p50-latency | p95-latency | boolean-nauwkeurigheid | gem. kosten / call |
|---|---|---|---|---|
| Anthropic direct | 2,1s | 4,7s | 99,4% | €0,018 |
| Bedrock (cross-region) | 2,6s | 5,9s | 99,3% | €0,016 |
| Vertex (europe-west4) | 3,1s | 7,2s | 99,1% | €0,017 |
| Qwen3-32B (Helsinki) | 1,4s | 3,2s | 96,8% | €0,004 |
Dit zijn de metingen van het team op hun specifieke KYC-payload, geen universele getallen. Jouw workload zal ze verschuiven. De vorm is echter consistent met elke vergelijkbare router-rebuild die we hebben opgeleverd: cross-region Bedrock is bij p50 ongeveer een halve seconde langzamer dan de directe API en op volume merkbaar goedkoper. Vertex is de langzaamste van de drie Claude-routes en het meest variabel, vooral omdat Europees verkeer nog steeds met regelmaat naar us-central1 wordt gerouteerd.
Het kwaliteitsgat van Qwen3
De Helsinki-box was met afstand de goedkoopste regel in de keten, een factor vier onder de rest, en warm de snelste. Hij was ook de slechtste op de vier booleans die ertoe doen. Op de tweehonderd handmatig gescoorde cases kwam Qwen3-32B op 96,8% overeen met de menselijke reviewer op de boolean-velden. Claude op elk van de drie gemanagede routes haalde 99,1% of beter. Drie procentpunten klinkt klein. Op twaalfduizend calls per week zijn dat driehonderdzestig verkeerde beslissingen per week.
Daarom gebruikten we Qwen3 niet als kwaliteitsequivalente fallback. We gebruikten hem als degraded mode. Valt de keten helemaal door, dan krijgt de output een vlag "verifieer voor goedkeuring" en gaat sowieso naar de menselijke reviewqueue, los van wat de booleans zeggen. De samenvatting is nog steeds nuttig. Hij wordt alleen niet meer vertrouwd.
Behandel je een kleiner open-weights model als drop-in fallback voor een frontier model op een gestructureerde-output-taak, dan lever je stilletjes een kwaliteitsregressie. Markeer degraded output expliciet en route die door een ander downstream-pad. De router is het makkelijke deel. Het contract op de output is het deel dat breekt.
De terugkerende Hacker News-thread over het vervangen van Claude of GPT door een lokaal model voor dagelijks coderen is om dezelfde reden interessant. De meeste mensen die het proberen komen niet terug, en degenen die wel terugkomen bedoelen meestal "ik heb de auto-complete naar een 7B-model gezet en de zware reasoning op de API gehouden". Dat is een router, geen vervanging. De eerlijke versie van "ga lokaal" is bijna altijd een gelaagde keten met een klein, helder klusje voor het lokale model.
De belasting van prompt portability
Het dure deel van de rebuild was niet de router. Het waren de prompts.
De samenvatter was dertien maanden van opgestapelde prompt-tweaks tegen Claude. De meeste van die tweaks waren XML-achtige structurele cues die Claude prachtig leest en Qwen3 als decoratie leest. Claude op Vertex was bijna identiek aan de directe API. Op Bedrock was hij bijna identiek, met twee kleine verschillen in hoe stop sequences omgaan met gestructureerde output en hoe Bedrock tool-use-fouten naar boven brengt. Qwen3 had een aparte prompt nodig, een aparte output parser en een aparte eval threshold.
Een concreet voorbeeld. De oorspronkelijke prompt vroeg Claude om output in deze vorm:
<kyc_summary>
<risk_flags>
<politically_exposed>false</politically_exposed>
<sanctions_hit>false</sanctions_hit>
</risk_flags>
<narrative>...</narrative>
</kyc_summary>
Claude hield zich er 99,9% van de tijd aan. Qwen3 plaatste de sluit-tag regelmatig binnen attributen of liet hem bij lange output helemaal weg. We zetten alles om naar JSON met een strikte schema-validator aan de uitgang, en accepteerden dat Claude nu ongeveer 1% langzamer was om dat te produceren. De router zou niet werken zonder het schema. Het schema zou niet werken zonder de herschrijving. Die volgorde kostte langer dan de router, de IAM-rotatie en de Vertex-onboarding bij elkaar.
De les is weinig glamoureus. Elke prompt die je voor één provider schrijft, wordt een privécontract met die provider. Hoe eigenaardiger de prompt, hoe hoger de switching cost.
Wat er in productie veranderde
Drie dingen, in volgorde van hoeveel ze uitmaakten.
Ten eerste werd de router het enige pad. Geen applicatiecode roept nog rechtstreeks een provider aan. De router handhaaft het timeout-budget, de circuit breaker, het eval-contract op de output en het kostenoverzicht. De bypass eruit halen kostte een week grep en een strenge code review.
Ten tweede verschoof het team zijn facturatierelaties. De directe API bleef. Voor Bedrock kwam er een Enterprise Discount Program-offerte en een aparte AWS-account voor de workload, wat de goedkoopste manier is om bij dit volume over Bedrock-tarieven te onderhandelen. Vertex ging op een committed-use-korting via het bestaande Google Workspace-contract. De Helsinki-box was een eenmalige investering, afgeschreven over vierentwintig maanden. De totale infrastructuuruitgaven aan LLM-inference daalden op de nieuwe blended mix met ongeveer achttien procent.
Ten derde kreeg de demo-omgeving een kill switch. Voor elk salesgesprek kan de SDR de router voor de komende twee uur in "Bedrock primair"-modus zetten. De redenering is hard maar eerlijk. Als er opnieuw een tier-compressie op de directe Anthropic-API valt, mag de demo niet de plek zijn waar we daar achter komen.
De cijfers zes weken later
Sinds de rebuild op 5 juni live ging, is de keten precies vier keer helemaal doorgevallen naar Qwen3 op drieënzeventigduizend vierhonderd calls. Drie daarvan waren een Vertex regio-outage die GCP uiteindelijk op zijn statuspagina zette. Eén was een Bedrock IAM-rotatie die we zelf veroorzaakten.
De blended kosten per KYC-samenvatting daalden van €0,018 naar €0,014. De p95-latency steeg met 0,4 seconde, omdat het timeout-budget op de eerste poging nu binnen een keten leeft, niet op één call. Niemand in het salesteam heeft de latency opgemerkt. Twee prospects hebben in hun security review gevraagd of het product een single-vendor LLM-afhankelijkheid heeft. Het antwoord is nu nee, en het team kan een diagram overhandigen.
Heb je iets in productie draaien tegen één LLM-provider, neem deze week een uur om een tweede route achter een feature flag te zetten. De meeste dagen heb je hem niet nodig. De dag dat het wel zo is, heb je de integratie al geschreven en de eval runner erop gericht. Toen we dit voor het Rotterdamse team bouwden, zat het langste werk niet in de router of de providers. Het zat in het gelijk laten gedragen van de prompts over drie modelfamilies heen. Loop je daar zelf vast op je AI-agents, dan hebben we het inmiddels vaak genoeg gedaan om te weten welke shortcuts echt zijn.
Kern
Een single-vendor LLM-afhankelijkheid is een inkooprisico. Bouw de router voordat de demo vastloopt, niet erna. Het werk aan prompt portability is wat de tijd kost.
FAQ
Waarom niet gewoon hogere directe-API-limieten met Anthropic onderhandelen?
Dat deden ze ook, parallel. De router is de aanvulling, niet de vervanging. Hogere limieten helpen op een normale dag. Een multi-provider-keten helpt op de dag dat de limieten zonder waarschuwing veranderen.
Heeft Qwen3 Claude voor productiecalls vervangen?
Nee. Qwen3 handelt alleen cases af die door alle drie de Claude-routes heen vallen, en die output wordt gemarkeerd voor verplichte menselijke review, ongeacht wat het model zegt.
Hoe lang duurde de hele rebuild?
Drie weken voor de router, de shadow runner en de uitrol. Het werk aan prompt portability om output uitwisselbaar te maken tussen providers kostte langer dan de routinglaag zelf.
Wat deden de blended kosten per call na de rebuild?
Ze daalden van €0,018 naar €0,014. Bedrock EDP-tarieven en Vertex committed-use zijn bij twaalfduizend calls per week iets goedkoper dan de directe API, en de Qwen3-bodem trekt het gemiddelde verder omlaag.
Is een zelf-gehost model de moeite waard voor een LLM-afhankelijke SaaS?
Alleen als vangnet in degraded mode, niet als kwaliteitsequivalente fallback. De economie van één H100 werkt als hij de derde of vierde route in de keten is, niet de primaire.