差分同期によるコスト最適化 — 変更があったものだけを処理する仕組み

数千件のデータを毎回処理すると時間もコストもかかる。コンテンツハッシュによる変更検知で効率的にデータを更新する方法

差分同期コスト最適化ハッシュAPI費用効率化Embedding
読了時間: 10分

はじめに

RAGシステムでは、データをベクトル化(Embedding)する必要があります。しかし、数千件のデータを毎回処理すると、以下の問題が発生します。

  • 時間がかかる:全件処理に数十分〜数時間
  • コストがかかる:Embedding APIは従量課金

この記事では、**変更があったデータだけを処理する「差分同期」**の仕組みを解説します。日常的な運用コストを抑えながら、データを最新に保つ方法です。

毎回フル処理する問題点

処理時間の問題

数百件
処理内容全件Embedding
所要時間(目安)数分
数千件
処理内容全件Embedding
所要時間(目安)約10〜30分
数万件
処理内容全件Embedding
所要時間(目安)数時間

毎回フル処理すると、「ちょっとマニュアルを1件追加しただけ」でも長時間待つことになります。

コストの問題

OpenAIのEmbedding APIは従量課金です。text-embedding-3-smallの場合、100万トークンあたり約$0.02です。

1件のFAQ
トークン数(目安)約500トークン
費用(目安)約$0.00001
数千件フル処理
トークン数(目安)約200万トークン
費用(目安)約$0.04
月4回フル処理
トークン数(目安)約800万トークン
費用(目安)約$0.16

一見安そうに見えますが、変更がないのに毎回お金を払うのは無駄です。

差分同期の考え方

基本的なアイデア

差分同期の基本的なアイデアは、**「変更があったものだけ処理する」**ことです。

差分同期の流れ
データを読み込む

各データソースから最新データを取得

ハッシュを計算

各データの「指紋」を計算

前回と比較

DBに保存されているハッシュと比較

差分だけ処理

ハッシュが異なるもののみEmbedding

コンテンツハッシュとは

コンテンツハッシュとは、データの内容を短い文字列(ハッシュ値)に変換したものです。同じ内容なら同じハッシュ値になります。

データ内容 → ハッシュ関数 → ハッシュ値

「返金の手続きについて...」 → MD5 → "a1b2c3d4e5f6..."
「返金の手続きについて...」 → MD5 → "a1b2c3d4e5f6..."(同じ)
「返金の手順について...」   → MD5 → "x7y8z9w0..."(異なる)

差分検知の仕組み

データベースに保存する際、ハッシュ値も一緒に保存しておきます。

source_id
役割データの識別子(FAQ-001など)
content
役割本文(検索対象)
content_hash
役割内容のハッシュ値(変更検知用)
embedding
役割ベクトル化されたデータ
updated_at
役割最終更新日時

同期時に、新しいデータのハッシュと保存済みのハッシュを比較します。

同じ
処理スキップ(Embedding不要)
異なる
処理再Embedding → 更新
source_idが新規
処理新規追加 → Embedding
DBにはあるが元データにない
処理削除

実装のポイント

ハッシュ計算の対象

ハッシュ計算には、Embeddingに影響する要素のみを含めます。

本文(content)
含めない作成日時
カテゴリ
含めない参照回数
タグ
含めないメタ情報(表示用)

メタ情報が変わっただけで再Embeddingしないよう、検索に関わる部分だけをハッシュ対象にします。

バルク処理

差分が見つかった場合、1件ずつAPIを呼ぶのではなく、まとめて処理します。

変更あり: 10件
↓
10件をまとめてEmbedding APIに送信
↓
10件まとめてDBに保存

これにより、API呼び出し回数を減らし、処理効率を上げています。

トランザクション管理

データの整合性を保つため、トランザクションで処理します。

トランザクション処理
トランザクション開始

BEGIN

削除

元データにないものを削除

更新

変更があったものを更新

追加

新規データを追加

コミット

COMMIT(途中でエラーなら ROLLBACK)

途中でエラーが発生しても、中途半端な状態にならないようにしています。

コスト削減効果

比較シナリオ

実際のプロジェクトでの比較です。

フル処理(初回)
処理時間約10分
API費用約$0.01
差分同期(変更なし)
処理時間数秒
API費用約$0
差分同期(10件追加)
処理時間約30秒
API費用約$0.0001
差分同期(100件更新)
処理時間約2分
API費用約$0.001

日常的な運用

通常の運用では、大きな変更がないケースがほとんどです。

  • マニュアルの軽微な修正:数件
  • 新しいFAQの追加:週に数件
  • 対応履歴の追加:月に数十〜数百件

差分同期により、これらの日常的な更新は数秒〜数分で完了します。

運用フロー

推奨される更新サイクル

毎日(必要に応じて)
対象データFAQマニュアル
所要時間数秒〜数分
週1回
対象データ対応履歴
所要時間数分〜十数分
月1回
対象データ全データ確認同期
所要時間数分〜十数分

同期コマンド

同期は、シンプルなコマンド1つで実行できます。

npm run sync-all

このコマンドを実行すると、以下の処理が自動で行われます。

  1. 各データソースからデータを取得
  2. ハッシュを比較して差分を検出
  3. 差分のみをEmbedding
  4. DBを更新
  5. 処理結果をレポート表示

処理結果のレポート

同期完了後、以下のような結果が表示されます。

=== 同期完了 ===
処理時間: 45秒
変更検出: 15件
  - 追加: 8件
  - 更新: 5件
  - 削除: 2件
スキップ: 5,235件
API費用: 約$0.0002

どのくらいの処理が行われたか、一目で確認できます。

フォールバック:フル同期

いつフル同期が必要か

差分同期が基本ですが、以下の場合はフル同期が必要になることがあります。

初回導入時
対応全データをEmbedding
Embeddingモデルを変更
対応全データを再Embedding
データ構造の大幅変更
対応全データを再処理
DBの破損・復旧
対応バックアップから復元 or フル同期

フル同期コマンド

必要な場合は、フル同期も可能です。

npm run sync-all --force

--force オプションを付けると、ハッシュ比較をスキップして全件処理します。

まとめ

差分同期により、以下を実現しました。

  1. 処理時間の短縮:日常的な更新は数秒〜数分で完了
  2. コストの削減:変更がない場合はAPI費用ゼロ
  3. 運用のしやすさ:コマンド1つで完了、レポートで結果確認

データ量が増えても、差分同期なら日常的な運用コストを抑えたままデータを最新に保てます。

関連記事