【Unity】OpenAIのAPIを呼び出してChatGPTと会話するテストプログラムを作成しました

UnityからOpenAIのAPIを呼び出してChatGPTと会話するテストプログラムを作成しました

作ったプログラムの内容と実装方法を説明します

実行画面

実行すると下のような画面が表示されます。 会話文を入力します。 送信ボタンをクリックすると回答が返ってきます。 会話文を入力して再度送信をクリックすると、回答が返ってきます。

実装方法

使用ライブラリ

今回はコミュニティで公開されているUnity用の com.openai.unity by RageAgainstThePixel ライブラリを使用しました。

github.com

ライブラリのインストール

Unity EditorのProject Settingsを開いてPackage Managerところで 下記のregistryを追加します。

Name: OpenUPM
URL: https://package.openupm.com
Scope(s):
com.openai
com.utilities

Package Managerを開いて My Registriesを選択します。 OpenAI packageを選択してダウンロードとインポートを行います。 依存パッケージは自動でダウンロードされます。

プログラム構成

プログラムは3つのクラスにわかれています。
①OpenAIのAPIを扱うOpenAiApiServiceクラス
②UI上の情報取得と更新を行うUIManagerクラス
③メインのプログラムを実行するProgramクラス

以下順番に解説します。

◇OpenAiApiServiceクラス

概要

フィールド、コンストラクタ、関数定義にわかれています。

フィールド定義
ApiKeyとAPIを呼び出すときに使うOpenAIClient変数を定義しています。

private const string ApiKey = "キー";
private readonly OpenAIClient openAiClient;

コンストラク
ApiKeyを引数にしてOpenAIClientオブジェクトを作成しています。

    public OpenAiApiService()
    {
        openAiClient = new OpenAIClient(ApiKey);
    }

関数定義

ChatGPTに質問を投げる関数として
CreateCompletionAsync(SynchronizationContext context, string systemPrompt, string firstQuestion, string lastAnswer)を定義しています

引数は

変数名 説明
SynchronizationContext context メインスレッドのコンテキストを受け取るための変数
string systemPrompt ChatGPTに演じてほしい役割を受け取る
string firstQuestion ChatGPTへ渡したいテキストを受け取る
string lastAnswer ChatGPTの前回の回答を受け取る

関数の中身

public async ValueTask CreateCompletionAsync(SynchronizationContext context, string systemPrompt, string firstQuestion, string lastAnswer)
    {
        var messages = new List<Message>
        {
            new Message(Role.System,systemPrompt),
            new Message(Role.User,firstQuestion),
            new Message(Role.Assistant,lastAnswer)
        };
        var response = await openAiClient.ChatEndpoint.GetCompletionAsync(new ChatRequest(messages, Model.GPT3_5_Turbo));
        var choice = response.Choices[0];
        var content = choice.Message.Content;
        context.Post(_ =>
        {
            UIManager.SetAnswer(content); //ChatGPTからの回答を画面に表示
        }, null);
    }
ヒント

最初知らずにプログラムを書いた時は、何も考えず public class OpenAiApiService : MonoBehaviour としていました。

この状態だと下記の警告がでます。

MonoBehaviourやその継承クラスをnewしてはいけないようです。 継承を削除したところ警告は消えました。 今回のケースでは、GameObjectを使用していないのでMonoBehaviorの継承は不要でした。

参考

shibuya24.info

OpenAiApiService.csコード
using OpenAI;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
using UnityEngine;
using OpenAI.Models;
using OpenAI.Chat;


public class OpenAiApiService
{
    #region フィールド定義
    private const string ApiKey = "キー";
    private readonly OpenAIClient openAiClient;
    #endregion

    #region コンストラクタ
    public OpenAiApiService()
    {
        openAiClient = new OpenAIClient(ApiKey);
    }
    #endregion

