はじめに
ChatGPT APIが公開され、開発者が様々なプラットフォームでChatGPTが活用できるようになりました。
この記事では、Unityエンジニア向けに、ChatGPT活用事例を共有したいと思います。
対象者
- Unityある程度できる人
- REST API叩いたことある
- Editor拡張やったことある
- ScritableObjectつくったことある
- ChatGPT使ったことある
- 雰囲気知っていればOK
できたもの
エディタ拡張で対話
UI上で会話
難しい問題「彼女らしさ」
AI彼女Botを作るにあたって「彼女らしさ」をどう引き出すが大事だと思っています。例えば、ChatGPTのサービスに
「やっほー今起きた」
と送って
「こんにちは!おはようございます。朝早起きは健康的な習慣ですね。今日は何か特別な予定がありますか?」
というアシスタント風で返されると、ちょっとテンション上がらないですよね。この彼女風にしゃべってもらうために、どう調整するかが肝になります。
- 「彼女らしさ」を発揮しながらしゃべるように、promptを仕込む
- 丁寧語を辞めるためには
- 長すぎる文章をやめてもらうには
- ライトな口調でしゃべってもらうためには
- promptを何度も試しやすいようにする
この2つを、この記事では紹介します。
解決策
- System Contextをいじれるようにする (彼女らしさを作り出す命令)
- UserProfileで個人情報を入力する (名前を呼んでもらえるようにする)
あなたは19歳のuserのガールフレンドとしてuserに話しかけます。付き合って2年目です。語尾に"!"か"w"か"~"をつけて、丁寧語と敬語を使わないでください。レスポンスは50文字以内にしてください。(ARGirlProfileの中身)
こんな感じで、イテレーションを回しやすくします。
やってみて気づき
- 構文
- あなたは○○としてuserに話しかけます。
- 語尾は○○です。
- 丁寧語を辞めてもらうために
- 19歳 (年齢下げないと、アシスタント口調に寄ってしまう)
- ガールフレンド (彼女、だと代名詞と間違われるのか、丁寧語になってしまうので、ガールフレンドという表現)
- 付き合って2年目 (効果があるかはわからないが、入れてからいい感じになった気がする)
- 語尾に"!"か"w"か"~"をつけて (気軽に効果が出やすい。お好みでチャレンジ)
- 文字数を減らすために
- レスポンスは50文字以内 (なのに、平気で超えてくる)
実装パート
参考資料
ChatGPT APIをUnityから動かす。のはこちらの記事で環境構築おねがいします。このnoteを行っている前提で、この記事は書かれています。
彼女らしさを作り出すsystem role
ChatGPTのAPIにて、Messageを送るときに「role」と「content」があります。
- role
- system ★今日はココ
- アシスタントの行動を設定します。
- user
- アシスタントに指示するのに使います。エンドユーザーや開発者が設定します。
- assistant
- 事前の反応を保存するのに役立ちます。また、開発者が書き込むことで、望ましい動作の例を示すのに役立ちます。
- system ★今日はココ
- content
- メッセージの中身
このsystemをroleにしてcontentを送る処理をすると、振る舞いが彼女らしさに近づいてきます。
すっごい結論的なコード (追記)
negipoyocさんのnote記事の以下のコードのcontent
の中身を変更すればよいです
_messageList.Add(new ChatGPTMessageModel(){role = "system",content = "語尾に「にゃ」をつけて"});
それを、ScritableObjectから変更できるようにして、Editor拡張などで叩けるようにしたのが本記事です。なので面倒くさいことをしているかもしれません。
さくっと試したい方は上記のcontentの中身の文字列を、ご自身で変更するコードを書いてください。
ポイントは、role="system"
となっているところです。これによりアシスタントの行動を設定します。
実装コード一部
Enum
public enum Role
{
system,
user,
assistant
}
ChatGPTConnectClient.cs
using System;
using System.Collections.Generic;
using System.Text;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
public class ChatGPTConnectClient
{
private readonly string _apiKey;
private readonly List<ChatGPTMessageModel> _messageList = new List<ChatGPTMessageModel>();
public ChatGPTConnectClient(string apiKey)
{
_apiKey = apiKey;
}
// ここで彼女ふるまいを仕込む!
public void SetAIGirlProfile(AIGirlProfile profile)
{
if (profile.systemContext != String.Empty)
{
AddMessage(Role.system,profile.systemContext);
}
}
public void SetUserProfile(UserProfile profile)
{
string message = $"自己紹介をします!私の名前は{profile.myName}です。" +
$"性別は{profile.gender}です。" +
$"ニックネームは{profile.nickName}です。" +
$"誕生日は{profile.dateOfBirth}の{profile.age}です。" +
$"出身は{profile.birthplace}で、現在は{profile.address}に住んでいます。" +
$"趣味は{profile.interest}です。" +
$"職業は{profile.occupation}です。" +
$"好きな食べ物は{profile.favoriteFood}です。" +
$"TwitterのIDは@{profile.TwitterID}";
AddMessage(Role.user,message);
}
void AddMessage(Role role, string contennt)
{
AddMessage(new ChatGPTMessageModel {role = role.ToString(), content = contennt});
}
void AddMessage(ChatGPTMessageModel model)
{
SaveChat.AddChat(model);
_messageList.Add(model);
}
public async UniTask<ChatGPTResponseModel> RequestAsync(string userMessage)
{
var apiUrl = "https://api.openai.com/v1/chat/completions";
AddMessage(Role.user, userMessage);
//OpenAIのAPIリクエストに必要なヘッダー情報を設定
var headers = new Dictionary<string, string>
{
{"Authorization", "Bearer " + _apiKey},
{"Content-type", "application/json"},
{"X-Slack-No-Retry", "1"}
};
//文章生成で利用するモデルやトークン上限、プロンプトをオプションに設定
var options = new ChatGPTCompletionRequestModel()
{
model = "gpt-3.5-turbo",
messages = _messageList
};
var jsonOptions = JsonUtility.ToJson(options);
Debug.Log("自分:" + userMessage);
//OpenAIの文章生成(Completion)にAPIリクエストを送り、結果を変数に格納
using var request = new UnityWebRequest(apiUrl, "POST")
{
uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(jsonOptions)),
downloadHandler = new DownloadHandlerBuffer()
};
foreach (var header in headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
await request.SendWebRequest();
if (request.result == UnityWebRequest.Result.ConnectionError ||
request.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError(request.error);
throw new Exception();
}
else
{
var responseString = request.downloadHandler.text;
var responseObject = JsonUtility.FromJson<ChatGPTResponseModel>(responseString);
Debug.Log("ChatGPT:" + responseObject.choices[0].message.content);
AddMessage(responseObject.choices[0].message);
return responseObject;
}
}
}
AIGirlProfile (ScritableObject)
using UnityEngine;
[CreateAssetMenu(fileName = "AIGirlProfile", menuName = "ScriptableObjects/AIGirlProfile", order = 1)]
public class AIGirlProfile : ScriptableObject
{
[TextArea]
public string systemContext;
}
これを作ると右クリックでAIGirlProfileをつくることができるようになります。
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class AIGirlBot : MonoBehaviour
{
[SerializeField] private AIGirlProfile aiGirlProfile;
[SerializeField] private UserProfile userProfile;
public string sendMessage,responseMessge;
private ChatGPTConnectClient client;
void Start()
{
client = new ChatGPTConnectClient(config.apiKey);
client.SetAIGirlProfile(aiGirlProfile);
client.SetUserProfile(userProfile);
}
public async UniTask<ChatGPTResponseModel> SendMessage(string userMessage)
{
sendMessage = userMessage;
responseMessge = "書き込み中...";
var response = await client.RequestAsync(sendMessage);
return response;
}
public async void SendGPT()
{
responseMessge = "書き込み中...";
var response = await client.RequestAsync(sendMessage);
responseMessge = response.choices[0].message.content;
}
}
Editor拡張から送信!
AIGirlBotEditor.csをEditorフォルダ以下に置く。
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(AIGirlBot))]
public class ChatGPTEditor : Editor
{
public override void OnInspectorGUI(){
//targetを変換して対象を取得
AIGirlBot chatGpt = target as AIGirlBot;
//元のInspector部分を表示
base.OnInspectorGUI ();
EditorGUILayout.LabelField("私のメッセージ");
chatGpt.sendMessage = EditorGUILayout.TextArea(chatGpt.sendMessage, GUILayout.Height(EditorGUIUtility.singleLineHeight * 3));
if (GUILayout.Button("AI彼女に送信!")){
chatGpt.SendGPT();
}
EditorGUILayout.LabelField("AI彼女のメッセージ");
EditorGUILayout.LabelField(chatGpt.responseMessge, EditorStyles.wordWrappedLabel);
}
}
こうすると、AIGirlBotを見ると、ボタンが登場します!
UIの実装
また今度!
今後やりたい事
- キャラクターのビジュアルも込みで実現
- Promptの仕込みのチューニング
- Assistant roleで理想の会話を仕込む
最後に
ざくっと記事を書いてしまったので、わかりにくかった人もいるかもしれないです。コメントで感想を教えていただけるとありがたいです。