Azure Open AI のOn Your Data のつまづきポイント

## はじめに

Azure Open AI の On Your Data をPythonのフレームワークであるStreamlit から使用した際に、APIがうまく使えなかったので、備忘録として「つまづきポイント」を残します。
基本的には「サンプルコードが古くてそのまま使えない」ことが原因ですので、そこを修正してあげれば動きます。

※StreamlitやOn Your Data の説明は省いています、ご了承ください。

:::note warn
注意
2024年4月3日時点での情報になります。
:::

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/use-your-data?tabs=ai-search

https://streamlit.io/

### 環境

- Python 3.11.5
- openai Version: 1.6.1
- streamlit Version: 1.32.2

## 手っ取り早く動くコード

.envファイルにAPIキーやエンドポイント等を設定すれば、以下のコードで動きます。
[参考：データ ソース - Azure AI 検索](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/references/azure-search?tabs=python)

```python:app.py
import os
import dotenv
import streamlit as st
from openai import AzureOpenAI

# .envファイルを読み込む
dotenv.load_dotenv()

# Azure OpenAI のクライアントを作成する
client = AzureOpenAI(
    azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"),
    api_key = os.getenv("AZURE_OPENAI_API_KEY"),
    api_version="2024-02-01"
)

# タイトルを表示する
st.title("On Your Data のテスト")
# 説明文を表示する
st.info("On Your Data のテストです。")

# チャットの履歴がない場合は、履歴を初期化する
if "history" not in st.session_state:
    st.session_state.history = []

# 履歴を表示する
for chat in st.session_state.history:
    with st.chat_message(chat["role"]):
        st.write(chat["text"])

# ユーザーの入力を取得する
user_input = st.chat_input("何か質問があれば入力してください。")

# チャットを送信した際の処理
if user_input:

    # ユーザーが入力した検索キーワードを表示する
    with st.chat_message("user"):
        st.write(user_input)
    
    # データソースから回答を得る
    chat_completion = client.chat.completions.create(
        model = os.getenv("AZURE_OPENAI_DEPLOYMENT"),
        messages=[
            {
                "role": "user",
                "content": user_input,
            },
        ],
        extra_body={
            "data_sources": [
                {
                    "type": "azure_search",
                    "parameters": {
                        "endpoint": os.getenv("AZURE_AI_SEARCH_ENDPOINT"),
                        "index_name": os.getenv("AZURE_AI_SEARCH_INDEX"),
                        "authentication": {
                            "type": "api_key",
                            "key": os.getenv("AZURE_AI_SEARCH_API_KEY"),
                        },
                    }
                }
            ],
        "max_tokens": 800,
        "temperature": 0.7,
        "top_p": 1.0,
        }
    )

    # 結果を表示する
    with st.chat_message("assistant"):
        st.write(chat_completion.choices[0].message.content)

    # 履歴に追加する
    st.session_state.history.append({"role": "user", "text": user_input})
    st.session_state.history.append({"role": "assistant", "text": chat_completion.choices[0].message.content})

```

```.env
AZURE_OPENAI_ENDPOINT=
AZURE_OPENAI_API_KEY=
AZURE_OPENAI_DEPLOYMENT=

AZURE_AI_SEARCH_ENDPOINT=
AZURE_AI_SEARCH_INDEX=
AZURE_AI_SEARCH_API_KEY=
```

## つまづき①：サンプルコードが新しいバージョンに対応していない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561386/233903ac-c9c7-dc3d-a82f-ae08052b4609.png)

上記のAzure AI Studio の「コードの表示」で出してくれるサンプルコードが「OpenAI Python APIライブラリ version 0.28.1」対応のもので、最新の「version 1.x」に対応していません。
このコードをコピペして別のアプリで使用しようとしたら、全然使えませんでした。
「0.28.1」のバージョンを指定してインストールすれば、おそらく動きます。（試してません）

以下のように「破壊的変更」のようです。

>version 0.28.1 から version 1.x へのアップグレードは破壊的変更であるため、コードをテストして更新する必要があります。
[OpenAI Python API ライブラリ 1.x への移行](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/how-to/migration?tabs=python-new%2Cdalle-fix#updates)

## つまづき②：MS Learn の情報が少し古い

:::note warn
注意
2024年4月3日時点での情報になります。
:::

以下のMS Learn に記載のコードをそのまま使用としましたが、上手くデータが取れませんでした。
[参考：Python アプリを作成する](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/use-your-data-quickstart?tabs=command-line%2Cpython-new&pivots=programming-language-python#create-the-python-app)

こちらは、APIのバージョンを「2023-08-01-preview」から「2024-02-01」に変更することでうまく動きました。

下記のコードに書き換えた差分を表示しています。
「extra_body」の部分でデータソースとしてAI Searchを指定するのですが、API のバージョンを「2024-02-01」に変更した際、書き方が少し変更されているので要注意です。

```diff_python
client = AzureOpenAI(
    azure_endpoint = os.environ["AZURE_OPENAI_ENDPOINT"],
    api_key = os.environ["AZURE_OPENAI_API_KEY"],
+   api_version="2024-02-01" # APIのバージョンを2024-02-01に変更する
)


completion = client.chat.completions.create(
    model=deployment,
    messages=[
        {
            "role": "user",
            "content": "What are my available health plans?",
        },
    ],
    extra_body={
-        "dataSources": [
+        "data_sources": [
            {
-               "type": "AzureCognitiveSearch",
+               "type": "azure_search", 
                "parameters": {
                    "endpoint": os.environ["AZURE_AI_SEARCH_ENDPOINT"],
-                   "key": os.environ["AZURE_AI_SEARCH_API_KEY"],
+                    "authentication": {
+                            "type": "api_key",
+                            "key": os.environ("AZURE_AI_SEARCH_API_KEY"),
+                    },
                    "indexName": os.environ["AZURE_AI_SEARCH_INDEX"]
                }
            }
        ]
    }
)

print(completion.model_dump_json(indent=2))
```

## 結論

現時点で以下2つのMS Learnを見るのが正解です。

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/references/azure-search?tabs=python

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/references/on-your-data?tabs=python

## 所感

- ライブラリやAPIのバージョンで新しいものを使用する際は要注意
- 特にAI系はアップデートが多く「破壊的変更」があるため、要注意
- どこかに正解の公式ドキュメントがあるため、それをひたすら探す

## 参考

- [OpenAI Python API ライブラリ 1.x への移行](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/how-to/migration?tabs=python-new%2Cdalle-fix)
- [独自のデータを使用して Azure OpenAI モデルとチャットする](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/use-your-data-quickstart?tabs=command-line%2Cpython-new&pivots=programming-language-python)
- [データ ソース - Azure AI 検索](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/references/azure-search?tabs=python)
- [独自のデータに基づく Azure OpenAI の API リファレンス](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/references/on-your-data?tabs=python)
- [Azure OpenAI On Your Data](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/use-your-data?tabs=ai-search)
- [streamlit](https://streamlit.io/)