前四半期のプロジェクト:28,840件の製品レコードをAIで一括エンリッチメント

前四半期、一見シンプルに見えるプロジェクトに取り組みました:28,840件の製品レコードをAIで生成した説明文、カテゴリー、メタデータで充実させることです。クライアントは膨大なeコマースカタログをヘッドレスCMSに移行しており、すべてのレコードにより良いコンテンツが必要でした。その結果、何十万ものレコードをAI APIに投げつけるときに起こりうるすべての問題(と成功)の実践的なレッスンとなりました。

これは理論的なガイドではありません。実際に構築したアーキテクチャ、実際に支払ったコスト、遭遇した障害モード、私たちを救ったパターンについて詳しく説明していきます。自分のプロジェクトでAI一括コンテンツエンリッチメントを検討しているなら、このドキュメントは数週間の苦しい試行錯誤を省くのに役立つはずです。

目次

手作業の代わりにAIエンリッチメントを選んだ理由

計算は容赦なくシンプルでした。クライアントは28,840件の製品レコードを持っており、各レコードは改写された説明文(150~300語)、3つのSEOフレンドリーなカテゴリータグ、メタディスクリプション、および非構造化テキストから抽出された構造化属性が必要でした。コピーライターが1レコードあたり8分で作業するという保守的な見積もりで、それは3,845時間の作業です。時給35ドルなら134,575ドルで、小規模なチームなら約6ヶ月かかります。

AIエンリッチメントは、API費用で11日間に3,200ドル未満、エンジニアリングとQA時間で約80時間で完了しました。開発時間も考慮すれば、総コストは手作業アプローチの約10分の1です。

ただし、誰も教えてくれないことがあります:難しい部分はAPIを呼び出すことではありません。それはその周囲のすべてです。データクリーニング、プロンプト調整、品質検証、エラーハンドリング、そしてあなたの職業選択に疑問を持つようになる避けられないエッジケース。

アーキテクチャ:パイプラインの構築方法

Node.jsアプリケーションとしてエンリッチメントパイプラインを構築しました。これはNext.js開発とTypeScriptに関する私たちのチームの専門知識を考えると理にかなっていました。高レベルアーキテクチャは以下の通りです:

ソースCSV → パーサー → バッチキュー → Claude API → レスポンス検証 → 出力ストア → QAダッシュボード

データレイヤー

ローカル処理データベースとしてSQLiteを使用しました。地味に聞こえますね?しかし、このようなバッチ処理では完璧です。管理するサーバーがなく、トランザクションは高速で、結果を簡単にクエリできます。各レコードは、その過程を追跡するステータス列を取得しました:

interface EnrichmentRecord {
  id: string;
  original_title: string;
  original_description: string;
  raw_attributes: string;
  status: 'pending' | 'processing' | 'completed' | 'failed' | 'needs_review';
  enriched_description: string | null;
  enriched_categories: string[] | null;
  enriched_meta: string | null;
  structured_attributes: Record<string, string> | null;
  attempts: number;
  last_error: string | null;
  token_usage: number;
  created_at: string;
  updated_at: string;
}

キューシステム

Redisを使用したBullMQを使用して単純なジョブキューを実装しました。各ジョブは単一のレコードエンリッチメントを表しました。以下のように構成しました:

  • 並行処理: 5つの並列ワーカー(この数字が重要な理由については後で説明します)
  • 最大リトライ: レコードあたり3回
  • バックオフ: 指数関数的、30秒から開始
  • ジョブタイムアウト: 60秒
const enrichmentQueue = new Queue('enrichment', {
  connection: redisConnection,
  defaultJobOptions: {
    attempts: 3,
    backoff: {
      type: 'exponential',
      delay: 30000,
    },
    timeout: 60000,
    removeOnComplete: false, // 監査用に保持
  },
});

処理ワーカー

各ワーカーはレコードをプルしてプロンプトを構成し、Claude APIを呼び出し、レスポンス構造を検証し、結果をバックアップに書き込みました。レスポンスが期待されたJSONスキーマと一致しない場合、私たちのデータセットを静かに破損させるのではなく、needs_reviewバケットに入れられました。

一括処理用のClaude APIの選択

Claude(特にClaude 3.5 Sonnet、および後にClaude 3.5 Haikuをより単純なタスク用に)に決める前に、3つのオプションを評価しました:

機能 Claude 3.5 Sonnet GPT-4o Gemini 1.5 Pro
入力コスト(100万トークンあたり) $3.00 $2.50 $1.25
出力コスト(100万トークンあたり) $15.00 $10.00 $5.00
レート制限(RPM、Tier 2) 1,000 500 360
JSONモード信頼性 優秀 良好 不安定
構造化出力品質 業界最高水準 非常に良好 良好
Batch API割引 50% 50% N/A

Q1 2025の時点での価格。現在の価格を確認してください -- これらは頻繁に変わります。

いくつかの理由でClaudeを選択しました。第一に、構造化出力の命令追従は、500レコードのテスト実行中に代替案よりも目立って優れていました。ほぼ29,000レコードを処理するとき、フォーマット準拠性における2%の改善でさえ、数百の手動修正を節約します。第二に、AnthropicのBatch APIは時間に左右されないの作業について50%割引を提供したため、経済性はさらに有利でした。

正直に言うと、GPT-4oでも問題なかったはずです。このスケールの違いは、生の品質よりもレート制限と価格についてです。しかし、JSON出力の一貫性はClaudeを決定づけた要因でした。

SonnetとHaikuの両方を使用した理由

ここに、API費用の約40%を節約した秘訣があります:すべてに同じモデルを使用しませんでした。製品説明はSonnetの品質が必要でした。しかし、カテゴリー分類と属性抽出?Haikuはほんの一部のコストでそれらを簡単に処理できました。

エンリッチメントを2つのパスに分割しました:

  1. パス1(Haiku): カテゴリー分類、属性抽出、基本的なメタデータ -- $0.25/1M入力、$1.25/1M出力
  2. パス2(Sonnet): 説明の改写、メタディスクリプション、SEOコンテンツ -- $3.00/1M入力、$15.00/1M出力

大規模なプロンプトエンジニアリング

これは、ほとんどのチュートリアルが失敗するところです。単一のプロンプトを表示して完了です。28,840レコードを同じプロンプトテンプレートで実行するとき、小さな欠陥が大きな問題に増幅されます。

プロンプトテンプレート

約15回の反復(はい、gitで追跡しました)の後、以下が機能したラフな構造です:

const buildPrompt = (record: SourceRecord): string => `
You are enriching product data for an e-commerce catalog. Generate the following for the product below:

1. A product description (150-300 words, second person, benefit-focused)
2. Exactly 3 category tags from this allowed list: ${CATEGORY_LIST}
3. A meta description (120-155 characters)
4. Structured attributes as key-value pairs

Rules:
- Do NOT invent features not present in the source data
- If information is ambiguous, use the "uncertain" flag
- Match the brand's tone: professional but approachable
- Description must be unique -- do not repeat the title verbatim in the first sentence

Respond ONLY with valid JSON matching this schema:
${JSON_SCHEMA}

Source product data:
Title: ${record.title}
Existing description: ${record.description}
Raw attributes: ${record.attributes}
Price: ${record.price}
Brand: ${record.brand}
`;

大規模なプロンプトのレッスン

出力フォーマットについて非常に具体的にしてください。 すべてのリクエストに完全なJSONスキーマを含めました。はい、トークンが追加されます。スキップしないでください。システム命令だけに頼ろうとした唯一の時間、フォーマット準拠が97%から81%に低下しました。

出力語彙を制約してください。 カテゴリータグの場合、明確な許可リストを提供しました。オープンエンドのカテゴリー化は、テストバッチ全体で847の一意なカテゴリーを生成しました。制約版?正確に42個のみ。

ハルシネーション用のガードレールを追加してください。 製品は、それらが持っていない機能に時々発展しました。「Do NOT invent features not present in the source data」を追加することで、幻覚属性が約70%削減されました。uncertainフラグを追加すると、残りのほとんどのケースが捕捉されました。

温度はあなたが考えるよりも重要です。 0.3に決定しました。それより低いと、説明は同様の製品全体で繰り返されるようになります。それより高いと、ブランドボイスと一致しないクリエイティブライティングが始まります。

レート制限、リトライ、そしてBANされないための技術

このセクションは本当に「最も多くのエンジニアリング時間がかかった部分」と呼ぶべきです。Anthropicのレート制限はよく文書化されていますが、ドキュメントを読むだけでは予想されるよりも持続的な負荷下で異なる動作をします。

レート制限戦略

Tier 2($40以上を費やした後に取得)では、Claudeは1分あたり1,000リクエストと1分あたり80,000トークンを提供します。平均リクエストが約1,200入力トークンと800出力トークンであることを実現するまでは気前よく聞こえます。つまり、トークン制限に達する前に約40の並行リクエストが実際の制限でした。

5つの並行ワーカーを実行し、各ワーカーは一度に1つのレコードを処理し、リクエスト間に200msの遅延がありました。これにより、1分あたり約15~20リクエスト、RPM制限をはるかに下回り、トークン予算内で快適に収まります。

const rateLimiter = new Bottleneck({
  maxConcurrent: 5,
  minTime: 200, // ms between requests
  reservoir: 900, // requests per minute (leaving buffer)
  reservoirRefreshAmount: 900,
  reservoirRefreshInterval: 60 * 1000,
});

なぜこんなに保守的なのか? レート制限に達するとカスケード障害が発生するためです。1つの429レスポンスはリトライを引き起こし、キューに追加され、並行処理圧力を増加させます。最初の実行の3時間で攻撃的な設定を使用して学習しました。リトライストームを引き起こし、パイプラインを実質的に45分間停止させました。

Batch API代替案

プロジェクトの途中で、部分的にAnthropicのBatch APIに切り替えました。個々のリクエストを作成する代わりに、リクエストのJSONLファイルをアップロードし、24時間以内に結果を取得します。トレードオフ:50%のコスト削減、ただしリアルタイムフィードバックが失われます。

パス1(Haiku分類)にはBatch APIを使用し、パス2(Sonnet説明)には実時間APIを使用しました。このハイブリッドアプローチは私たちにとって甘いポイントでした -- 高価なクリエイティブワークへの高速フィードバック、商品分類の一括経済学。

品質管理:人間を含むループの現実

AI エンリッチメントが完全に自動化されていると言う人は、嘘をついているか、大規模なことをしていません。初期に問題をキャッチし、ゴミが本番環境に到達するのを防ぐQAプロセスを構築しました。

自動検証

すべてのAPIレスポンスは受け入れられる前に検証を経ました:

const validateEnrichment = (result: EnrichmentResult): ValidationOutcome => {
  const issues: string[] = [];
  
  // Length checks
  if (result.description.length < 400 || result.description.length > 2000) {
    issues.push('description_length');
  }
  
  // Category validation
  const invalidCats = result.categories.filter(c => !ALLOWED_CATEGORIES.includes(c));
  if (invalidCats.length > 0) issues.push('invalid_categories');
  
  // Meta description length
  if (result.meta.length > 160) issues.push('meta_too_long');
  
  // Hallucination signals
  const hallucination_phrases = ['I think', 'probably', 'might be', 'as an AI'];
  if (hallucination_phrases.some(p => result.description.includes(p))) {
    issues.push('possible_hallucination');
  }
  
  // Duplicate detection (fuzzy match against already-processed records)
  if (isDuplicateDescription(result.description)) {
    issues.push('duplicate_content');
  }
  
  return {
    valid: issues.length === 0,
    issues,
    needsReview: issues.length > 0 && issues.length < 3,
    rejected: issues.length >= 3,
  };
};

手動レビューサンプリング

すべて処理されたレコードの5%(約1,440)をサンプルで手動レビューしました。QAチームは各を精度、ブランドボイス、完全性で採点しました。実際のレビューの数字は以下の通りです:

メトリック スコア
事実の正確性 94.2%
ブランドボイスの一致 87.6%
フォーマット準拠 97.1%
カテゴリー精度 91.8%
改正が必要なレコード 8.3%
完全に拒否されたレコード 1.9%

