メタデータフィルタリング:ベクトル検索の精度を上げるためのタグ付け戦略

RAG(Retrieval-Augmented Generation)システムを構築した際、「検索しても無関係なドキュメントばかりヒットする」という問題に直面したことはありませんか?

これは、ベクトル検索(意味的検索)単体では、あくまで「類似性」しか判断できないためです。「類似しているが、条件(日時、著者、カテゴリ)が異なる」ドキュメントを除外することは苦手なのです。

そこで重要になるのが、メタデータフィルタリングです。ベクトル検索を行う前(または後)に、SQLのような条件フィルタを適用することで、検索範囲を絞り込み、精度を劇的に向上させることができます。

ベクトル検索の課題:類似性 ≠ 関連性

例えば、「2025年の売上報告書を見せて」と質問したとします。

単純なベクトル検索だと、「2024年の売上報告書」や「2023年の売上予測」といったドキュメントも、「売上」「報告書」という意味的な類似性が高いため、上位にランクインしてしまいます。これらをLLMに渡すと、誤った情報を回答してしまう(ハルシネーション)原因になります。

メタデータ戦略:何をタグ付けすべきか?

精度を高めるためには、ドキュメントのチャンク分割時に、適切なメタデータを付与しておく必要があります。

1. 構造化メタデータ

最も基本的かつ効果的なメタデータです。

  • 日時: 作成日、更新日、有効期限(created_at, updated_at, valid_until
  • 著者・担当部署: author, department
  • ドキュメント種別: doc_type (e.g., “report”, “policy”, “memo”)
  • バージョン: version (e.g., “v1.0”, “v2.0”)

これらを付与しておけば、「created_at >= '2025-01-01' かつ doc_type == 'report'」というフィルタ条件で検索範囲を限定できます。

2. コンテキスト情報の付与

チャンク分割によって失われた文脈情報をメタデータとして保持します。

  • 親ドキュメントのタイトル: parent_doc_title
  • セクション見出し: section_header (e.g., “第1章:概要”, “第2章:詳細”)
  • キーワード: keywords (LLMに抽出させた要約キーワードなど)

3. 動的メタデータ(Dynamic Metadata)

ユーザーの属性や状態に応じて変化するメタデータです。

  • 権限レベル: access_level (e.g., “public”, “internal”, “confidential”)
    • ユーザーの権限に応じて検索結果を出し分けるRBAC(Role-Based Access Control)を実現できます。
  • 言語: language (e.g., “ja”, “en”)

Pinecone / Weaviate / ChromaDB での実装例

多くのモダンなベクターストアは、メタデータフィルタリングを標準でサポートしています。

# Pineconeの例
index.query(
    vector=[0.1, 0.2, ...],
    filter={
        "category": {"$in": ["finance", "sales"]},  # カテゴリがfinanceかsales
        "year": {"$gte": 2025}                       # 年が2025以上
    },
    top_k=5
)

このフィルタリング処理は、多くのエンジンで「Pre-filtering(前処理フィルタリング)」として実装されており、ベクトル検索の計算コストを削減する副次的なメリットもあります。

結論:タグ付けこそが検索エンジニアの腕の見せ所

「ゴミを入れてもゴミしか出ない(Garbage In, Garbage Out)」はRAGの世界でも真実です。

高精度なEmbeddingsモデルを選ぶことも重要ですが、それ以上に、泥臭いメタデータ設計とタグ付け戦略が、実運用における検索精度を左右します。データパイプラインを見直し、「いつ」「誰が」「どのような文脈で」その情報を必要とするかを想像しながら、適切なタグを設計しましょう。