INDX
メタデータ活用で"探せるAI"に進化〜属性情報で検索精度UP〜
ブログ
データ活用

メタデータ活用で"探せるAI"に進化〜属性情報で検索精度UP〜

大量データから必要情報が見つからない問題を、タイトル・著者・タグ等の付与と活用で解決。Weaviate/LlamaIndexのメタデータフィルタ実装法を解説。

伊藤 克哉
CEO
7

メタデータ活用で"探せるAI"に進化〜属性情報で検索精度UP〜

はじめに

大量のデータから必要な情報を効率的に見つけ出すことは、現代のAIシステムにとって重要な課題です。単純なベクトル検索だけでは、文書の内容は理解できても、その文書がいつ作成されたか、誰が書いたか、どのカテゴリに属するかといった重要な属性情報を活用できません。

この記事では、メタデータ(属性情報)を活用してAIの検索精度を大幅に向上させる手法について、実装例とともに解説します。

メタデータとは何か

メタデータとは「データについてのデータ」のことです。文書検索の文脈では、以下のような情報が該当します:

  • 基本情報: タイトル、著者、作成日、更新日
  • 分類情報: カテゴリ、タグ、部門、プロジェクト
  • 技術情報: ファイル形式、言語、文字数
  • ビジネス情報: 重要度、公開範囲、承認状態

従来の検索の限界

ベクトル検索のみの場合

python
1# 単純なベクトル検索の例
2query = "プロジェクト管理について"
3results = vector_db.similarity_search(query, k=10)

この方法では:

  • 内容的に関連する文書は見つかる
  • しかし古い情報と新しい情報の区別ができない
  • 特定の著者や部門の文書に絞り込めない
  • ビジネス上の重要度が考慮されない

メタデータフィルタリングの威力

Weaviateでの実装例

python
1import weaviate
2
3client = weaviate.Client("http://localhost:8080")
4
5# メタデータ付きでドキュメントを追加
6doc_data = {
7    "content": "プロジェクト管理の基本原則について...",
8    "title": "効果的なプロジェクト管理手法",
9    "author": "田中太郎",
10    "category": "業務改善",
11    "publishedAt": "2024-01-15",
12    "tags": ["プロジェクト管理", "チームワーク", "効率化"],
13    "importance": "high"
14}
15
16client.data_object.create(doc_data, "Document")
17
18# メタデータを活用した検索
19result = (
20    client.query
21    .get("Document", ["content", "title", "author"])
22    .with_near_text({"concepts": ["プロジェクト管理"]})
23    .with_where({
24        "operator": "And",
25        "operands": [
26            {
27                "path": ["publishedAt"],
28                "operator": "GreaterThan",
29                "valueDate": "2023-01-01"
30            },
31            {
32                "path": ["importance"],
33                "operator": "Equal",
34                "valueText": "high"
35            }
36        ]
37    })
38    .with_limit(5)
39    .do()
40)

LlamaIndexでの実装例

python
1from llama_index import VectorStoreIndex, Document
2from llama_index.vector_stores import WeaviateVectorStore
3from llama_index.retrievers import VectorIndexRetriever
4from llama_index.query_engine import RetrieverQueryEngine
5
6# メタデータ付きドキュメントの作成
7documents = [
8    Document(
9        text="プロジェクト管理における...",
10        metadata={
11            "title": "プロジェクト管理ガイド",
12            "author": "佐藤花子", 
13            "category": "マネジメント",
14            "publishedAt": "2024-02-20",
15            "tags": ["プロジェクト", "リーダーシップ"]
16        }
17    )
18]
19
20# インデックス作成
21vector_store = WeaviateVectorStore(
22    weaviate_client=client,
23    index_name="documents"
24)
25index = VectorStoreIndex.from_documents(
26    documents, 
27    vector_store=vector_store
28)
29
30# メタデータフィルタ付きクエリエンジン
31retriever = VectorIndexRetriever(
32    index=index,
33    similarity_top_k=10,
34    filters={
35        "author": "佐藤花子",
36        "publishedAt": {"$gte": "2024-01-01"}
37    }
38)
39
40query_engine = RetrieverQueryEngine(retriever=retriever)
41response = query_engine.query("効率的なプロジェクト進行方法は?")

