Azure AI Searchの共有プライベートリンクがBasicプランでも使えるようになったので試してみた

この記事は [**セゾンテクノロジー Advent Calendar 2024**](https://qiita.com/advent-calendar/2024/saison-tech) 18日目の記事です

## はじめに
Azure AI Searchは、強力な検索機能を提供するサービスですが、共有プライベートリンクはStandard S2プラン以上でのみ利用可能でした。しかし、最近のアップデートにより、Basicプランでもこの機能が利用できるようになりました。本記事では、実際にこの機能を試してみた結果を共有します。

https://learn.microsoft.com/ja-jp/azure/search/search-indexer-howto-access-private

## 目次
1. [Azure AI Searchとは](#azure-ai-searchとは)
2. [共有プライベートリンクとは](#共有プライベートリンクとは)
3. [主なシナリオ](#主なシナリオ)
4. [前提条件](#前提条件)
4. [設定手順](#設定手順)
5. [まとめ](#まとめ)

## Azure AI Searchとは
Azure AI Searchは、Microsoft Azureが提供するフルマネージドの検索サービスです。テキスト検索、ファセット検索、地理空間検索など、多様な検索機能を提供し、開発者がアプリケーションに簡単に統合できるようになっています。

## 共有プライベートリンクとは
既存のプライベートリンクとは違い、リソースは Microsoftで管理されるため、ユーザープライベートエンドポイントやプライベートDNSゾーン等を管理する必要がありません。
また、リソース所有者による明示的な承認が必要です。

## 主なシナリオ
Azure AI Searchの「データのインポートとベクター化」機能を使ってデータをインデックスする際に、Azure OpenAI Service(以下、AOAI)の埋め込みモデルを使ってベクトル化を実施します。
その際、***AOAI側でネットワークの制限をしている場合***、インデクサー実行時に接続エラーとなってしまいます。
このように"送信" (インデクサー) 接続でプライベート接続を行う場合に、共有プライベートリンクが必要になります。

## 前提条件
| ワークロード                                   | 階層の要件       | リージョンの要件   | サービス作成の要件                |
|----------------------------------------------|------------------|-------------------|----------------------------------|
| スキルセットのないインデクサー               | Basic 以上        | なし                | なし                              |
| 埋め込みスキルを含むスキルセット (垂直統合) | Basic 以上        | 大容量リージョン    | 2024 年 4 月 3 日より後          |
| 他の組み込みまたはカスタム スキルを使うスキルセット | Standard 2 (S2) 以上 | なし                | 2024 年 4 月 3 日より後          |

「データのインポートとベクター化」機能を使用する場合は「埋め込みスキルを含むスキルセット (垂直統合)」のワークロードに該当します。
AI Searchのリソース作成が[「2024年4月3日より後」](https://qiita.com/nohanaga/items/d0922a812a41af2c7f64)である点に注意が必要です。

また、共有プライベートリンクは、使用量に基づいて課金されるプレミアム機能です。   
共有プライベートリンクを設定すると、プライベート エンドポイントの料金が Azure 請求書に追加される点もご留意ください。

https://azure.microsoft.com/pricing/details/private-link/

## 設定手順

### データ準備
まずはデータソースとなるStorage Accoutを作成し、blobコンテナーに適当なドキュメントをアップロードしておきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/b8efb077-6754-2afc-90fa-6ee19b9291b3.png)

### データのインポートとベクター化
① AI SearchのマネージドIDを有効化し、Storage Accoutの「ストレージ BLOB データ閲覧者」ロールを割り当てておきます

:::note warn
※余談ですが、Storage Accoutをネットワーク制限していた場合には例外として「信頼されたサービスの一覧にある Azure サービスがこのストレージ アカウントにアクセスすることを許可します」にチェックが入っていればマネージドIDの認証で接続が可能です。
:::

② AI Search「データのインポートとベクター化」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/5e91b502-dc45-8ec8-3fe4-920afd7c51f5.png)

③ 埋め込みに使用するモデルを選択
事前にAOAIにデプロイ済みのモデルから選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/eebc2a6a-c61e-bf0a-d856-eb6cf2acd6b7.png)

④ 後はデフォルトで作成
これにより、データソース、スキルセット、インデクサー、インデックスが一気に作成されます。
しかし、前述の通りAOAIでネットワーク制限をしている場合にはインデクサーの実行でエラーとなります。
```
Web Api response status: 'Forbidden', Web Api response details: '{"error":{"code":"403","message": "Access denied due to Virtual Network/Firewall rules."}}'
```

### 共有プライベートリンクの作成
① AI Searchのネットワーク⇒共有プライベートアクセス⇒共有プライベートリンクを追加する

③ 接続先のAOAIリソースを指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/3cfcb121-3543-1600-13a2-47994c68bbeb.png)
作成するとPendingのステータスになる

③ AOAI側のネットワーク⇒プライベートエンドポイント接続⇒承認

### インデクサーの再実行
①インデクサーのJSONで以下のパラメーターを設定する
```json
"parameters": {
        "configuration": {
            "executionEnvironment": "private"
        }
    }
```

<details><summary>インデクサーJSON全体</summary>

```json
{
  "@odata.context": "https://xxxxxx.search.windows.net/$metadata#indexers/$entity",
  "@odata.etag": "\"xxxxxxxxxx\"",
  "name": "xxxxxxx-indexer",
  "description": null,
  "dataSourceName": "xxxxxxxx-datasource",
  "skillsetName": "xxxxxxxxx-skillset",
  "targetIndexName": "xxxxxxxxx",
  "disabled": null,
  "schedule": null,
  "parameters": {
    "batchSize": null,
    "maxFailedItems": null,
    "maxFailedItemsPerBatch": null,
    "base64EncodeKeys": null,
    "configuration": {
      "executionEnvironment": "private",
      "dataToExtract": "contentAndMetadata",
      "parsingMode": "default"
    }
  },
  "fieldMappings": [
    {
      "sourceFieldName": "metadata_storage_name",
      "targetFieldName": "title",
      "mappingFunction": null
    }
  ],
  "outputFieldMappings": [],
  "cache": null,
  "encryptionKey": null
}
```
</details>  


② インデクサーを実行　成功すればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/57675b52-3736-6273-1923-fc46973ccc97.png)

### 実行結果確認
作成されたインデックスに対して検索を行います。
今回はAzure OpenAI Studio改めAI Foundryで簡単に試してみます。

チャットプレイグランドから「データを追加する」
データソースはAI Searchを選択して、リソースとインデックスを指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/e6c7c5cf-1f7c-80c5-bfb9-e5cbe5e9c8d3.png)

検索の種類は「ハイブリッド＋セマンティック」で既存のセマンティック構成を指定
AI Searchによって作成されたセマンティック構成はインデックスから確認、更新できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/cc07b423-f43e-c18c-4606-93196c3ba28b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/2d880967-f7ee-603a-b73e-f58d7edf09e9.png)

取り込んだデータに対して関連のあるプロンプトを投げて、結果と参照されたチャンクの情報が返ってくれば成功です。
お疲れ様でした！

## まとめ
共有プライベートリンクがBasicプランでも利用可能になったことで、AI Searchはランニングコストが結構かかるのでプランは抑えたいが、最低限のセキュリティは担保したい、、というニーズに対応されたことは嬉しいですね。
ただ、共有プライベートリンクという概念がとっつきにくく、自身の整理のためにも手順をまとめてみました。
どなたかの参考になれば幸いです。

## 参考リンク
https://learn.microsoft.com/ja-jp/azure/search/search-indexer-howto-access-private

https://azure.github.io/jpazpaas/2024/01/25/search-private-access-to-openai.html