【Unity】GPT3.5とDALLE APIを試せるアプリを作ってみた！ 〜OpenAIとAzure OpenAI Service両対応〜

# はじめに
今回、Azure OpenAI Serviceと本家のOpenAIの2種類のAPIに対応したUnityアプリを開発しました。このアプリはGPT3.5とDALLEの両方を試せる仕様になっており、簡単なスクリプトで構成されています。
この記事ではアプリ概要、使い方、コードに関して説明しています。最後まで読んでいただけますと大変嬉しいです！

# アプリの使い方
UIは以下のようになっています。パネルの左上部でAPIの種類（GPT or DALLE）を選択できます。中央上部のテキストフィールドでは、生成したいテキストや画像に対する文章を入力することができます。
吹き出し横のピエロの絵文字はボタンとなっています。このボタンでAPI提供元(Azure or OpenAI)を変更可能です。
![UI](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579608/29898ac5-b57c-eb01-f924-1c5b7f0fd792.gif)

# 開発環境
Unity Editor(2021.3.15)を使用しました。吹き出しのアセットには「[2D Atlas Speech bubbles Alphabet Numbers](https://assetstore.unity.com/packages/2d/environments/2d-atlas-speech-bubbles-alphabet-numbers-88398)」を、絵文字のアセットには「[Free emojis pixel art](https://assetstore.unity.com/packages/2d/gui/icons/free-emojis-pixel-art-231243)」を使用しています。
また、Azureでは以下のAPIのバージョンがデフォルトとなっています。  
| API (Azure) | バージョン |
| --- | --- |
| GPT | 2023-05-15 |
| DALLE | 2023-06-01-preview |

:::note info
コストに関しては公式サイトをご確認ください。
[Azure OpenAI Service の価格](https://azure.microsoft.com/ja-jp/pricing/details/cognitive-services/openai-service/)
[OpenAI Pricing](https://openai.com/pricing)
:::

:::note warn
それぞれのサービスは従量課金制です。使用した分だけ請求されます。
:::

# GitHub
以下でUnityプロジェクトを公開しています。ぜひご覧ください。

https://github.com/yusuke-1105/GPT-DALLE-UI

# 使い方
1\. OpenAIのAPIキー、及びAzure OpenAI Serviceの使用インスタンス名とAPIキーをそれぞれ用意してください。

:::note
**片方のAPIのみでも動作可。**
例えばAzureは使用せずに、OpenAI APIのみの使用といった使い方でもエラーなしに動作します。
:::
:::note
**Azureのインスタンス名とAPIキーの取得方法に関して**
Azureポータル/インスタンスページの「Keys and Endpoint」で取得可能です。画面左上の"docs-test-001 "がインスタンス名で、真ん中"KEY 1 "または "KEY 2 "がAPIキーとなります。
![image](https://learn.microsoft.com/en-us/azure/ai-services/openai/media/quickstarts/endpoint.png#lightbox)
:::

2\. Unityでプロジェクトを開いてください。
3. `Assets/Scenes`配下の`GPT and DALLE`を開いてください。
4. ヒエラルキー中の`EventSystem`を選択します。
5. AzureOpenAIフィールドの空欄に必要項目を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579608/a6fdec84-59a2-1752-51c5-6cd4d90f3c4e.png)

:::note
**Api Key Azure**
Azure OpenAI ServiceのAPIキー。
**Instance Name Azure**
Azure OpenAI Serviceのインスタンス名。
**Deployment Name Azure**
Azure OpenAI Studio上でGPTモデルをデプロイした際のデプロイメントネーム。
**Api Key OpenAI**
OpenAIのAPIキー。
:::

:::note
Azure OpenAI Studioのモデルデプロイに関しては[こちら](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/how-to/create-resource?pivots=web-portal#deploy-a-model)をご確認ください。
:::

6\. (オプション) `Canvas > Response UI > UI GPT > Text`のフォントを日本語に対応したものに変更してください。GPTの振舞いを設定するために、メインディレクトリの`AgentSettings.txt`を変更してください。`EventSystem` の `Max Tokens` と `Memorable Pairs` の値を変更してください。

:::note
**Max Tokens**
生成されるテキストの最大トークン数。
:::
:::note
**Memorable Pairs**
`user` と `assistant` の会話のペアの数。
0と設定すると会話は一方通行になり、0以上に設定すると会話は双方向になります。
:::

7\. **再生ボタンを押して、テキスト入力欄に適当な文字列を入力し、「submit」ボタンをクリックしてください。** すると、生成されたテキストまたは画像が表示されます。

# ヒエラルキー(Canvas)の解説
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579608/f63c5b36-297a-cf69-d104-30983eb563a3.png)

- Background
    背面の白いパネル。
- FeatureSelection
    APIの種類(GPT or DALLE)選択のためのゲームオブジェクト。
    - Capsule
        選択されている機能を表示するカプセル型のオブジェクト。
    - Text GPT
        「GPT」と書かれたテキスト。
    - Text DALLE
        「DALLE」と書かれたテキスト。
    - Button
        テキスト「GPT」と「DALLE」にまたがる透明のボタン。クリックするとAPIの種類が変更されます。
- TextField
    ユーザが操作するフィールド。
    - InputField
        ユーザがテキスト入力するインプットフィールド。
    - Capsule
        「Submit」ボタン。クリックされるとButtonManipulator.csの`SubmitButtonPressed`関数が呼び出されます(詳細は後述)。
- ResponseUI
APIからのレスポンスを表示する欄。
    - UI GPT
        GPTによる生成テキストを表示するゲームオブジェクト。
    - UI DALLE
        DALLEによる生成画像を表示するゲームオブジェクト。
    - Speechbubble_1
        吹き出しゲームオブジェクト。
- Emojis
    API提供元を表示するゲームオブジェクト。
    - Azure
        ピエロの絵文字と「Azure」と書かれたテキスト。
    - OpenAI
        ジョーカーの絵文字と「OpenAI」と書かれたテキスト。

# コードの解説
コードは`Assets/Scripts`配下にあります。合計で4ファイルあり、それぞれのファイルは以下の機能を持っています。
### ButtonManipulator.cs
**システムの中央部にあたるスクリプト。**
`SmoothMove`関数は`FeatureSelection > Capsule`の動きを制御します。
`ChangeSource`関数と`CommandToEmoji`関数は、吹き出し横の絵文字ボタンを管理しており、ボタンが押下された際にAPI提供元の変更や絵文字の回転を制御します。
`SubmitButtonPressed`関数は、テキストフィールド横の「submit」ボタンが押された際に呼び出されます。入力されたテキストを取得しAzureOpenAI.cs内の関数を呼び出します。

### RotateEmoji.cs
**絵文字ボタンを回転させるためのスクリプト。**
指定された絵文字(のゲームオブジェクト)が回転していない場合は回転するように、逆に回転している場合は回転しないように制御します。

### HTTPRequest.cs
**APIと通信するためのスクリプト。**
関数`Link`は、AzureOpenAI.csで作成されたリクエストボディをサービス提供元に送信します。
関数`GetImage`はURL先の画像を取得します。

### AzureOpenAI.cs
**OpenAIとAzure OpenAI ServiceのAPIと通信するための準備をするスクリプト。**
両サービスのAPIのパラメータやデータの返し方は統一されているため、共通のコードを記述しています。

:::note info
APIとの通信部では、Microsoft社が公開しているパッケージ「Azure.AI.OpenAI」を使用していません。代わりに一般的なHTTP用スクリプトを用いています。
(私自身の勉強のためです。)
:::

#### GPTに関して
`Awake`関数では、`AgentSettings.txt`からGPTの振る舞いに関する設定文字列を読み込み、変数`previousMessage`に格納します。そして、`GPTCompletion`関数では、ユーザの入力内容を`previousMessage`に追加格納します。
また、APIに送信するリクエストボディを以下の様に定義しています。
```C#
var request = new
{
    model = versionGPTOpenAI,
    max_tokens = maxTokens,
    stop = stop,
    messages = previousMessage
};
```
- `model`
OpenAI APIで使用するモデル名。
Azureの場合、この部分は不要ですが、リクエストの中身として格納されていても問題がないため、両サービスとも共通でこのコードを使用しています。
- `max_tokens`
生成されるテキストの最大トークン数。
- `stop`
GPTが生成を停止するトリガーになる文字。
- `messages`
生成してほしい文章の内容を定義した辞書内包型のリスト。
GPTの振る舞いを指示する`system`とユーザによる入力テキスト`user`が含まれています。

`EventSystem`の`Memorable Pairs`を1以上にした場合、以下のコードが実行されます。
```C#
Dictionary<string, string> ResponseMessage = new Dictionary<string, string>
{
    {"role", "assistant"},
    {"content", textGPT.text}
};

previousMessage.Add(ResponseMessage);

if (previousMessage.Count >= 1 + memorablePairs * 2)
{
    previousMessage.RemoveAt(1);
    previousMessage.RemoveAt(1);
}
```
このコードの前半では、GPTのレスポンステキストを`assistant`として変数`previousMessage`に追加格納します。後半では、変数`previousMessage`に格納されている`user`と`assistant`の会話ペア数が`Memorable Pairs`を超えていないかをチェックしています。数が超えていた場合、一番古い会話を削除します。

#### DALLE
画像生成に関してはGPTと異なり、過去の会話履歴や振る舞い設定の記述が不要なため、シンプルな構造になっています。

```C#
var request = new
{
    n = 1,
    size = "512x512",
    prompt = text
};
```
- `n`
生成する画像の枚数。
- `size`
DALLEが生成する画像の大きさ。
256x256, 512x512, 1024x1024の3種類から選択できます。
- `prompt`
生成してほしい画像の内容を記述したテキスト。

# 今後の展望
#### GPT
現行バージョンでは、`Memorable Pairs`が1以上の場合であっても過去の会話履歴がUI上に表示されません。そのため、会話履歴を動的に表示する仕組みの導入を検討したいと思います。
#### DALLE
現行バージョンでは、1度に1枚しか画像を表示できません。そのため、複数画像表示の対応を検討したいと思います。

# まとめ
いかがだったでしょうか？ GPT及びDALLEは生成AIとして世界的に需要が高まっている技術です。ですが、私が調査したところ、GPT3.5とDALLE、さらにOpenAIとAzure OpenAI Serviceの全てをカバーしているUnityプロジェクトは公開されていないようでした。そこで、[GPT-DALLE-UI](https://github.com/yusuke-1105/GPT-DALLE-UI)とその解説記事を公開することにしました。
もし、読みにくい部分や不明な点、なにか質問等がありましたら、コメントやGitHub issueでお聞きいただけますと嬉しいです☺️

[修正]
モデルデプロイ(Azure OpenAI Studio)に関する記述を追記しました。

# 参考
[OpenAI API](https://openai.com/blog/openai-api)
[Azure OpenAI Service REST API reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference)