NLWeb とりあえず触ってみたゾ

直近話題の(?)のNLWebをローカル環境とAzureでHello Worldしてみました。

https://news.microsoft.com/source/features/company-news/introducing-nlweb-bringing-conversational-interfaces-directly-to-the-web/

正直、NLWebの利点を十分に理解できていない段階で恐縮なのですが、せっかくなので備忘録として残しておきます。

https://github.com/microsoft/NLWeb

## ローカルでHello World
[こちらのREADME](https://github.com/microsoft/NLWeb/blob/main/HelloWorld.md)通りに環境を構築し、.envにOpenAIのAPIキーを記述します。
.env.templateには他の環境変数も書かれてますが、他の記載は必要ありません。

次に、`code/config/config_llm.yaml` と `code/config/config_embedding.yaml`と`code/config/config_retrieval.yaml` の最初の行を修正します。

```diff:code/config/config_llm.yaml
+ preferred_provider: openai
- preferred_provider: azure_openai

providers:
 ...
```

ローカルのベクターデータベースにテスト用のデータを追加します。
今回はAzure UpdateのRSSフィードを追加してみます。

```bash
python -m tools.db_load https://www.microsoft.com/releasecommunications/api/v2/azure/rss Azure-Update
```

データを追加できたら起動します。
```
python app-file.py
```

追加したRSSフィードが英語なので、英語で質問してみます。

![img.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/9122b9d2-4c59-4e79-9207-40f967acd425.gif)

なお、追加したサイトを削除する場合は `--only-delete <サイト名>` で削除できるようです。
```
python -m tools.db_load --only-delete Azure-Update
```

## AzureでHello World
次に[こちらの手順](https://github.com/microsoft/NLWeb/blob/main/docs/Azure.md#azure-openai-endpoint-creation)でAzureにデプロイしてみます。
前もってAzureOpenAIとAzure AI Searchをプロビジョニングしておきます。
また、先ほど変更した`config_**.yaml`のデフォルトプロパイダーは`azure_openai` に変更しておきます。

```diff:code/config/config_llm.yaml
+ preferred_provider: azure_openai
- preferred_provider: openai

providers:
 ...
```

**Azure OpenAI**

![aoai-deploy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/3f0c6c0f-e300-45fd-8ed7-572c8fc577c9.png)

- US East2
  - gpt-4.1
  - gpt-4.1-mini
  - text-embedding-3-small

East US2のリージョンに上記モデルをデプロイし、エンドポイントとAPIキーをメモします。
![aoaikey.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/1d03dbf8-563e-48ce-8472-cb7d66ab8f44.png)


**Azure AI Search**
- Japan East: フリープラン

こちらもエンドポイントとAPIキーをメモします。
![ai-search-key.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/b1b44e89-952f-4bd8-b7d7-1fc673f041a0.png)

[手順通り](https://github.com/microsoft/NLWeb/blob/main/docs/Azure.md#azure-openai-endpoint-creation)にAZ CLIでApp Serviceを作成します。
```bash
rg=<リソースグループ名>
plan=<AppServiceプラン名>
appservice=<Service名>

az appservice plan create --name $plan --resource-group $rg --sku P1v3 --is-linux

az webapp create --resource-group $rg --plan $plan --name $appservice --runtime "PYTHON:3.13"
```
作成したAppServiceの環境変数を設定します。
```bash
az webapp config appsettings set --resource-group $rg --name $appservice --settings \
AZURE_VECTOR_SEARCH_ENDPOINT=<AISearchのエンドポイント> \
AZURE_VECTOR_SEARCH_API_KEY=<AISearchのAPIキー> \
AZURE_OPENAI_ENDPOINT=<AzureOpenAIエンドポイント> \
AZURE_OPENAI_API_KEY=<AzureOpenAIのAPIキー> \
WEBSITE_RUN_FROM_PACKAGE=1 \
SCM_DO_BUILD_DURING_DEPLOYMENT=true \
NLWEB_OUTPUT_DIR=/home/data
```

アプリをデプロイします！
```bash
az webapp config set --resource-group $rg \
--name $appservice --startup-file "startup.sh"

git archive --format zip --output ./app.zip main

az webapp deployment source config-zip --resource-group $rg \
--name $appservice --src ./app.zip
```

App ServiceのポータルメニューからSSHで接続し、ローカルで実行した際と同じ用にRSSフィードを追加するコマンドを実行しましょう。

![ssh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/cb0bda77-b369-4b53-9aa8-1e2f250a1dba.png)

実行後に、AI Searchを覗いてみるとドキュメントが追加されていることがわかります。
![ai-search.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/b216c0c0-62ef-4b6c-8b36-3cfa388a2a29.png)

App ServiceのデプロイされたURLからアプリの動作を確認してみるとローカルと同じ用に動作することが確認できました。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/e1e98373-62ae-4fa1-9d03-dc50055fe5f0.png)

## おわりに
今回は、ローカルとAzureでHello Worldしてみました。
正直まだNLWebの美味しさがよく分かってないので、今後追っていこうと思います！