We verrijkten 28.840 records met AI: Lessen uit bulk data processing
AI-aangedreven bulkverrijking van productgegevens: architectuur, kosten en lessen uit 28.840 records
Vorig kwartaal hebben we een project aangepakt dat op papier eenvoudig klonk: 28.840 productrecords verrijken met AI-gegenereerde beschrijvingen, categorieën en metagegevens. De klant had een enorme e-commerce-catalogus die naar een headless CMS migreerde, en elk record had beter content nodig. Wat volgde was een meesterclass in alles wat fout kan gaan -- en goed -- wanneer je tienduizenden records naar een AI API stuurt.
Dit is geen theoretische gids. Ik ga je door de daadwerkelijke architectuur lopen die we hebben gebouwd, de exacte kosten die we hebben betaald, de foutmodi die we hebben ervaren, en de patronen die ons hebben gered. Als je AI bulkverrijking van content voor je eigen project overweegt, zou dit je een paar weken pijnlijke ontdekking moeten besparen.
Inhoudsopgave
- Waarom we AI-verrijking boven handmatig werk hebben gekozen
- De architectuur: hoe we de pipeline hebben gebouwd
- Claude API kiezen voor bulkverwerking
- Prompt Engineering op schaal
- Tarieflimieten, herhalingen en de kunst om niet verbannen te worden
- Kwaliteitscontrole: de menselijke tussenkomst in realiteit
- Werkelijke kostenafbouw
- Wat we verkeerd hebben begrepen
- Wat we de volgende keer anders zouden doen
- Veelgestelde vragen
Waarom we AI-verrijking boven handmatig werk hebben gekozen
De rekenkunde was brute eenvoudig. Onze klant had 28.840 productrecords -- elk ervan had een herschreven beschrijving nodig (150-300 woorden), drie SEO-vriendelijke categorietags, een metabeschrijving en gestructureerde attributen geëxtraheerd uit ongestructureerde tekst. Bij een conservatieve schatting van 8 minuten per record voor een menselijke copywriter, gaat het om 3.845 uur werk. Bij $35/uur ben je naar $134.575 aan het kijken en ongeveer 6 maanden verstreken tijd met een klein team.
We hebben de AI-verrijking in 11 dagen voltooid voor onder de $3.200 aan API-kosten, plus ongeveer 80 uur engineering en QA-tijd. Ook rekening houdend met onze ontwikkelingsuren, bedroeg de totale kosten ongeveer een tiende van de handmatige aanpak.
Maar hier is het ding dat niemand je vertelt: het moeilijke onderdeel is niet het aanroepen van de API. Het is alles eromheen. Gegevensopschoning, prompt-afstemming, kwaliteitsvalidatie, foutafhandeling, en de onvermijdelijke randgevallen die je doen twijfelen aan je carrièrekeuzes.
De architectuur: hoe we de pipeline hebben gebouwd
We hebben de verrijkingspipeline gebouwd als een Node.js-toepassing, wat logisch was gezien onze teamexpertise in Next.js-ontwikkeling en TypeScript. Hier is de architectuur op hoog niveau:
Bron CSV → Parser → Batchenwachtrij → Claude API → Antwoordvalidatie → Uitvoeropslag → QA Dashboard
De datalaag
We gebruikten SQLite als onze lokale verwerkingsdatabase. Klinkt oninteressant, toch? Maar voor batchverwerking zoals dit is het perfect. Geen server om te beheren, transacties zijn snel, en je kunt je resultaten gemakkelijk opvragen. Elk record kreeg een statuskolom die de reis volgt:
interface EnrichmentRecord {
id: string;
original_title: string;
original_description: string;
raw_attributes: string;
status: 'pending' | 'processing' | 'completed' | 'failed' | 'needs_review';
enriched_description: string | null;
enriched_categories: string[] | null;
enriched_meta: string | null;
structured_attributes: Record<string, string> | null;
attempts: number;
last_error: string | null;
token_usage: number;
created_at: string;
updated_at: string;
}
Het wachtrij-systeem
We implementeerden een eenvoudige jobwachtrij met BullMQ ondersteund door Redis. Elke taak vertegenwoordigde een enkele recordverrijking. We configureerden het met:
- Gelijktijdigheid: 5 parallelle workers (meer hierover later)
- Max pogingen: 3 per record
- Backoff: Exponentieel, beginnend bij 30 seconden
- Job-timeout: 60 seconden
const enrichmentQueue = new Queue('enrichment', {
connection: redisConnection,
defaultJobOptions: {
attempts: 3,
backoff: {
type: 'exponential',
delay: 30000,
},
timeout: 60000,
removeOnComplete: false, // Behouden voor auditing
},
});
De verwerkingsworker
Elke worker haalde een record op, constructeerde de prompt, riep Claude's API aan, valideerde de responsstructuur, en schreef de resultaten terug. Als de respons niet aan ons verwachte JSON-schema voldeed, ging het in een needs_review bucket in plaats van onze dataset stiekem te beschadigen.
Claude API kiezen voor bulkverwerking
We evalueerden drie opties voordat we Claude kozen (specifiek Claude 3.5 Sonnet, en later Claude 3.5 Haiku voor eenvoudigere taken):
| Functie | Claude 3.5 Sonnet | GPT-4o | Gemini 1.5 Pro |
|---|---|---|---|
| Invoerkosten (per 1M tokens) | $3,00 | $2,50 | $1,25 |
| Uitvoerkosten (per 1M tokens) | $15,00 | $10,00 | $5,00 |
| Tarieflimieten (RPM, Tier 2) | 1.000 | 500 | 360 |
| JSON-modus betrouwbaarheid | Uitstekend | Goed | Inconsistent |
| Kwaliteit van gestructureerde uitvoer | Best in class | Erg goed | Goed |
| Batchkorting API | 50% | 50% | N/A |
Prijzen per Q1 2025. Controleer huidige prijzen -- deze veranderen regelmatig.
We kozen voor Claude om een paar redenen. Ten eerste was de instructievolging voor gestructureerde uitvoer merkbaar beter dan de alternatieven tijdens onze testrun van 500 records. Als je bijna 29K records verwerkt, spaart zelfs een verbetering van 2% in formaatcompliance je honderden handmatige correcties. Ten tweede bood Anthropic's Batch API een korting van 50% voor niet-tijdsgevoelige werk, wat de economie nog gunstiger maakte.
Eerlijk gezegd zou GPT-4o prima zijn geweest. De verschillen op deze schaal gaan meer over tarieflimieten en prijzen dan over ruwe kwaliteit. Maar Claude's consistentie met JSON-uitvoer was de beslissende factor.
Waarom we zowel Sonnet als Haiku gebruikten
Hier is een truc die ons ongeveer 40% besparing op API-kosten opleverde: we gebruikten niet hetzelfde model voor alles. Productbeschrijvingen hadden Sonnet's kwaliteit nodig. Maar categorieëclassificatie en attributenextractie? Haiku handelde die prima af voor een fractie van de kosten.
We verdeelden de verrijking in twee passages:
- Passage 1 (Haiku): Categorieëclassificatie, attributenextractie, basale metagegevens -- $0,25/1M invoer, $1,25/1M uitvoer
- Passage 2 (Sonnet): Beschrijving herschrijven, metabeschrijvingen, SEO-inhoud -- $3,00/1M invoer, $15,00/1M uitvoer
Prompt Engineering op schaal
Dit is waar de meeste tutorials je in de steek laten. Ze tonen je een enkele prompt en noemen het gedaan. Als je 28.840 records door dezelfde prompttemplate stuurt, worden kleine gebreken versterkt tot massieve problemen.
De Prompt Template
Na ongeveer 15 iteraties (ja, we volgden ze in git), hier is de ruwe structuur die werkte:
const buildPrompt = (record: SourceRecord): string => `
You are enriching product data for an e-commerce catalog. Generate the following for the product below:
1. A product description (150-300 words, second person, benefit-focused)
2. Exactly 3 category tags from this allowed list: ${CATEGORY_LIST}
3. A meta description (120-155 characters)
4. Structured attributes as key-value pairs
Rules:
- Do NOT invent features not present in the source data
- If information is ambiguous, use the "uncertain" flag
- Match the brand's tone: professional but approachable
- Description must be unique -- do not repeat the title verbatim in the first sentence
Respond ONLY with valid JSON matching this schema:
${JSON_SCHEMA}
Source product data:
Title: ${record.title}
Existing description: ${record.description}
Raw attributes: ${record.attributes}
Price: ${record.price}
Brand: ${record.brand}
`;
Lessen over prompts op schaal
Wees absurd specifiek over uitvoerindeling. We voegden het volledige JSON-schema in elk verzoek in. Ja, het voegt tokens toe. Nee, sla het niet over. De ene keer dat we probeerden alleen op systeeminstructies te vertrouwen, daalde onze formaatcompliance van 97% naar 81%.
Beperk de uitvoervocabulaire. Voor categorietags voorzagen we een expliciete toegestane lijst. Open-ended categorisering produceerde 847 unieke categorieën in onze testbatch. De beperkte versie? Precies de 42 die we wilden.
Voeg guardrails toe voor hallucinatie. Producten zouden af en toe features krijgen die ze niet hadden. Het toevoegen van "Do NOT invent features not present in the source data" reduceerde gehalluceerde attributen met ongeveer 70%. Het toevoegen van de uncertain vlag ving de meeste resterende gevallen op.
Temperatuur is belangrijker dan je denkt. We kozen voor 0,3. Lager dan dat en beschrijvingen werden repetitief over vergelijkbare producten. Hoger en we begonnen creatief schrijven dat niet overeenkwam met de merkidentiteit.
Tarieflimieten, herhalingen en de kunst om niet verbannen te worden
Deze sectie zou eigenlijk "het onderdeel dat het meest engineering-tijd kostte" moeten heten. Anthropic's tarieflimieten zijn goed gedocumenteerd maar gedragen zich anders onder aanhoudende belasting dan je zou verwachten van het lezen van de docs.
Onze Tarieflimietstrategie
Op Tier 2 (die je krijgt na $40+ uitgaven) geeft Claude je 1.000 verzoeken per minuut en 80.000 tokens per minuut. Klinkt royaal tot je beseft dat ons gemiddelde verzoek ongeveer 1.200 invoertokens en 800 uitvoertokens was. Dat betekende dat onze praktische limiet ongeveer 40 gelijktijdige verzoeken was voordat we tokenlimieten raakten.
We draaiden 5 gelijktijdige workers, elk één record tegelijk verwerkend, met een vertraging van 200ms tussen verzoeken. Dit gaf ons ongeveer 15-20 verzoeken per minuut -- ver onder de RPM-limiet en comfortabel binnen tokenbudgetten.
const rateLimiter = new Bottleneck({
maxConcurrent: 5,
minTime: 200, // ms tussen verzoeken
reservoir: 900, // verzoeken per minuut (buffer laten)
reservoirRefreshAmount: 900,
reservoirRefreshInterval: 60 * 1000,
});
Waarom zo voorzichtig? Omdat het raken van tarieflimieten cascaderende fouten veroorzaakt. Eén 429-response triggert een herpoging, die aan de wachtrij wordt toegevoegd, wat gelijktijdigheidsprobleem verhoogt. We leerden dit de hardste weg tijdens uur 3 van onze eerste echte run, toen agressieve instellingen een herbeprobingsstorm veroorzaakten die de pipeline effectief 45 minuten stilzette.
Het Batch API-alternatief
Halverwege het project schakelden we gedeeltelijk over naar Anthropic's Batch API. In plaats van individuele verzoeken te doen, upload je een JSONL-bestand met verzoeken en krijg je resultaten binnen 24 uur terug. De ruil: 50% kostenbesparing, maar je verliest real-timefeedback.
We gebruikten de Batch API voor Passage 1 (Haiku-classificatie) en real-time API voor Passage 2 (Sonnet-beschrijvingen). Deze hybride aanpak was het zoete plekje voor ons -- snelle feedback op het dure creatieve werk, batcheconomie op de commodityclassificatie.
Kwaliteitscontrole: de menselijke tussenkomst in realiteit
Iedereen die zegt dat AI-verrijking volledig geautomatiseerd is, liegt of heeft het niet op schaal gedaan. We hebben een QA-proces gebouwd dat problemen vroeg opving en voorkwam dat afval in productie terechtkwam.
Geautomatiseerde validatie
Elke API-respons werd gevalideerd voordat deze werd geaccepteerd:
const validateEnrichment = (result: EnrichmentResult): ValidationOutcome => {
const issues: string[] = [];
// Lengtecontroles
if (result.description.length < 400 || result.description.length > 2000) {
issues.push('description_length');
}
// Categorievalidatie
const invalidCats = result.categories.filter(c => !ALLOWED_CATEGORIES.includes(c));
if (invalidCats.length > 0) issues.push('invalid_categories');
// Metabeschrijvingslengte
if (result.meta.length > 160) issues.push('meta_too_long');
// Hallucinatie-signalen
const hallucination_phrases = ['I think', 'probably', 'might be', 'as an AI'];
if (hallucination_phrases.some(p => result.description.includes(p))) {
issues.push('possible_hallucination');
}
// Dubbel detectie (fuzzy match tegen al verwerkte records)
if (isDuplicateDescription(result.description)) {
issues.push('duplicate_content');
}
return {
valid: issues.length === 0,
issues,
needsReview: issues.length > 0 && issues.length < 3,
rejected: issues.length >= 3,
};
};
Steekproef handmatig onderzoek
We sampelden 5% van alle verwerkte records (ongeveer 1.440) voor handmatig onderzoek. Ons QA-team gaf elk een score op nauwkeurigheid, merkidentiteit en volledigheid. Hier zijn de cijfers uit onze werkelijke review:
| Metriek | Score |
|---|---|
| Feitelijke nauwkeurigheid | 94,2% |
| Merkidentiteit match | 87,6% |
| Formaatcompliance | 97,1% |
| Categorieaccuratesse | 91,8% |
| Records die revisie nodig hebben | 8,3% |
| Records volledig afgewezen | 1,9% |
Die 8,3% die revisie nodig hebben is belangrijke context. Het betekent dat ongeveer 2.400 records menselijke bewerking nodig hadden. Nog steeds veel minder dan handmatig alle 28.840 schrijven -- maar het is niet nul. Budget ervoor.
Werkelijke kostenafbouw
Transparantie-tijd. Hier is wat we eigenlijk hebben uitgegeven:
| Kostencategorie | Bedrag |
|---|---|
| Claude 3.5 Haiku (Passage 1 - Batch API) | $312 |
| Claude 3.5 Sonnet (Passage 2 - Real-time) | $2.147 |
| Mislukte/herneem-verzoeken (~6% overhead) | $189 |
| Redis-hosting (2 weken) | $15 |
| Engineering-tijd (80 uur × $150) | $12.000 |
| QA-review-tijd (40 uur × $45) | $1.800 |
| Totaal | $16.463 |
| Alleen API-kosten | $2.648 |
Vergelijk dat met de schatting van $134.575 voor volledig handmatig werk. Ook alle engineering- en QA-tijd meegerekend, we zitten op ongeveer 12% van de handmatige kosten. En de pipeline is herbruikbaar -- de volgende keer dat we een soortgelijk project uitvoeren, daalt de engineering-kosten naar bijna nul.
De API-kosten per record liepen uit op ongeveer $0,092. Minder dan een dubbeltje per record voor AI-verrijking. Dat is het getal dat executives doen opschrikt.
Wat we verkeerd hebben begrepen
Gegevensopschoning onderschat
We hebben 3 dagen besteed aan alleen al het schoonmaken van de brongegevens voordat we ze naar Claude stuurden. Records hadden HTML-entiteiten, Unicode-rommel, afgekapte beschrijvingen, en velden in de verkeerde kolommen. Garbage in, garbage out is niet zomaar een cliché -- het is de fundamentele wet van bulkverwerking met AI.
De Batch API niet vanaf dag één gebruiken
We hebben ongeveer $400 extra in API-kosten verbrand door Passage 1 door de real-time API te draaien voordat we ontdekten dat de Batch API de helft zou hebben gekost. Lees de volledige documentatie voordat je begint. Alles ervan.
Onvoldoende dubbeledetectie
Onze eerste dubbeledetectie was te naïef -- eenvoudige stringafstemming. Claude zou beschrijvingen genereren die structureel identiek waren maar iets andere woorden gebruikten voor vergelijkbare producten. We moesten semantische gelijkeniscontrole (met behulp van embeddings) implementeren om deze op te vangen, wat een dag werk toevoegde.
JSON-parsefouten
Ongeveer 2,4% van de responses kwam terug met misvormd JSON. Soms een trailing komma, soms een ontsnapte aanhalingsteken in een productbeschrijving. We hadden van begin af aan een tolerantere JSON-parser moeten implementeren in plaats van deze als harde fouten te behandelen.
// Wat we van dag één hadden moeten doen
const parseResponse = (raw: string): EnrichmentResult | null => {
try {
return JSON.parse(raw);
} catch {
// Probeer JSON uit markdown codeblokken te extraheren
const jsonMatch = raw.match(/```json?\n?([\s\S]*?)\n?```/);
if (jsonMatch) {
try { return JSON.parse(jsonMatch[1]); } catch { /* fall through */ }
}
// Probeer jsonrepair bibliotheek als laatste redmiddel
try { return JSON.parse(jsonrepair(raw)); } catch { return null; }
}
};
Wat we de volgende keer anders zouden doen
Start met een pilotproject van 1.000 records voordat je jezelf aan de volledige run verbindt. We deden 500 en het was niet genoeg om alle randgevallen aan het licht te brengen.
Gebruik gestructureerde outputs van het begin af. Anthropic ondersteunt nu gereedschappengebruik met gedefinieerde schema's, wat de meeste JSON-parseproblemen elimineert. We migreerden halfweg en wensten dat we daar waren begonnen.
Bouw het QA-dashboard eerst. We bouwden het reactief nadat problemen verschenen. Het hebben ervan vanaf dag één zou problemen in de eerste 100 records hebben opgemerkt in plaats van de eerste 2.000.
Investeer in betere embeddings voor dedup. We gebruikten een dedicated embeddingsmodel (zoals
text-embedding-3-small) van het begin voor semantische dubbeledetectie.Overweeg hybride modelrouting. Sommige records zijn eenvoudig (t-shirts met basale attributen) en sommige zijn complex (elektronika met tientallen specs). Records routeren -- zelfs voor beschrijvingen -- zou nog eens 20-30% op API-kosten hebben bespaard.
Als je een soortgelijk project plant en de pijnlijke delen wilt overslaan, hebben we herbruikbare pipelines voor dit soort werk gebouwd als onderdeel van onze headless CMS-ontwikkelingspraktijk. Graag meer specifieke informatie delen.
Veelgestelde vragen
Hoe lang duurt het om 28.000+ records met AI te verrijken? Onze werkelijke verwerkingstijd bedroeg ongeveer 11 dagen, inclusief pipelineont
wikkeling, testen, verwerking en QA-review. De eigenlijke API-verwerking zelf (verzoeken versturen en responses ontvangen) nam ongeveer 48 uur aaneengesloten draaiing in beslag. Als je uitsluitend de Batch API gebruikt, verwacht je 24-48 uur voor verwerking plus 3-5 dagen voor engineering en QA.
Wat zijn de kosten per record voor AI-contentverrijking? Met Claude 3.5 Sonnet en Haiku in combinatie bedroegen onze API-kosten ongeveer $0,092 per record voor het genereren van een productbeschrijving, categorieën, metabeschrijving en gestructureerde attributen. Je kilometers zullen variëren afhankelijk van invoer/uitvoerlengten en welk model je kiest. Batch API-verwerking halveert dit ongeveer.
Is Claude of GPT-4 beter voor bulkgegevensverrijking? Beide werken goed. We kozen Claude 3.5 Sonnet vanwege zijn superieure JSON-formaatcompliance tijdens onze testen (97,1% versus ~94% voor GPT-4o). GPT-4o is echter iets goedkoper voor uitvoertokens. Als je verrijking voornamelijk classificatie is in plaats van contentgeneratie, is het verschil verwaarloosbaar. Test beide met 500 records voordat je je committeert.
Hoe handel je tarieflimieten af bij het doen van duizenden API-aanroepen? Gebruik een bibliotheek voor tariefbegrenzing zoals Bottleneck, stel voorzichtige gelijktijdigheid in (5-10 parallelle verzoeken), implementeer exponentiële backoff voor herhalingen, en laat een buffer van 10-15% onder gepubliceerde tarieflimieten. Voor niet-tijdsgevoelig werk vermijdt Anthropic's Batch API tariefbeperkingsproblemen geheel en kost 50% minder.
Welk percentage van AI-verrijkte records heeft handmatig onderzoek nodig? In ons project had 8,3% van de records enige vorm van menselijke bewerking nodig en 1,9% werd volledig afgewezen en handmatig herschreven. Je cijfers zullen afhangen van gegevenskwaliteit, prompt-engineering, en aanvaardbare kwaliteitsnormen. Plan voor 5-15% menselijke tussenkomst als realistische baseline.
Kan AI bulkverrijking meerdere talen aan? Ja, maar kwaliteit varieert aanzienlijk per taal. Claude en GPT-4 behandelen grote Europese talen goed, maar nauwkeurigheid daalt voor minder gebruikte talen. We raden aan om aparte promptsjablonen per taal uit te voeren en moedertaalsprekers in je QA-steekproef te hebben. Verwacht dat het percentage handmatig onderzoek ongeveer verdubbelt voor niet-Engelstalige inhoud.
Hoe voorkom je AI-hallucinaties in productgegevens? Drie lagen: promptinstructies die uitdrukkelijk verzonnen features verbieden, een "uncertain" vlag voor ambigue gegevens, en geautomatiseerde validatie die verrijkte attributen vergelijkt met brongegevens. We gebruikten ook semantische gelijkenisscoring om beschrijvingen te markeren die te ver afweken van de originele productinformatie. Dit reduceerde gehalluceerde attributen met ongeveer 70%.
Is het de moeite waard om een custom pipeline te bouwen of zou ik een bestaand gereedschap moeten gebruiken? Voor onder de 1.000 records kunnen tools zoals Clay, Bardeen, of zelfs een goed gestructureerde Google Sheets + Apps Script setup werken. Daar boven betaalt een custom pipeline zichzelf snel terug. De controle over herhaalde logica, kwaliteitsvalidatie, en kostoptimalisatie die een custom oplossing biedt, is essentieel op schaal. Onze pipeline was ruwweg 2.000 regels TypeScript -- niet triviaal, maar ook geen massaal project. Controleer onze prijspagina als je wilt dat we er één voor je gebruiksscenario bouwen.