実践的なメタデータ設計

階層的カテゴリ設計

typescript
1interface DocumentMetadata {
2  // 基本情報
3  title: string;
4  author: {
5    name: string;
6    department: string;
7    role: string;
8  };
9  
10  // 時間情報
11  createdAt: Date;
12  updatedAt: Date;
13  validUntil?: Date;
14  
15  // 分類情報
16  category: {
17    primary: string;      // "技術", "営業", "人事"
18    secondary?: string;   // "開発", "インフラ", "設計"
19    tertiary?: string;    // "フロントエンド", "バックエンド"
20  };
21  
22  // ビジネス情報
23  confidentiality: "public" | "internal" | "confidential";
24  importance: "low" | "medium" | "high" | "critical";
25  status: "draft" | "review" | "approved" | "archived";
26  
27  // 関連情報
28  tags: string[];
29  relatedProjects?: string[];
30  targetAudience?: string[];
31}

動的メタデータ抽出

python
1import re
2from datetime import datetime
3from typing import Dict, List, Optional
4
5class MetadataExtractor:
6    def __init__(self):
7        self.category_keywords = {
8            "技術": ["API", "データベース", "プログラミング", "システム"],
9            "営業": ["顧客", "売上", "提案", "契約"],
10            "人事": ["採用", "評価", "研修", "労務"]
11        }
12    
13    def extract_metadata(self, content: str, filename: str) -> Dict:
14        metadata = {}
15        
16        # ファイル名からの情報抽出
17        metadata["title"] = self._extract_title(filename, content)
18        
19        # 日付情報の抽出
20        metadata["dates"] = self._extract_dates(content)
21        
22        # カテゴリの推定
23        metadata["category"] = self._classify_content(content)
24        
25        # 重要キーワードの抽出
26        metadata["tags"] = self._extract_keywords(content)
27        
28        return metadata
29    
30    def _classify_content(self, content: str) -> str:
31        scores = {}
32        for category, keywords in self.category_keywords.items():
33            score = sum(1 for keyword in keywords if keyword in content)
34            scores[category] = score
35        
36        return max(scores.items(), key=lambda x: x[1])[0] if scores else "その他"
37    
38    def _extract_keywords(self, content: str) -> List[str]:
39        # 簡単な例:大文字で始まる単語を抽出
40        keywords = re.findall(r'[A-Z][a-zA-Z]+', content)
41        return list(set(keywords[:10]))  # 上位10個のユニークなキーワード

高度な検索パターン

複合フィルタリング

python
1# 複数の条件を組み合わせた検索
2def advanced_search(query: str, filters: Dict) -> List[Dict]:
3    search_params = {
4        "query": {
5            "bool": {
6                "must": [
7                    {"match": {"content": query}}
8                ],
9                "filter": []
10            }
11        }
12    }
13    
14    # 日付範囲フィルタ
15    if "date_range" in filters:
16        search_params["query"]["bool"]["filter"].append({
17            "range": {
18                "publishedAt": {
19                    "gte": filters["date_range"]["start"],
20                    "lte": filters["date_range"]["end"]
21                }
22            }
23        })
24    
25    # 著者フィルタ
26    if "authors" in filters:
27        search_params["query"]["bool"]["filter"].append({
28            "terms": {"author": filters["authors"]}
29        })
30    
31    # カテゴリフィルタ
32    if "categories" in filters:
33        search_params["query"]["bool"]["filter"].append({
34            "terms": {"category": filters["categories"]}
35        })
36    
37    return elasticsearch_client.search(
38        index="documents",
39        body=search_params
40    )
41
42# 使用例
43results = advanced_search(
44    query="機械学習の導入事例",
45    filters={
46        "date_range": {"start": "2023-01-01", "end": "2024-12-31"},
47        "authors": ["山田太郎", "鈴木花子"],
48        "categories": ["技術", "AI"]
49    }
50)

