Nous avons enrichi 28 840 enregistrements avec l'IA : leçons du traitement de données en masse
Enrichir 28 840 enregistrements de produits avec l'IA : architecture, coûts et leçons apprises
Le trimestre dernier, nous avons entrepris un projet qui semblait simple sur le papier : enrichir 28 840 enregistrements de produits avec des descriptions, des catégories et des métadonnées générées par l'IA. Le client avait un immense catalogue de commerce électronique migrant vers un CMS découplé, et chaque enregistrement avait besoin d'un meilleur contenu. Ce qui a suivi a été un cours magistral sur tout ce qui peut mal tourner -- et bien tourner -- quand vous envoyez des dizaines de milliers d'enregistrements à une API IA.
Ce n'est pas un guide théorique. Je vais vous montrer l'architecture exacte que nous avons construite, les coûts précis que nous avons payés, les modes de défaillance que nous avons rencontrés, et les patterns qui nous ont sauvés. Si vous envisagez l'enrichissement de contenu en masse par IA pour votre propre projet, cela devrait vous faire économiser quelques semaines de découverte douloureuse.
Table des matières
- Pourquoi nous avons choisi l'enrichissement par IA plutôt que le travail manuel
- L'architecture : comment nous avons construit le pipeline
- Choisir Claude API pour le traitement en masse
- L'ingénierie des invites à grande échelle
- Limites de débit, tentatives et l'art de ne pas se faire bannir
- Contrôle de qualité : la réalité avec humain dans la boucle
- Ventilation réelle des coûts
- Ce que nous avons mal fait
- Ce que nous ferions différemment la prochaine fois
- FAQ
Pourquoi nous avons choisi l'enrichissement par IA plutôt que le travail manuel
Les mathématiques étaient brutalement simples. Notre client avait 28 840 enregistrements de produits -- chacun avait besoin d'une description réécrite (150-300 mots), trois balises de catégorie optimisées pour le SEO, une méta-description et des attributs structurés extraits de texte non structuré. À une estimation conservatrice de 8 minutes par enregistrement pour un rédacteur humain, cela représente 3 845 heures de travail. À 35 $/heure, vous regardez 134 575 $ et environ 6 mois de temps écoulé avec une petite équipe.
Nous avons terminé l'enrichissement par IA en 11 jours pour moins de 3 200 $ en coûts d'API, plus environ 80 heures de temps d'ingénierie et d'assurance qualité. Même en tenant compte de nos heures de développement, le coût total était environ un dixième de l'approche manuelle.
Mais voici ce que personne ne vous dit : la partie difficile n'est pas d'appeler l'API. C'est tout ce qui l'entoure. Nettoyage des données, ajustement des invites, validation de la qualité, gestion des erreurs, et les inévitables cas limites qui vous font remettre en question vos choix de carrière.
L'architecture : comment nous avons construit le pipeline
Nous avons construit le pipeline d'enrichissement en tant qu'application Node.js, ce qui avait du sens compte tenu de l'expertise de notre équipe en développement Next.js et TypeScript. Voici l'architecture de haut niveau :
CSV source → Parser → File d'attente → Claude API → Validateur de réponse → Stockage de sortie → Tableau de bord QA
La couche de données
Nous avons utilisé SQLite comme base de données de traitement local. Cela semble peu séduisant, n'est-ce pas ? Mais pour le traitement en masse comme celui-ci, c'est parfait. Pas de serveur à gérer, les transactions sont rapides et vous pouvez interroger facilement vos résultats. Chaque enregistrement avait une colonne de statut suivi sa progression :
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;
}
Le système de file d'attente
Nous avons implémenté une simple file d'attente de travaux utilisant BullMQ soutenu par Redis. Chaque travail représentait un enrichissement d'enregistrement unique. Nous l'avons configuré avec :
- Concurrence : 5 workers parallèles (plus sur le pourquoi de ce chiffre plus tard)
- Tentatives max : 3 par enregistrement
- Backoff : Exponentiel, commençant à 30 secondes
- Délai d'attente du travail : 60 secondes
const enrichmentQueue = new Queue('enrichment', {
connection: redisConnection,
defaultJobOptions: {
attempts: 3,
backoff: {
type: 'exponential',
delay: 30000,
},
timeout: 60000,
removeOnComplete: false, // Garder pour auditer
},
});
Le worker de traitement
Chaque worker récupérait un enregistrement, construisait l'invite, appelait l'API Claude, validait la structure de la réponse et écrivait les résultats en retour. Si la réponse ne correspondait pas à notre schéma JSON attendu, elle allait dans un bucket needs_review plutôt que de corrompre silencieusement notre ensemble de données.
Choisir Claude API pour le traitement en masse
Nous avons évalué trois options avant de nous installer sur Claude (spécifiquement Claude 3.5 Sonnet, et plus tard Claude 3.5 Haiku pour les tâches plus simples) :
| Fonctionnalité | Claude 3.5 Sonnet | GPT-4o | Gemini 1.5 Pro |
|---|---|---|---|
| Coût d'entrée (par 1M de tokens) | 3,00 $ | 2,50 $ | 1,25 $ |
| Coût de sortie (par 1M de tokens) | 15,00 $ | 10,00 $ | 5,00 $ |
| Limites de débit (RPM, Tier 2) | 1 000 | 500 | 360 |
| Fiabilité du mode JSON | Excellent | Bon | Incohérent |
| Qualité des résultats structurés | Meilleure de sa catégorie | Très bon | Bon |
| Rabais API par lot | 50% | 50% | S/O |
Tarifs à partir de Q1 2025. Vérifiez les tarifs actuels -- ceux-ci changent fréquemment.
Nous avons choisi Claude pour quelques raisons. Premièrement, sa capacité à suivre les instructions pour la sortie structurée était notablement meilleure que les alternatives lors de notre test de 500 enregistrements. Quand vous traitez près de 29 000 enregistrements, même une amélioration de 2% dans la conformité du format vous économise des centaines de corrections manuelles. Deuxièmement, l'API Batch d'Anthropic offrait une réduction de 50% pour le travail non sensible au temps, ce qui rendait l'économie encore plus favorable.
Honnêtement, GPT-4o aurait également fonctionné. Les différences à cette échelle concernent davantage les limites de débit et les prix que la qualité brute. Mais la cohérence de Claude avec la sortie JSON a été le facteur décisif.
Pourquoi nous avons utilisé Sonnet et Haiku
Voici un truc qui nous a économisé environ 40% sur les coûts d'API : nous n'avons pas utilisé le même modèle pour tout. Les descriptions de produits avaient besoin de la qualité de Sonnet. Mais la classification de catégories et l'extraction d'attributs ? Haiku s'en est très bien tiré à une fraction du coût.
Nous avons divisé l'enrichissement en deux passages :
- Passage 1 (Haiku) : Classification de catégories, extraction d'attributs, métadonnées de base -- 0,25 $/1M entrée, 1,25 $/1M sortie
- Passage 2 (Sonnet) : Réécriture de descriptions, méta-descriptions, contenu SEO -- 3,00 $/1M entrée, 15,00 $/1M sortie
L'ingénierie des invites à grande échelle
C'est là où la plupart des tutoriels vous laissent tomber. Ils vous montrent une seule invite et appellent cela un jour. Quand vous envoyez 28 840 enregistrements à travers le même modèle d'invite, les petits défauts s'amplifient en problèmes massifs.
Le modèle d'invite
Après environ 15 itérations (oui, nous les avons suivi dans git), voici la structure approximative qui a fonctionné :
const buildPrompt = (record: SourceRecord): string => `
Vous enrichissez les données de produits pour un catalogue de commerce électronique. Générez ce qui suit pour le produit ci-dessous :
1. Une description de produit (150-300 mots, à la deuxième personne, axée sur les avantages)
2. Exactement 3 balises de catégorie de cette liste autorisée : ${CATEGORY_LIST}
3. Une méta-description (120-155 caractères)
4. Des attributs structurés comme des paires clé-valeur
Règles :
- N'INVENTEZ PAS de fonctionnalités absentes des données source
- Si les informations sont ambigues, utilisez le drapeau « incertain »
- Corresponder au ton de la marque : professionnel mais accessible
- La description doit être unique -- ne répétez pas le titre textuellement dans la première phrase
Répondez UNIQUEMENT avec un JSON valide correspondant à ce schéma :
${JSON_SCHEMA}
Données de produit source :
Titre : ${record.title}
Description existante : ${record.description}
Attributs bruts : ${record.attributes}
Prix : ${record.price}
Marque : ${record.brand}
`;
Leçons sur les invites à grande échelle
Soyez absurdement spécifique sur le format de sortie. Nous avons inclus le schéma JSON complet dans chaque demande. Oui, cela ajoute des tokens. Non, ne le sautez pas. La seule fois où nous avons essayé de compter sur les instructions système seules, notre conformité au format a chuté de 97% à 81%.
Limitez le vocabulaire de sortie. Pour les balises de catégories, nous avons fourni une liste autorisée explicite. La catégorisation ouverte a produit 847 catégories uniques dans notre lot de test. La version contrainte ? Exactement les 42 que nous voulions.
Ajoutez des garde-fous pour l'hallucination. Les produits seraient occasionnellement dotés de fonctionnalités qu'ils n'avaient pas. L'ajout de « N'INVENTEZ PAS de fonctionnalités absentes des données source » a réduit les attributs hallucinations d'environ 70%. Ajouter le drapeau incertain a attrapé la plupart des cas restants.
La température compte plus que vous ne le pensez. Nous nous sommes stabilisés à 0,3. Plus bas que cela et les descriptions deviennent répétitives pour des produits similaires. Plus haut et nous commençons à avoir une écriture créative qui ne correspond pas à la voix de la marque.
Limites de débit, tentatives et l'art de ne pas se faire bannir
Cette section devrait vraiment s'appeler « la partie qui a pris le plus de temps d'ingénierie ». Les limites de débit d'Anthropic sont bien documentées mais se comportent différemment sous charge soutenue que ce que vous attendez de la lecture des documents.
Notre stratégie de limite de débit
Au Tier 2 (que vous obtenez après avoir dépensé 40 $+), Claude vous donne 1 000 demandes par minute et 80 000 tokens par minute. Cela semble généreux jusqu'à ce que vous réalisiez que notre demande moyenne était d'environ 1 200 tokens d'entrée et 800 tokens de sortie. Cela signifiait que notre limite pratique était d'environ 40 demandes simultanées avant de frapper les limites de tokens.
Nous avons exécuté 5 workers simultanés, chacun traitant un enregistrement à la fois, avec un délai de 200 ms entre les demandes. Cela nous a donné environ 15-20 demandes par minute -- bien en dessous de la limite RPM et confortablement dans les budgets de tokens.
const rateLimiter = new Bottleneck({
maxConcurrent: 5,
minTime: 200, // ms entre demandes
reservoir: 900, // demandes par minute (laissant un buffer)
reservoirRefreshAmount: 900,
reservoirRefreshInterval: 60 * 1000,
});
Pourquoi si conservateur ? Parce que frapper les limites de débit cause des défaillances en cascade. Une réponse 429 déclenche une tentative, qui ajoute à la file d'attente, qui augmente la pression de concurrence. Nous avons appris cela à la dure pendant l'heure 3 de notre première vraie exécution, quand les paramètres agressifs ont causé une tempête de tentatives qui a effectivement paralysé le pipeline pendant 45 minutes.
L'alternative API par lot
À mi-chemin du projet, nous avons partiellement basculé vers l'API Batch d'Anthropic. Au lieu de faire des demandes individuelles, vous téléchargez un fichier JSONL de demandes et obtenez les résultats en 24 heures. L'échange : réduction de coût de 50%, mais vous perdez les commentaires en temps réel.
Nous avons utilisé l'API Batch pour le Passage 1 (classification Haiku) et l'API en temps réel pour le Passage 2 (descriptions Sonnet). Cette approche hybride était le point doux pour nous -- retours rapides sur le travail créatif coûteux, économies de batch sur la classification de marchandise.
Contrôle de qualité : la réalité avec humain dans la boucle
Quiconque vous dit que l'enrichissement par IA est entièrement automatisé ment ou n'a pas fait cela à grande échelle. Nous avons construit un processus QA qui a attrapé les problèmes tôt et empêché les ordures d'arriver en production.
Validation automatisée
Chaque réponse d'API a été validée avant d'être acceptée :
const validateEnrichment = (result: EnrichmentResult): ValidationOutcome => {
const issues: string[] = [];
// Vérifications de longueur
if (result.description.length < 400 || result.description.length > 2000) {
issues.push('description_length');
}
// Validation de catégorie
const invalidCats = result.categories.filter(c => !ALLOWED_CATEGORIES.includes(c));
if (invalidCats.length > 0) issues.push('invalid_categories');
// Longueur de méta-description
if (result.meta.length > 160) issues.push('meta_too_long');
// Signaux d'hallucination
const hallucination_phrases = ['I think', 'probably', 'might be', 'as an AI'];
if (hallucination_phrases.some(p => result.description.includes(p))) {
issues.push('possible_hallucination');
}
// Détection de doublons (correspondance floue par rapport aux enregistrements déjà traités)
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,
};
};
Échantillonnage d'examen manuel
Nous avons échantillonné 5% de tous les enregistrements traités (environ 1 440) pour examen manuel. Notre équipe QA a noté chacun sur la précision, la voix de marque et l'exhaustivité. Voici les chiffres de notre examen réel :
| Métrique | Score |
|---|---|
| Précision factuelle | 94,2% |
| Correspondance de voix de marque | 87,6% |
| Conformité au format | 97,1% |
| Précision de catégorie | 91,8% |
| Enregistrements nécessitant révision | 8,3% |
| Enregistrements complètement rejetés | 1,9% |
Ces 8,3% nécessitant révision sont importants en contexte. Cela signifie qu'environ 2 400 enregistrements avaient besoin d'une édition humaine. Toujours beaucoup moins que d'écrire manuellement les 28 840 -- mais ce n'est pas zéro. Budgétisez-le.
Ventilation réelle des coûts
Moment de transparence. Voici ce que nous avons réellement dépensé :
| Catégorie de coûts | Montant |
|---|---|
| Claude 3.5 Haiku (Passage 1 - API par lot) | 312 $ |
| Claude 3.5 Sonnet (Passage 2 - Temps réel) | 2 147 $ |
| Demandes échouées/tentatives (~6% de frais généraux) | 189 $ |
| Hébergement Redis (2 semaines) | 15 $ |
| Temps d'ingénierie (80 hrs × 150 $) | 12 000 $ |
| Temps d'examen QA (40 hrs × 45 $) | 1 800 $ |
| Total | 16 463 $ |
| Coûts d'API uniquement | 2 648 $ |
Comparez cela à l'estimation de 134 575 $ pour le travail entièrement manuel. Même en incluant tous les temps d'ingénierie et QA, nous sommes à environ 12% du coût manuel. Et le pipeline est réutilisable -- la prochaine fois que nous exécutons un projet similaire, le coût d'ingénierie tombe à quasi zéro.
Le coût d'API par enregistrement était d'environ 0,092 $. Moins d'un dime par enregistrement pour l'enrichissement par IA. C'est le chiffre qui fait que les dirigeants se redressent.
Ce que nous avons mal fait
Sous-estimer le nettoyage des données
Nous avons passé 3 jours juste à nettoyer les données source avant de les envoyer à Claude. Les enregistrements avaient des entités HTML, des ordures Unicode, des descriptions tronquées et des champs dans les mauvaises colonnes. Garbage in, garbage out n'est pas qu'un cliché -- c'est la loi fondamentale du traitement par IA en masse.
Ne pas utiliser l'API Batch dès le premier jour
Nous avons brûlé environ 400 $ supplémentaires en coûts d'API en exécutant le Passage 1 via l'API en temps réel avant de découvrir que l'API Batch aurait été la moitié du prix. Lisez la documentation complète avant de commencer. Tout ça.
Détection de doublons insuffisante
Notre détection de doublons initiale était trop naïve -- simple correspondance de chaîne. Claude générerait des descriptions structurellement identiques mais utilisant des mots légèrement différents pour des produits similaires. Nous avons dû implémenter la vérification de similarité sémantique (en utilisant les embeddings) pour les attraper, ce qui a ajouté un jour de travail.
Défaillances d'analyse JSON
Environ 2,4% des réponses revenaient avec du JSON malformé. Parfois une virgule traînante, parfois une guillemet non échappé dans une description de produit. Nous aurions dû implémenter un analyseur JSON plus tolérant dès le départ plutôt que de traiter ces comme des défaillances dures.
// Ce que nous aurions dû faire dès le premier jour
const parseResponse = (raw: string): EnrichmentResult | null => {
try {
return JSON.parse(raw);
} catch {
// Essayer d'extraire JSON à partir de blocs de code markdown
const jsonMatch = raw.match(/```json?\n?([\s\S]*?)\n?```/);
if (jsonMatch) {
try { return JSON.parse(jsonMatch[1]); } catch { /* continuer */ }
}
// Essayer la bibliothèque jsonrepair en dernier recours
try { return JSON.parse(jsonrepair(raw)); } catch { return null; }
}
};
Ce que nous ferions différemment la prochaine fois
Commencez avec un pilote de 1 000 enregistrements avant de vous engager dans l'exécution complète. Nous avons fait 500 et ce n'était pas assez pour révéler tous les cas limites.
Utilisez les sorties structurées dès le début. Anthropic supporte maintenant l'utilisation d'outils avec schémas définis, ce qui élimine la plupart des problèmes d'analyse JSON. Nous avons migré vers cela à mi-chemin et souhaitons avoir commencé là.
Construisez le tableau de bord QA en premier. Nous l'avons construit de manière réactive après que les problèmes aient surgi. L'avoir dès le premier jour aurait attrapé les problèmes dans les premiers 100 enregistrements au lieu des 2 000 premiers.
Investissez dans de meilleurs embeddings pour la déduplication. Nous utiliserions un modèle d'embedding dédié (comme
text-embedding-3-small) dès le départ pour la détection de doublons sémantiques.Considérez l'acheminement de modèles hybrides. Certains enregistrements sont simples (t-shirts avec des attributs de base) et certains sont complexes (électronique avec des dizaines de spécifications). L'acheminement d'enregistrements simples à Haiku et de enregistrements complexes à Sonnet -- même pour les descriptions -- aurait économisé un autre 20-30% sur les coûts d'API.
Si vous planifiez un projet similaire et souhaitez sauter les parties douloureuses, nous avons construit des pipelines réutilisables pour ce type de travail dans le cadre de notre pratique de développement CMS découplé. Heureux de partager plus de détails.
FAQ
Combien de temps faut-il pour enrichir 28 000+ enregistrements avec l'IA ?
Notre temps de traitement réel a été d'environ 11 jours, y compris le développement du pipeline, les tests, le traitement et la révision QA. Le traitement par API lui-même (envoi de demandes et obtention de réponses) a pris environ 48 heures d'exécution continue. Si vous utilisez l'API Batch exclusivement, attendez 24-48 heures pour le traitement plus 3-5 jours pour l'ingénierie et QA.
Quel est le coût par enregistrement pour l'enrichissement de contenu par IA ?
En utilisant Claude 3.5 Sonnet et Haiku en combinaison, notre coût d'API était approximativement 0,092 $ par enregistrement pour générer une description de produit, des catégories, une méta-description et des attributs structurés. Votre kilométrage variera en fonction des longueurs d'entrée/sortie et du modèle que vous choisissez. Le traitement par API par lot réduit ceci environ de moitié.
Claude ou GPT-4 est-il meilleur pour l'enrichissement de données en masse ?
Les deux fonctionnent bien. Nous avons choisi Claude 3.5 Sonnet en raison de sa conformité supérieure au format JSON lors de nos tests (97,1% vs ~94% pour GPT-4o). Cependant, GPT-4o est légèrement moins cher pour les tokens de sortie. Si votre enrichissement est principalement une classification plutôt qu'une génération de contenu, la différence est négligeable. Testez les deux avec 500 enregistrements avant de vous engager.
Comment gérez-vous les limites de débit lors d'effectuer des milliers d'appels d'API ?
Utilisez une bibliothèque de limiteur de débit comme Bottleneck, définissez une concurrence conservatrice (5-10 demandes parallèles), implémentez un backoff exponentiel pour les tentatives, et laissez un buffer de 10-15% en dessous des limites de débit publiées. Pour le travail non sensible au temps, l'API Batch d'Anthropic évite les problèmes de limite de débit entièrement et coûte 50% moins cher.
Quel pourcentage d'enregistrements enrichis par IA nécessitent un examen humain ?
Dans notre projet, 8,3% des enregistrements avaient besoin d'une certaine forme d'édition humaine et 1,9% ont été complètement rejetés et réécrits manuellement. Vos chiffres dépendront de la qualité des données, de l'ingénierie des invites et des seuils de qualité acceptables. Planifiez 5-15% d'intervention humaine comme base réaliste.
L'enrichissement en masse par IA peut-il gérer plusieurs langues ?
Oui, mais la qualité varie considérablement selon la langue. Claude et GPT-4 gèrent bien les principales langues européennes, mais la précision baisse pour les langues moins courantes. Nous recommandons d'exécuter des modèles d'invite séparés par langue et d'avoir des locuteurs natifs dans votre échantillon QA. Attendez-vous à ce que le pourcentage d'examen humain se double environ pour le contenu non anglais.
Comment prévenez-vous les hallucinations d'IA dans les données de produits ?
Trois couches : les instructions d'invite interdisant explicitement les fonctionnalités inventées, un drapeau « incertain » pour les données ambigues, et la validation automatisée comparant les attributs enrichis aux données source. Nous avons également utilisé la notation de similarité sémantique pour signaler les descriptions qui s'écartaient trop des informations de produit d'origine. Cela a réduit les attributs hallucinations d'environ 70%.
Vaut-il la peine de construire un pipeline personnalisé ou devrais-je utiliser un outil existant ?
Pour moins de 1 000 enregistrements, des outils comme Clay, Bardeen ou même une configuration bien structurée de Google Sheets + Apps Script peuvent fonctionner. Au-delà de cela, un pipeline personnalisé se paie rapidement. Le contrôle sur la logique de tentative, la validation de qualité et l'optimisation des coûts qu'une solution personnalisée fournit est essentiel à grande échelle. Notre pipeline était environ 2 000 lignes de TypeScript -- pas trivial, mais pas un énorme projet non plus. Consultez notre page de tarification si vous souhaitez que nous en construisions un pour votre cas d'usage.