Home AWS公式ブログ解説: OpenSearch Serviceでスパース×デンスベクトルを統合したRAGハイブリッド検索
投稿
キャンセル

✍️ AWS公式ブログ解説: OpenSearch Serviceでスパース×デンスベクトルを統合したRAGハイブリッド検索

ブログ概要(Summary)

AWS公式ブログ「Integrate sparse and dense vectors to enhance knowledge retrieval in RAG using Amazon OpenSearch Service」は、Amazon OpenSearch Serviceでのスパースベクトル(BM25的手法)デンスベクトル(セマンティック検索)を統合したハイブリッド検索の実装パターンを解説しています。

OpenSearch 2.11以降で導入されたNeural Sparse Search機能と従来のkNN検索を組み合わせることで、RAG(Retrieval-Augmented Generation)の検索精度を大幅に向上させる手法を、AWS環境での完全な実装手順とともに提供しています。

この記事は Zenn記事: BM25×ベクトル検索のハイブリッド実装:RRFで検索精度を30%向上させる実践ガイド の深掘りです。

情報源

  • 種別: 企業テックブログ
  • URL: AWS Big Data Blog
  • 組織: Amazon Web Services (AWS)
  • 発表日: 2024年

技術的背景(Technical Background)

なぜハイブリッド検索がRAGに必要か

Amazon Bedrock Knowledge Basesなどの標準的なRAG実装は、デンスベクトル検索を中心に構築されています。しかし、実運用では以下の課題が発生します:

  1. 専門用語の取りこぼし: 医療用語、法律用語、製品型番などは埋め込みモデルの学習データに含まれていない場合がある
  2. 固有名詞の不一致: 人名・企業名・プロジェクト名でのベクトル類似度は信頼性が低い
  3. 略語・略称: 「K8s」「ML」「LLM」等の略称はベクトル空間での表現が不安定

Zenn記事で指摘されている「RuntimeError: CUDAのようなエラーメッセージ検索でヒット率が低下する」問題は、まさにこの課題の典型例です。

OpenSearch のハイブリッド検索アーキテクチャ

OpenSearch Serviceは2つのベクトル検索をネイティブサポートしています:

graph TD
    A[クエリ] --> B[OpenSearch]
    B --> C1[BM25<br>テキストフィールド]
    B --> C2[Neural Sparse<br>スパースベクトル]
    B --> C3[kNN<br>デンスベクトル]
    C1 --> D[ハイブリッドスコアリング<br>正規化+重み付け]
    C2 --> D
    C3 --> D
    D --> E[統合結果]

スパースベクトルの仕組み

OpenSearch 2.11で導入されたNeural Sparse Searchは、BM25のキーワードマッチングとニューラルネットワークの意味理解を組み合わせたスパース検索手法です。

処理フロー:

  1. WordPieceトークナイザでテキストをトークン化(約32,000語彙)
  2. Transformerモデルが各トークンに重要度スコアを付与
  3. 大部分のスコアは0または0に近く、「スパース」な表現になる
  4. クエリとドキュメントのスパースベクトル間でドット積を計算
\[\text{score}(q, d) = \sum_{t \in q \cap d} w_q(t) \cdot w_d(t)\]

ここで、

  • $w_q(t)$: クエリにおけるトークン$t$の重要度(Transformerが付与)
  • $w_d(t)$: ドキュメントにおけるトークン$t$の重要度
  • $q \cap d$: クエリとドキュメントに共通するトークン集合

BM25との違い: BM25がTF-IDFの統計的手法で語の重要度を計算するのに対し、Neural SparseはTransformerが文脈を考慮して重要度を学習します。これにより、同じスパースな表現でありながら、意味的な汎化能力を持ちます。

デンスベクトル(kNN検索)

OpenSearch ServiceのkNN機能は、HNSW(Hierarchical Navigable Small World)やIVF(Inverted File Index)アルゴリズムに基づく近似最近傍探索を提供します。

対応モデル:

  • Amazon Titan Embeddings V2 (1024次元)
  • OpenAI text-embedding-3-large (3072次元)
  • Cohere Embed (1024次元)
  • SentenceTransformers系(任意次元)

実装アーキテクチャ(Architecture)

OpenSearch インデックス設計

ハイブリッド検索用のインデックスは、テキストフィールド + スパースベクトル + デンスベクトルの3層構造で設計します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import boto3
from opensearchpy import OpenSearch, RequestsHttpConnection
from requests_aws4auth import AWS4Auth


def create_hybrid_index(
    client: OpenSearch,
    index_name: str = "hybrid-rag",
    dense_dim: int = 1024,
) -> None:
    """ハイブリッド検索用のOpenSearchインデックスを作成する。

    Args:
        client: OpenSearchクライアント
        index_name: インデックス名
        dense_dim: デンスベクトルの次元数
    """
    index_body = {
        "settings": {
            "index": {
                "knn": True,
                "knn.algo_param.ef_search": 512,
            },
            "default_pipeline": "hybrid-ingest-pipeline",
        },
        "mappings": {
            "properties": {
                # テキストフィールド(BM25用)
                "content": {
                    "type": "text",
                    "analyzer": "standard",
                },
                # スパースベクトルフィールド
                "content_sparse": {
                    "type": "rank_features",
                },
                # デンスベクトルフィールド
                "content_embedding": {
                    "type": "knn_vector",
                    "dimension": dense_dim,
                    "method": {
                        "engine": "nmslib",
                        "space_type": "cosinesimil",
                        "name": "hnsw",
                        "parameters": {
                            "ef_construction": 512,
                            "m": 16,
                        },
                    },
                },
                # メタデータ
                "metadata": {
                    "type": "object",
                    "properties": {
                        "source": {"type": "keyword"},
                        "category": {"type": "keyword"},
                        "date": {"type": "date"},
                    },
                },
            },
        },
    }
    client.indices.create(index=index_name, body=index_body)

ハイブリッド検索クエリ

OpenSearch 2.11以降では、search pipeline機能を使ってハイブリッド検索のスコア統合を宣言的に設定できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def hybrid_search(
    client: OpenSearch,
    query_text: str,
    query_embedding: list[float],
    index_name: str = "hybrid-rag",
    k: int = 10,
    bm25_weight: float = 0.3,
    sparse_weight: float = 0.3,
    dense_weight: float = 0.4,
) -> list[dict]:
    """3方向ハイブリッド検索を実行する。

    BM25 + Neural Sparse + kNNの結果を正規化して重み付き統合する。

    Args:
        client: OpenSearchクライアント
        query_text: テキストクエリ
        query_embedding: クエリのデンスベクトル
        index_name: インデックス名
        k: 返却する文書数
        bm25_weight: BM25の重み(0-1)
        sparse_weight: スパース検索の重み(0-1)
        dense_weight: デンス検索の重み(0-1)

    Returns:
        検索結果のリスト
    """
    search_body = {
        "size": k,
        "query": {
            "hybrid": {
                "queries": [
                    # BM25 (テキストマッチ)
                    {
                        "match": {
                            "content": {
                                "query": query_text,
                            }
                        }
                    },
                    # Neural Sparse (スパースベクトル)
                    {
                        "neural_sparse": {
                            "content_sparse": {
                                "query_text": query_text,
                                "model_id": "sparse-model-id",
                            }
                        }
                    },
                    # kNN (デンスベクトル)
                    {
                        "knn": {
                            "content_embedding": {
                                "vector": query_embedding,
                                "k": k * 2,
                            }
                        }
                    },
                ],
            }
        },
        "search_pipeline": {
            "phase_results_processors": [
                {
                    "normalization-processor": {
                        "normalization": {
                            "technique": "min_max",
                        },
                        "combination": {
                            "technique": "arithmetic_mean",
                            "parameters": {
                                "weights": [
                                    bm25_weight,
                                    sparse_weight,
                                    dense_weight,
                                ]
                            },
                        },
                    }
                }
            ],
        },
    }

    response = client.search(index=index_name, body=search_body)
    return [
        {
            "content": hit["_source"]["content"],
            "score": hit["_score"],
            "metadata": hit["_source"].get("metadata", {}),
        }
        for hit in response["hits"]["hits"]
    ]

Search Pipeline設定

OpenSearchのSearch Pipelineは、検索クエリの前処理と後処理を宣言的に定義できるメカニズムです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def create_search_pipeline(
    client: OpenSearch,
    pipeline_name: str = "hybrid-search-pipeline",
) -> None:
    """ハイブリッド検索用のSearch Pipelineを作成する。

    BM25・Neural Sparse・kNNの3つのサブクエリ結果を
    min-max正規化後に重み付き平均で統合する。
    """
    pipeline_body = {
        "description": "Hybrid search pipeline for RAG",
        "phase_results_processors": [
            {
                "normalization-processor": {
                    "normalization": {
                        "technique": "min_max",
                    },
                    "combination": {
                        "technique": "arithmetic_mean",
                        "parameters": {
                            "weights": [0.3, 0.3, 0.4],
                        },
                    },
                }
            }
        ],
    }
    client.transport.perform_request(
        "PUT",
        f"/_search/pipeline/{pipeline_name}",
        body=pipeline_body,
    )

パフォーマンス最適化(Performance)

