Serverless Operations, inc

>_cd /blog/id_52384i41x5fx

title

生成AI×検索の最適解:OpenSearchのベクトル検索を実装してみる

OpenSearch はもともと全文検索エンジンとして発展してきましたが、生成AIの発展に呼応して需要が高まっている、テキストや画像を埋め込みモデルで数値ベクトルに変換し、意味的な近さをもとに検索できる ベクトル検索機能 を備えています。さらに、セマンティック検索 や ハイブリッド検索(キーワード+ベクトル) も実現でき、RAG(検索拡張生成)やレコメンドなど生成AI時代のユースケースに最適なプラットフォームといえます。

このシリーズブログでは検索方式を1個ずつ触っていきたいと思います。

ベクトル検索 と生成AI

生成AI(大規模言語モデル)は自然言語で答えを作り出す能力に優れていますが、最新の情報や企業内のナレッジをそのまま覚えているわけではありません。そこで登場するのが ベクトル検索 です。

  • ベクトル検索とは
    テキストや画像を埋め込みモデルで数値ベクトルに変換し、その意味的な距離をもとに関連情報を検索する仕組みです。「意味が近い文書」を探せるので、単なるキーワード検索より精度が高い結果を返せます。
  • 生成AIとの組み合わせ(RAG: Retrieval-Augmented Generation)
    1. ユーザーが自然文で質問
    2. 同じモデルで質問をベクトル化
    3. ベクトル検索で関連文書を取得
    4. その文書をプロンプトに加えて LLM に回答を生成させる

この仕組みによって、生成AIが社内ドキュメントや最新データを参照しながら答えを出せる ようになります。

さっそくやってみる

ではベクトル検索をやっていきます。このブログではよりコスト効率の高いAmazon OpenSearch Serverlessを使っていきます。

1. OpenSearch Serverless コレクションの作成

Amazon OpenSearch のマネージメントコンソール左ペインから、 Serverless → Collections を選択し、 コレクションを作成 をクリックします。

適当な名前を付け コレクションタイプ で ベクトル検索 を選択します。 デプロイタイプ は冗長化オプションを外します。

セキュリティ タイプでは 標準 を選択します。暗号化オプションはデフォルトのままで問題ありません。

ネットワークアクセス設定 は パブリック を選択し、 リソースタイプ は2つともチェックを付けておきます。 次へ をクリックします。

次の画面ではコレクションとインデックスへのアクセス管理を設定します。

コレクションとインデックス

OpenSearch Serverless における コレクション は、複数のインデックスをまとめる論理的なコンテナであり、アクセス制御やセキュリティ設定、利用形態(全文検索用・タイムシリーズ用・ベクトル検索用など)はコレクション単位で管理されます。
一方、インデックス はコレクションの中に作られるデータ保存・検索の単位で、フィールド定義(マッピング)を持ち、実際にドキュメントを格納して検索する対象となります。
イメージとしては「コレクションがデータベース、インデックスがテーブル」に相当し、1つのコレクションの中に複数のインデックスを作成できますが、コレクションをまたいだ検索はできません。

まず IAMユーザ のARNを取得しておき、コピーします。

アクセス許可 はすべて有効にして 次へ をクリックします。

次の画面では適当な名前を付けて 次へ をクリックします。

次のインデックス作成画面は Skip to review and create をクリックします。インデックスが無ければデータはコレクションに格納することはできませんが、後程開発者向けコンソールからベクトル検索用インデックスを作成します。

最後の画面で 送信 をクリックして、画面が変わるまで数分間待ちます。

以下の画面に変わればコレクションの作成は成功です。

2. データアクセスの設定

(記事執筆2025年9月時点では、コレクション作成時に行ったデータアクセス設定が正しく反映されず、以下の手順で再度設定しなおす必要があるようです。)

データアクセスの管理 をクリックします。

アクセスポリシーを作成 をクリックし、適当な名前を付けます。 インデックスのアクセス許可 では 特定のインデックスまたはインデックスパターン で vector*と入力します。後程作成するインデックスの名前を vectorindex にします。

現れるダイアログで以下の様に設定を行い 保存 をクリックします。

最後に 作成 をクリックするとデータアクセスポリシーが作成されます。

3. 開発者コンソールへのアクセスとベクトルインデックスの作成

コレクションの詳細画面で表示されるダッシュボードURLにアクセスを行います。

左ペインのハンバーガーメニューから Dev Tools をクリックします。

コンソールでインデックス作成クエリを実行します。

PUT housing-index
{
   "settings": {
      "index.knn": true
   },
   "mappings": {
      "properties": {
         "housing-vector": {
            "type": "knn_vector",
            "dimension": 3
         },
         "title": {
            "type": "text"
         },
         "price": {
            "type": "long"
         },
         "location": {
            "type": "geo_point"
         }
      }
   }
}

