{
  "title": "Die B2B-Commerce-Suche im KI-Zeitalter reparieren",
  "excerpt": "Ich habe 775.000 Produktdatensätze geprüft, die die Suche für vier B2B-Distributoren antreiben. Die Consumer-Giganten haben bereits veröffentlicht, wie man solche Kataloge mit LLMs repariert, aber der Fachhandel hat das Playbook noch nicht aufgegriffen. Hier ist es – angepasst für HVAC, Sanitär und Elektro, mit echten Kosten.",
  "content_html": "<p>Die B2B-Commerce-Suche hat ein dunkles Geheimnis: Der Ranking-Algorithmus ist selten das Problem.</p>\n<p>Wenn die Ergebnisse schlecht sind, greifen Teams zu den spannenden Hebeln. Boosts, Synonyme, Embeddings, Reranker und in letzter Zeit LLM-Query-Understanding. Ich habe all diese Hebel selbst gezogen. Letzte Woche habe ich stattdessen das Unspektakuläre getan: Ich habe jeden Produktdatensatz aus vier produktiven Suchindizes exportiert – 775.051 Datensätze von vier B2B-Distributoren aus den Bereichen HVAC, Sanitär, Industriebedarf und Befestigungstechnik – und die Daten selbst geprüft. Ich habe zwölf KI-Agenten parallel laufen lassen, jeder mit einer Dimension beauftragt: Abdeckung, Textqualität, Teilenummern, Marken, Preisgestaltung, Spezifikationen, Kategorien, Medien, Duplikate.</p>\n<p>Das Ergebnis wäre für jeden, der schon an einem Distributorenkatalog gearbeitet hat, nichts Aufregendes – und für jeden, der nicht, schockierend. Aber das, was mir hängen geblieben ist, ist folgendes: Die Lösung ist bereits veröffentlicht. Amazon, DoorDash und Instacart haben zwei Jahre damit verbracht, in öffentlichen Engineering-Blogs genau zu beschreiben, wie sie LLMs nutzen, um unordentliche Kataloge im großen Maßstab zu bereinigen, zu labeln und anzureichern. Der Fachhandel hat das größtenteils nicht mitbekommen. Das ist seltsam, denn seine Kataloge sind unordentlicher, seine Queries sind mehr wert (ein Auftragnehmer, der einen 4.000-Dollar-Kondensator bestellt, nicht ein 12-Dollar-Mittagessen), und seine Kataloggrößen machen die Ökonomik tatsächlich einfacher.</p>\n<p>Dieser Post ist also genau dieses Playbook, angepasst für den Fachhandel: die Datenprobleme, die die B2B-Suche kaputtmachen, wie man sie heute Nachmittag mit einem Coding-Agenten findet, wie man sie mit LLMs repariert – inklusive UNSPSC-Kategorisierung – und was das Ganze kostet. Spoiler zum Letzteren: weniger als man um zwei Größenordnungen vermuten würde.</p>\n<h2>Das Playbook existiert bereits, nur nicht in unserer Branche</h2>\n<p>Einiges von dem, was die Marketplace-Unternehmen veröffentlicht haben:</p>\n<ul>\n<li>DoorDash nutzt LLMs mit Retrieval-Augmented Generation, um <a href=\"https://careersatdoordash.com/blog/how-doordash-leverages-llms-for-better-search-retrieval/\">seinen Produkte-Wissensgraphen aufzubauen und die Such-Retrieval zu verbessern</a>: automatisierte Markenextraktion, Attributextraktion und Entity Linking über Millionen von Händler-bereitgestellten Artikeln.</li>\n<li>Amazon hat <a href=\"https://www.amazon.science/blog/building-commonsense-knowledge-graphs-to-aid-product-recommendation\">COSMO</a> gebaut, einen LLM-generierten Wissensgraphen, der verbindet, was Kunden meinen („Schuhe für Schwangere“) mit dem, was Produkte sind („rutschfeste Schuhe“). Sie berichten von bis zu 60 % Verbesserung der Empfehlungsperformance in Offline-Tests.</li>\n<li>Instacart hat LLMs in den Such-Stack integriert, um <a href=\"https://tech.instacart.com/supercharging-discovery-in-search-with-llms-556c585d4720\">Discovery-Content zu generieren</a>, und <a href=\"https://www.instacart.com/company/tech-innovation/building-the-intent-engine-how-instacart-is-revamping-query-understanding-with-llms\">baut das Query Understanding darum herum neu auf</a> – wobei die rechenintensive Generation offline in Batches erfolgt, genau um die Kosten niedrig zu halten.</li>\n</ul>\n<p>Der gemeinsame Nenner ist leicht zu übersehen: Die LLM-Erfolge finden auf Katalogseite und offline statt. Diese Unternehmen bereichern die Daten, bevor eine einzige Query eintrifft. Discovery-Systeme teilen sich in eine Offline-Seite, die Artefakte aufbaut (den Katalog, die Indizes, die Embeddings), und eine Online-Seite, die darüber retrieved und rankt, und fast der gesamte veröffentlichte LLM-Nutzen landet offline. Die Ranking-Schicht kann nur so gut sein wie das, was ihr gefüttert wird.</p>\n<p>Stellen Sie sich nun daneben einen typischen HVAC- oder Sanitär-Distributorenkatalog vor. Er ist zusammengeflickt aus ERP-Exporten, Lieferanten-Tabellen, Einkaufsverbund-Feeds und einem CSV-Parser, den vor zehn Jahren jemand geschrieben hat. Die Produkt„titel“ sind Rechnungskurzschrift für Lagerpicker. Und der Such-Traffic, der darauf trifft, ist so intent-dicht, wie Such-Traffic nur sein kann: zur Hälfte exakte Teilenummern, der Rest Fachjargon wie „3/4 cxc 90 ell“. Ehrlich gesagt fällt mir kein besseres Umfeld für dieses Playbook ein. Die Queries sind wertvoll, die Daten sind reparierbar, und bei einigen hunderttausend SKUs kostet ein vollständiger LLM-Durchlauf über den Katalog einige hundert Dollar. Nicht Millionen. Hundert.</p>\n<p>Hier ist das Audit-Schema, denn Sie werden es reproduzieren wollen:</p>\n<pre><code class=\"language-mermaid\">flowchart TD\n    D[\"Raw catalog dump&lt;br/&gt;(JSONL, one record per line)\"] --> C1[\"Per-catalog coverage agents&lt;br/&gt;null rates, distributions, dead fields\"]\n    D --> C2[\"Dimension agents&lt;br/&gt;text · part numbers · brands · pricing&lt;br/&gt;specs · categories · media · duplicates\"]\n    C1 --> S[\"Synthesis: findings ranked by&lt;br/&gt;severity, with example records\"]\n    C2 --> S\n    S --> F[\"Fix pipeline: rules + LLM enrichment&lt;br/&gt;+ UNSPSC classification + ingest gates\"]\n</code></pre>\n<p>Ein Hinweis zum Format, bevor es an die Prompts geht. Alles unten funktioniert mit einem generischen Dump: ein JSON-Objekt pro Zeile, jedes mit einer eindeutigen <code>id</code> und den Feldern des Datensatzes. Elasticsearch, OpenSearch, Solr, Algolia, Typesense, ein PIM, ein einfacher Datenbankexport – egal. Jede Plattform kann diese Form produzieren, und jeder Prompt hier läuft dagegen.</p>\n<h2>Kategorie 1: Stille Pipeline-Fehler</h2>\n<p>Das Schlimmste, was ich fand, waren nicht schlechte Daten, die jemand geschrieben hat. Es waren Daten, die die Pipeline auf dem Weg hinein zerstört hat, ohne es jemandem zu sagen.</p>\n<p>Diese Kataloge bauen ihren primären Suchtext zur Ingest-Zeit auf, mittels eines skripteten Regex, der Dimensionen und Einheiten aus Produkttiteln extrahiert. Bei langen Titeln sprengt der Regex die Komplexitätsgrenze der Engine, der Prozessor scheitert, und der Datensatz wird trotzdem indexiert – mit einem Error-Feld, das niemand liest, und überhaupt keinem primären Suchfeld.</p>\n<img src=\"/assets/images/b2b-search-silent-failures.png\" alt=\"Dokumente ohne primäres Suchfeld indexiert, nach Distributoren\" />\n<p>Über den gesamten Bestand waren 52.570 Datensätze im Index, für den Haupt-Query-Pfad unsichtbar. Im schlimmsten Katalog ist das jedes siebte Produkt. Kein Dashboard hat es erkannt, weil nichts fehlgeschlagen ist. Die Pipeline lieferte Tag ein, Tag aus, monatelang Erfolg zurück.</p>\n<p>Zwei weitere Befunde aus derselben Familie. Der veraltete Spiegel: Ein Feld hält die Wahrheit (eine Entitlement-Map pro Account), und ein zweites, abgeflachtes Feld spiegelt sie für das Filtering. In einem Katalog war der Spiegel bei 19,85 % der Datensätze abgedriftet – 31.000 aktive Produkte, die fälschlicherweise vor der account-gefilterten Suche verborgen waren. Und der eingefrorene Katalog: Ein Index hatte sich seit zehn Wochen nicht aktualisiert, während seine Geschwister täglich updateten. Die Verfügbarkeit wurde überwacht. Die Aktualität nicht.</p>\n<p>Die Fixes sind langweilig, und genau das ist der Punkt. Alarmieren Sie bei der Abdeckung abgeleiteter Felder (abgeleitetes Feld leer, während seine Quelle es nicht ist). Pflegen Sie nicht zwei Repräsentationen einer Tatsache; leiten Sie das filterbare Feld zur Schreibzeit ab. Überwachen Sie die Datenaktualität pro Katalog so, wie Sie die Verfügbarkeit überwachen.</p>\n<p>Hier ist der Prompt, um das alles in Ihrem eigenen Katalog zu finden (Claude Code oder <code>codex exec</code>; die Mechanik am Ende):</p>\n<pre><code class=\"language-text\">Here is a dump of my product catalog as JSONL files in ./dump/ - one JSON\nrecord per line, each with a unique \"id\" and the product fields. My field/\nschema definition (if any) is in schema.json.\n\nWrite streaming Python (don't load it all in memory) to find silent pipeline\nfailures:\n1. Records carrying any error/exception field a pipeline stamped on them.\n2. For every DERIVED field (concatenations, *_search, *_normalized variants):\n   records where the derived field is empty but its obvious source field\n   is not.\n3. Pairs of fields that look like mirrors of each other (one nested/rich,\n   one flat/filterable): quantify how often they disagree.\n4. The distribution of updated/indexed timestamps: is any slice of the\n   catalog frozen while the rest refreshes?\n\nReport each finding with counts, % of catalog, 5 example ids, and whether the\naffected records are active/visible. Rank by severity.\n</code></pre>\n<h2>Kategorie 2: Platzhalter und Sentinels, oder: Daten, die lügen</h2>\n<p>Null-Checks sind das Datenqualitäts-Tool, das jeder bereits hat. Platzhalter besiegen sie, denn ein Platzhalter ist befüllt. Er ist nur nicht wahr.</p>\n<p>Mein Lieblingsbeispiel: In einem 313K-SKU-Katalog hatte das Markenfeld 741 unterschiedliche Werte und 99,99 % Population. Klingt gesund. Der Top-Wert, auf 88,8 % aller Datensätze, war „Approved Vendor“. Ein ERP-Platzhalter. Die beliebteste echte Marke deckte 0,3 % des Katalogs ab. Jedes Marken-Facet, jeder Marken-Boost und jedes markenbewusste Rewrite war also für neun Zehntel des Katalogs effektiv tot, während die Facet-UI fröhlich „Approved Vendor“ als Filter Nr. 1 anbot.</p>\n<p>Wenn man erst einmal hinschaut, ist das Zeug überall:</p>\n<ul>\n<li>Ein „kein Preis“-Sentinel von <code>99999999.000000</code> auf 55 % eines Katalogs. Der Medianpreis dieses Index lag bei neunundneunzig Millionen Dollar.</li>\n<li>Produktnamen, buchstäblich <code>\"unknown\"</code>, bei 64 Datensätzen, die dann für die Query „unknown“ ranken.</li>\n<li>Ein boolescher <code>true</code>-Wert, in ein Textbeschreibungsfeld bei 1.516 Live-Produkten geschrieben. Die Engine zwang ihn in den String „true“, wodurch diese Produkte durch die Suche nach „true“ auffindbar wurden.</li>\n<li>227 UPCs, gespeichert als <code>\"6.71E+11\"</code> – Excels wissenschaftliche Notation, wobei 51 unverbundene Produkte diesen einen „Identifier“ teilen – plus weitere 15.121 UPCs, denen die führende Null fehlt. Excel schlägt wieder zu.</li>\n<li>Ein Befestigungstechnik-Katalog, dessen Markenfeld Produktliniennamen enthielt („C6L Lockbolts“, „Tool Parts“), während die eigentliche Marke ein Feld weiter saß.</li>\n</ul>\n<p>Die Lehre, die ich aus dieser Kategorie ziehe: Prüfen Sie die Top-N-Werteverteilungen pro Feld, nicht die Null-Raten. Ein Feld, das zu 100 % befüllt ist, kann zu 88 % Müll sein, und kein Null-Check wird Ihnen das je sagen. Blocklisten Sie dann die Sentinels beim Ingest, mappen Sie sie auf echte Nulls mit expliziten <code>has_price</code>- / <code>has_real_image</code>-Flags, setzen Sie Typ-Assertions an der Grenze und validieren Sie Identifier strukturell. Behandeln Sie alles, was jemals durch eine Tabelle ging, als verdächtig.</p>\n<pre><code class=\"language-text\">Same JSONL dump. For every field in my catalog, compute the top-20 value\ndistribution (full pass, streaming).\n\nFlag: (a) any single value covering >10% of records - placeholder/sentinel\ncandidates like \"Approved Vendor\", \"unknown\", 99999999, 0.0; (b) values whose\ntype differs from the field's dominant type (booleans in text fields, floats\namong strings); (c) identifier fields (UPC/EAN/GTIN/part numbers): validate\nchecksum and length, flag scientific notation, stripped leading zeros,\nembedded whitespace/unicode, and identifiers shared by multiple records;\n(d) category-like or product-line values sitting in brand fields.\n\nFor each flag: count, % of catalog, 5 verbatim examples with ids, and a\none-line proposed ingest rule that would have rejected it.\n</code></pre>\n<h2>Kategorie 3: Verarmte Inhalte</h2>\n<p>Distributorenkataloge werden von ERPs geschrieben, nicht von Merchandisern. Der Text ist Rechnungskurzschrift: <code>PROPRESS 2-1/2X1 CXC RED CPLG</code>, <code>HC HX 18X8 W</code>. In einem Katalog hatten ein Drittel der Produktnamen weniger als 60 % Wörter aus dem Wörterbuch. In einem anderen waren die Rohfelder für Name und Beschreibung zu 100 % null – der Text existierte nur innerhalb abgeleiteter Such-Blobs, sodass alles, das die Rohfelder las (Ergebnistitel, Embedding-Inputs, Relevance-Judge-Prompts), nichts las, und niemand wusste es.</p>\n<img src=\"/assets/images/b2b-search-field-coverage.png\" alt=\"Felderabdeckung über vier produktive B2B-Indizes\" />\n<p>Diese Heatmap ist mein absolutes Lieblingsartefakt aus dem Audit. Jedes dieser Felder existiert in jedem Schema. Die Zahlen zeigen, wie viel von jedem tatsächlich Daten hält. Die Zeile, die mich trifft, ist die der ML-Descriptions: Die Anreicherungsschicht, die die Schemas für jeden Datensatz versprachen, war bei achtzehn Dokumenten von 775.051 befüllt. Achtzehn. Jeder „use enrichment if present“-Zweig in der Scoring-Pipeline war ein stilles No-Op. Schema ist ein Anspruch; nur Abdeckung ist Fakt.</p>\n<p>Diese Kategorie ist die, in der das veröffentlichte Playbook am direktesten anwendbar ist – DoorDashs Attributextraktion, Instacarts Offline-Generation – und der Kostenabschnitt unten kalkuliert es. Aber vier Regeln sind in einem Teilenummern-Geschäft wichtig, teilweise gelernt aus dem, was der vorherige Anreicherungsversuch falsch gemacht hat:</p>\n<ul>\n<li>Expandieren, nicht ersetzen. Generieren Sie den kundenlesbaren Titel neben dem rohen Rechnungsstring und indexieren Sie beides. Die Query des Servicetechnikers und die des Hausbesitzers sollten beide treffen.</li>\n<li>Codes niemals ausschreiben. Diese achtzehn Pionier-Datensätze? Der Generator hatte Modellnummern ausgeschrieben: „RGF one hundred eighty“. Kein Auftragnehmer wird das je tippen. Codes bleiben wörtlich; Expansionen sind Ergänzungen, keine Ersetzungen.</li>\n<li>Struktur extrahieren, während Sie dabei sind. Derselbe Durchlauf, der einen Titel umschreibt, kann <code>{size, material, connection_type}</code> für Facets ausgeben.</li>\n<li>Fachjargon ist ein Synonym-Problem, kein Rewrite-Problem. <code>CXC</code>, <code>ELL</code>, <code>CPLG</code>, „t-stat“ gehören in die Synonym-Expansion auf Analyzer-Ebene, einmalig gefixt. Zahlen Sie nicht dafür, sie in eine halbe Million Datensätze umzuschreiben.</li>\n</ul>\n<pre><code class=\"language-text\">Same dump. Assess text quality of the customer-facing fields (name, short/long\ndescription) with a full streaming pass:\n\n1. Effective-title coverage: % of ACTIVE records with a non-empty display name.\n2. Readability: % ALL-CAPS names; % with under 60% dictionary-word tokens\n   (abbreviation salad); names under 10 chars; digits-only descriptions.\n3. Junk: HTML/CMS markup, encoding artifacts, embedded operational notes\n   (*** NOT A PHYSICAL ITEM ***), CSV column spillover.\n4. Duplication: identical short/long descriptions; boilerplate shared by 20+\n   SKUs; exact-length clusters (254/255/500 chars) indicating upstream\n   VARCHAR caps.\n5. Enrichment reality check: for every ML/enriched/embedding field the schema\n   promises, its actual coverage.\n\nNumbers, 5 verbatim examples each, severity, and which issues need an LLM\nenrichment pass vs. a pipeline fix vs. a synonym-layer fix.\n</code></pre>\n<h2>Kategorie 4: Teilenummern sind heilig</h2>\n<p>Die Hälfte des B2B-Such-Traffics besteht aus jemandem, der eine Teilenummer tippt. Exakter Match ist dort das ganze Spiel, und er scheitert auf leise Weise.</p>\n<p>In einem Katalog war die indexierte Teilenummer bei 99,99 % der Datensätze eine interne numerische SKU. Die Nummer des Herstellers – die auf der Schachtel gedruckte – existierte nur innerhalb von Freitextbeschreibungen. 183.000 Produkte, unerreichbar über die Nummer, die ein Kunde tatsächlich tippen würde.</p>\n<p>Ein anderer Katalog hatte ein Varianten-Expansionssystem: Bindestriche entfernen, Segmente zusammenfassen, Varianten indexieren, damit die verzeihende PN-Suche funktioniert. Gute Idee. Aber der Generator war kombinatorisch (bis zu 80 Varianten pro Produkt), und bei 11.826 Datensätzen entsprach eine generierte Variante eines Produkts exakt der kanonischen Teilenummer eines anderen Produkts. Befestigungstechnik ist der brutale Fall, denn die Interpunktion kodiert physische Dimensionen: <code>702-1-5/32</code> ist ein 1-5/32-Zoll-Teil und <code>702-15/32</code> ist ein 15/32-Zoll-Teil, und beide normalisieren zu <code>7021532</code>. Ein Kunde tippt eine exakte Teilenummer und bekommt ein Münzwurf zwischen dem richtigen Teil und seinem dimensionalen Cousin.</p>\n<p>Ebenfalls in dieser Kategorie: Wettbewerber-Cross-References („Ich habe die Ferguson-Nummer, was ist Ihre?“), gespeichert als nicht aufgeteilte, pipe-delimitierte Blobs wie <code>19MU82|Fastenal 0269214|Ferguson M48222407</code>, in Feldern, die als nicht durchsuchbar konfiguriert sind, sodass eine zentrale B2B-Verkaufsbewegung einfach nicht funktioniert. Und Whitespace-Forks – dieselbe Teilenummer dreimal indexiert (plain, mit NBSP gepolstert, mit Trailing-Space) – plus Mojibake-Zwillinge aus doppelt dekodiertem UTF-8.</p>\n<p>Das Prinzip, das das meiste davon repariert: Exakt muss strukturell, nicht probabilistisch, Fuzzy schlagen. Eine interpunktionsbewahrende Exakt-Klausel, strikt über jeder normalisierten Ebene gescored, niemals ein flacher Match, bei dem eine Variante einen kanonischen Treffer gleichziehen kann. Prüfen Sie generierte Varianten gegen den kanonischen PN-Raum vor dem Indexieren und verwerfen Sie Kollisionen. Unicode-Normalisierung vor der Ableitung von Record-IDs. Teilen Sie die Cross-Reference-Blobs in Arrays auf; das ist eine einzelne Zeile Ingest-Code, die eine ganze Query-Klasse freischaltet.</p>\n<pre><code class=\"language-text\">Same dump. My users search by part number; audit exact-match integrity:\n\n1. Which PN-ish fields exist (part_number, mpn, alt/competitor/customer PNs,\n   UPC), their coverage, and which are configured unsearchable while holding\n   real data.\n2. Records whose only searchable PN token is an internal SKU (real\n   manufacturer PN appears only inside description text - show the pattern).\n3. If there's a PN-variant/keyword expansion field: find every generated\n   variant that equals a DIFFERENT record's canonical PN (normalize:\n   lowercase, strip punctuation). Show colliding pairs - especially\n   fraction-bearing fastener sizes.\n4. PNs differing only by whitespace/invisible chars/case from another\n   record's PN (duplicate identity forks).\n5. Multi-value cross-reference fields stored as delimited blobs instead of\n   arrays.\n\nCounts, examples, and for each issue the ingest rule or query-builder change\nthat fixes it.\n</code></pre>\n<p>Ein paar Dinge, die nicht oben hineinpassten, aber einen Satz verdienen: Ein Synonym-Generator, der jede Zahl in Wire-Gauge-Begriffe verwandelte, sodass <code>REF#259286</code> zu „259286 awg“ wurde und ein <code>#8-32</code>-Schraubengewinde zu „8 gauge wire“ (tausende Datensätze matchen nun auf elektrische Queries, mit denen sie nichts zu tun haben); interne Flags einer E-Commerce-Plattform, indexiert als durchsuchbare Produktspezifikationen, 2,1 Millionen Junk-Key-Value-Paare; und 18 Schemafelder, die auf null Datensätzen befüllt waren. Expansions-Generatoren brauchen Context-Gates. Totes Schema ist ein falsches Versprechen, auf das irgendwann jemand aufbauen wird.</p>\n<h2>Die fehlende Schicht: UNSPSC und Produkte, die nicht wissen, was sie sind</h2>\n<p>In meinem Audit fehlten Kategoriefelder bei 83 % eines Katalogs. Attribut-Keys hatten überhaupt keine Taxonomie – 7.068 unterschiedliche Spec-Keys in einem 156K-Datensatz-Katalog, 64 % davon bei weniger als zehn Produkten verwendet, mit Drift wie <code>horse_power</code> vs. <code>horsepower</code>, die dasselbe Attribut über Facets hinweg spalteten. Und das Detail, zu dem ich immer wieder zurückkehre: Zwei der vier Kataloge hatten UNSPSC-Felder in ihren Schemas sitzen, gemappt und bereit, auf exakt null Datensätzen befüllt. Jemand wusste, dass Kategorisierung wichtig ist, baute den Slot und füllte ihn nie. Ich würde darauf wetten, warum: 300K SKUs per Hand in eine Taxonomie zu klassifizieren ist ein Jahr Arbeit für das Katalogteam, also blieb es für immer auf der Roadmap.</p>\n<p>Wenn Sie außerhalb von B2B sind: UNSPSC ist der UN Standard Products and Services Code, die Taxonomie, die Beschaffungssysteme sprechen. Das ist der Teil, den die Consumer-Suche nicht hat: Die Einkaufssysteme Ihrer Kunden verlangen ihn. Punchout-Kataloge, E-Procurement-Plattformen und Spend-Analysis-Tools sind um UNSPSC-Codes herum gebaut. Ein Distributor, dessen Katalog saubere Codes trägt, kann sich in das Einkaufssystem eines Auftragnehmers oder eines Krankenhauses einhängen. Einer ohne sie ist eine PDF-Preisliste mit einer Suchbox. Intern ist es auch das, was Kategoriefacets, kategorie-scoped Ranking („eine Zahl, die sich als Wire Gauge parsen lässt, sollte nur Wire boosten“), Cross-Catalog-Dedup und die Context-Gates für jeden Expansions-Generator in Kategorie 4 antreibt.</p>\n<p>Und es ist jetzt ein Batch-Job. Das ist dieselbe Art von Problem, die DoorDash beschreibt, mit LLMs zu lösen – High-Volume-Labeling gegen ein kontrolliertes Vokabular – und der Ansatz lässt sich direkt übertragen:</p>\n<ul>\n<li>Hierarchisch klassifizieren, nicht flach. UNSPSC hat vier Ebenen (Segment, Family, Class, Commodity). Lassen Sie das Modell die Family aus ~450 Optionen wählen, dann die Class innerhalb dieser Family. Zwei kleine, eingeschränkte Entscheidungen schlagen eine 50.000-fache Wahl, und Sie können den relevanten Taxonomie-Slice in den Context füttern, so wie DoorDash das Entity Linking auf sein Vokabular einschränkt.</li>\n<li>Zuerst auf Class-Ebene stoppen. Class-Codes schalten bereits Facets und Procurement-Integration frei; Commodity-Level-Präzision kann später kommen, wo sie sich bezahlt macht.</li>\n<li>Nach Confidence routen. Ein kleines Modell nimmt die klaren Fälle, Low-Confidence-Datensätze eskalieren zu einem größeren Modell, und anhaltende Meinungsverschiedenheiten gehen in eine Human Queue, die gleichzeitig als Ihr Eval-Set dient.</li>\n</ul>\n<p>Die Kosten sind fast peinlich zu tippen. Klassifizierung ist eine Short-Output-Aufgabe: ungefähr 300 Input-Tokens pro Datensatz und 30 Output. Bei Claude Haiku 4.5 zum Batch-API-Preis sind das etwa 17 Cent pro tausend SKUs. Mein gesamter 775K-Datensatz-Bestand würde für etwa 130 $ klassifiziert werden. Das, was jahrelang auf der Roadmap saß, weil es ein Jahr manuelle Arbeit war, kostet weniger als das Team-Mittagessen, bei dem Sie darüber diskutieren würden.</p>\n<pre><code class=\"language-text\">You classify B2B distributor products into UNSPSC. Attached: the UNSPSC\nfamily list (level 2, ~450 entries) as reference data.\n\nFor each product record (title, description, brand, part number, any existing\ncategory text), output JSON:\n- unspsc_family: the 4-digit family code - choose ONLY from the attached list\n- family_confidence: high | low\n- rationale: one short phrase (e.g. \"copper press fitting -> pipe fittings\")\n\nRules: judge from the product's function, not its brand. Trade shorthand:\nCXC/FPT/MPT are pipe connections, ELL is elbow, CPLG is coupling, a bare\nfraction+material is usually a fitting size. If the record is not a physical\nproduct (freight lines, upcharges, \"*** NOT A PHYSICAL ITEM ***\"), output\nNOT_A_PRODUCT. Use low confidence liberally - low-confidence records get a\nsecond pass with the class-level list.\n</code></pre>\n<h2>Das Cleanup-Playbook, mit der tatsächlichen Rechnung</h2>\n<p>Die Fix-Pipeline hat drei Ebenen plus den Klassifizierungs-Job. Der Trick ist, das Geld auf der richtigen Ebene auszugeben, denn Sie brauchen fast nie einen LLM-Call pro Datensatz.</p>\n<pre><code class=\"language-mermaid\">flowchart LR\n    A[\"Tier 0 - Rules&lt;br/&gt;deterministic code&lt;br/&gt;~$0\"] --> B[\"Tier 1 - LLM on unique values&lt;br/&gt;brands · spec keys · units&lt;br/&gt;tens of dollars\"]\n    B --> C[\"Tier 2 - LLM per record&lt;br/&gt;titles · attributes · UNSPSC&lt;br/&gt;hundreds of dollars\"]\n    C --> G[\"Ingest gates&lt;br/&gt;every fix becomes a validator\"]\n</code></pre>\n<p><strong>Tier 0 ist deterministischer Code und im Grunde kostenlos.</strong> HTML entfernen und Entities dekodieren. Sentinels in echte Nulls mit expliziten Flags verwandeln. UPCs reparieren (left-pad, wissenschaftliche Notation ablehnen, Prüfziffer validieren). IDs Unicode-normalisieren. Die pipe-delimitierten Cross-References aufteilen. PN-Varianten verwerfen, die mit der kanonischen Nummer eines anderen Produkts kollidieren. Totes Schema löschen. Ein Agent schreibt diese Skripte in einer Session, und diese Ebene hat ungefähr die Hälfte von allem repariert, was mein Audit fand. Machen Sie es vor jeder Anreicherung, oder Sie zahlen einem Modell dafür, Titel wunderschön umzuschreiben für Produkte, die Ihre Pipeline stillschweigend verworfen hat.</p>\n<p><strong>Tier 1 lässt das LLM über eindeutige Werte laufen, nicht über Datensätze.</strong> Das ist der Trick, der Vokabular-Probleme billig macht: Mein schlimmster Katalog hatte 313K Datensätze, aber nur 741 unterschiedliche Marken-Strings. Marken-Normalisierung ist ein Job über 741 Strings – Casing-Varianten clustern, Sub-Marken auf Parents mappen, Platzhalter flaggen – nicht 313K Calls. Derselbe Move für Spec-Keys und Unit-Suffixe. Jeder Vokabular-Job kostet einstellige Dollar-Beträge, und sein Output ist eine statische Alias-Tabelle, die Ihr Ingest für immer deterministisch anwendet. Die gesamte Ebene kostet für einen Bestand 20–50 $, meist ausgegeben für Review-Passes.</p>\n<pre><code class=\"language-text\">Attached: the full distinct-value distribution of my brand and manufacturer\nfields (value, record_count). Produce a normalization table:\n\ncanonical_brand, parent_manufacturer, [raw variants], confidence\n\nRules: cluster case/punctuation/whitespace variants; map sub-brands to parent\nmanufacturers as a separate column (don't merge); mark placeholder values as\nNULL_SENTINEL; mark product lines or categories misfiled as brands as\nNOT_A_BRAND. Flag uncertain clusters rather than guessing. Output CSV, then\nwrite a script that applies it at ingest and logs unmatched new values.\n</code></pre>\n<p><strong>Tier 2 ist der Per-Record-Durchlauf</strong>: ein kundenlesbarer Titel, Search-Expansionen, strukturierte Attribute und eine wiederhergestellte Hersteller-Teilenummer für jedes Produkt. Pro SKU ist es klein – ungefähr 400 Input-Tokens (der Datensatz plus Shared Instructions, die Prompt Caching fast kostenlos machen) und 250 Output. Zwei Hebel, bevor Sie überhaupt ein Modell wählen: Anreichern, was live ist (in meinem schlimmsten Katalog waren nur 14 % der Datensätze aktiv, ein sofortiger Schnitt um 86 %), und eine Batch-API nutzen, wo der Anbieter eine hat, da Anreicherung keine Latenz-Anforderung hat und sowohl Anthropic als auch OpenAI 50 % für Batch abziehen.</p>\n<p>Ich habe denselben Job über alles hinweg kalkuliert, was Sie 2026 plausibel nutzen würden – Claude, <a href=\"https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/\">GPT-5.6s drei Tiers</a> (Sol, Terra, Luna), Kimi K3, GLM-5.2, Qwen3.5 und <a href=\"https://deepseek.ai/pricing\">DeepSeek V4</a>. Dieselbe Workload, vom Anbieter veröffentlichte Preise, Batch-Rabatt angewendet, wo er existiert:</p>\n<img src=\"/assets/images/b2b-search-enrichment-cost-by-model.png\" alt=\"Kosten zur Anreicherung von 775K SKUs über Modelle hinweg\" />\n<p>Ich musste diese Zahlen doppelt prüfen, weil sie sich falsch anfühlten. Die Worst-Case-Version dieses Projekts – ein Frontier-Modell, jede einzelne SKU, überhaupt kein Filtering – beträgt im schlimmsten Fall etwa 3.800 $. Das Minimum liegt unter hundert Dollar: DeepSeek V4-Flash würde den gesamten Bestand für etwa den Preis eines ordentlichen Bohrers anreichern. Die Version, die Sie tatsächlich laufen lassen würden, mischt Tiers: ein Budget-Modell (Haiku, Luna, Qwen Flash, DeepSeek) für die mechanische Mehrheit, ein Mid-Tier-Modell (Sonnet, Terra, GLM-5.2) für den Abbreviation-Salad-Schwanz, den das Kleine als Low-Confidence flaggt, und ein Frontier-Modell, das eine 1–2 %-Stichprobe als Quality Gate stichprobenartig prüft. Das landet bei einigen hundert Dollar für die Live-SKUs, unabhängig davon, welche Anbieter Sie wählen.</p>\n<p>Zwei Einschränkungen, die die Grafik nicht zeigen kann. Billige Modelle sind nur billig, wenn ihr Output Ihr Eval übersteht – führen Sie den 200-Sample-Vergleich durch, bevor Sie sich festlegen, denn ein Budget-Modell, das 5 % der Teilenummern verstümmelt, kostet mehr als das Frontier-Modell, das das nicht tut. Und Ihr Katalog sind Wettbewerbsdaten: Preisgestaltung, Cross-References und Kunden-Teilenummern stecken in diesen Datensätzen, also prüfen Sie die Data-Retention- und Trainings-Bedingungen jedes Anbieters, bevor Sie 775K Datensätze an den billigsten Endpunkt auf der Liste schicken. Für viele Distributoren allein dieser Check schließt einige Anbieter ein oder aus, und die Open-Weight-Optionen (DeepSeek, GLM, Qwen, Kimi) haben die zusätzliche Eigenschaft, dass Sie sie selbst hosten können, wenn die Daten überhaupt nicht das Haus verlassen dürfen.</p>\n<p>Addieren Sie den ~130 $-UNSPSC-Durchlauf (der sich über Modelle hinweg gleich skaliert – im Budget-Tier sinkt er auf Kleingeld) und die Vokabular-Arbeit, und die gesamte Katalog-Transformation läuft irgendwo zwischen einigen hundert und einigen tausend Dollar, verglichen mit Arbeit, die früher ein Jahr eines Merchandising-Teams war. Es ist dieselbe Offline-Batch-Ökonomik, die Instacart beschreibt, nur im Maßstab eines Fachhandels-Katalogs, wo die Zahlen klein genug werden, um sie auf eine Kreditkarte zu packen.</p>\n<p>Wie das bei einem echten Datensatz aus meinem Audit aussieht:</p>\n<pre><code class=\"language-text\">IN:  part_number: 1678619\n     short_desc:  PROPRESS 2-1/2X1 CXC RED CPLG 20685\n     brand:       (placeholder)\n\nOUT: display_title:      2-1/2\" x 1\" ProPress Copper Reducing Coupling\n     search_expansions:  [\"copper press fitting\", \"reducing coupling\",\n                          \"press x press coupling\"]\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 (pipe fittings)\n     confidence:         high\n</code></pre>\n<p>Schauen Sie sich die Zeile <code>extracted_mpn</code> an. Derselbe Durchlauf stellt Hersteller-Teilenummern wieder her, die zuvor in Beschreibungstexten vergraben waren – in einem meiner Kataloge sind das 183K Produkte, die über die auf der Schachtel gedruckte Nummer auffindbar werden. Das allein bezahlt den Batch-Job.</p>\n<pre><code class=\"language-text\">You enrich B2B distributor product records for search. For each input record\n(raw ERP name, descriptions, brand, part number, category) produce JSON:\n\n- display_title: customer-readable, <=80 chars, Title Case. Expand trade\n  abbreviations (CXC -> copper x copper connection, ELL -> elbow, CPLG ->\n  coupling, RED -> reducing). Keep every part number, model code, and\n  dimension VERBATIM as typed - never spell codes out in words.\n- search_expansions: 2-5 phrases a customer might type that don't appear in\n  the raw text (plain-English product type, common trade names). Never invent\n  specs absent from the source.\n- attributes: {name: {value, unit}} extracted ONLY from the source text.\n- extracted_mpn: manufacturer part number if present in the description text\n  (usually the token after the brand) and absent from the PN fields, else null.\n- confidence: high | low. Use low whenever you had to guess; a low answer\n  routed to review beats a confident hallucination.\n</code></pre>\n<p>Zwei Produktionshinweise: Packen Sie die Shared Instructions in ein cached Prompt-Prefix (Caching reduziert die Input-Seite um bis zu 90 % zusätzlich zum Batch-Rabatt), und nutzen Sie Structured Outputs mit einem JSON-Schema, damit Sie niemals eine Parse-Failure-Steuer zahlen.</p>\n<p><strong>Die letzte Ebene ist die, die Leute überspringen, und es ist die, die sich multipliziert.</strong> Das Audit ist eine einmalige Bereinigung wert. Die Validatoren, die es generiert, sind jeden zukünftigen Feed wert. Beenden Sie die Fix-Session mit:</p>\n<pre><code class=\"language-text\">Turn every issue we just fixed into an automated check that fails the ingest\npipeline loudly: derived-field coverage assertions, sentinel blocklists, type\nassertions on text fields, identifier checksum validation, PN-variant\ncollision checks, id hygiene rules, UNSPSC coverage tracking, and a data-\nfreshness alarm per catalog. Emit them as tests CI runs against a sample of\nevery new feed.\n</code></pre>\n<p>Überspringen Sie das, und dieselben ERP-Exporte werden denselben Müll innerhalb eines Quartals neu generieren, und Sie zahlen die Bereinigung doppelt. Ich weiß es, weil zwei der Bugs, die mein Audit fand, offensichtlich schon einmal gefixt worden waren und zurückgekommen sind.</p>\n<h2>Die Prompts ausführen</h2>\n<p>Ein Setup-Schritt: Exportieren Sie Ihren Katalog nach JSONL, ein JSON-Objekt pro Zeile mit einer eindeutigen <code>id</code> und den Feldern des Datensatzes. Was auch immer Ihre Plattform ist – Elasticsearch, OpenSearch, Solr, Algolia, Typesense, ein PIM, die Datenbank dahinter – bitten Sie einfach den Agenten, den Exporter zu schreiben:</p>\n<pre><code class=\"language-text\">Write a script that exports every product record from my catalog [describe:\nsearch index name / database table / PIM export] to ./dump/records-{n}.jsonl\nin 50k-record chunks - one JSON object per line with a unique \"id\" plus all\nfields - and my field/schema definition (if the platform has one) to\nschema.json. Verify the exported count matches the source count.\n</code></pre>\n<p>Mit Claude Code werfen Sie einen beliebigen Kategorie-Prompt in eine Session in diesem Verzeichnis. Er schreibt und führt die Analyse selbst, volle Durchläufe ohne Sampling-Ausreden, und kommt mit Record-Level-Belegen zurück. Wenn Sie sagen „fix it now“, produziert dieselbe Session den Ingest-Validator, das Backfill-Skript und den Batch-Enrichment-Job. Führen Sie die Kategorien als parallele Sessions oder Subagenten aus; so habe ich meine laufen lassen, und das gesamte Audit dauerte etwa zwölf Minuten Wall-Clock-Zeit. Mit Codex handhabt <code>codex exec</code> die Read-Only-Analyse-Durchläufe mit denselben Prompts; behalten Sie die Write-Side-Fixes in einer Session, die Sie reviewen.</p>\n<p>Drei Regeln, die ich auf die harte Tour gelernt habe:</p>\n<ol>\n<li>Verlangen Sie volle Durchläufe und Belege. „Analyze this data“ lädt zu Sampling und Vibes ein. Fordern Sie exakte Counts und fünf Beispiel-Record-IDs pro Befund, und jede Behauptung wird überprüfbar.</li>\n<li>Regeln vor Anreicherung. Reparieren Sie die Wahrheit, bevor Sie Prosa hinzufügen, oder Sie halluzinieren Marken für die 88 % der Datensätze, die mit „Approved Vendor“ gelabelt sind.</li>\n<li>Sample, eval, then batch. Feuern Sie niemals einen 775K-Datensatz-Batch aus einem unvalidierten Prompt ab. 200 Samples, ein Scoring-Pass, dann skalieren. Das Eval-Harness kostet eine Agent-Session und entrisikiert die gesamte Ausgabe.</li>\n</ol>\n<h2>Die Fachhandelsbranche verdient Marketplace-Grade-Search</h2>\n<p>Amazon, DoorDash und Instacart haben ihre Katalog-LLM-Arbeit nicht aus Nächstenliebe veröffentlicht. Sie haben sie veröffentlicht, weil die Techniken allgemein sind und der Burggraben die Ausführung ist. Und die Techniken lassen sich auf den Fachhandel besser übertragen als auf fast jede andere Branche, die mir einfällt: Die Kataloge sind klein genug, dass ein voller Durchlauf billig ist, die Daten sind schlecht genug, dass der Spielraum enorm ist, die Queries tragen echtes Geld, und die Beschaffungswelt läuft bereits auf einer Taxonomie, die ein Batch-Job jetzt für etwa 130 $ befüllen kann.</p>\n<p>Von den Dutzenden Problemen, die mein Audit in vier Distributorenkatalogen ans Licht brachte, war keines einzelne von der Ranking-Schicht aus sichtbar. Alle haben sie verschlechtert. Die Supply Chain, die diese Indizes füttert – die ERP-Exporte, die Lieferanten-Tabellen, die uralten CSV-Parser – ist genau das, was das veröffentlichte Playbook repariert. Die Distributoren, die es laufen lassen, werden Marketplace-Grade-Search auf Katalogen haben, auf denen ihre Wettbewerber noch nicht einmal eine Kondensatorspule finden können.</p>\n<p>Die Frage „Is anything wrong with my data?“ hat früher zwei Wochen der Zeit von jemandem gekostet, deshalb hat sie niemand gestellt. Jetzt kostet sie einen Prompt. Stellen Sie sie.</p>",
  "source_hash": "sha256:beacaae673c45f36f13a5677632c8df46a2339c6bdea51c65c49f762d7f0805c",
  "model": "moonshotai/kimi-k2.6",
  "generated_at": "2026-08-07T07:46:16.122715+00:00"
}