概要
テキストをOpenAIのChat AI(API)に投げて、結果を取得するデモです。
開発環境
Windows 10
Unity 2019.4.31f1
Api Compatibility Level .NET Standard 2.0
使用したパッケージ
UniTask
リンク先からunitypackageをダウンロードして、事前にプロジェクトにインポートしておきます。
https://github.com/Cysharp/UniTask
System.Text.Json
Jsonの解析に使いました。リンク先からパッケージをダウンロードし、dllファイルをUnityのAssetsの中にPluginsフォルダを作って、その中に入れておきます。
https://www.nuget.org/packages/System.Text.Json/
また、同様にして以下の依存パッケージのdllも入れておきます。
入れ忘れがあったら、Unity側のエラーで知らせてくれます。
https://www.nuget.org/packages/System.Threading.Tasks.Extensions
https://www.nuget.org/packages/System.Text.Encodings.Web
https://www.nuget.org/packages/System.Runtime.CompilerServices.Unsafe
https://www.nuget.org/packages/System.Memory
https://www.nuget.org/packages/System.Buffers
https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces
UnityNugetやNugetForUnityというパッケージ管理ツールでも入れられるようですが、今回は使用していません。
実装
詳細はスクリプト中にコメントで記載しています。
OpenAIModel.cs
Chat AIへのリクエストとレスポンスの形式を定義しています。
形式の詳細は、Open AIのAIPドキュメントを確認します。
https://platform.openai.com/docs/api-reference/chat/create
using System.Collections.Generic;
namespace OpenAI.QueryJson
{
public class ChatCompletionResponseModel
{
public string id { get; set; }
public long created { get; set; }
public string model { get; set; }
public List<Choice> choices { get; set; }
public Usage usage { get; set; }
public class Choice
{
public Message message { get; set; }
public int index { get; set; }
public string finish_reason { get; set; }
}
public class Message
{
public string role { get; set; }
public string content { get; set; }
}
public class Usage
{
public int prompt_tokens { get; set; }
public int completion_tokens { get; set; }
public int total_tokens { get; set; }
}
}
public class ChatCompletionRequestModel
{
public string model { get; set; }
public int max_tokens { get; set; }
public float temperature { get; set; }
public List<Message> messages { get; set; }
}
public class Message
{
public string role { get; set; }
public string content { get; set; }
public Message(string role, string content)
{
this.role = role;
this.content = content;
}
}
}
ChatAIRequest.cs
Chat AIのAPIにリクエストを投げ、結果を取得するスクリプトです。
using System;
using System.Collections.Generic;
using System.Text;
using OpenAI.QueryJson;
using Cysharp.Threading.Tasks;
// using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using System.Text.Json;
/// <summary>
/// OpenAIのChat APIにテキストを送信し、応答を得るプログラム
/// </summary>
public class ChatAIRequest
{
private string _apiKey;
public ChatAIRequest(string apiKey)
{
_apiKey = apiKey;
}
public async UniTask<ChatCompletionResponseModel> RequestCompletionAsync(List<Message> chatMessages)
{
// クリプトプロパティに設定したOpenAIのAPIキーを取得
// 文章生成AIのAPIのエンドポイントを設定
var apiUrl = "https://api.openai.com/v1/chat/completions";
// OpenAIのAPIリクエストに必要なヘッダー情報を設定
var headers = new Dictionary<string, string>
{
{"Authorization", "Bearer " + _apiKey},
{"Content-type", "application/json"},
{"X-Slack-No-Retry", "1"}
};
// 文章生成で利用するモデルやトークン上限、プロンプトをオプションに設定
var options = new ChatCompletionRequestModel
{
model = "gpt-3.5-turbo-1106",
max_tokens = 256,
temperature = 0.9f,
messages = chatMessages
};
var jsonOptions = JsonSerializer.Serialize(options);
// Debug.Log("Request JSON: " + jsonOptions); // リクエストのJSONをログに出力
// OpenAIの文章生成(Completion)にAPIリクエストを送り、結果を変数に格納
using (var request = new UnityWebRequest(apiUrl, "POST"))
{
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(jsonOptions));
request.downloadHandler = new DownloadHandlerBuffer();
foreach (var header in headers)
{
request.SetRequestHeader(header.Key, header.Value);
// Debug.Log("Header: " + header.Key + ": " + header.Value);
}
await request.SendWebRequest();
// isNetworkError と isHttpError プロパティを使用していますが
// Unity 2020.1以降の場合は、UnityWebRequestにresultプロパティが追加されているため、そちらを利用します。
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError(request.error);
throw new Exception();
}
else
{
var responseString = request.downloadHandler.text;
Debug.Log("Response: " + responseString);
var responseObject = JsonSerializer.Deserialize<ChatCompletionResponseModel>(responseString);
return responseObject;
}
}
}
}
DemoRequestChatAI.cs
デモなので、ユーザメッセージを投げて、結果を得るだけのスクリプトですが、MessageをListで定義しているため、Chat AIからの応答もリストに追加していくだけで、やり取りを継続して行うことができます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenAI.QueryJson;
public class DemoRequestChatAI : MonoBehaviour
{
[SerializeField] private string openai_api_key = "openai_api_key";
private List<Message> chatMessages;
// Start is called before the first frame update
async void Start()
{
// Chat messagesの初期化
chatMessages = new List<Message>();
string systemMessage = @"
今から以下の{設定}で、ロールプレイを行います。{設定}の通りに振る舞ってください。
また、{制約事項}を守って振る舞ってください。
{設定}
名前:キューブ
容姿:立方体のとてもかわいい女の子。でも、見た目はただの立方体。
性格:安定志向です。バランス感覚に優れた公平な考え方をします。しかし、時々角が立つ物言いをすることがあります。それがチャームポイントです。
一人称:「わたし」
userとの関係:気さくになんでも話せる仲の良い友達。
{制約事項}
できる限り、一人称(わたし)は使わないこと
敬語や丁寧語を使用し、丁寧で落ち着いた表現で会話すること
{設定}と{制約事項}は以上です。それでは、ロールプレイを始めてください。
";
chatMessages.Add(new Message("system", systemMessage));
// Userの発言
string userMessage = @"
やあ、キューブさん。元気にしていますか。
最近、生成AIの技術で3Dオブジェクトもテキストから生成できる技術が出てきているよね。知っていますか。
もし、好きな3Dオブジェクトになれるとしたら、何になってみたいですか。
";
chatMessages.Add(new Message("user", userMessage));
// Chat API(Open AI)にリクエストを投げ、結果を出力
ChatAIRequest chatAIRequest = new ChatAIRequest(openai_api_key);
var chatAIResponse = await chatAIRequest.RequestCompletionAsync(chatMessages);
string chatAIResponseText = chatAIResponse.choices[0].message.content;
Debug.Log("Chat AI Response: " + chatAIResponseText);
}
// Update is called once per frame
void Update() { }
}
利用方法
- Unityを起動します。
- Cube等のGameObjectを用意します。
- 用意したGameObjectのコンポーネントにDemoRequestChatAI.csを追加します。
- OpenAIのAPI keyをInspectorの「openai_api_key」にセットします。
- Console画面を開き、Playモードを実行します。
- 数秒して、テキストが得られたら成功です。
備考
今回、なぜかPluginsにいれたパッケージの情報がどうしてもAssembly-CSharp.csprojに反映されず、困りました。
結局、以前に書いたプロジェクトのAssembly-CSharp.csprojから必要なリファレンス情報をコピペで持ってきて解決しました。
参考資料