はじめに
社内の情報は、さまざまな場所に分散しています。マニュアルは社内Wiki、過去の対応履歴はスプレッドシート、商品情報は別のシステム——これらを横断して検索できないことが、業務効率を下げる大きな原因になっています。
この記事では、複数のデータソースを1つのデータベースに統合し、「1回の検索で全データを横断できる」仕組みを構築した方法を解説します。
分散データの課題
情報が見つからない問題
| データの種類 | 保存場所 | 検索方法 |
|---|---|---|
| 対応マニュアル | 社内Wiki | Wiki内検索 |
| 過去の対応履歴 | スプレッドシート | Ctrl+F で手動検索 |
| 見積データ | 別のスプレッドシート | シート内検索 |
| 商品マスタ | また別のスプレッドシート | 品番で検索 |
それぞれのツールで検索する必要があり、「どこに書いてあるか分からない」「検索しても見つからない」という問題が発生します。
統合のメリット
これらを1つのデータベースに統合すると、以下のメリットがあります。
- 1回の検索で全データを横断:質問に関連する情報を、ソースを問わず取得
- 意味による検索:「返金」で検索しても「払い戻し」「キャンセル」がヒット
- 参照元の明示:どのデータソースから取得したかを表示
データソースの統合設計
統一スキーマの設計
複数のデータソースを統合するには、**共通のデータ構造(スキーマ)**が必要です。
| フィールド | 説明 | 例 |
|---|---|---|
| content | 検索対象のテキスト本文 | 「返金のご依頼を承りました...」 |
| source_type | データの種類 | faq / ticket / estimate / product |
| source_id | 元データの識別子 | FAQ-001, TICKET-12345 |
| metadata | 追加情報(JSON) | カテゴリ、日付、タグなど |
| embedding | ベクトル化されたデータ | [0.12, -0.34, ...] |
| content_hash | 変更検知用のハッシュ | a1b2c3d4... |
データソースごとの変換
各データソースは形式が異なるため、統一スキーマへの変換処理が必要です。
Markdown形式
行ごとのデータ
文書ファイル
content + source_type + metadata + embedding
横断検索が可能に
各データソースの取り込み
FAQマニュアル(JSON形式)
手動で整備した対応マニュアルは、JSON形式で管理しています。
{
"id": "FAQ-001",
"question": "返金の手続きはどうすればいいですか?",
"answer": "返金のご依頼を承りました...",
"category": "決済",
"tags": ["返金", "キャンセル"]
}
これを統一スキーマに変換する際、質問と回答を結合してcontentとします。
対応履歴(スプレッドシート)
過去の対応履歴は、スプレッドシートに蓄積されています。
| 日付 | 問い合わせ内容 | 対応内容 | 結果 |
|---|---|---|---|
| 2024/01/15 | 注文した商品が届かない | 配送状況を確認し... | 解決 |
| 2024/01/16 | 返品したい | 返品条件を説明し... | 対応中 |
これらを1行ずつ取り込み、問い合わせ内容と対応内容を結合してcontentとします。
見積データ・商品マスタ
見積データや商品マスタは、検索時に補足情報として使われます。
「この修理の見積もりはいくらくらい?」
→ 見積データから関連情報を取得
→ 「〇〇の修理は△△円〜□□円が目安です」と回答
Embedding処理の実装
OpenAI Embedding APIの利用
テキストをベクトルに変換するには、OpenAIのEmbedding APIを使用します。
| モデル | 次元数 | 特徴 |
|---|---|---|
| text-embedding-3-small | 1536 | コスト効率が良い、十分な精度 |
| text-embedding-3-large | 3072 | より高精度だがコストも高い |
このプロジェクトでは、コストと精度のバランスからtext-embedding-3-smallを採用しています。
バッチ処理
数千件のデータを処理する場合、1件ずつAPIを呼ぶと時間がかかります。そこで、バッチ処理で効率化しています。
各データソースからデータを読み込み
前回と変更があるかチェック
変更があるものだけをまとめてEmbedding
ベクトルとメタデータをDBに保存
横断検索の実装
ソースタイプによるフィルタリング
必要に応じて、特定のデータソースのみを検索することも可能です。
-- FAQのみを検索
WHERE source_type = 'faq'
-- FAQと対応履歴を検索
WHERE source_type IN ('faq', 'ticket')
検索結果の活用
検索結果は、AIへの入力として使われます。
【検索結果】
1. [FAQ] 返金の手続きについて
返金のご依頼は、以下の手順でお受けしています...
2. [対応履歴] 2024/01/10の対応事例
同様のケースでは、〇〇の方法で対応しました...
3. [見積] △△の料金表
この作業の目安価格は□□円です...
AIは、これらの情報を元に回答を生成します。
なぜ同じテーブルに入れるのか
別テーブル vs 同じテーブル
データソースごとに別テーブルを作る方法もありますが、このプロジェクトでは同じテーブルに統合しました。
| アプローチ | メリット | デメリット |
|---|---|---|
| 別テーブル | データ構造を柔軟に設計可能 | 横断検索が複雑になる |
| 同じテーブル | 1クエリで全データを検索可能 | スキーマの統一が必要 |
統合の利点
同じテーブルに入れることで、以下の利点があります。
-
質問の意図を推測しなくていい
- 「マニュアルを見たいのか」「過去事例を見たいのか」をユーザーが指定しなくていい
- 質問内容に応じて、最適な情報が自動で選ばれる
-
検索ロジックがシンプル
- 1回のクエリで完結
- 複数テーブルをUNIONする必要がない
-
類似度の比較が正確
- 同じEmbeddingモデルで変換されているため、類似度が比較可能
データ品質の確保
重複データの排除
同じ内容が複数回取り込まれないよう、source_idによる重複チェックを行います。
既存データがある場合:
- content_hashが同じ → スキップ
- content_hashが異なる → 更新
欠損データの処理
必須フィールドが欠けているデータは、取り込み時にスキップまたは補完します。
| フィールド | 欠損時の処理 |
|---|---|
| content | 空の場合はスキップ(検索対象にならない) |
| source_type | 必須(データソースの識別に必要) |
| metadata | 空でも可(オプション情報) |
まとめ
分散データを統合することで、以下を実現しました。
- 1回の検索で全データを横断:質問に最適な情報がソースを問わず見つかる
- 意味ベースの検索:キーワードの揺れに対応
- 参照元の明示:どのデータソースから取得したかが分かる
データ統合は初期構築に手間がかかりますが、一度仕組みを作れば、新しいデータソースの追加も容易になります。