    #region CreateCompletionAsync()関数
    public async ValueTask CreateCompletionAsync(SynchronizationContext context, string systemPrompt, string firstQuestion, string lastAnswer)
    {
        var messages = new List<Message>
        {
            new Message(Role.System,systemPrompt),
            new Message(Role.User,firstQuestion),
            new Message(Role.Assistant,lastAnswer)
        };
        var response = await openAiClient.ChatEndpoint.GetCompletionAsync(new ChatRequest(messages, Model.GPT3_5_Turbo));
        var choice = response.Choices[0];
        var content = choice.Message.Content;
        Debug.Log(content);
        context.Post(_ =>
        {
            UIManager.SetAnswer(content);
        }, null);
    }
    #endregion

◇UIManagerクラス

概要

UIManagerでは3つの関数を定義しています。

関数名 説明
GetQuestion()関数 Input Fieldに入力したテキストを取得して返す
GetAnswer()関数 画面に表示されているChatGPTからの回答を取得して返す
SetAnswer()関数 ChatGPTからの回答(string)を引数に取り、回答欄に表示する
UIManager.csコード
using UnityEngine;
using UnityEngine.UI;

public class UIManager : MonoBehaviour
{
    #region GetQuestion()関数 //Input Fieldに入力したテキストを取得して返す
    public static string GetQuestion()
    {
        var go = GameObject.Find("QText");
        var question = go.GetComponent<Text>();
        return question.text;
    }
    #endregion

    #region GetAnswer()関数 //画面に表示されているChatGPTからの回答を取得して返す
    public static string GetAnswer()
    {
        var go = GameObject.Find("Answer");
        var answer = go.GetComponent<Text>();
        return answer.text;
    }
    #endregion

    #region SetAnswer()関数 //ChatGPTからの回答(string)を引数に取り、回答欄に表示する
    public static void SetAnswer(string answertext)
    {
        var go = GameObject.Find("Answer");
        var answer = go.GetComponent<Text>();
        answer.text = answertext;
    }
    #endregion
}

◇Programクラス

概要

Programクラスではフィールド定義の後、Start()関数の定義とButtonClick()関数 の定義を行っています。

フィールド定義
フィールドではメインスレッドを保存する変数を定義しています。
private static SynchronizationContext context;

Start()関数
context変数にメインスレッドを保存しています。
context = SynchronizationContext.Current;

ButtonClick()関数
UI上のボタンがクリックされたら、必要なパラメータを設定してCreateCompletionAsyncを呼び出す関数

    public static async void ButtonClick()
    {
        var openAiApiService = new OpenAiApiService();
        var systemPrompt = "あなたは私は仲の良い友人です。二人はとても仲が良くフランクな口調で会話します。";
        var firstQuestion = UIManager.GetQuestion();
        var lastAnswer = UIManager.GetAnswer();
        await openAiApiService.CreateCompletionAsync(context, systemPrompt, firstQuestion, lastAnswer);
    }

この関数をボタンオブジェクトのOn Clickに設定しています。


値を返さない関数(void)にしています。値を返す関数だとボタンオブジェクトのOn Clickに設定しようとしても リストにでできませんでした。voidにするとでてくるようになりました。そもそもasyncだと返り値を返せないけれど。。


Program.csコード
using UnityEngine;
using System.Threading;

public class Program : MonoBehaviour
{
    #region フィールド
    private static SynchronizationContext context; //スレッドを保存する変数
    #endregion

    #region Start()関数
    public void Start()
    {
        context = SynchronizationContext.Current;
    }
    #endregion

    #region ButtonClick()関数
    public static async void ButtonClick()
    {
        var openAiApiService = new OpenAiApiService();
        var systemPrompt = "あなたは私は仲の良い友人です。二人はとても仲が良くフランクな口調で会話します。";
        var firstQuestion = UIManager.GetQuestion();
        var lastAnswer = UIManager.GetAnswer();
        await openAiApiService.CreateCompletionAsync(context, systemPrompt, firstQuestion, lastAnswer);
    }
    #endregion
}

まとめ

コミュニティで公開されているライブラリを使って、OpenAIの APIを呼び出して会話プログラムを実装する方法を解説しました。 ライブラリを使えば簡単にOpenAIのAPIを呼び出すことができるので、これを使えばいろいろ面白いことができそうですね!

参考

下記Udemyの講座を参考にしました。 講座ではC#用のBetalgo.OpenAI by Betalgoのライブラリを使用しています。 Betalgo.OpenAIライブラリはC#ver10.0の機能まで使用しているようで、Unityでは使えませんでしたが、 講座でのコードの書き方等が参考になりました。(UnityではC#Ver8.0の一部までしか使えないようです)

【OpenAI API入門】C#でChatGPTを動かしてみよう!【画像生成、翻訳、文字起こしなど】(.NET7対応)