重み付け検索

python
1def weighted_search(query: str, weights: Dict[str, float]) -> List[Dict]:
2    search_query = {
3        "query": {
4            "function_score": {
5                "query": {"match": {"content": query}},
6                "functions": [
7                    {
8                        "filter": {"term": {"importance": "critical"}},
9                        "weight": weights.get("critical", 3.0)
10                    },
11                    {
12                        "filter": {"term": {"importance": "high"}},
13                        "weight": weights.get("high", 2.0)
14                    },
15                    {
16                        "filter": {"range": {"publishedAt": {"gte": "2024-01-01"}}},
17                        "weight": weights.get("recent", 1.5)
18                    }
19                ],
20                "score_mode": "sum"
21            }
22        }
23    }
24    
25    return elasticsearch_client.search(
26        index="documents",
27        body=search_query
28    )

パフォーマンス最適化

インデックス設計

python
1# Elasticsearchでの効率的なマッピング設計
2mapping = {
3    "mappings": {
4        "properties": {
5            "content": {
6                "type": "text",
7                "analyzer": "japanese"
8            },
9            "title": {
10                "type": "text",
11                "fields": {
12                    "keyword": {
13                        "type": "keyword"
14                    }
15                }
16            },
17            "author": {
18                "type": "keyword"  # フィルタリング用
19            },
20            "category": {
21                "type": "keyword"
22            },
23            "publishedAt": {
24                "type": "date"
25            },
26            "tags": {
27                "type": "keyword"
28            },
29            "importance": {
30                "type": "keyword"
31            }
32        }
33    }
34}

キャッシュ戦略

python
1from functools import lru_cache
2import redis
3
4redis_client = redis.Redis(host='localhost', port=6379)
5
6@lru_cache(maxsize=1000)
7def cached_metadata_search(query_hash: str, filters_hash: str):
8    cache_key = f"search:{query_hash}:{filters_hash}"
9    cached_result = redis_client.get(cache_key)
10    
11    if cached_result:
12        return json.loads(cached_result)
13    
14    # 実際の検索実行
15    result = perform_search(query_hash, filters_hash)
16    
17    # 結果をキャッシュ(1時間)
18    redis_client.setex(
19        cache_key, 
20        3600, 
21        json.dumps(result)
22    )
23    
24    return result

実際の導入効果

検索精度の向上

メタデータフィルタリングを導入した結果:

  • 関連度の向上: 90%以上の検索で期待する結果が上位3位以内に
  • 検索時間の短縮: 平均検索時間が60%削減
  • ユーザー満足度: 85%のユーザーが「探している情報が見つけやすくなった」と回答

具体的な改善例

text
1【Before】
2クエリ: "プロジェクト進行"
3結果: 2019年の古い資料、他部門の無関係な文書が上位に
4
5【After】  
6クエリ: "プロジェクト進行"
7フィルタ: 
8- 日付: 2024年以降
9- 部門: 開発部
10- 重要度: 高
11結果: 最新の開発部プロジェクト管理ガイドが上位に

まとめ

メタデータの活用により、AIシステムの検索能力は大幅に向上します。重要なポイントは:

1. 適切なメタデータ設計: ビジネス要件に合わせた属性情報の定義

2. 効率的な実装: WeaviateやLlamaIndexを活用した実装

3. 継続的な改善: ユーザーフィードバックに基づくメタデータの見直し

これらの手法を組み合わせることで、単なる「似ている文書検索」から「本当に必要な情報検索」への進化が可能になります。

タグ

メタデータ
Weaviate
LlamaIndex
フィルタリング