🔍

AWS OpenSearchを活用したAIシステムで罠にかかった話

に公開

AIフォーオールカンパニーでプロジェクトサポートを行っているへいたです。
僕は毎日PRのレビューをするマシーンとして生きています。
ある日、OpenSearchを使用したAIサービスで「結果が思うようにならない」という質問を受けたので調査してみると、知らずに使っていると気づかない罠があることを知りました。

OpenSearchを使う前に知っておきたい罠

AIのRAGシステムでベクトル検索などを活用するとき、AWSのOpenSearch Serviceを導入するケースがあります。しかし、いくつか罠があり、開発の途中で無駄に時間を過ごしてしまう可能性があります。

罠1:サービスを作った本人しかコンソールに表示されない

「OpenSearchドメインを作成したのに、他の開発メンバーのAWSコンソールに表示されない」
これはEC2や他のサービスを使用していると見えていることが当たり前で、存在しないものだと勘違いしてしまいます。見られない理由がわからずハゲるかと思いました。

なぜ他のユーザーは見えないのか?

OpenSearchには、IAMポリシーとは別にドメイン単位でのアクセスポリシー(リソースベースポリシー)が存在します。このポリシーで許可されていないIAMユーザーやロールは、たとえIAM側で許可されていてもドメインにアクセスできません。
見えていないことに疑問に感じて、IAMでOpenSearchに関するポリシーをいじくり回しましたが、本質的には、ドメイン単位のアクセス設定が必要でした。(コンソール上に説明文とかほしいですよね。)

対策:IAMとドメインポリシーの両方でアクセスを許可する

この問題を解決するために、以下の設定を行いました。

OpenSearch Serviceへのアクセスを許可したいIAMユーザーやロールに、AWS管理ポリシーのAmazonOpenSearchServiceReadOnlyAccessや、より制限されたカスタムポリシーをアタッチ。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "es:DescribeDomain",
                "es:ListDomainNames"
            ],
            "Resource": "*"
        }
    ]
}

次に、OpenSearchドメインのアクセスポリシーで、アクセスを許可したいIAMユーザーやロールのARNを指定。作成したユーザーが含まれていたため、見られるのは作った人だけ・・・?と気づくことができましたが、すべてのポリシーを見てそれを調べるというのは、骨が折れました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::ACCOUNT_ID:user/user-A",
          "arn:aws:iam::ACCOUNT_ID:role/role-B"
        ]
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:REGION:ACCOUNT_ID:domain/YOUR_DOMAIN_NAME/*"
    }
  ]
}

罠2:検索結果が10,000件しか返ってこない

「大量のドキュメントから関連情報を検索したはずが、なぜかヒット数が10,000件で頭打ちになり、期待した結果が得られない」という問題に直面することがあります。特に、検索結果の重複を排除する前の件数でこの上限に達すると、本来取得できるはずのデータが欠落してしまいます。

これはOpenSearch(Elasticsearch)の仕様によるもので、デフォルトでは、検索のパフォーマンスを維持するため、正確にカウントするヒット数の上限 (track_total_hits) が10,000件に設定されています。

これを超えると、OpenSearchは正確な件数を返すのをやめ、「10,000件以上のヒットがあった」という概算値を返します。AIシステムがこの値を元に処理を行うと、全件を処理したつもりが、実際には一部のデータしか扱えていなかった、という事態に陥ります。

チャンク化による件数の肥大化

今回のケースでは「ヒットしたファイルの件数を知りたい」というシステムだったため、各チャンクが個別のヒットとしてカウントされてしまい、1つのファイルが100個のチャンクに分割され、そのうち10個のチャンクが検索クエリにヒットした場合、ヒット数は「10件」と表示されていました。

多くのファイルがこのようにヒットすると、チャンク単位のヒット数はあっという間に10,000件の上限に達してしまいます。その結果、実際にはまだ検索結果があるにもかかわらず、上限に達したことで検索が打ち切られ、本来取得できるはずのファイルの情報が欠落していました

ファイル単位で結果を折りたたむ(Collapse)
「ヒットしたファイルの種類ごとに、最も関連性の高いチャンクを1つずつ取得したい」や「ファイルの総数と名称を取得したい」という要件であれば、collapseパラメータを使用するのが便利です。これにより、検索結果の重複をなくし、ファイル単位での結果一覧を簡単に得られます。

GET /my-index/_search
{
  "query": {
    "match_all": {}
  },
  "collapse": {
    "field": "x-amz-bedrock-kb-source-uri.keyword"
  }
}

まとめ

AWS OpenSearchは、AIシステムにおけるセマンティック検索やベクトル検索の強力な基盤となります。しかし、その機能を最大限に活かすためには、今回ご紹介したようなAWSならではの権限管理の仕組みや、OpenSearch自体の仕様を正しく理解しておくことが不可欠です。(色々なサービスが出すぎて覚えてられない)

そして、OpenSearchはめちゃくちゃ費用がかさみます。ありえないほどに。サービスとしては非常に便利ではありますが、個人的にはポスグレなどを使用したベクトルデータベースを構築したほうがおサイフには優しいのかなと考えています。(便利は高い)

株式会社メンバーズ AIフォーオールカンパニー

Discussion