ストーリー
田
田中VPoE
ハイブリッド検索に加えて、メタデータフィルタリングも検索精度の向上に大きく貢献する
あなた
Step 2で設計したメタデータを検索時に使うんですね
あ
田
田中VPoE
そうだ。「開発部の技術文書の中から」「2025年以降の議事録から」といった条件で検索対象を絞り込む。これにより、無関係なドキュメントがヒットするのを防げる
あなた
フィルタリングはベクトル検索の前にやるべきですか?後にやるべきですか?
あ
田
田中VPoE
いい質問だ。プリフィルタとポストフィルタ、それぞれにメリットとデメリットがある。ユースケースに応じて使い分けよう
プリフィルタ vs ポストフィルタ
比較
| 観点 | プリフィルタ | ポストフィルタ |
|---|
| 実行タイミング | ベクトル検索の前 | ベクトル検索の後 |
| 仕組み | フィルタ条件に合うドキュメントのみを検索対象にする | 全ドキュメントを検索後、条件に合わない結果を除外 |
| メリット | 検索対象が絞られるため高速、結果が安定 | ベクトル検索の精度に影響なし |
| デメリット | フィルタ後のデータが少ないとTop-K不足になる | 除外後に結果が少なくなる可能性、無駄な検索コスト |
| 適するケース | フィルタ条件が明確で、対象データが十分にある場合 | フィルタ条件が曖昧、またはフィルタ後のデータ量が不明な場合 |
プリフィルタの流れ:
[フィルタ条件] doc_type = "技術文書" AND department = "開発部"
↓
[ベクトルDB] フィルタ条件に合うベクトルのみを対象に類似度検索
↓
[結果] Top-K(すべてフィルタ条件を満たす)
ポストフィルタの流れ:
[ベクトルDB] 全ベクトルを対象に類似度検索 → Top-K取得
↓
[フィルタ適用] doc_type = "技術文書" AND department = "開発部"
↓
[結果] フィルタ後の結果(K件より少ない可能性あり)
フィルタ演算子
Qdrantで利用可能なフィルタ
| 演算子 | 説明 | 例 |
|---|
| match (keyword) | 完全一致 | doc_type = "技術文書" |
| match (text) | テキスト検索 | content contains "RAG" |
| range | 範囲指定 | updated_at >= "2025-01-01" |
| geo | 地理的範囲 | (位置情報がある場合) |
| values_count | 配列要素数 | tags の数 >= 2 |
| is_null | NULL判定 | project is not null |
| has_id | ID指定 | 特定のIDリストに含まれるか |
複合フィルタの構築
複合フィルタの例:
Must(AND条件):
- doc_type = "技術文書"
- updated_at >= "2025-01-01"
Should(OR条件):
- department = "開発部"
- department = "インフラ部"
Must Not(NOT条件):
- tags contains "deprecated"
→ 結果: 2025年以降に更新された、開発部またはインフラ部の技術文書で、
deprecatedタグが付いていないもの
ファセット検索
ファセット検索は、検索結果をメタデータのカテゴリ別に集計する手法です。
ファセット検索のUI例:
検索: "RAGの構築方法"
[検索結果] 45件
[ファセット(絞り込みパネル)]
├── ドキュメント種別
│ ├── 技術文書 (28件)
│ ├── 議事録 (12件)
│ └── FAQ (5件)
├── 部門
│ ├── 開発部 (30件)
│ ├── インフラ部 (10件)
│ └── データチーム (5件)
├── 更新時期
│ ├── 直近1ヶ月 (15件)
│ ├── 直近3ヶ月 (25件)
│ └── それ以前 (5件)
└── 技術スタック
├── Python (20件)
├── LangChain (15件)
└── Qdrant (10件)
ファセット実装のポイント
| ポイント | 説明 |
|---|
| 集計対象のフィールド | カーディナリティが低い(値の種類が少ない)フィールドが適切 |
| 表示順 | 件数の多い順が一般的 |
| 動的更新 | フィルタを適用するとファセットの件数も再計算される |
| パフォーマンス | ベクトルDB側での集計がサポートされていない場合、アプリケーション側で実装 |
自動フィルタ生成
ユーザーの自然言語クエリからメタデータフィルタを自動生成する手法です。
自動フィルタ生成の流れ:
ユーザーの質問: "開発部が先月作った技術文書でRAGに関するもの"
→ LLMでフィルタ条件を抽出:
{
"department": "開発部",
"doc_type": "技術文書",
"updated_at": { "gte": "2026-02-01", "lte": "2026-02-28" },
"search_query": "RAG"
}
→ フィルタ条件をベクトルDBのクエリに変換
→ メタデータフィルタ + ベクトル検索を実行
自動フィルタ生成の設計
| 要素 | 実装方法 |
|---|
| フィルタ候補の定義 | 利用可能なフィルタフィールドと値の候補をLLMに提示 |
| LLMプロンプト | 構造化出力(JSON)でフィルタ条件を返すように指示 |
| バリデーション | LLM出力のフィルタ条件をスキーマで検証 |
| フォールバック | フィルタ生成に失敗した場合はフィルタなしで検索 |
「自動フィルタ生成は便利だが、LLMの出力が常に正確とは限らない。バリデーションとフォールバックは必須だ」 — 田中VPoE
NetShop社のフィルタリング設計
| フィルタ | タイプ | 用途 |
|---|
| doc_type | プリフィルタ | ドキュメント種別の絞り込み |
| department | プリフィルタ | 部門ごとの検索 |
| updated_at | プリフィルタ | 鮮度による絞り込み |
| tags | プリフィルタ | タグベースの絞り込み |
| tech_stack | プリフィルタ | 技術領域での絞り込み |
| has_code | ポストフィルタ | コードを含むドキュメントの優先 |
まとめ
| ポイント | 内容 |
|---|
| プリフィルタ | 検索前に絞り込み。高速で安定した結果 |
| ポストフィルタ | 検索後に絞り込み。ベクトル検索精度に影響なし |
| ファセット検索 | カテゴリ別の集計でユーザーの絞り込みをサポート |
| 自動フィルタ | LLMで自然言語からフィルタ条件を自動抽出 |
チェックリスト
次のステップへ
次は「検索パイプライン構築」を学びます。Retriever、Reranker、Generatorを統合した検索パイプラインの設計に取り組みましょう。
推定読了時間: 30分