以下が戻れば作成が成功しています。

{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "vectorindex"
}

4. テスト用ベクトルデータの登録と検索テスト

以下のコマンドを実行すると10件のベクトルデータが挿入されます。ベクトルデータは元データの文字列とベクトル化された値のペアで保存されることが一般的です。これは埋め込みベクトルは数値化された特徴量であり、可逆変換ではないため原文は復元できません。検索の結果を表示させるためには、元データをペアで保存させておくことが必要なためです。

POST housing-index/_bulk
{ "index": {} }
{ "housing-vector": [10,20,30], "title": "2 bedroom in downtown Seattle", "price": 2800, "location": { "lat": 47.61, "lon": -122.33 } }
{ "index": {} }
{ "housing-vector": [15,25,35], "title": "1 bedroom near university", "price": 1800, "location": { "lat": 47.66, "lon": -122.30 } }
{ "index": {} }
{ "housing-vector": [20,30,40], "title": "3 bedroom house with garden", "price": 3500, "location": { "lat": 47.55, "lon": -122.28 } }
{ "index": {} }
{ "housing-vector": [12,22,32], "title": "Studio apartment", "price": 1200, "location": { "lat": 47.70, "lon": -122.25 } }
{ "index": {} }
{ "housing-vector": [18,28,38], "title": "Condo with lake view", "price": 4000, "location": { "lat": 47.68, "lon": -122.20 } }
{ "index": {} }
{ "housing-vector": [14,24,34], "title": "Downtown loft", "price": 3000, "location": { "lat": 47.61, "lon": -122.35 } }
{ "index": {} }
{ "housing-vector": [16,26,36], "title": "Townhouse in quiet area", "price": 2700, "location": { "lat": 47.72, "lon": -122.15 } }
{ "index": {} }
{ "housing-vector": [11,21,31], "title": "Shared house room", "price": 800, "location": { "lat": 47.74, "lon": -122.18 } }
{ "index": {} }
{ "housing-vector": [19,29,39], "title": "Luxury villa", "price": 10000, "location": { "lat": 47.50, "lon": -122.40 } }
{ "index": {} }
{ "housing-vector": [13,23,33], "title": "Small cabin near woods", "price": 1500, "location": { "lat": 47.80, "lon": -122.50 } } 

この手順ではテスト用にシンプルな3次元ベクトルを用いていますが、実際には数百次元のベクトルとなります。OpenSearch自体はベクトル化の機能は提供されていないため、基本的に外部モデル(Bedrock, Hugging Face など)が担います。事前にBedrockなどを用いて埋め込みモデルでベクトル化を行っておく必要があります。

登録が完了すれば以下の様な値が戻ります。

{
  "took": 169,
  "errors": false,
  "items": [
    {
      "index": {
        "_index": "vectorindex",
        "_id": "1%3A0%3AwjbCQpkBDXSNq6nExneN",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 0,
          "successful": 0,
          "failed": 0
        },
        "_seq_no": 0,
        "_primary_term": 0,
        "status": 201
      }
    },
<snip>

では最後にベクトルを用いた検索を行います。以下のクエリを実行します。

GET vectorindex/_search
{
    "size": 2,
    "query": {
        "knn": {
            "housing-vector": {
                "vector": [
                    10,
                    20,
                    30
                ],
                "k": 2
            }
        }
    }
}

先ほど登録した10件のベクトルのうち、近しいベクトル2件が表示されます。

{
  "took": 27,
  "timed_out": false,
  "_shards": {
    "total": 0,
    "successful": 0,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "vectorindex",
        "_id": "1%3A0%3AwjbCQpkBDXSNq6nExneN",
        "_score": 1,
        "_source": {
          "housing-vector": [
            10,
            20,
            30
          ],
          "title": "2 bedroom in downtown Seattle",
          "price": 2800,
          "location": {
            "lat": 47.61,
            "lon": -122.33
          }
        }
      },
      {
        "_index": "vectorindex",
        "_id": "1%3A0%3AyTbCQpkBDXSNq6nExneP",
        "_score": 0.25,
        "_source": {
          "housing-vector": [
            11,
            21,
            31
          ],
          "title": "Shared house room",
          "price": 800,
          "location": {
            "lat": 47.74,
            "lon": -122.18
          }
        }
      }
    ]
  }
}

ベクトルの値と元の言葉の意味は相関性があるため、近いベクトルの値を持つアイテムは、近い意味を持つ言葉、ということになります。これにより OpenSearch を使った意味検索が実現できます。

Written by
編集部

亀田 治伸

Kameda Harunobu

  • Facebook->
  • X->
  • GitHub->

Share

Facebook->X->
Back
to list
<-