その8.3%の改正が必要というのは重要なコンテキストです。それは約2,400レコードが人間による編集が必要であることを意味します。それでも、すべての28,840を手作業で書くことよりはるかに少ないです -- しかしそれはゼロではありません。それを予算に入れてください。

実際のコスト内訳

透明性の時間です。実際に費やしたもの:

コストカテゴリー 金額
Claude 3.5 Haiku(パス1 - Batch API) $312
Claude 3.5 Sonnet(パス2 - リアルタイム) $2,147
失敗/リトライリクエスト(~6%オーバーヘッド) $189
Redisホスティング(2週間) $15
エンジニアリング時間(80時間×$150) $12,000
QAレビュー時間(40時間×$45) $1,800
合計 $16,463
APIコストのみ $2,648

$134,575の完全手作業見積もりと比較してください。すべてのエンジニアリングとQA時間を含めても、手作業コストの約12%です。さらに、パイプラインは再利用可能です -- 次回同様のプロジェクトを実行するとき、エンジニアリング費用はほぼゼロに低下します。

レコードあたりのAPIコストは約$0.092でした。レコードあたり10セント未満のAIエンリッチメント。これは経営陣が身を乗り出す数字です。

間違ったこと

データクリーニングを過小評価

Claudeに送信する前に、ソースデータをクリーニングするだけで3日費やしました。レコードはHTMLエンティティ、Unicodeゴミ、切り詰められた説明、および間違った列のフィールドを持っていました。ゴミが入れば、ゴミが出る -- これは単なる決まり文句ではなく、一括AI処理の基本法則です。

最初からBatch APIを使用していない

パス1を実時間APIで実行してからBatch APIが半額であることを発見することで、約400ドルの余分なAPI費用を燃焼させました。開始する前に完全なドキュメンテーションを読んでください。すべて。

不十分な重複検出

最初の重複検出は単純すぎました -- 文字列マッチングのみ。Claudeは、同様の製品の説明を構造的に同一ですが、やや異なる単語を使用して生成されました。これらをキャッチするためにセマンティック類似度チェック(埋め込みを使用)を実装する必要があり、これは1日の作業を追加しました。

JSONパース障害

レスポンスの約2.4%は、不正なJSONで戻ってきました。時々末尾のコンマ、時々製品説明のエスケープされていない引用符。最初からこれらを困難な障害として扱うのではなく、より寛容なJSONパーサーを実装すべきでした。

// 最初からすべきだったこと
const parseResponse = (raw: string): EnrichmentResult | null => {
  try {
    return JSON.parse(raw);
  } catch {
    // Try to extract JSON from markdown code blocks
    const jsonMatch = raw.match(/```json?\n?([\s\S]*?)\n?```/);
    if (jsonMatch) {
      try { return JSON.parse(jsonMatch[1]); } catch { /* fall through */ }
    }
    // Try jsonrepair library as last resort
    try { return JSON.parse(jsonrepair(raw)); } catch { return null; }
  }
};

次回は何を変えるか

  1. 完全な実行に提供する前に1,000レコードのパイロットから始めます。 500をしました、そしてそれはすべてのエッジケースを浮き上がらせるのに十分ではありませんでした。

  2. 最初から構造化出力を使用します。 Anthropicは現在、定義されたスキーマでツール使用をサポートしており、ほとんどのJSONパース問題を排除します。途中でこれに移行し、ここから始めたいと思います。

  3. 最初にQAダッシュボードを構築します。 問題が現れた後に反応的に構築しました。1日目からそれを持つことは、最初の2,000レコード代わりに最初の100レコードの問題をキャッチしたであろう。

  4. dedup用のより良い埋め込みに投資します。 最初から専用の埋め込みモデル(text-embedding-3-smallなど)をセマンティック重複検出に使用します。

  5. ハイブリッドモデルルーティングを検討します。 一部のレコードは単純です(基本的な属性のあるTシャツ)、一部は複雑です(数十の仕様を持つ電子機器)。単純なレコードをHaikuにルーティングし、複雑なレコードをSonnetにルーティング -- 説明についても -- API費用でさらに20~30%を節約したであろう。

