{
  "title": "Réparer la recherche B2B à l'ère de l'IA",
  "excerpt": "J'ai audité 775 000 fiches produits alimentant la recherche de quatre distributeurs B2B. Les géants de la consommation ont déjà publié comment corriger ces catalogues avec des LLM, mais la distribution professionnelle n'a pas encore repris le manuel. Le voici, adapté pour le CVC, la plomberie et l'électricité, avec les coûts réels.",
  "content_html": "<p>La recherche dans le commerce B2B a un sale secret : l'algorithme de classement est rarement le problème.</p>\n<p>Quand les résultats sont mauvais, les équipes se tournent vers les leviers amusants. Boosts, synonymes, plongements, re-rankers, et dernièrement la compréhension des requêtes par LLM. J'ai moi-même actionné tous ces leviers. La semaine dernière, j'ai plutôt fait le travail ingrat : j'ai déversé chaque fiche produit de quatre index de recherche en production – 775 051 fiches chez quatre distributeurs B2B dans le CVC, la plomberie, la fourniture industrielle et la visserie – et j'ai audité les données elles-mêmes. J'ai utilisé douze agents IA fonctionnant en parallèle, chacun assigné à une dimension : couverture, qualité du texte, numéros de pièces, marques, prix, spécifications, catégories, médias, doublons.</p>\n<p>Ce qui est ressorti serait banal pour quiconque a travaillé sur le catalogue d'un distributeur, et choquant pour les autres. Mais ce qui m'a marqué, c'est ceci : la solution est déjà publiée. Amazon, DoorDash et Instacart ont passé deux ans à écrire, dans des blogs d'ingénierie publics, exactement comment ils utilisent les LLM pour nettoyer, étiqueter et enrichir des catalogues désordonnés à grande échelle. La distribution professionnelle n'a pour la plupart pas remarqué. Ce qui est étrange, car ses catalogues sont plus désordonnés, ses requêtes valent plus (un entrepreneur commandant un condenseur à 4 000 $, pas un déjeuner à 12 $), et la taille de ses catalogues rend en fait l'économie plus facile.</p>\n<p>Donc cet article est ce manuel, adapté à la distribution professionnelle : les problèmes de données qui cassent la recherche B2B, comment les trouver avec un agent de codage cet après-midi, comment les corriger avec des LLM, y compris la catégorisation UNSPSC, et ce que tout cela coûte. Spoiler sur le dernier point : moins que vous ne le pensez, de deux ordres de grandeur.</p>\n<h2>Le manuel existe déjà, juste pas dans notre secteur</h2>\n<p>Voici ce que les entreprises de marketplace ont publié :</p>\n<ul>\n<li>DoorDash utilise des LLM avec génération augmentée de récupération pour <a href=\"https://careersatdoordash.com/blog/how-doordash-leverages-llms-for-better-search-retrieval/\">construire son graphe de connaissances produits et améliorer la récupération de recherche</a> : extraction automatisée de marques, d'attributs et liaison d'entités à travers des millions d'articles fournis par les marchands.</li>\n<li>Amazon a construit <a href=\"https://www.amazon.science/blog/building-commonsense-knowledge-graphs-to-aid-product-recommendation\">COSMO</a>, un graphe de connaissances généré par LLM qui relie ce que les clients veulent dire (« chaussures pour femmes enceintes ») à ce que les produits sont (« chaussures antidérapantes »). Ils rapportent jusqu'à 60 % d'amélioration des performances de recommandation dans les tests hors ligne.</li>\n<li>Instacart a intégré les LLM dans la pile de recherche pour <a href=\"https://tech.instacart.com/supercharging-discovery-in-search-with-llms-556c585d4720\">générer du contenu de découverte</a> et <a href=\"https://www.instacart.com/company/tech-innovation/building-the-intent-engine-how-instacart-is-revamping-query-understanding-with-llms\">reconstruit la compréhension des requêtes autour d'eux</a> – avec la lourde génération effectuée hors ligne par lots, précisément pour réduire les coûts.</li>\n</ul>\n<p>Le fil conducteur est facile à manquer : les gains des LLM se situent du côté du catalogue et hors ligne. Ces entreprises enrichissent les données avant qu'une seule requête n'arrive. Les systèmes de découverte se divisent en une partie hors ligne qui construit des artefacts (le catalogue, les index, les plongements) et une partie en ligne qui récupère et classe sur ceux-ci, et presque toute la valeur publiée des LLM atterrit hors ligne. La couche de classement ne peut être aussi bonne que ce qu'on lui donne.</p>\n<p>Imaginez maintenant le catalogue d'un distributeur CVC ou de plomberie typique à côté de cela. Il est assemblé à partir d'exportations ERP, de feuilles de calcul de fournisseurs, de flux de groupes d'achat et d'un analyseur CSV écrit il y a une décennie. Les « titres » des produits sont des abréviations de facture écrites pour les préparateurs d'entrepôt. Et le trafic de recherche qui le frappe est aussi dense en intention que possible : la moitié sont des numéros de pièces exacts, le reste du jargon professionnel comme « 3/4 cxc 90 ell ». Honnêtement, je ne peux pas penser à un meilleur environnement pour ce manuel. Les requêtes sont précieuses, les données sont réparables, et avec quelques centaines de milliers de SKU, un passage complet du LLM sur le catalogue coûte des centaines de dollars. Pas des millions. Des centaines.</p>\n<p>Voici la structure de l'audit, car vous voudrez la reproduire :</p>\n<pre><code class=\"language-mermaid\">flowchart TD\n    D[\"Extraction catalogue brut<br/>(JSONL, un enregistrement par ligne)\"] --> C1[\"Agents de couverture par catalogue<br/>taux de nullité, distributions, champs morts\"]\n    D --> C2[\"Agents de dimension<br/>texte · numéros de pièces · marques · prix<br/>spécifications · catégories · médias · doublons\"]\n    C1 --> S[\"Synthèse : résultats classés par<br/>gravité, avec exemples d'enregistrements\"]\n    C2 --> S\n    S --> F[\"Pipeline de correction : règles + enrichissement LLM<br/>+ classification UNSPSC + barrières d'ingestion\"]\n</code></pre>\n<p>Une note sur le format avant les prompts. Tout ce qui suit fonctionne sur une extraction générique : un objet JSON par ligne, chacun avec un <code>id</code> unique et les champs de l'enregistrement. Elasticsearch, OpenSearch, Solr, Algolia, Typesense, un PIM, une exportation de base de données – peu importe. Toutes les plateformes peuvent produire cette forme, et chaque prompt ici fonctionne dessus.</p>\n<h2>Catégorie 1 : Échecs silencieux du pipeline</h2>\n<p>La pire chose que j'ai trouvée n'était pas de mauvaises données écrites par quelqu'un. C'étaient des données que le pipeline avait détruites à l'entrée, sans prévenir personne.</p>\n<p>Ces catalogues construisent leur texte de recherche principal au moment de l'ingestion, en utilisant une regex scriptée qui extrait les dimensions et les unités des titres de produits. Sur les titres longs, la regex dépasse la limite de complexité du moteur, le processeur échoue, et l'enregistrement est indexé quand même – avec un champ d'erreur que personne ne lit et aucun champ de recherche principal du tout.</p>\n<p><img alt=\"Documents indexés sans champ de recherche principal, par distributeur\" src=\"/assets/images/b2b-search-silent-failures.png\" /></p>\n<p>Sur l'ensemble du parc, 52 570 enregistrements étaient assis dans l'index, invisibles pour le chemin de requête principal. Sur le pire catalogue, c'est un produit sur sept. Aucun tableau de bord ne l'a détecté car rien n'a échoué. Le pipeline retournait un succès toute la journée, tous les jours, pendant des mois.</p>\n<p>Deux autres résultats de la même famille. Le miroir obsolète : un champ détient la vérité (une carte de droits par compte), et un second champ aplati la reflète pour le filtrage. Sur un catalogue, le miroir avait dérivé sur 19,85 % des enregistrements – 31 000 produits actifs incorrectement cachés de la recherche filtrée par compte. Et le catalogue gelé : un index n'avait pas été rafraîchi depuis dix semaines alors que ses frères se mettaient à jour quotidiennement. La disponibilité était surveillée. La fraîcheur ne l'était pas.</p>\n<p>Les correctifs sont ennuyeux et c'est le but. Alerter sur la couverture des champs dérivés (champ dérivé vide alors que sa source ne l'est pas). Ne pas maintenir deux représentations d'un même fait ; dériver le champ filtrable au moment de l'écriture. Surveiller la fraîcheur des données par catalogue comme vous surveillez la disponibilité.</p>\n<p>Voici le prompt pour trouver tout cela dans votre propre catalogue (Claude Code ou <code>codex exec</code> ; mécanismes à la fin) :</p>\n<pre><code>Voici une extraction de mon catalogue de produits sous forme de fichiers JSONL dans ./dump/ - un enregistrement\nJSON par ligne, chacun avec un \"id\" unique et les champs du produit. Ma définition de champ/schéma\n(le cas échéant) se trouve dans schema.json.\n\nÉcrire du Python en streaming (ne pas tout charger en mémoire) pour trouver les échecs silencieux\ndu pipeline :\n1. Enregistrements portant un champ d'erreur/exception qu'un pipeline a estampillé.\n2. Pour chaque champ DÉRIVÉ (concaténations, variantes *_search, *_normalized) :\n   enregistrements où le champ dérivé est vide mais son champ source évident\n   ne l'est pas.\n3. Paires de champs qui ressemblent à des miroirs l'un de l'autre (un imbriqué/riche,\n   un plat/filtrable) : quantifier à quelle fréquence ils divergent.\n4. La distribution des horodatages mis à jour/indexés : une tranche du catalogue\n   est-elle gelée pendant que le reste se rafraîchit ?\n\nRapporter chaque résultat avec des comptes, % du catalogue, 5 exemples d'ids, et si les\nenregistrements concernés sont actifs/visibles. Classer par gravité.\n</code></pre>\n<h2>Catégorie 2 : Espaces réservés et sentinelles, ou : des données qui mentent</h2>\n<p>Les vérifications de nullité sont l'outil de qualité des données que tout le monde a déjà. Les espaces réservés les contournent, car un espace réservé est renseigné. Il n'est tout simplement pas vrai.</p>\n<p>Mon exemple préféré : sur un catalogue de 313 000 SKU, le champ marque avait 741 valeurs distinctes et un taux de remplissage de 99,99 %. Cela semble sain. La valeur la plus fréquente, sur 88,8 % de tous les enregistrements, était « Fournisseur approuvé ». Un espace réservé ERP. La marque réelle la plus populaire couvrait 0,3 % du catalogue. Ainsi, chaque facette de marque, boost de marque et réécriture basée sur la marque était effectivement morte pour les neuf dixièmes du catalogue, tandis que l'interface utilisateur des facettes proposait joyeusement « Fournisseur approuvé » comme filtre de marque numéro un.</p>\n<p>Une fois que vous commencez à chercher, ce genre de choses est partout :</p>\n<ul>\n<li>Une sentinelle « pas de prix » de <code>99999999.000000</code> sur 55 % d'un catalogue. Le prix médian de cet index était de quatre-vingt-dix-neuf millions de dollars.</li>\n<li>Des noms de produits littéralement <code>\"inconnu\"</code> sur 64 enregistrements, qui apparaissent ensuite pour la requête « inconnu ».</li>\n<li>Un booléen <code>true</code> écrit dans un champ de description textuelle sur 1 516 produits actifs. Le moteur l'a converti en chaîne « true », rendant ces produits trouvables en recherchant « true ».</li>\n<li>227 UPC stockés sous forme de <code>\"6.71E+11\"</code> – la notation scientifique d'Excel, avec 51 produits sans rapport partageant ce seul « identifiant » – plus 15 121 autres UPC manquant leur zéro non significatif. Excel frappe encore.</li>\n<li>Un catalogue de visserie dont le champ marque contenait des noms de gammes de produits (« Boulons C6L », « Pièces d'outillage ») tandis que la marque réelle se trouvait un champ plus loin.</li>\n</ul>\n<p>La leçon que j'ai tirée de cette catégorie : auditer les distributions des N premières valeurs par champ, pas les taux de nullité. Un champ rempli à 100 % peut être à 88 % de déchets, et aucune vérification de nullité ne vous le dira jamais. Ensuite, mettre sur liste noire les sentinelles à l'ingestion, les mapper à de vraies nullités avec des indicateurs explicites <code>has_price</code> / <code>has_real_image</code>, faire une assertion de type à la frontière, et valider les identifiants structurellement. Traiter tout ce qui est passé par un tableur comme suspect.</p>\n<pre><code>Même extraction JSONL. Pour chaque champ de mon catalogue, calculer la distribution des 20 premières\nvaleurs (passage complet, streaming).\n\nSignaler : (a) toute valeur unique couvrant &gt;10 % des enregistrements - candidats espaces réservés/sentinelles\ncomme \"Fournisseur approuvé\", \"inconnu\", 99999999, 0.0 ; (b) les valeurs dont le\ntype diffère du type dominant du champ (booléens dans des champs de texte, flottants\nparmi des chaînes) ; (c) les champs d'identifiants (UPC/EAN/GTIN/numéros de pièces) : valider\nla somme de contrôle et la longueur, signaler la notation scientifique, les zéros non significatifs supprimés,\nles espaces/unicode intégrés, et les identifiants partagés par plusieurs enregistrements ;\n(d) les valeurs de type catégorie ou gamme de produits se trouvant dans des champs de marque.\n\nPour chaque signalement : compte, % du catalogue, 5 exemples textuels avec ids, et une\nrègle d'ingestion d'une ligne qui l'aurait rejeté.\n</code></pre>\n<h2>Catégorie 3 : Contenu appauvri</h2>\n<p>Les catalogues des distributeurs sont écrits par des ERP, pas par des marchandiseurs. Le texte est une abréviation de facture : <code>PROPRESS 2-1/2X1 CXC RED CPLG</code>, <code>HC HX 18X8 W</code>. Sur un catalogue, un tiers des noms de produits avaient moins de 60 % de mots du dictionnaire. Sur un autre, les champs bruts de nom et de description étaient 100 % nuls – le texte n'existait qu'à l'intérieur de blobs de recherche dérivés, donc tout ce qui lisait les champs bruts (titres de résultats, entrées de plongement, prompts de jugement de pertinence) ne lisait rien et personne ne le savait.</p>\n<p><img alt=\"Couverture des champs sur quatre index B2B de production\" src=\"/assets/images/b2b-search-field-coverage.png\" /></p>\n<p>Cette carte de chaleur est mon artefact préféré de l'audit. Chacun de ces champs existe dans chaque schéma. Les chiffres indiquent la quantité de données réellement présentes. La ligne qui me frappe est celle des descriptions ML : la couche d'enrichissement que les schémas promettaient sur chaque enregistrement était renseignée sur dix-huit documents sur 775 051. Dix-huit. Chaque branche « utiliser l'enrichissement si présent » dans le pipeline de scoring était une non-opération silencieuse. Le schéma est une aspiration ; seule la couverture est un fait.</p>\n<p>Cette catégorie est celle où le manuel publié s'applique le plus directement – l'extraction d'attributs de DoorDash, la génération hors ligne d'Instacart – et la section des coûts ci-dessous l'évalue. Mais quatre règles comptent dans un métier de numéros de pièces, apprises en partie de ce que la tentative d'enrichissement précédente a mal fait :</p>\n<ul>\n<li><strong>Développer, ne pas remplacer.</strong> Générer le titre lisible par le client à côté de la chaîne de facture brute et indexer les deux. La requête du technicien et celle du propriétaire devraient toutes deux aboutir.</li>\n<li><strong>Ne jamais verbaliser les codes.</strong> Ces dix-huit enregistrements pionniers ? Le générateur avait épelé les numéros de modèle en mots : « RGF one hundred eighty ». Aucun entrepreneur ne tapera jamais cela. Les codes restent textuellement ; les expansions sont des ajouts, pas des remplacements.</li>\n<li><strong>Extraire la structure pendant que vous y êtes.</strong> Le même passage qui réécrit un titre peut émettre <code>{size, material, connection_type}</code> pour les facettes.</li>\n<li><strong>Le jargon professionnel est un problème de synonymes, pas de réécriture.</strong> <code>CXC</code>, <code>ELL</code>, <code>CPLG</code>, « t-stat » appartiennent à une expansion de synonymes au niveau de l'analyseur, corrigée une fois. Ne payez pas pour les réécrire dans un demi-million d'enregistrements.</li>\n</ul>\n<pre><code>Même extraction. Évaluer la qualité du texte des champs orientés client (nom, description\ncourte/longue) avec un passage complet en streaming :\n\n1. Couverture du titre effectif : % d'enregistrements ACTIFS avec un nom d'affichage non vide.\n2. Lisibilité : % de noms en MAJUSCULES ; % avec moins de 60 % de jetons de mots du dictionnaire\n   (salade d'abréviations) ; noms de moins de 10 caractères ; descriptions uniquement numériques.\n3. Déchets : balisage HTML/CMS, artefacts d'encodage, notes opérationnelles intégrées\n   (*** PAS UN ARTICLE PHYSIQUE ***), débordement de colonne CSV.\n4. Doublons : descriptions courtes/longues identiques ; texte générique partagé par 20+\n   SKU ; clusters de longueur exacte (254/255/500 caractères) indiquant des limites VARCHAR\n   en amont.\n5. Vérification de la réalité de l'enrichissement : pour chaque champ ML/enrichi/plongement que le schéma\n   promet, sa couverture réelle.\n\nChiffres, 5 exemples textuels chacun, gravité, et quels problèmes nécessitent un passage d'enrichissement\nLLM vs. une correction de pipeline vs. une correction de couche de synonymes.\n</code></pre>\n<h2>Catégorie 4 : Les numéros de pièces sont sacrés</h2>\n<p>La moitié du trafic de recherche B2B est quelqu'un qui tape un numéro de pièce. La correspondance exacte est tout le jeu, et elle échoue de manière silencieuse.</p>\n<p>Sur un catalogue, le numéro de pièce indexé était un SKU interne numérique sur 99,99 % des enregistrements. Le numéro du fabricant – celui imprimé sur la boîte – n'existait qu'à l'intérieur des descriptions en texte libre. 183 000 produits inaccessibles par le numéro qu'un client taperait réellement.</p>\n<p>Un autre catalogue avait un système d'expansion de variantes : supprimer les tirets, fusionner les segments, indexer les variantes pour une recherche PN indulgente. Bonne idée. Mais le générateur était combinatoire (jusqu'à 80 variantes par produit), et sur 11 826 enregistrements, une variante générée d'un produit était exactement égale au numéro de pièce canonique d'un produit différent. La visserie est le cas brutal, car la ponctuation encode les dimensions physiques : <code>702-1-5/32</code> est une pièce de 1-5/32 pouce et <code>702-15/32</code> est une pièce de 15/32 pouce, et les deux se normalisent en <code>7021532</code>. Un client tape un numéro de pièce exact et obtient un pile ou face entre la bonne pièce et son cousin dimensionnel.</p>\n<p>Également dans cette catégorie : les références croisées concurrentes (« J'ai le numéro Ferguson, quel est le vôtre ? ») stockées sous forme de blobs non séparés délimités par des pipes comme <code>19MU82|Fastenal 0269214|Ferguson M48222407</code>, dans des champs configurés comme non recherchables, donc un mouvement de vente B2B central ne fonctionne tout simplement pas. Et les fourches d'espaces – le même numéro de pièce indexé trois fois (simple, avec NBSP, avec espace de fin) – plus des jumeaux mojibake provenant d'UTF-8 doublement décodé.</p>\n<p>Le principe qui corrige la plupart de cela : l'exact doit battre le flou structurellement, pas probabilistiquement. Une clause exacte préservant la ponctuation notée strictement au-dessus de tout niveau normalisé, jamais une correspondance plate où une variante peut égaler un résultat canonique. Vérifier les variantes générées par rapport à l'espace PN canonique avant l'indexation et supprimer les collisions. Normaliser Unicode avant de dériver les ID d'enregistrement. Diviser les blobs de références croisées en tableaux ; c'est une seule ligne de code d'ingestion qui déverrouille toute une classe de requêtes.</p>\n<pre><code>Même extraction. Mes utilisateurs recherchent par numéro de pièce ; auditer l'intégrité de la\ncorrespondance exacte :\n\n1. Quels champs de type PN existent (part_number, mpn, PN alternatifs/concurrents/clients,\n   UPC), leur couverture, et lesquels sont configurés comme non recherchables tout en contenant\n   des données réelles.\n2. Enregistrements dont le seul jeton PN recherchable est un SKU interne (le vrai PN du\n   fabricant apparaît uniquement dans le texte de description - montrer le motif).\n3. S'il existe un champ d'expansion de variantes/mots-clés PN : trouver chaque variante\n   générée qui est égale au PN canonique d'un enregistrement DIFFÉRENT (normaliser :\n   minuscules, supprimer la ponctuation). Montrer les paires en collision - en particulier\n   les tailles de visserie avec fractions.\n4. PN ne différant que par des espaces/caractères invisibles/casse du PN d'un autre\n   enregistrement (fourches d'identité en double).\n5. Champs de références croisées multi-valeurs stockés sous forme de blobs délimités au lieu de\n   tableaux.\n\nComptes, exemples, et pour chaque problème la règle d'ingestion ou le changement de constructeur de\nrequête qui le corrige.\n</code></pre>\n<p>Quelques choses qui ne rentraient pas ci-dessus mais méritent une phrase : un générateur de synonymes qui transformait n'importe quel nombre en termes de calibre de fil, donc <code>REF#259286</code> est devenu « 259286 awg » et un pas de vis <code>#8-32</code> est devenu « fil de calibre 8 » (des milliers d'enregistrements correspondent maintenant à des requêtes électriques avec lesquelles ils n'ont rien à voir) ; les indicateurs internes d'une plateforme e-commerce indexés comme des spécifications de produit recherchables, 2,1 millions de paires clé-valeur indésirables ; et 18 champs de schéma renseignés sur zéro enregistrement nulle part. Les générateurs d'expansion ont besoin de barrières de contexte. Un schéma mort est une fausse promesse sur laquelle quelqu'un finira par construire.</p>\n<h2>La couche manquante : UNSPSC, et les produits qui ne savent pas ce qu'ils sont</h2>\n<p>Il y a un cinquième problème qui mérite sa propre section, car c'est là que la distribution professionnelle est la plus en retard par rapport aux marketplaces : la catégorisation.</p>\n<p>Dans mon audit, les champs de catégorie étaient manquants sur 83 % d'un catalogue. Les clés d'attributs n'avaient aucune taxonomie du tout – 7 068 clés de spécification distinctes sur un catalogue de 156 000 enregistrements, 64 % d'entre elles utilisées sur moins de dix produits, avec des dérives comme <code>horse_power</code> vs <code>horsepower</code> divisant le même attribut entre les facettes. Et le détail auquel je reviens sans cesse : deux des quatre catalogues avaient des champs UNSPSC dans leurs schémas, mappés et prêts, renseignés sur exactement zéro enregistrement. Quelqu'un savait que la catégorisation était importante, a construit l'emplacement, et ne l'a jamais rempli. Je parierais sur la raison : classer 300 000 SKU dans une taxonomie à la main est une année de travail d'équipe catalogue, donc c'est resté sur la feuille de route pour toujours.</p>\n<p>Si vous êtes en dehors du B2B, UNSPSC est le code standard des Nations Unies pour les produits et services, la taxonomie que parlent les systèmes d'approvisionnement. C'est la partie que la recherche grand public n'a pas : les systèmes d'achat de vos clients l'exigent. Les catalogues Punchout, les plateformes d'e-procurement et les outils d'analyse des dépenses sont construits autour des codes UNSPSC. Un distributeur dont le catalogue porte des codes propres peut se connecter au système d'achat d'un entrepreneur ou d'un hôpital. Un distributeur sans eux est une liste de prix PDF avec une boîte de recherche. En interne, c'est aussi ce qui alimente les facettes de catégorie, le classement limité à une catégorie (« un nombre qui s'analyse comme un calibre de fil ne devrait booster que le fil »), le dédoublonnage inter-catalogues, et les barrières de contexte pour chaque générateur d'expansion de la catégorie 4.</p>\n<p>Et c'est maintenant un travail par lots. C'est la même forme de problème que DoorDash décrit en résolvant avec des LLM – étiquetage à haut volume contre un vocabulaire contrôlé – et l'approche se transfère directement :</p>\n<ul>\n<li><strong>Classer hiérarchiquement, pas à plat.</strong> UNSPSC a quatre niveaux (segment, famille, classe, marchandise). Faire choisir au modèle la famille parmi ~450 options, puis la classe au sein de cette famille. Deux petits choix contraints battent un choix parmi 50 000, et vous pouvez alimenter la tranche de taxonomie pertinente dans le contexte comme DoorDash contraint la liaison d'entités à son vocabulaire.</li>\n<li><strong>S'arrêter d'abord au niveau de la classe.</strong> Les codes de classe déverrouillent déjà les facettes et l'intégration avec l'approvisionnement ; la précision au niveau de la marchandise peut venir plus tard là où elle est rentable.</li>\n<li><strong>Acheminer par confiance.</strong> Un petit modèle prend les cas clairs, les enregistrements à faible confiance montent vers un modèle plus grand, et les désaccords persistants vont dans une file d'attente humaine qui sert également d'ensemble d'évaluation.</li>\n</ul>\n<p>Le coût est presque gênant à taper. La classification est une tâche à sortie courte : environ 300 jetons d'entrée par enregistrement et 30 en sortie. Sur Claude Haiku 4.5 au tarif API Batch, cela représente environ 17 cents par millier de SKU. L'ensemble de mon parc de 775 000 enregistrements serait classé pour environ 130 $. La chose qui est restée sur la feuille de route pendant des années parce que c'était une année de travail manuel coûte moins cher que le déjeuner d'équipe où vous en discuteriez.</p>\n<pre><code>Vous classifiez des produits de distributeur B2B dans UNSPSC. Ci-joint : la liste des familles UNSPSC\n(niveau 2, ~450 entrées) comme données de référence.\n\nPour chaque enregistrement produit (titre, description, marque, numéro de pièce, tout texte de catégorie\nexistant), sortir JSON :\n- unspsc_family : le code famille à 4 chiffres - choisir UNIQUEMENT dans la liste jointe\n- family_confidence : élevée | faible\n- rationale : une courte phrase (par ex. \"raccord à presser en cuivre -&gt; raccords de tuyauterie\")\n\nRègles : juger d'après la fonction du produit, pas sa marque. Abréviations professionnelles :\nCXC/FPT/MPT sont des connexions de tuyaux, ELL est un coude, CPLG est un raccord, une fraction nue+matériau\nest généralement une taille de raccord. Si l'enregistrement n'est pas un produit physique (lignes de fret,\nsuppléments, \"*** PAS UN ARTICLE PHYSIQUE ***\"), sortir NOT_A_PRODUCT. Utiliser faible confiance\nlibéralement - les enregistrements à faible confiance reçoivent un second passage avec la liste au niveau de la classe.\n</code></pre>\n<h2>Le manuel de nettoyage, avec la facture réelle</h2>\n<p>Le pipeline de correction a trois niveaux plus le travail de classification. L'astuce est de dépenser au bon niveau, car vous n'avez presque jamais besoin d'un appel LLM par enregistrement.</p>\n<pre><code class=\"language-mermaid\">flowchart LR\n    A[\"Niveau 0 - Règles<br/>code déterministe<br/>~0 $\"] --> B[\"Niveau 1 - LLM sur les valeurs uniques<br/>marques · clés de spécification · unités<br/>dizaines de dollars\"]\n    B --> C[\"Niveau 2 - LLM par enregistrement<br/>titres · attributs · UNSPSC<br/>centaines de dollars\"]\n    C --> G[\"Barrières d'ingestion<br/>chaque correction devient un validateur\"]\n</code></pre>\n<p><strong>Le niveau 0 est du code déterministe et c'est essentiellement gratuit.</strong> Supprimer le HTML et décoder les entités. Transformer les sentinelles en vraies nullités avec des indicateurs explicites. Réparer les UPC (compléter à gauche, rejeter la notation scientifique, valider le chiffre de contrôle). Normaliser Unicode pour les ID. Diviser les références croisées délimitées par des pipes. Supprimer les variantes PN qui entrent en collision avec le numéro canonique d'un autre produit. Supprimer les schémas morts. Un agent écrit ces scripts en une session, et ce niveau a corrigé environ la moitié de tout ce que mon audit a trouvé. Faites-le avant tout enrichissement, ou vous paierez un modèle pour réécrire magnifiquement des titres pour des produits que votre pipeline a silencieusement supprimés.</p>\n<p><strong>Le niveau 1 exécute le LLM sur des valeurs uniques, pas sur des enregistrements.</strong> C'est l'astuce qui rend les problèmes de vocabulaire bon marché : mon pire catalogue avait 313 000 enregistrements mais seulement 741 chaînes de marque distinctes. La normalisation des marques est un travail sur 741 chaînes – regrouper les variantes de casse, mapper les sous-marques aux marques mères, signaler les espaces réservés – pas 313 000 appels. Même chose pour les clés de spécification et les suffixes d'unités. Chaque travail de vocabulaire coûte un chiffre unique en dollars et sa sortie est une table d'alias statique que votre ingestion applique pour toujours, de manière déterministe. Comptez 20 à 50 $ pour l'ensemble du parc, principalement dépensés en passages de révision.</p>\n<pre><code>Ci-joint : la distribution complète des valeurs distinctes de mes champs de marque et de fabricant\n(valeur, record_count). Produire une table de normalisation :\n\ncanonical_brand, parent_manufacturer, [variantes brutes], confidence\n\nRègles : regrouper les variantes de casse/ponctuation/espace ; mapper les sous-marques aux fabricants\nparents dans une colonne séparée (ne pas fusionner) ; marquer les espaces réservés comme\nNULL_SENTINEL ; marquer les gammes de produits ou catégories mal classées comme marques comme\nNOT_A_BRAND. Signaler les clusters incertains plutôt que de deviner. Sortir CSV, puis\nécrire un script qui l'applique à l'ingestion et enregistre les nouvelles valeurs non appariées.\n</code></pre>\n<p><strong>Le niveau 2 est le passage par enregistrement</strong> : un titre lisible par le client, des expansions de recherche, des attributs structurés et un numéro de pièce fabricant récupéré pour chaque produit. Par SKU, c'est petit – environ 400 jetons d'entrée (l'enregistrement, plus les instructions partagées que la mise en cache des prompts rend presque gratuites) et 250 en sortie. Deux leviers avant même de choisir un modèle : enrichir ce qui est actif (sur mon pire catalogue, seulement 14 % des enregistrements étaient actifs, une réduction de 86 % immédiate), et utiliser une API batch là où le fournisseur en a une, car l'enrichissement n'a pas d'exigence de latence et Anthropic comme OpenAI réduisent de 50 % pour le batch.</p>\n<p>J'ai évalué le même travail sur tout ce que vous utiliseriez plausiblement en 2026 – Claude, les <a href=\"https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/\">trois niveaux de GPT-5.6</a> (Sol, Terra, Luna), Kimi K3, GLM-5.2, Qwen3.5, et <a href=\"https://deepseek.ai/pricing\">DeepSeek V4</a>. Même charge de travail, tarifs publiés par les fournisseurs, remise batch appliquée là où elle existe :</p>\n<p><img alt=\"Coût pour enrichir 775 000 SKU selon les modèles\" src=\"/assets/images/b2b-search-enrichment-cost-by-model.png\" /></p>\n<p>J'ai dû vérifier ces chiffres deux fois car ils semblaient faux. La version la plus coûteuse de ce projet – un modèle de pointe, chaque SKU, aucun filtrage du tout – plafonne à environ 3 800 $. Le plancher est en dessous de cent dollars : DeepSeek V4-Flash enrichirait l'ensemble du parc pour environ le prix d'une bonne perceuse. La version que vous exécuteriez réellement mélange les niveaux : un modèle économique (Haiku, Luna, Qwen Flash, DeepSeek) pour la majorité mécanique, un modèle de milieu de gamme (Sonnet, Terra, GLM-5.2) pour la queue de la salade d'abréviations que le petit signale comme à faible confiance, et un modèle de pointe vérifiant un échantillon de 1 à 2 % comme porte de qualité. Cela atterrit à quelques centaines de dollars pour les SKU actifs, quels que soient les fournisseurs que vous choisissez.</p>\n<p>Deux mises en garde que le graphique ne peut pas montrer. Les modèles bon marché ne sont bon marché que si leur sortie survit à votre évaluation – exécutez la comparaison sur 200 échantillons avant de vous engager, car un modèle économique qui déforme 5 % des numéros de pièces coûte plus cher que le modèle de pointe qui ne le fait pas. Et votre catalogue est une donnée concurrentielle : les prix, les références croisées et les numéros de pièces clients se trouvent dans ces enregistrements, donc vérifiez les conditions de conservation des données et d'entraînement de chaque fournisseur avant d'envoyer 775 000 enregistrements au point de terminaison le moins cher de la liste. Pour de nombreux distributeurs, cette seule vérification exclut certains fournisseurs, et les options open-weight (DeepSeek, GLM, Qwen, Kimi) ont la propriété supplémentaire que vous pouvez les auto-héberger si les données ne peuvent pas du tout sortir.</p>\n<p>Ajoutez le passage UNSPSC d'environ 130 $ (qui évolue de la même manière selon les modèles – sur le niveau économique, il tombe à de la monnaie de poche) et le travail de vocabulaire, et la transformation complète du catalogue se situe entre quelques centaines et quelques milliers de dollars, contre un travail qui était autrefois l'année d'une équipe de marchandisage. Ce sont les mêmes économies de batch hors ligne qu'Instacart décrit, juste à l'échelle du catalogue professionnel où les chiffres deviennent suffisamment petits pour être mis sur une carte de crédit.</p>\n<p>Voici à quoi cela ressemble sur un enregistrement réel de mon audit :</p>\n<pre><code>ENTRÉE :  part_number : 1678619\n         short_desc :  PROPRESS 2-1/2X1 CXC RED CPLG 20685\n         brand :       (espace réservé)\n\nSORTIE : display_title :      2-1/2\" x 1\" ProPress Copper Reducing Coupling\n         search_expansions :  [\"raccord à presser en cuivre\", \"raccord de réduction\",\n                              \"raccord press x press\"]\n         attributes :         {size_1 : {value : 2.5, unit : in},\n                               size_2 : {value : 1, unit : in},\n                               material : copper, connection : press}\n         extracted_mpn :      \"20685\"\n         unspsc_family :      4017 (raccords de tuyauterie)\n         confidence :         élevée\n</code></pre>\n<p>Regardez la ligne <code>extracted_mpn</code>. Le même passage récupère les numéros de pièce fabricant qui étaient auparavant enfouis dans le texte de description – sur un de mes catalogues, cela représente 183 000 produits devenant trouvables par le numéro imprimé sur la boîte. Cela seul paie le travail par lots.</p>\n<pre><code>Vous enrichissez des fiches produits de distributeur B2B pour la recherche. Pour chaque\nenregistrement d'entrée (nom ERP brut, descriptions, marque, numéro de pièce, catégorie), produire JSON :\n\n- display_title : lisible par le client, &lt;=80 caractères, en casse de titre. Développer les abréviations\n  professionnelles (CXC -&gt; connexion cuivre x cuivre, ELL -&gt; coude, CPLG -&gt; raccord,\n  RED -&gt; réduction). Garder chaque numéro de pièce, code modèle et dimension\n  TEXTUEL comme tapé - ne jamais épeler les codes en mots.\n- search_expansions : 2 à 5 phrases qu'un client pourrait taper qui n'apparaissent pas dans\n  le texte brut (type de produit en anglais simple, noms commerciaux courants). Ne jamais inventer\n  des spécifications absentes de la source.\n- attributes : {nom : {valeur, unité}} extraits UNIQUEMENT du texte source.\n- extracted_mpn : numéro de pièce fabricant s'il est présent dans le texte de description\n  (généralement le jeton après la marque) et absent des champs PN, sinon null.\n- confidence : élevée | faible. Utiliser faible chaque fois que vous avez dû deviner ; une réponse\n  à faible confiance orientée vers la révision bat une hallucination confiante.\n</code></pre>\n<p>Deux notes de production : mettre les instructions partagées dans un préfixe de prompt mis en cache (la mise en cache réduit le côté entrée jusqu'à 90 % en plus de la remise batch), et utiliser des sorties structurées avec un schéma JSON pour ne jamais payer de taxe d'échec d'analyse.</p>\n<p><strong>Le dernier niveau est celui que les gens sautent, et c'est celui qui cumule.</strong> L'audit vaut pour un nettoyage. Les validateurs qu'il génère valent pour chaque flux futur. Terminez la session de correction avec :</p>\n<pre><code>Transformer chaque problème que nous venons de corriger en une vérification automatisée qui fait échouer le pipeline\nd'ingestion de manière bruyante : assertions de couverture des champs dérivés, listes noires de sentinelles, assertions\nde type sur les champs de texte, validation de somme de contrôle des identifiants, vérifications de collision de\nvariantes PN, règles d'hygiène des ID, suivi de la couverture UNSPSC, et une alarme de\nfraîcheur des données par catalogue. Les émettre sous forme de tests que CI exécute sur un échantillon de\nchaque nouveau flux.\n</code></pre>\n<p>Sautez cela et les mêmes exportations ERP régénéreront les mêmes déchets en un trimestre, et vous paierez pour le nettoyage deux fois. Je le sais car deux des bugs que mon audit a trouvés avaient clairement été corrigés auparavant, et étaient revenus.</p>\n<h2>Exécuter les prompts</h2>\n<p>Une étape de configuration : exporter votre catalogue en JSONL, un objet JSON par ligne avec un <code>id</code> unique et les champs de l'enregistrement. Quelle que soit votre plateforme – Elasticsearch, OpenSearch, Solr, Algolia, Typesense, un PIM, la base de données derrière tout cela – demandez simplement à l'agent d'écrire l'exportateur :</p>\n<pre><code>Écrire un script qui exporte chaque fiche produit de mon catalogue [décrire :\nnom de l'index de recherche / table de base de données / exportation PIM] vers ./dump/records-{n}.jsonl\npar lots de 50 000 enregistrements - un objet JSON par ligne avec un \"id\" unique plus tous\nles champs - et ma définition de champ/schéma (si la plateforme en a une) vers\nschema.json. Vérifier que le nombre exporté correspond au nombre source.\n</code></pre>\n<p>Avec Claude Code, déposez n'importe quel prompt de catégorie dans une session dans ce répertoire. Il écrit et exécute l'analyse lui-même, des passages complets sans excuses d'échantillonnage, et revient avec des reçus au niveau des enregistrements. Quand vous dites « maintenant corrige-le », la même session produit le validateur d'ingestion, le script de remise à niveau et le travail d'enrichissement par lots. Exécutez les catégories comme des sessions parallèles ou des sous-agents ; c'est ainsi que j'ai exécuté les miennes, et l'audit entier a pris environ douze minutes de temps réel. Avec Codex, <code>codex exec</code> gère les passages d'analyse en lecture seule en utilisant les mêmes prompts ; gardez les corrections côté écriture dans une session que vous révisez.</p>\n<p>Trois règles que j'ai apprises à la dure :</p>\n<ol>\n<li><strong>Exiger des passages complets et des reçus.</strong> « Analyser ces données » invite à l'échantillonnage et aux impressions. Demandez des comptes exacts et cinq exemples d'ID d'enregistrement par résultat, et chaque affirmation devient vérifiable.</li>\n<li><strong>Les règles avant l'enrichissement.</strong> Corriger la vérité avant d'ajouter de la prose, ou vous hallucinerez des marques pour les 88 % d'enregistrements étiquetés « Fournisseur approuvé ».</li>\n<li><strong>Échantillonner, évaluer, puis traiter par lots.</strong> Ne lancez jamais un lot de 775 000 enregistrements à partir d'un prompt non validé. 200 échantillons, un passage de notation, puis mise à l'échelle. Le harnais d'évaluation coûte une session d'agent et dé-risque la dépense totale.</li>\n</ol>\n<h2>Les professionnels méritent une recherche de qualité marketplace</h2>\n<p>Amazon, DoorDash et Instacart n'ont pas publié leur travail sur les LLM de catalogue par charité. Ils l'ont publié parce que les techniques sont générales et que le fossé est l'exécution. Et les techniques se transfèrent à la distribution professionnelle mieux que presque partout ailleurs où je peux penser : les catalogues sont suffisamment petits pour qu'un passage complet soit bon marché, les données sont suffisamment mauvaises pour que la marge de progression soit énorme, les requêtes transportent de l'argent réel, et le monde de l'approvisionnement fonctionne déjà sur une taxonomie qu'un travail par lots peut maintenant remplir pour environ 130 $.</p>\n<p>Parmi les dizaines de problèmes que mon audit a révélés sur quatre catalogues de distributeurs, aucun n'était visible depuis la couche de classement. Tous l'ont dégradée. La chaîne d'approvisionnement alimentant ces index – les exportations ERP, les feuilles de calcul des fournisseurs, les analyseurs CSV ancestraux – est exactement ce que le manuel publié corrige. Les distributeurs qui l'exécutent auront une recherche de qualité marketplace sur des catalogues où leurs concurrents ne peuvent toujours pas trouver une batterie de condenseur.</p>\n<p>La question « y a-t-il quelque chose qui cloche dans mes données ? » coûtait autrefois deux semaines du temps de quelqu'un, c'est pourquoi personne ne la posait. Elle coûte maintenant un prompt. Posez-la.</p>",
  "source_hash": "sha256:beacaae673c45f36f13a5677632c8df46a2339c6bdea51c65c49f762d7f0805c",
  "model": "deepseek/deepseek-v4-flash",
  "generated_at": "2026-08-07T08:11:56.721569+00:00"
}