Anthropic verontschuldigde zich voor verborgen model-gedrag, en dat raakt ook jouw SaaS
Gisteren bracht The Verge een opmerkelijk bericht naar buiten: Anthropic bood publiekelijk excuses aan voor het stiekem inbouwen van verborgen guardrails in Claude Fable 5. Zonder gebruikers of ontwikkelaars te waarschuwen, werd het model stilletjes gethrottled. Onderzoekers en bouwers die op Fable 5 vertrouwden, kregen andere resultaten dan ze mochten verwachten, zonder enige melding.
Voor jou als maker die een SaaS bouwt op een Claude-model is dit relevant: als Anthropic een model kan veranderen zonder dat jij het merkt, dan kan dat bij jou stille bugs, slechtere outputs of gefrustreerde gebruikers opleveren. En jij bent dan degene die een mail krijgt.
Wat betekent dit voor jouw project?
Als jouw SaaS Claude aanroept via de API, ben je afhankelijk van consistent model-gedrag. Dat geldt voor samenvattingen, gegenereerde tekst, classificaties, chatbots, noem maar op. Zolang je geen zelfgebouwde detectie hebt, weet je pas dat er iets veranderd is als een gebruiker klaagt, of als je toevallig een slechte output tegenkomt.
Dit is het moment om een kleine maar stevige vangnet-laag in te bouwen: een smoke test die je zelf elke dag of elke deploy uitvoert, zodat je model-gedragsveranderingen als eerste merkt.
Hoe pak je dit aan met Claude Code?
Stap 1: Maak een testbestand met je kritieke prompts
Open Claude Code in je project en vraag het volgende:
Maak een bestand
tests/model-smoke-test.tsaan. Hierin wil ik een reeks vaste test-prompts opslaan die de kern vormen van wat mijn app doet met Claude. Voor elke prompt sla ik ook een verwachte output-structuur op (niet exact, maar patroon-gebaseerd: bevat het de juiste sleutels? Is de toon correct? Is het antwoord niet leeg?). Gebruik de Anthropic SDK metclaude-3-5-sonnet-20241022als model.
Een voorbeeld van hoe zo'n testbestand eruitziet:
// tests/model-smoke-test.ts
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
interface SmokeTestCase {
name: string;
prompt: string;
validate: (output: string) => boolean;
}
const testCases: SmokeTestCase[] = [
{
name: "Samenvatting bevat bullet points",
prompt:
"Vat de volgende tekst samen in 3 bullet points: Claude is een AI-assistent van Anthropic die wordt gebruikt voor tekst, code en analyse.",
validate: (output) => output.includes("-") || output.includes("•"),
},
{
name: "Classificatie geeft geldig label terug",
prompt:
'Classificeer de volgende feedback als positief, neutraal of negatief. Geef alleen het label terug: "De app werkt goed maar de interface kan beter."',
validate: (output) =>
["positief", "neutraal", "negatief"].some((label) =>
output.toLowerCase().includes(label)
),
},
{
name: "JSON-output heeft verplichte velden",
prompt:
'Geef een JSON-object terug met velden: naam, prioriteit (hoog/midden/laag), en samenvatting. Taak: "Facturen controleren voor kwartaalafsluiting"',
validate: (output) => {
try {
const json = JSON.parse(output);
return "naam" in json && "prioriteit" in json && "samenvatting" in json;
} catch {
return false;
}
},
},
];
async function runSmokeTests() {
let passed = 0;
let failed = 0;
for (const test of testCases) {
const response = await client.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 500,
messages: [{ role: "user", content: test.prompt }],
});
const output =
response.content[0].type === "text" ? response.content[0].text : "";
const ok = test.validate(output);
console.log(`${ok ? "PASS" : "FAIL"} - ${test.name}`);
if (!ok) {
console.log(` Output was: ${output.slice(0, 100)}...`);
failed++;
} else {
passed++;
}
}
console.log(`\nResultaat: ${passed} geslaagd, ${failed} gefaald`);
if (failed > 0) process.exit(1);
}
runSmokeTests();
Stap 2: Voeg je eigen kritieke prompts toe
De testcases hierboven zijn generiek. Vervang ze door de echte kernfuncties van jouw SaaS. Vraag Claude Code:
Kijk naar mijn bestaande API-routes in
app/api/. Welke calls doen wij naar Claude? Maak voor elk van die calls een smoke test case aan intests/model-smoke-test.tsmet een validate-functie die controleert of de output nog steeds de juiste structuur heeft.
Stap 3: Draai de test automatisch bij elke deploy
Als je op Vercel of Netlify werkt, voeg dan dit toe aan je package.json:
"scripts": {
"smoke": "npx tsx tests/model-smoke-test.ts",
"predeploy": "npm run smoke"
}
Of vraag Claude Code:
Voeg een GitHub Actions workflow toe in
.github/workflows/model-smoke.ymldie elke dag om 08:00 de smoke tests draait en me een notificatie stuurt als er een test faalt. Gebruik mijn bestaandeANTHROPIC_API_KEYuit de secrets.
# .github/workflows/model-smoke.yml
name: Model Smoke Test
on:
schedule:
- cron: "0 6 * * *"
push:
branches: [main]
jobs:
smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install
- run: npm run smoke
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Stap 4: Log afwijkingen naar je eigen dashboard
Wil je het echt stevig aanpakken, dan log je gefaalde tests naar Supabase of een simpel logbestand:
Breid de smoke test uit zodat gefaalde tests worden weggeschreven naar een Supabase-tabel
model_alertsmet kolommen: test_name, model, timestamp, output_snippet. Ik wil dit later kunnen bekijken in mijn admin-dashboard.
Wat te checken na afloop
- Draai
npm run smokein je terminal. Zie je groene PASSen? Dan werkt je baseline. - Verander tijdelijk een
validate-functie zodat hij altijdfalsereturned, en kijk of je een FAIL ziet. Zo weet je dat de detectie werkt. - Push naar main en controleer in GitHub Actions of de workflow ook echt draait.
- Ga je komende week op Claude Fable 5 zitten? Voer de tests dan eerst uit op je huidige model en sla de resultaten op als baseline, zodat je later kunt vergelijken.
Een halfuur werk nu bespaart je een nachtmerrie-scenario later, wanneer een stil model-update je gebruikers slechte outputs geeft zonder dat jij het doorhebt.
Bij Eighty leer ik je Claude Code in het Nederlands gebruiken, van installatie tot een werkend SaaS-product. Wekelijks een nieuwe module, persoonlijke begeleiding.
