📚
【Unity × ChatGPT API × WhisperAPI】音声認識AIアシスタントを実装する
恋愛メタバースMemoriaを運営するFlamers CTOの設楽(だーら)です。
今回は、OpenAIのAPIを用いて、UnityからAIアシスタントを実装する過程を記事に残したいと思います。
やりたいこと
- OpenAIのAPIを使ったAIアシスタントを作る
- こちらは声で話しかけ、相手は文字で返答する(いずれ音声合成もやりたい)
- こちらの声の音声認識はWhisperAPIを利用
- 相手の返答は、WhisperAPIによって書き起こされた文字を元に、ChatGPT APIを利用
環境
- Unity 2021.3.7f1
完成物
- リポジトリはこちら ソースコードはこちらをご覧下さい。
全体の構成
詳細
- ソースコードはリポジトリを見ていただければよいので、ここではそれぞれについて補足をします。
1. 録音について
- こちらの記事を参考にしました。
- インスタンス変数としてAudioClipを持つのはとても簡単ですね。
2. AudioClipからwavへの変換について
- WhisperAPIは、記事執筆時点で以下のファイル形式に対応しています。公式リファレンス
mp3, mp4, mpeg, mpga, m4a, wav, webm.
- AudioClipからwav形式に変換するのが一番簡単そうだったのでそうしました。
- こちらの記事を参考にしました。
- 戻り値のbyte[]型の値を、WhisperAPIに音源データとして直接渡しています。
WavConverter.cs
// wavへの変更を担当するクラスの中
public static byte[] ToWav(this AudioClip audioClip)
{
using var stream = new MemoryStream();
WriteRiffChunk(audioClip, stream);
WriteFmtChunk(audioClip, stream);
WriteDataChunk(audioClip, stream);
return stream.ToArray();
}
- なお、WhisperAPIをたたくリクエストのBodyを作る際、ファイル名の拡張子は.wavにしないとエラーではじかれます。ファイル名は適当でよいですが、拡張子は正しく指定する必要があります
WhisperAPIConnection.cs
// UnityWebRequestでWhisperAPIをたたいているクラスのメソッドの中
form.Add(new MultipartFormFileSection("file", recordData, "recordData.wav", "multipart/form-data"));
3. WhisperAPIからレスポンスを受け取る
- 特にありません。
- WhisperAPIは、直前とのつながりなどはないため、シンプルなレスポンス(textだけのjson)となっています。
4,5. ChatGPT APIにリクエストを送る/レスポンスを受け取る
- 特筆すべき点は2つあります。
-
- メッセージの履歴を毎回キャッシュし、リクエストの度に全てを載せて送っていること
ChatGPTConnection.cs
// フィールドにキャッシュするためのリストがあります。
private readonly List<ChatGPTMessageModel> _messageList = new();
// リクエストを送る前
// 自分の打った文を、role="user"としてリストに追加しています。
_messageList.Add(new ChatGPTMessageModel()
{
role = "user",
content = userMessage
});
// レスポンスが返ってきた後
// messageの中身を、リストに保存していることが分かります。
// roleはAIを意味する"assistant"です。
var responseObject = JsonUtility.FromJson<ChatGPTResponseModel>(responseString);
_messageList.Add(responseObject.choices[0].message);
-
- 最初に、systemとしてAIの人格の設定が可能です
ChatGPTConnection
_messageList.Add(new ChatGPTMessageModel()
{
role = "system",
content = "あなたは僕の職場の上司の女性です。仲良しなので、敬語ではなく、砕けた口調で返答してください。返答は短めにすること。"
});
6. viewに表示する
- 特になし
今後
- アバターが読み上げて発話してくれるようにしたい。リップシンクなどもいれたい
参考
- 自分自身のスクラップ
- ChatGPTの実装について一番参考にした記事
-
ライブラリを公開してくれている人
- このライブラリ、今回は使ってないけど読み込むのとても勉強になりました。ありがとうございます!
- WhisperAPIの実装で参考にした記事
- こちらのスレッドも参考になりました
追記
- プロフィールを前もって入力してAIを育てる試みをしている記事を見て、自分も同様の実装を試みた
- 「systemに対しての指示」と、「user(自分)の自己紹介」を最初に送るようにする。その内容をScriptableObjectで作成する
実装
Profile
SystemProfile.cs
using UnityEngine;
namespace ChatGPTAPI.Config
{
[CreateAssetMenu(fileName = "SystemProfile", menuName = "ChatGPTAPI/SystemProfile", order = 1)]
public class SystemProfile : ScriptableObject
{
[TextArea] public string systemContent;
}
}
UserProfile.cs
using UnityEngine;
namespace ChatGPTAPI.Config
{
[CreateAssetMenu(fileName = "UserProfile", menuName = "ChatGPTAPI/UserProfile", order = 2)]
public class UserProfile : ScriptableObject
{
public string nickName;
public string age;
public string sex;
public string birthPlace;
public string job;
public string currentTask;
public string hobby;
public string favoriteFood;
public string favoriteColor;
public string favoriteAnimal;
public string favoriteSeason;
public string favoriteGame;
public string favoriteMovie;
public string favoriteMusic;
public string favoriteBook;
public string favoritePlace;
}
}
- これでScriptableObjectを作成
- ChatGPTのAPIをつかさどるクラスに渡し、messageとしておく。
ChatGPTConnection.cs
...
~~ コンストラクタなど ~~
public void AddSystemProfile(SystemProfile profile)
{
var model = new ChatGPTMessageModel()
{
role = Role.system.ToString(),
content = profile.systemContent
};
AddMessage(model);
}
public void AddUserProfile(UserProfile profile)
{
var model = new ChatGPTMessageModel()
{
role = Role.user.ToString(),
content = $"自己紹介をします!私の名前は{profile.nickName}です。" +
$"年齢は{profile.age}歳です。" +
$"性別は{profile.sex}です。" +
$"生まれた場所は{profile.birthPlace}です。" +
$"仕事は{profile.job}です。" +
$"今やっているタスクは{profile.currentTask}です。" +
$"趣味は{profile.hobby}です。" +
$"好きな食べ物は{profile.favoriteFood}です。" +
$"好きな色は{profile.favoriteColor}です。" +
$"好きな動物は{profile.favoriteAnimal}です。" +
$"好きな季節は{profile.favoriteSeason}です。" +
$"好きなゲームは{profile.favoriteGame}です。" +
$"好きな映画は{profile.favoriteMovie}です。" +
$"好きな音楽は{profile.favoriteMusic}です。" +
$"好きな本は{profile.favoriteBook}です。" +
$"好きな場所は{profile.favoritePlace}です。"
};
AddMessage(model);
}
private void AddMessage(ChatGPTMessageModel model)
{
_messageList.Add(model);
}
- Scene内のManagerで[SerializeField]でScriptableObjectを受け取り、ChatGPTConnectionのコンストラクト後に渡すなどする
結果
- 以下のように、自分のプロフィール情報を汲み取った回答をしてくれる
恋愛メタバースMemoriaを運営するFlamers, Inc.のTech Blogです。 Unity / C# / VR系の記事を中心に投稿します。 常時積極採用中!wantedly.com/companies/flamers
Discussion