レイテンシの内訳

コンポーネントレイテンシ備考
BM25検索1-5msテキストインデックスのみ
Neural Sparse検索10-20msスパースモデル推論含む
kNN検索10-30msHNSW探索
スコア統合<1ms正規化+重み付き平均
合計22-56ms並列実行で最適化可能

2024年のパフォーマンス改善

OpenSearch 2.x系列でハイブリッド検索のパフォーマンスが大幅に改善されました:

  • 条件付きスコアリングロジック: 不要な計算のスキップ
  • データ構造の最適化: 重複計算の排除
  • ハイブリッド検索の並列化: 最大25%のレイテンシ改善
  • 結果: 全体で最大4倍のレイテンシ改善

チューニングポイント

パラメータ推奨値影響
ef_search512検索精度 vs レイテンシ
m (HNSW)16インデックスサイズ vs 検索精度
ef_construction512インデックス構築時間 vs 検索精度
重み配分BM25:0.3, Sparse:0.3, Dense:0.4ドメイン依存

運用での学び(Production Lessons)

OpenSearch Serverless vs Managed

観点ServerlessManaged
コストOCU単位(最低2 OCU)インスタンス単位
スケーリング自動手動/Auto Scaling
ハイブリッド検索✅ 対応✅ 対応
Neural Sparse⚠️ 一部制限✅ 完全対応
推奨ユースケース小〜中規模大規模・カスタマイズ必要

障害パターンと対策

障害原因対策
kNNタイムアウトef_searchが高すぎるef_search=256に下げる
スパースモデル遅延モデルサイズが大きい軽量モデルに切替
メモリ不足インデックスサイズ超過ノード追加/UltraWarm活用
スコア不均衡重み設定が不適切評価データでチューニング

モニタリング戦略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import boto3

cloudwatch = boto3.client("cloudwatch")


def setup_opensearch_alarms(domain_name: str) -> None:
    """OpenSearch Serviceの監視アラームを設定する。

    Args:
        domain_name: OpenSearchドメイン名
    """
    alarms = [
        {
            "AlarmName": f"{domain_name}-search-latency",
            "MetricName": "SearchLatency",
            "Namespace": "AWS/ES",
            "Statistic": "p99",
            "Period": 300,
            "Threshold": 100,  # 100ms P99
            "ComparisonOperator": "GreaterThanThreshold",
            "Dimensions": [
                {"Name": "DomainName", "Value": domain_name},
                {"Name": "ClientId", "Value": boto3.client("sts").get_caller_identity()["Account"]},
            ],
        },
        {
            "AlarmName": f"{domain_name}-jvm-pressure",
            "MetricName": "JVMMemoryPressure",
            "Namespace": "AWS/ES",
            "Statistic": "Maximum",
            "Period": 300,
            "Threshold": 85,  # 85%
            "ComparisonOperator": "GreaterThanThreshold",
        },
    ]

    for alarm in alarms:
        cloudwatch.put_metric_alarm(**alarm, EvaluationPeriods=2)

学術研究との関連(Academic Connection)

Bruch et al. (2022) との関連

OpenSearchのハイブリッド検索は、融合関数としてmin-max正規化 + 重み付き平均(線形結合)を採用しています。Bruch et al. (2022)の研究では、RRFが線形結合を上回ることが示されていますが、OpenSearchは2024年にRRF融合も追加サポートしています。

Blended RAG (2024) との関連

OpenSearchの3方向ハイブリッド検索(BM25 + Sparse + Dense)は、Blended RAGの提案する構成と整合しています。メタデータフィルタリングもOpenSearchのfilter句で実現可能です。

SPLADEとの関係

OpenSearchのNeural Sparse Searchは、SPLADE等の学習済みスパース検索モデルと類似のアプローチです。WordPieceベースの32,000語彙空間でスパース表現を生成する点は共通ですが、OpenSearchはAWSの管理サービスとして提供されるため、モデルのデプロイ・運用が簡素化されています。

Production Deployment Guide

AWS実装パターン(コスト最適化重視)

トラフィック量別の推奨構成:

規模月間リクエスト推奨構成月額コスト主要サービス
Small~3,000 (100/日)Serverless$150-250OpenSearch Serverless + Lambda + Bedrock
Medium~30,000 (1,000/日)Managed$500-1,200OpenSearch t3.medium × 2 + Lambda
Large300,000+ (10,000/日)Dedicated$2,000-5,000OpenSearch r6g.large × 3 + ECS

Small構成の詳細 (月額$150-250):

  • OpenSearch Serverless: 2 OCU ($140/月)
  • Lambda: クエリ処理 ($10/月)
  • Bedrock (Titan Embeddings): ベクトル化 ($20/月)
  • S3: ドキュメント保管 ($5/月)