同様のプロジェクトを計画していて、苦しい部分をスキップしたい場合、ヘッドレスCMS開発慣行の一部として、この種の作業用に再利用可能なパイプラインを構築しました。具体的により多くを共有する準備ができています。

よくある質問

28,000以上のレコードをAIで充実させるのにはどのくらいの時間がかかりますか? 実際の処理時間は約11日間でした。パイプライン開発、テスト、処理、QAレビューを含む。API処理自体(リクエスト送信と応答取得)は約48時間の継続的な実行を取りました。Batch APIを独占的に使用している場合、処理に24~48時間、エンジニアリングとQAに3~5日を期待してください。

AI コンテンツエンリッチメントのレコードあたりのコストは? Claude 3.5 SonnetとHaikuの組み合わせを使用して、製品説明、カテゴリー、メタディスクリプション、構造化属性を生成するためのAPIコストは、レコードあたり約$0.092でした。選択したモデルに基づいて、入力/出力の長さとコストはバリエーションが異なります。Batch API処理はこれをおよそ半減させます。

バルクデータエンリッチメントにはClaudeまたはGPT-4が優れていますか? 両方がうまく機能します。Claude 3.5 Sonnetを選択したのは、テスト中(97.1%対GPT-4oの約94%)のJSONフォーマット準拠が優れているためです。ただし、GPT-4oは出力トークンの価格がわずかに低いです。エンリッチメントが主に分類ではなくコンテンツ生成である場合、違いは無視できます。提供する前に500レコードで両方をテストしてください。

何千ものAPI呼び出しを行うときにレート制限をどのように処理しますか? Bottleneckなどのレート制限ライブラリを使用し、保守的な並行処理(5~10の並列リクエスト)を設定し、リトライの指数バックオフを実装し、公開されたレート制限の下に10~15%のバッファを残します。時間に左右されない作業の場合、AnthropicのBatch APIはレート制限の懸念を回避し、50%安く費用がかかります。

AIで充実したレコードの何パーセントが人間によるレビューが必要ですか? 私たちのプロジェクトでは、レコードの8.3%が何らかの形の人間による編集が必要で、1.9%は完全に拒否され、手動で改写されました。データ品質、プロンプトエンジニアリング、および許容可能な品質しきい値に応じて、数字は異なります。現実的なベースラインとして、5~15%の人間による介入を計画してください。

AI一括エンリッチメントは複数の言語を処理できますか? はい、しかし品質は言語によって大幅に異なります。ClaudeとGPT-4は主要なヨーロッパ言語をよく処理しますが、あまり一般的でない言語の精度は低下します。言語ごとに異なるプロンプトテンプレートを実行し、あなたのQAサンプルでネイティブスピーカーを持つことをお勧めします。英語以外のコンテンツの人間レビュー率は大体2倍になることを期待してください。

AI一括エンリッチメントで製品データのハルシネーション(幻覚)を防ぐにはどうすればよいですか? 3つのレイヤー:明示的に発明された機能を禁止するプロンプト指示、曖昧なデータについての「不確実」フラグ、およびエンリッチされた属性をソースデータと比較する自動検証。また、セマンティック類似度スコアリングを使用して、元の製品情報から大きく異なる説明にフラグを立てました。これは幻覚属性をおよそ70%削減しました。

カスタムパイプラインを構築する価値があるのか、それとも既存のツールを使用すべきか? 1,000レコード未満の場合、Clay、Bardeen、または適切に構造化されたGoogle Sheets + Apps Scriptセットアップなどのツールが機能できます。それ以上では、カスタムパイプラインはそれ自身の支払いをすぐに行います。カスタムソリューションが提供するリトライロジック、品質検証、およびコスト最適化の制御は、規模で不可欠です。私たちのパイプラインはおよそ2,000行のTypeScriptでした -- 些細ではありませんが、大規模なプロジェクトではもありません。ユースケース用に1つを構築することに興味がある場合は、価格ページを確認してください。