{
  "title": "AI時代におけるB2Bコマース検索の修正",
  "excerpt": "私は4つのB2Bディストリビューターの検索を支える775,000件の製品レコードを監査しました。コンシューマー大手はLLMを使ってこのようなカタログを修正する方法をすでに公開していますが、貿易流通業界はそのプレイブックを採用していません。ここに、HVAC、配管、電気向けに適応させ、実際のコストを含めて紹介します。",
  "content_html": "<p>B2Bコマース検索には汚い秘密があります。ランキングアルゴリズムが問題になることはめったにありません。</p>\n\n<p>結果が悪いと、チームは楽しいレバーに手を伸ばします。ブースト、同義語、埋め込み、リランカー、そして最近ではLLMクエリ理解。私自身もそれらのレバーをすべて引いてきました。先週、私は代わりに地味なことをしました。4つの本番検索インデックスからすべての製品レコード（HVAC、配管、産業用供給、ファスナーを取り扱う4つのB2Bディストリビューターの775,051件）をダンプし、データ自体を監査しました。12のAIエージェントを並列で実行し、それぞれが1つの次元（カバレッジ、テキスト品質、部品番号、ブランド、価格、仕様、カテゴリ、メディア、重複）を担当しました。</p>\n\n<p>返ってきた結果は、ディストリビューターのカタログで働いたことがある人には何の変哲もないものであり、そうでない人には衝撃的なものでした。しかし、私が心に残ったのは、修正方法はすでに公開されているということです。Amazon、DoorDash、Instacartは、公開エンジニアリングブログで、LLMを使用して大規模な乱雑なカタログをクリーンアップ、ラベル付け、エンリッチする方法を正確に2年間書き続けてきました。貿易流通業界はほとんど気づいていません。それは奇妙です。なぜなら、彼らのカタログはより乱雑で、クエリはより価値が高く（12ドルのランチではなく4,000ドルのコンデンサーを注文する請負業者）、カタログサイズが実際に経済性を容易にしているからです。</p>\n\n<p>そこで、この投稿はそのプレイブックを貿易流通向けに適応させたものです。B2B検索を壊すデータ問題、コーディングエージェントで今日の午後にそれらを見つける方法、UNSPSC分類を含むLLMで修正する方法、そして全体のコスト。最後のネタバレ：あなたが推測するよりも2桁少ないです。</p>\n\n<h2>プレイブックはすでに存在する、ただ我々の業界にはないだけ</h2>\n\n<p>マーケットプレイス企業が公開したものの一部：</p>\n\n<ul>\n<li>DoorDashは、LLMと検索拡張生成（RAG）を使用して<a href=\"https://careersatdoordash.com/blog/how-doordash-leverages-llms-for-better-search-retrieval/\">製品知識グラフを構築し、検索検索を改善</a>しています。数百万のマーチャント提供アイテムにわたる自動ブランド抽出、属性抽出、エンティティリンキング。</li>\n<li>Amazonは<a href=\"https://www.amazon.science/blog/building-commonsense-knowledge-graphs-to-aid-product-recommendation\">COSMO</a>を構築しました。これはLLM生成の知識グラフで、顧客が意味するもの（「妊婦用の靴」）と製品が何であるか（「滑り止めの靴」）を結び付けます。オフラインテストでレコメンデーション性能が最大60%向上したと報告しています。</li>\n<li>InstacartはLLMを検索スタックに組み込み、<a href=\"https://tech.instacart.com/supercharging-discovery-in-search-with-llms-556c585d4720\">ディスカバリーコンテンツを生成</a>し、<a href=\"https://www.instacart.com/company/tech-innovation/building-the-intent-engine-how-instacart-is-revamping-query-understanding-with-llms\">クエリ理解をLLM中心に再構築</a>しています。重い生成はコストを抑えるためにオフラインでバッチ処理されます。</li>\n</ul>\n\n<p>共通の糸は見逃しやすいです：LLMの勝利はカタログ側とオフライン側にあります。これらの企業は、クエリが到着する前にデータをエンリッチします。ディスカバリーシステムは、アーティファクト（カタログ、インデックス、埋め込み）を構築するオフライン側と、それらを検索してランク付けするオンライン側に分かれており、公開されたLLM価値のほぼすべてがオフラインにあります。ランキングレイヤーは、与えられたものと同じくらいしか良くなりません。</p>\n\n<p>次に、典型的なHVACまたは配管ディストリビューターのカタログをそれと比較してください。それはERPエクスポート、サプライヤーのスプレッドシート、購買グループのフィード、そして10年前に誰かが書いたCSVパーサーから縫い合わされています。製品「タイトル」は、倉庫のピッカーのために書かれた請求書の略語です。そして、それにヒットする検索トラフィックは、検索トラフィックの中で最も意図密度が高いものです：半分は正確な部品番号、残りは「3/4 cxc 90 ell」のような業界用語です。正直なところ、このプレイブックにこれ以上の環境は考えられません。クエリは価値があり、データは修正可能で、数十万SKUの場合、カタログ全体に対する完全なLLMパスは数百ドルかかります。数百万ではありません。数百です。</p>\n\n<p>監査の形は次のとおりです。再現したいと思うでしょう：</p>\n\n<pre><code class=\"language-mermaid\">flowchart TD\n    D[\"Raw catalog dump<br/>(JSONL, one record per line)\"] --&gt; C1[\"Per-catalog coverage agents<br/>null rates, distributions, dead fields\"]\n    D --&gt; C2[\"Dimension agents<br/>text · part numbers · brands · pricing<br/>specs · categories · media · duplicates\"]\n    C1 --&gt; S[\"Synthesis: findings ranked by<br/>severity, with example records\"]\n    C2 --&gt; S\n    S --&gt; F[\"Fix pipeline: rules + LLM enrichment<br/>+ UNSPSC classification + ingest gates\"]\n</code></pre>\n\n<p>プロンプトの前に形式について1つ注意点。以下はすべて汎用ダンプで動作します：1行に1つのJSONオブジェクト、それぞれに一意の<code>id</code>とレコードのフィールドがあります。Elasticsearch、OpenSearch、Solr、Algolia、Typesense、PIM、プレーンなデータベースエクスポート - 関係ありません。すべてのプラットフォームがこの形状を生成でき、ここにあるすべてのプロンプトはそれに対して実行されます。</p>\n\n<h2>カテゴリ1：サイレントパイプライン障害</h2>\n\n<p>私が見つけた最悪のものは、誰かが書いた悪いデータではありませんでした。それは、パイプラインが入り口で誰にも知らせずに破壊したデータでした。</p>\n\n<p>これらのカタログは、インジェスト時に製品タイトルから寸法と単位を抽出するスクリプト化された正規表現を使用して、主要な検索テキストを構築します。長いタイトルでは正規表現がエンジンの複雑さの上限を超え、プロセッサが失敗し、レコードはとにかくインデックスされます - 誰も読まないエラーフィールドと、主要な検索フィールドがまったくない状態で。</p>\n\n<p><img src=\"/assets/images/b2b-search-silent-failures.png\" alt=\"Documents indexed with no primary search field, by distributor\" /></p>\n\n<p>フリート全体で、52,570件のレコードがインデックスに存在し、メインのクエリパスから見えませんでした。最悪のカタログでは、7製品に1つです。何も失敗しなかったため、ダッシュボードはそれを検出しませんでした。パイプラインは毎日、毎時、何ヶ月も成功を返しました。</p>\n\n<p>同じファミリーからのさらに2つの発見。古いミラー：1つのフィールドが真実を保持し（アカウントごとのエンタイトルメントマップ）、2番目のフラット化されたフィールドがフィルタリング用にそれをミラーリングします。あるカタログでは、ミラーがレコードの19.85%でドリフトしていました - 31,000のアクティブな製品がアカウントフィルタリングされた検索から誤って隠されていました。そして凍結されたカタログ：1つのインデックスが10週間更新されず、兄弟は毎日更新されていました。稼働時間は監視されていました。鮮度は監視されていませんでした。</p>\n\n<p>修正は退屈で、それがポイントです。派生フィールドのカバレッジ（ソースが空でないのに派生フィールドが空）にアラートを出します。1つの事実の2つの表現を維持しないでください。フィルタリング可能なフィールドを書き込み時に導出します。カタログごとにデータの鮮度を稼働時間と同じように監視します。</p>\n\n<p>自分のカタログでこれらすべてを見つけるためのプロンプトは次のとおりです（Claude Codeまたは<code>codex exec</code>、メカニクスは最後に）：</p>\n\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\n<h2>カテゴリ2：プレースホルダーとセンチネル、または嘘をつくデータ</h2>\n\n<p>NULLチェックは、誰もがすでに持っているデータ品質ツールです。プレースホルダーはそれらを無効にします。なぜなら、プレースホルダーは入力されているからです。ただ真実ではないだけです。</p>\n\n<p>私のお気に入りの例：ある313K SKUのカタログでは、ブランドフィールドに741の異なる値があり、99.99%の入力率でした。健全に聞こえます。トップの値は、全レコードの88.8%で「承認済みベンダー」でした。ERPのプレースホルダーです。最も人気のある実際のブランドはカタログの0.3%をカバーしていました。したがって、すべてのブランドファセット、ブランドブースト、ブランド認識リライトは、カタログの9割に対して事実上死んでおり、ファセットUIは「承認済みベンダー」をブランドフィルターの第1位として陽気に提供していました。</p>\n\n<p>探し始めると、このようなものはどこにでもあります：</p>\n\n<ul>\n<li>あるカタログの55%に「価格なし」のセンチネル<code>99999999.000000</code>。そのインデックスの中央価格は9900万ドルでした。</li>\n<li>製品名が文字通り<code>\"unknown\"</code>の64レコード。その後、「unknown」というクエリでランク付けされます。</li>\n<li>1,516のライブ製品のテキスト説明フィールドにブール値<code>true</code>が書き込まれました。エンジンはそれを文字列「true」に強制変換し、それらの製品は「true」を検索することで見つけられるようになりました。</li>\n<li>227のUPCが<code>\"6.71E+11\"</code>として保存されています - Excelの科学的記数法で、51の無関係な製品がその1つの「識別子」を共有しています - さらに15,121のUPCが先頭のゼロを失っています。Excelが再び襲います。</li>\n<li>ファスナーカタログのブランドフィールドに製品ライン名（「C6Lロックボルト」、「ツールパーツ」）が保持され、実際のブランドは1つ隣のフィールドにありました。</li>\n</ul>\n\n<p>このカテゴリから学んだ教訓：フィールドごとのNULL率ではなく、トップN値の分布を監査します。100%入力されたフィールドは88%がゴミである可能性があり、NULLチェックでは決してわかりません。次に、インジェスト時にセンチネルをブロックリストに登録し、明示的な<code>has_price</code> / <code>has_real_image</code>フラグで実際のNULLにマップし、境界で型アサーションを行い、識別子を構造的に検証します。スプレッドシートを通過したものはすべて疑わしいものとして扱います。</p>\n\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 &gt;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\n<h2>カテゴリ3：貧弱なコンテンツ</h2>\n\n<p>ディストリビューターのカタログは、マーチャンダイザーではなくERPによって書かれています。テキストは請求書の略語です：<code>PROPRESS 2-1/2X1 CXC RED CPLG</code>、<code>HC HX 18X8 W</code>。あるカタログでは、製品名の3分の1が辞書単語の60%未満でした。別のカタログでは、生の名前と説明フィールドが100% NULLでした - テキストは派生検索ブロブ内にのみ存在し、生のフィールドを読むもの（結果タイトル、埋め込み入力、関連性判断プロンプト）は何も読んでおらず、誰も知りませんでした。</p>\n\n<p><img src=\"/assets/images/b2b-search-field-coverage.png\" alt=\"Field coverage across four production B2B indices\" /></p>\n\n<p>そのヒートマップは、監査から得た私の一番のお気に入りのアーティファクトです。それらのフィールドのすべてがすべてのスキーマに存在します。数字は、それぞれが実際にデータを保持している量です。私が心を打たれる行はML説明の行です：スキーマがすべてのレコードで約束したエンリッチメントレイヤーは、775,051件のうち18件のドキュメントに入力されていました。18件。スコアリングパイプラインの「エンリッチメントが存在する場合は使用」ブランチはすべてサイレントなノーオペレーションでした。スキーマは願望であり、カバレッジだけが事実です。</p>\n\n<p>このカテゴリは、公開されたプレイブックが最も直接適用される場所です - DoorDashの属性抽出、Instacartのオフライン生成 - そして以下のコストセクションで価格を設定します。しかし、部品番号ビジネスでは4つのルールが重要であり、以前のエンリッチメント試行が間違えたことから部分的に学びました：</p>\n\n<ul>\n<li>拡張し、置き換えない。生の請求書文字列と一緒に顧客が読めるタイトルを生成し、両方をインデックスします。カウンター技術者のクエリと住宅所有者のクエリの両方がヒットするはずです。</li>\n<li>コードを言葉にしない。あの18件の先駆的なレコード？ジェネレーターはモデル番号を言葉で綴っていました：「RGF one hundred eighty」。請負業者がそれを入力することは決してありません。コードはそのままにし、拡張は追加であり、置き換えではありません。</li>\n<li>その間に構造を抽出する。タイトルを書き換える同じパスで、ファセット用に<code>{size, material, connection_type}</code>を出力できます。</li>\n<li>業界用語は同義語の問題であり、書き換えの問題ではありません。<code>CXC</code>、<code>ELL</code>、<code>CPLG</code>、「t-stat」はアナライザーレベルの同義語拡張に属し、一度修正します。50万レコードに書き換えるためにお金を払わないでください。</li>\n</ul>\n\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\n<h2>カテゴリ4：部品番号は神聖です</h2>\n\n<p>B2B検索トラフィックの半分は、誰かが部品番号を入力しています。そこでは完全一致がすべてであり、静かな方法で失敗します。</p>\n\n<p>あるカタログでは、インデックスされた部品番号はレコードの99.99%で内部数値SKUでした。メーカー番号 - 箱に印刷されている番号 - はフリーテキストの説明内にのみ存在しました。183,000の製品が、顧客が実際に入力する番号では到達できませんでした。</p>\n\n<p>別のカタログにはバリアント拡張システムがありました：ダッシュを削除し、セグメントを折りたたみ、寛容なPN検索が機能するようにバリアントをインデックスします。良いアイデアです。しかし、ジェネレーターは組み合わせ的であり（製品ごとに最大80バリアント）、11,826レコードで、ある製品の生成されたバリアントが別の製品の正規の部品番号と正確に等しくなりました。ファスナーは残酷なケースです。なぜなら、句読点が物理的な寸法をエンコードするからです：<code>702-1-5/32</code>は1-5/32インチの部品であり、<code>702-15/32</code>は15/32インチの部品であり、両方とも<code>7021532</code>に正規化されます。顧客は正確な部品番号を入力し、正しい部品とその寸法上のいとこの間のコインフリップを得ます。</p>\n\n<p>また、このカテゴリには：競合他社の相互参照（「ファーガソン番号を持っていますが、あなたのは何ですか？」）が、<code>19MU82|Fastenal 0269214|Ferguson M48222407</code>のような分割されていないパイプ区切りのブロブとして保存され、検索不可として構成されたフィールドにあるため、コアなB2B販売モーションが機能しません。そして、空白のフォーク - 同じ部品番号が3回インデックスされている（プレーン、NBSPパディング、末尾スペース） - さらに、二重デコードされたUTF-8からのモジバケ双子。</p>\n\n<p>これをほとんど修正する原則：完全一致は構造的にファジーを打ち負かさなければならず、確率的にではありません。句読点を保持する完全一致句は、正規化されたティアよりも厳密に上にスコアリングされ、バリアントが正規のヒットとタイできるフラットな一致は決してありません。インデックスする前に生成されたバリアントを正規のPNスペースと照合し、衝突をドロップします。レコードIDを導出する前にUnicode正規化します。相互参照ブロブを配列に分割します。それは1行のインジェストコードで、クエリクラス全体をアンロックします。</p>\n\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\n<p>上記に収まらなかったが一文に値するいくつかのこと：数字をワイヤゲージ用語に変える同義語ジェネレーターがあり、<code>REF#259286</code>が「259286 awg」になり、<code>#8-32</code>ネジ山が「8 gauge wire」になりました（何千ものレコードが関係のない電気クエリにマッチするようになりました）。eコマースプラットフォームの内部フラグが検索可能な製品仕様としてインデックスされ、210万のジャンクなキーと値のペア。そして、どこにもゼロレコードで入力された18のスキーマフィールド。拡張ジェネレーターにはコンテキストゲートが必要です。死んだスキーマは、誰かが最終的にその上に構築する誤った約束です。</p>\n\n<h2>欠けているレイヤー：UNSPSC、そして自分が何であるかを知らない製品</h2>\n\n<p>独自のセクションに値する5番目の問題があります。それは、貿易流通がマーケットプレイスから最も遅れている場所だからです：分類です。</p>\n\n<p>私の監査では、カテゴリフィールドはあるカタログの83%で欠落していました。属性キーにはタクソノミーがまったくなく、ある156Kレコードのカタログには7,068の異なる仕様キーがあり、その64%が10未満の製品で使用され、<code>horse_power</code>と<code>horsepower</code>のようなドリフトが同じ属性をファセット間で分割していました。そして、私が何度も思い出す詳細：4つのカタログのうち2つにはUNSPSCフィールドがスキーマにあり、マッピングされ、準備されていましたが、正確にゼロレコードに入力されていました。誰かが分類が重要だと知っていて、スロットを構築し、決して埋めませんでした。理由に賭けてもいいです：300K SKUを手動でタクソノミーに分類することはカタログチームの1年間の仕事であり、それは永遠にロードマップに残りました。</p>\n\n<p>B2Bの外にいる場合、UNSPSCは国連標準製品サービスコードであり、調達システムが話すタクソノミーです。これはコンシューマー検索にはない部分です：あなたの顧客の購買システムがそれを要求します。パンチアウトカタログ、e調達プラットフォーム、支出分析ツールはUNSPSCコードを中心に構築されています。クリーンなコードを持つカタログを運ぶディストリビューターは、請負業者や病院の購買システムにプラグインできます。それがないものは、検索ボックス付きのPDF価格表です。内部的には、カテゴリファセット、カテゴリスコープのランキング（「ワイヤゲージとして解析される数字はワイヤのみをブーストするべき」）、カタログ間の重複排除、およびカテゴリ4のすべての拡張ジェネレーターのコンテキストゲートを強化します。</p>\n\n<p>そして、それは今やバッチジョブです。これはDoorDashがLLMで解決すると説明するのと同じ形の問題です - 制御された語彙に対する高ボリュームのラベリング - そしてアプローチは直接転送されます：</p>\n\n<ul>\n<li>フラットではなく階層的に分類します。UNSPSCには4つのレベル（セグメント、ファミリー、クラス、コモディティ）があります。モデルに約450のオプションからファミリーを選択させ、次にそのファミリー内のクラスを選択させます。2つの小さな制約された選択は、1つの50,000方向の選択よりも優れており、DoorDashがエンティティリンキングを語彙に制約するのと同じように、関連するタクソノミースライスをコンテキストにフィードできます。</li>\n<li>最初にクラスレベルで停止します。クラスコードはすでにファセットと調達統合をアンロックします。コモディティレベルの精度は、それが価値を発揮するところで後で来ることができます。</li>\n<li>信頼度でルーティングします。小さなモデルが明確なケースを処理し、低信頼度のレコードは大きなモデルにエスカレーションし、永続的な不一致は人間のキューに行き、それが評価セットを兼ねます。</li>\n</ul>\n\n<p>コストは書くのが恥ずかしいほどです。分類は短い出力タスクです：レコードあたり約300入力トークンと30出力。Claude Haiku 4.5のバッチAPI価格で、1,000 SKUあたり約17セントです。私の775Kレコードのフリート全体は約130ドルで分類されます。手動作業の1年間だったために何年もロードマップにあったものが、それを議論するチームランチよりも安いです。</p>\n\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 -&gt; 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\n<h2>クリーンアッププレイブック、実際の請求書付き</h2>\n\n<p>修正パイプラインには3つのティアと分類ジョブがあります。コツは適切なティアで費やすことです。なぜなら、レコードごとにLLM呼び出しがほとんど必要ないからです。</p>\n\n<pre><code class=\"language-mermaid\">flowchart LR\n    A[\"Tier 0 - Rules<br/>deterministic code<br/>~$0\"] --&gt; B[\"Tier 1 - LLM on unique values<br/>brands · spec keys · units<br/>tens of dollars\"]\n    B --&gt; C[\"Tier 2 - LLM per record<br/>titles · attributes · UNSPSC<br/>hundreds of dollars\"]\n    C --&gt; G[\"Ingest gates<br/>every fix becomes a validator\"]\n</code></pre>\n\n<p><strong>ティア0は決定論的なコードで、基本的に無料です。</strong>HTMLを削除し、エンティティをデコードします。センチネルを明示的なフラグで実際のNULLに変換します。UPCを修復します（左パディング、科学的記数法の拒否、チェックディジットの検証）。IDをUnicode正規化します。パイプ区切りの相互参照を分割します。別の製品の正規番号と衝突するPNバリアントをドロップします。死んだスキーマを削除します。エージェントがセッションでこれらのスクリプトを書き、このティアは私の監査が見つけたものの約半分を修正しました。エンリッチメントの前にこれを行ってください。そうしないと、パイプラインが静かにドロップした製品のタイトルを美しく書き換えるためにモデルにお金を払うことになります。</p>\n\n<p><strong>ティア1はレコードではなく一意の値に対してLLMを実行します。</strong>これは語彙問題を安くするトリックです：私の最悪のカタログには313Kレコードがありましたが、ブランド文字列は741しかありませんでした。ブランド正規化は741文字列に対する1つのジョブです - ケースバリアントをクラスタリングし、サブブランドを親にマッピングし、プレースホルダーをフラグ付けします - 313K呼び出しではありません。仕様キーと単位サフィックスにも同じ動きをします。各語彙ジョブは1桁のドルで、その出力はインジェストが永遠に決定論的に適用する静的エイリアステーブルです。ティア全体をフリートで20〜50ドルと呼び、主にレビューパスに費やされます。</p>\n\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\n<p><strong>ティア2はレコードごとのパスです</strong>：顧客が読めるタイトル、検索拡張、構造化属性、およびすべての製品の回復されたメーカー部品番号。SKUあたりは小さく、約400入力トークン（レコードと、プロンプトキャッシングがほぼ無料にする共有指示）と250出力です。モデルを選ぶ前に2つのレバー：ライブのものをエンリッチします（私の最悪のカタログではレコードの14%だけがアクティブで、そこで86%削減されます）、そしてプロバイダーが持っている場合はバッチAPIを使用します。エンリッチメントにはレイテンシー要件がなく、AnthropicとOpenAIの両方がバッチで50%オフにするからです。</p>\n\n<p>私は2026年に使用する可能性のあるすべてのモデルで同じジョブの価格を設定しました - Claude、<a href=\"https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/\">GPT-5.6の3つのティア</a>（Sol、Terra、Luna）、Kimi K3、GLM-5.2、Qwen3.5、<a href=\"https://deepseek.ai/pricing\">DeepSeek V4</a>。同じワークロード、プロバイダー公開レート、バッチ割引が存在する場合は適用：</p>\n\n<p><img src=\"/assets/images/b2b-search-enrichment-cost-by-model.png\" alt=\"Cost to enrich 775K SKUs across models\" /></p>\n\n<p>これらの数字は間違っていると感じたので、再確認する必要がありました。このプロジェクトの最悪のケース - フロンティアモデル、すべてのSKU、フィルタリングなし - は約3,800ドルで上限に達します。下限は100ドル未満です：DeepSeek V4-Flashは、まともなドリルの価格でフリート全体をエンリッチします。実際に実行するバージョンはティアを混在させます：機械的な多数派には予算モデル（Haiku、Luna、Qwen Flash、DeepSeek）、小さなモデルが低信頼度としてフラグを付けた略語サラダの尾部にはミッドティアモデル（Sonnet、Terra、GLM-5.2）、そして品質ゲートとして1〜2%のサンプルをスポットチェックするフロンティアモデル。選択するベンダーに関係なく、ライブSKUで数百ドルになります。</p>\n\n<p>チャートが示せない2つの注意点。安いモデルは、その出力が評価に耐える場合にのみ安いです。コミットする前に200サンプルの比較を実行してください。部品番号の5%を台無しにする予算モデルは、それをしないフロンティアモデルよりもコストがかかります。そして、あなたのカタログは競争力のあるデータです：価格、相互参照、顧客部品番号がそれらのレコードに含まれているため、リストで最も安いエンドポイントに775Kレコードを送信する前に、各プロバイダーのデータ保持とトレーニング条件を確認してください。多くのディストリビューターにとって、そのチェックだけで一部のベンダーを選別し、オープンウェイトオプション（DeepSeek、GLM、Qwen、Kimi）は、データがまったく出られない場合に自己ホストできるという追加の特性があります。</p>\n\n<p>約130ドルのUNSPSCパス（モデル間で同じようにスケーリングします - 予算ティアではポケットチェンジに下がります）と語彙作業を追加すると、カタログ変換全体は数百ドルから数千ドルの間で実行され、かつてマーチャンダイジングチームの1年間だった作業に対してです。これはInstacartが説明するのと同じオフラインバッチ経済であり、貿易カタログ規模では数字がクレジットカードに載せられるほど小さくなります。</p>\n\n<p>私の監査からの実際のレコードでどのように見えるか：</p>\n\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\n<p><code>extracted_mpn</code>行を見てください。同じパスが、以前は説明テキストに埋もれていたメーカー部品番号を回復します - 私のカタログの1つでは、183Kの製品が箱に印刷された番号で見つけられるようになります。それだけでバッチジョブの代金を支払います。</p>\n\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, &lt;=80 chars, Title Case. Expand trade\n  abbreviations (CXC -&gt; copper x copper connection, ELL -&gt; elbow, CPLG -&gt;\n  coupling, RED -&gt; 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\n<p>2つの本番ノート：共有指示をキャッシュされたプロンプトプレフィックスに置きます（キャッシングはバッチ割引に加えて入力側を最大90%削減します）、そしてJSONスキーマで構造化出力を使用して、パース失敗税を決して支払わないようにします。</p>\n\n<p><strong>最後のティアは人々がスキップするもので、複利で効くものです。</strong>監査は1回のクリーンアップの価値があります。それが生成するバリデータは、将来のすべてのフィードの価値があります。修正セッションを次のように終了します：</p>\n\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\n<p>これをスキップすると、同じERPエクスポートが四半期内に同じゴミを再生成し、クリーンアップに2回支払うことになります。私の監査が見つけたバグのうち2つは明らかに以前に修正されており、戻ってきたからです。</p>\n\n<h2>プロンプトの実行</h2>\n\n<p>1つのセットアップステップ：カタログをJSONLにダンプし、1行に1つのJSONオブジェクト、一意の<code>id</code>とレコードのフィールドを持たせます。プラットフォームが何であれ - Elasticsearch、OpenSearch、Solr、Algolia、Typesense、PIM、その背後にあるデータベース - エージェントにエクスポーターを書くように依頼するだけです：</p>\n\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\n<p>Claude Codeでは、そのディレクトリのセッションに任意のカテゴリプロンプトをドロップします。それは分析自体を書き、実行し、サンプリングの言い訳なしでフルパスを行い、レコードレベルの領収書を持って戻ってきます。「今すぐ修正」と言うと、同じセッションがインジェストバリデータ、バックフィルスクリプト、バッチエンリッチメントジョブを生成します。カテゴリを並列セッションまたはサブエージェントとして実行します。それが私の実行方法であり、監査全体は約12分の壁時計時間かかりました。Codexでは、<code>codex exec</code>が同じプロンプトを使用して読み取り専用の分析パスを処理します。書き込み側の修正は、レビューするセッションに保持します。</p>\n\n<p>苦労して学んだ3つのルール：</p>\n\n<ol>\n<li>フルパスと領収書を要求します。「このデータを分析」はサンプリングと雰囲気を招きます。正確な数と発見ごとに5つの例のレコードIDを要求すると、すべての主張がチェック可能になります。</li>\n<li>エンリッチメントの前にルール。散文を追加する前に真実を修正します。そうしないと、「承認済みベンダー」とラベル付けされたレコードの88%に対してブランドを幻覚させます。</li>\n<li>サンプル、評価、そしてバッチ。検証されていないプロンプトから775Kレコードのバッチを決して発射しないでください。200サンプル、スコアリングパス、そしてスケール。評価ハーネスは1つのエージェントセッションのコストで、支出全体のリスクを軽減します。</li>\n</ol>\n\n<h2>貿易業界はマーケットプレイス級の検索に値する</h2>\n\n<p>Amazon、DoorDash、Instacartは、慈善としてカタログLLMの作業を公開しませんでした。彼らは、技術が一般的であり、堀が実行であるため公開しました。そして、その技術は、私が考えられるほとんどどこよりも貿易流通にうまく転送されます：カタログは十分に小さく、フルパスが安く、データは十分に悪く、ヘッドルームは巨大で、クエリは実際のお金を運び、調達世界はすでにバッチジョブが約130ドルで入力できるタクソノミーで実行されています。</p>\n\n<p>私の監査が4つのディストリビューターカタログで表面化した数十の問題のうち、ランキングレイヤーから見えるものは1つもありませんでした。それらのすべてがそれを劣化させました。それらのインデックスを供給するサプライチェーン - ERPエクスポート、サプライヤーのスプレッドシート、古代のCSVパーサー - は、公開されたプレイブックが修正するまさにそのものです。それを実行するディストリビューターは、競合他社がまだコンデンサーコイルを見つけられないカタログでマーケットプレイス級の検索を持つでしょう。</p>\n\n<p>「私のデータに何か問題があるか？」という質問は、かつて誰かの2週間の時間を要し、それが誰も尋ねなかった理由です。今は1つのプロンプトのコストです。尋ねてください。</p>",
  "source_hash": "sha256:729970b39c1b22c143ba55272659d6cc431b0f8889282089a4ea4eaa71f75000",
  "model": "~deepseek/deepseek-v4-flash-latest",
  "generated_at": "2026-08-06T08:54:56.156692+00:00"
}