Medium構成の詳細 (月額$500-1,200):

  • OpenSearch Managed: t3.medium.search × 2 ($200/月)
  • Lambda/ECS: クエリ処理 ($50/月)
  • Bedrock: 埋め込み+生成 ($300/月)
  • ElastiCache: 検索結果キャッシュ ($50/月)

コスト試算の注意事項:

  • 上記は2026年2月時点のAWS ap-northeast-1リージョン料金に基づく概算値です
  • OpenSearch Serverless OCU料金はアイドル時も発生します
  • 最新料金は AWS料金計算ツール で確認してください

Terraformインフラコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
resource "aws_opensearch_domain" "hybrid_rag" {
  domain_name    = "hybrid-rag"
  engine_version = "OpenSearch_2.13"

  cluster_config {
    instance_type          = "t3.medium.search"
    instance_count         = 2
    zone_awareness_enabled = true

    zone_awareness_config {
      availability_zone_count = 2
    }
  }

  ebs_options {
    ebs_enabled = true
    volume_size = 100
    volume_type = "gp3"
  }

  encrypt_at_rest {
    enabled = true
  }

  node_to_node_encryption {
    enabled = true
  }

  domain_endpoint_options {
    enforce_https       = true
    tls_security_policy = "Policy-Min-TLS-1-2-PFS-2023-10"
  }
}

resource "aws_cloudwatch_metric_alarm" "search_latency" {
  alarm_name          = "opensearch-search-latency-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "SearchLatency"
  namespace           = "AWS/ES"
  period              = 300
  statistic           = "p99"
  threshold           = 100
  alarm_description   = "OpenSearch検索P99レイテンシ100ms超"

  dimensions = {
    DomainName = aws_opensearch_domain.hybrid_rag.domain_name
    ClientId   = data.aws_caller_identity.current.account_id
  }
}

セキュリティベストプラクティス

  • 暗号化: 保管時+転送時+ノード間通信すべてKMS暗号化
  • TLSポリシー: TLS 1.2以上必須(Policy-Min-TLS-1-2-PFS-2023-10)
  • アクセス制御: IAMベースのドメインアクセスポリシー
  • VPC配置: パブリックアクセス無効化、VPCエンドポイント経由
  • 監査: CloudTrailでAPI呼び出しをログ記録

コスト最適化チェックリスト

アーキテクチャ選択:

  • ~100 req/日 → OpenSearch Serverless (2 OCU) - $150-250/月
  • ~1000 req/日 → OpenSearch Managed (t3.medium × 2) - $500-1,200/月
  • 10000+ req/日 → OpenSearch Dedicated (r6g.large × 3) - $2,000-5,000/月

OpenSearch最適化:

  • UltraWarm: アクセス頻度の低いインデックスを自動階層化
  • gp3 EBSボリューム: gp2比で20%コスト削減
  • Reserved Instances: 1年コミットで最大35%削減
  • スパースモデル: 軽量版を選択してスループット向上

検索パイプライン最適化:

  • BM25+Sparse+Dense並列実行
  • 検索結果キャッシュ(ElastiCache、TTL 5分)
  • search pipeline正規化設定の最適化
  • ef_search値の調整(精度vs速度トレードオフ)

監視・アラート:

  • SearchLatency P99 監視
  • JVMMemoryPressure 監視(85%閾値)
  • ClusterStatus 監視
  • 月額コスト予算アラート(AWS Budgets)

まとめと実践への示唆

AWS OpenSearch Serviceでのハイブリッド検索は、Zenn記事で紹介されているElasticsearchのretriever構文と概念的に同一であり、AWSマネージドサービスとして運用負荷を大幅に削減できます。

主要なポイント:

  • OpenSearch 2.11以降のNeural Sparse Searchは、BM25を超えるスパース検索を提供
  • BM25 + Sparse + Denseの3方向ハイブリッドが最高精度
  • Search Pipeline機能で融合ロジックを宣言的に管理
  • 2024年のパフォーマンス改善で最大4倍のレイテンシ改善

実務への示唆: AWSを使用している場合、OpenSearch Serviceのハイブリッド検索機能はZenn記事のElasticsearch実装と互換性があり、マネージドサービスの恩恵(自動スケーリング、暗号化、監視)を追加で享受できます。

参考文献

この投稿は CC BY 4.0 でライセンスされています。

AWS解説: LangGraphとAmazon Bedrockで構築するマルチエージェントシステム

SIGIR 2009論文解説: Reciprocal Rank Fusion — ハイブリッド検索の基盤となったランク統合手法の原論文