azure-search-openai-demo を低コストで試す

Azure で RAG を構築する最初のステップとして、[azure-search-openai-demo](https://github.com/Azure-Samples/azure-search-openai-demo) を試したい！ でも支払いは最小限にしたい！ という場合のガイドです。

基本的に [deploy_lowcost.md](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/deploy_lowcost.md) に従って構築を進めます。ほとんどアクセスがなければ数百円程度で試せると思います。


## 開発環境の選択肢

開発環境として、GitHub Codespaces、VS Code Dev Containers、ローカル環境の3つがあります。

### GitHub Codespaces

お手軽ですが、無料で使える範囲に制限があります。コードを書き換えてしばらくしたら自動的に削除されてしまう、というようなことが起こりえます。

### VS Code Dev Containers

個人で試す場合にはお手軽ですが、会社だと Windows や Mac で Docker Desktop を使わずに Docker 互換環境を用意しなければなりません。Nested VM が禁止されている Windows VDI 環境でも利用できません。Blob Storage へのアップロードが禁止されている場合にも利用できません。

### ローカル環境

環境が汚れること、Blob Storage へのアップロードが禁止されている場合に利用できないことが欠点です。

私の環境だけかもしれませんが、Ubuntu で Python 3.12 だと文書のアップロード時に azd からトークンを取得できずにタイムアウトしてしまう事象があり、この点でもローカル環境は選択しにくいです。

Windows の場合には、環境は汚れますが、Python 3.12 でも問題は発生しませんでした。

### 個人的なおすすめ

個人で試す場合には Dev Containers がおすすめです。

Blob Storage への文書のアップロードが禁止されている会社では Azure 上の Windows VM にローカル環境をつくるのがおすすめです。ただしこの場合は、低コストでは済まなくなります。Azure Bastion も使うのであれば、Bastion Developer が使えるリージョンに作ると安上がりです。


## ローカル環境を用意する

### 必要なものをインストール

[README.md#local-environment](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/README.md#local-environment) に記載のものをインストールします。

私は Windows Server 2022 に、デフォルトで作成される built-in Administrator (全プロセスが管理者権限) とは別に普通の管理者ユーザーを作成し、そこに環境を構築しました。winget が使えなかったり、管理者として実行しないとインストールできないインストーラーが複数あったりと、Windows Server 特有のトラブルはありましたが、無事に必要なものを揃えることができました。

### リポジトリーのクローン

リポジトリーをクローンして、最新のリリース タグ (以下の例では 2025-01-29b) から作業用ブランチを切ってチェックアウトします。

```console
$ git clone https://github.com/Azure-Samples/azure-search-openai-demo.git
$ git switch -c work 2025-01-29b
```

[README.md](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/README.md) では `azd init -t azure-search-openai-demo` でリポジトリーが初期化されるからクローンの必要はないとされていますが、コミットが１件もなく、upstream も設定されないので、履歴の確認や更新が大変です。


## 設定とデプロイ

### デプロイ用の環境設定

公式手順の通り以下を実施します。

```console
$ azd auth login
$ azd env new
```

`azd env new` で設定した名前が、Azure のリソース グループの名前に使われます (rg-<設定した名前>)。

### デプロイ設定のカスタマイズ

いろいろ試した結果、私は以下のように設定しています。

```console
$ sed -i -e 's/host: containerapp/# host: containerapp/' -e 's/# host: appservice/host: appservice/' azure.yaml
$ azd env set DEPLOYMENT_TARGET appservice
$ azd env set AZURE_APP_SERVICE_SKU F1
$ azd env set AZURE_SEARCH_SERVICE_SKU free
$ azd env set AZURE_DOCUMENTINTELLIGENCE_SKU F0
$ azd env set USE_LOCAL_PDF_PARSER true
$ azd env set USE_LOCAL_HTML_PARSER true
$ azd env set AZURE_COSMOSDB_SKU free
$ azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT chat4
$ azd env set AZURE_OPENAI_CHATGPT_MODEL gpt-4o-mini
$ azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_SKU GlobalStandard
$ azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_CAPACITY 100
$ azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_VERSION 2024-07-18
$ azd env set AZURE_OPENAI_EMB_MODEL_NAME text-embedding-3-small
$ azd env set AZURE_OPENAI_EMB_DIMENSIONS 256
$ azd env set AZURE_OPENAI_EMB_DEPLOYMENT_VERSION 1
$ azd env set USE_CHAT_HISTORY_BROWSER true
$ azd env set USE_SPEECH_INPUT_BROWSER true
$ azd env set USE_SPEECH_OUTPUT_BROWSER true
$ azd env set AZURE_SEARCH_QUERY_LANGUAGE ja
$ azd env set AZURE_SEARCH_ANALYZER_NAME ja.microsoft
```

公式手順では optional となっていますが、ここで [deploy_lowcost.md](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/deploy_lowcost.md) に従って費用を節約しています。ただし、ドキュメントにある通り、機能は制限されます。

この時点で、使用するモデル (チャット用、埋め込み用、機能を有効化するならビジョン用も) と OpenAI 用のリージョンを検討することをお勧めします。OpenAI の API はリージョンによって使えるものが大きく異なりますが、このスクリプトでは１か所しか指定できません。上記では共に eastus2 で使える gpt-4o-mini と text-embedding-3-small というモデルを使うことにしましたが、両方を使えるリージョンは少なく、[Global standard model availability](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=global-standard%2Cstandard-chat-completions#global-standard-model-availability) と [Standard models by endpoint](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=global-standard%2Cstandard-embeddings#standard-models-by-endpoint) を見比べて選択する必要があります。o1 や o3-mini が使えるリージョンもありますが、[Availability](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=global-standard%2Cstandard-chat-completions#availability) にある通り、登録が必要なので通常は使えません。

チャット用のモデルはデフォルトで GPT-3.5 ですが、日本語での問い合わせに英語で返答する確率が高いのでお勧めしません。[deploy_features.md#using-gpt-4](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/deploy_features.md#using-gpt-4) に従って変更すると良いでしょう。

また、日本語の文書を投入する場合は Azure AI Search で使われるクエリー言語とアナライザーを [deploy_existing.md#azure-ai-search-resource](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/deploy_existing.md#azure-ai-search-resource) に従って変更すると良いでしょう。

`AZURE_OPENAI_CHATGPT_DEPLOYMENT_CAPACITY` は、日本語や画像を使うとトークン数が超過してしまうことから、多めに１分あたり 100k トークンとしています[^capacity_unit]。

[^capacity_unit]: 単位は [Manage Azure OpenAI Service quota の Deployment](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/quota?tabs=rest#deployment) の Request body のところに記載がありました。

テキストのエンベディングは、容量削減のため 256 次元にしています。たくさん文書を追加するのでなければ、次元数を抑える必要はありません。

[login_and_acl.md](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/login_and_acl.md) に記載の認証の設定は、社内文書で試すなどの場合には必須ですが、今回は扱いません。

### デプロイ

`azd up` を実行すると以下を聞かれます。

- 使用するサブスクリプション
- Azure の主なリソースのリージョン
- OpenAI のリソースのリージョン
- Document Intelligence resource group のリージョン

Document Intelligence は eastus、その他は eastus2 としました。Azure の主なリソースの場所として japaneast を選択すると、App Service の何かの制限に引っかかってデプロイできないので注意が必要です。

デプロイには 30 分くらいかかります。


## 動作確認

デプロイが終わると、画面に URL が表示されますので、そこにアクセスしてください。

URL がわからなくなってしまったら、https://portal.azure.com/ で Resource gorups > azure-search-openai-demo のリソース > Type が App Service の app-background-<ランダムな文字列> を開き、Default domain にアクセスすれば表示できます。

文書を追加したい場合には [data_ingestion.md](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/data_ingestion.md)、もっといろいろな機能を試したい場合には [deploy_features.md](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/deploy_features.md) などを参考にしてください。


## 後片付け

### 文書と索引だけ削除したい場合

[data_ingestion.md#removing-documents](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/docs/data_ingestion.md#removing-documents) の通りに `./scripts/prepdocs.sh --removeall` を実行します。

再投入をする場合は md5 ファイルを削除する必要があります。md5 ファイルにはスペースが含まれるファイル名もあるため、Windows では Git Bash を使うなどして、以下のように削除します。

```console
$ find ./data -name "*.md5" -print0 | xargs -0 rm
```

### 環境全体を削除したい場合

[README.md#clean-up](https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/README.md#clean-up) にあるように `azd down` でもよいのですが、続けるかを聞かれた後、リソースを削除してよいか聞かれるまでにしばらく待たされます。

リソースも削除するのであれば、`azd down --purge` を実行するのがおすすめです。


## トラブル

### Azure OpenAI と Document Intelligence のデプロイに失敗する

`azd down` の際に purge できていないと、削除済みの状態で残ってしまい、クォータのようなものに引っかかって作成が失敗します。

ポータルで対象サービスの Manage deleted resources から purge してください。

### AzureDeveloperCliCredential.get_token failed: Timed out waiting for Azure Developer CLI

私の環境だけかもしれませんが、Ubuntu で Python 3.12 を使った場合に発生しました。Pytho 3.12 は動作対象外なので、Python 3.11 が入っている Dev Container を使うのが良いと思います。

### pymupdf で `ImportError: DLL load failed while importing _extra: The specified module could not be found.` というエラーが発生する

Windows Server 2022 で発生しました。[サポートされている最新の Visual C++ 再頒布可能パッケージのダウンロード](https://learn.microsoft.com/ja-jp/cpp/windows/latest-supported-vc-redist)から入手する必要があります。

### `azd deploy` で `azd env set` で設定した環境変数が反映されない

https://portal.azure.com/ で Resource gorups > azure-search-openai-demo のリソース > Type が App Service の app-background-<ランダムな文字列> を開き、Settings > Environmet variables から手で設定してしまうのが早いです。

### USE_CHAT_HISTORY_COSMOS true を設定すると `:( Application Error` になってしまう

環境変数 COSMOS_DB_ACCOUNT が設定されている必要があるのですが、自動的に設定されないようなので、手で設定してください。