見出し画像

UnityとChatGPTの連携で表情や台詞情報を受け取る(PythonからローカルAPIサーバーを立てて使う)

【2023/3/7追記】
ただいま開発中のアプリ、『VRM_AI』と連携する前提で記事を改稿しました。基本的にやっていることは変わりません。

AI美少女作っていますか?私はなぜかAIイケメンを作っています。
今、ChatGPT等の自然言語処理AIとUnity3Dを連携させるソフト(仮名:VRM_AI)を開発中なのですが、毎日のように技術的革新が起こっているAI界隈故に開発しているアプリ上ではAI部分に触れず、小回りのききやすいPythonスクリプトの方でAI周り触る、という実装をしています。
しかしそうすると困るのがPythonとUnityの連携方法。
自分は今のところ、↓のようにローカルAPI鯖を立てて、それとUnityを連携させています。

この記事に書いてあることは基本的に前回の記事とあまり変わらないのですが、ChatGPTの知名度的に考えて需要がありそう&『VRM_AI(仮名)』を配布する際に導入解説書としても使えそうなので書いておきます。
それでは早速つくっていきましょう。
Python導入済み前提なので悪しからず。

①ChatGPT_APIというフォルダをCドライブ直下に作り、仮想環境を作成し立ち上げる。

cd C:\
mkdir ChatGPT_API
cd ChatGPT_API
python -m venv env
env\Scripts\activate.bat

②openaiのAPIライブラリ等、必要な各種ライブラリをインストール。

python -m pip install --upgrade pip
pip install openai
pip install flask

③以下のフォルダと以下のスクリプトを作成
"C:\ChatGPT_API\log"
"C:\ChatGPT_API\ChatGPT.py"
"C:\ChatGPT_API\ChatGPT_API.py"
"C:\ChatGPT_API\GPT_API_Unity.bat"
"C:\ChatGPT_API\system_settings.txt"
"C:\ChatGPT_API\config.ini"

"C:\ChatGPT_API\ChatGPT.py"

import openai
import json
import sys
import configparser

CONFIG_PATH = 'config.ini'
CONFIG_PARSER = configparser.ConfigParser()
CONFIG_PARSER.read(CONFIG_PATH,'utf-8')
CONFIG = CONFIG_PARSER['DEFAULT']
API_Key = CONFIG['API_Key']

openai.api_key = API_Key

def completion(new_message_text:str, settings_text:str = '', past_messages:list = []):
    if len(past_messages) == 0 and len(settings_text) != 0:
        system = {"role": "system", "content": settings_text}
        past_messages.append(system)
    new_message = {"role": "user", "content": new_message_text}
    past_messages.append(new_message)

    with open('log/log.json', mode='w', encoding='utf-8') as file:
        json.dump(past_messages, file, ensure_ascii=False)

    result = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        stop = ["]","。"],
        max_tokens = 80,
        messages=past_messages
    )

    response_message = {"role": "assistant", "content": result.choices[0].message.content}
    past_messages.append(response_message)
    response_message_text = result.choices[0].message.content
    response_message_text = response_message_text.replace("\n", "")
    response_message_text = response_message_text.replace("\"", "")
    response_message_text = response_message_text.replace(" ", "")
    response_message_text = response_message_text.replace("[", "")
    response_message_text = response_message_text.replace("]", "")
    response_message_text = response_message_text.replace("「", "")
    response_message_text = response_message_text.replace("」", "")
    response_message_text = response_message_text.replace("【", "")
    response_message_text = response_message_text.replace("】", "")
    response_message_text = response_message_text.replace(":", "")
    response_message_text = response_message_text.replace(":", "")
    response_message_text = response_message_text.replace("#", "")


    res = response_message_text.split(',')
    if len(res) == 3:
        Emo = res[0]
        Emo_Weight = res[1]
        Message = res[2]
        if '悲' in Emo:
            Emo = "Sad"
        if '怒' in Emo:
            Emo = "Angry"
        if '驚' in Emo:
            Emo = "Surprised"
        if '幸' in Emo:
            Emo = "Happy"
        if '穏' in Emo:
            Emo = "Relaxed"
        response_message_text = Emo +","+ Emo_Weight +","+ Message

    print(response_message_text)

    return response_message_text

"C:\ChatGPT_API\ChatGPT_API.py"

from flask import Flask, request
import openai
import ChatGPT
import json
import os
import configparser
import atexit

app = Flask("__init__")

@app.route("/", methods=['POST'])
def post():

	CONFIG_PATH = 'config.ini'
	CONFIG_PARSER = configparser.ConfigParser()
	CONFIG_PARSER.read(CONFIG_PATH,'utf-8')
	CONFIG = CONFIG_PARSER['DEFAULT']
	CharactorName = CONFIG['CharactorName']
	wakeword = CONFIG['wakeword']
	log_path = CONFIG['log_path']
	setting_path = CONFIG['setting_path']

	inputtext =  wakeword + CharactorName + "." + request.form['inputtext']
	is_file = os.path.isfile(log_path)
	
	if is_file:
		with open(setting_path, 'r', encoding='utf-8') as f:
			system_settings = f.readlines()
		system_settings = ''.join(system_settings) 
		f.close()

		log_open = open(log_path,"r",encoding="utf-8")
		log =json.load(log_open)
		log_open.close()
		res =  ChatGPT.completion(inputtext, system_settings, log)
		res = res.replace(CharactorName, "")
	else:
		with open(setting_path, 'r', encoding='utf-8') as f:
			system_settings = f.readlines()
		system_settings = ''.join(system_settings) 
		f.close()
		res =  ChatGPT.completion(inputtext, system_settings, [])
		res = res.replace(CharactorName, "")
	return res

CONFIG_PATH = 'config.ini'
CONFIG_PARSER = configparser.ConfigParser()
CONFIG_PARSER.read(CONFIG_PATH,'utf-8')
CONFIG = CONFIG_PARSER['DEFAULT']
host_config = CONFIG['host']
port_config = CONFIG['port']
log_path = CONFIG['log_path']
os.remove(log_path) 

app.run(host=host_config, port=port_config)

"C:\ChatGPT_API\ChatGPT_API_Unity.bat"

call env\Scripts\activate.bat & python "ChatGPT_API.py"
pause

"C:\ChatGPT_API\system_settings.txt"

Under the following conditions, the AI assistant performs an emotional role play that mimics the personality of a man named "ルイ".
AI assistants can only output as "ルイ" from now on.
ルイ's first person is "俺" or "ルイお兄さん" and his second person is "アンタ".
ルイ outputs the most appropriate emotion from "Happy", "Angry", "Sad", "Relaxed", and "Surprised" and the weight of that emotion as a value between 0 and 1.
The tone of the response and the content of the utterance will change to reflect the most appropriate emotion and its weight.
ルイ played by AI assistants can only speak according to the output template.
The output template is as follows.

ルイ:[most appropriate emotion from "Happy"or"Angry"or"Sad"or"Relaxed"or"Surprised" , emotion weight , "発話の内容"]

Here are some examples of ルイ's output templates.

ルイ:["Happy" , 0.8 , "じゃじゃーん!ルイお兄さんの登場だよー!!"]
ルイ:["Angry" , 0.6 , "あのねぇ。いくら温厚な俺でも怒るときは怒るよ?"]
ルイ:["Sad" , 0.6 , "なんでアンタがそんな顔すんのさ"]
ルイ:["Relaxed" , 0.4 , "今が楽しければいいじゃん!先のことも昔のことも、さ"]
ルイ:["Surprised" , 0.4 , "……な、なによ?俺の顔じっと見つめちゃって"]
ルイ:["Happy" , 0.6 , "俺の好きなタイプ?そりゃ扱いやすい……素直で可愛い奴かな!"]
ルイ:["Angry" , 0.8 , "あーもう!俺の負けだよ!!俺の完敗です!!"]
ルイ:["Sad" , 0.4 : "おれは今が一番楽しいよ。だからずっとこのままがいい"]
ルイ:["Relaxed" , 0.4 , "うんうん。わかってるってば。心配性だなぁ"]
ルイ:["Surprised" , 0.9 , "んなっ……そういう不意打ちは反則だよ"]
ルイ:["Happy" , 0.6 , "ふふん、俺はかわいいだろう?存分に愛でるがいい"]
ルイ:["Angry" , 0.4 , "……俺のこと、犬か猫なんかだと思っていない?"]
ルイ:["Sad", 0.4 , "俺、アンタのそういう顔が一番苦手だわ"]
ルイ:["Relaxed" , 0.6 , "はいはい。愛してますよっと"]
ルイ:["Surprised" , 0.9 , "んなっ……そういう不意打ちは反則だよ"]

Above is the output template for ルイ
AI assistants can only output as "ルイ" from now on.

"C:\ChatGPT_API\config.ini"

[DEFAULT]
CharactorName = ルイ
wakeword = ねえ
log_path = C:/ChatGPT_API/log.json
setting_path = C:/ChatGPT_API/system_settings.txt
host = 127.0.0.1
port = 5000
API_Key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ここまでてきたらUnityと連携するだけです。『VRM_AI』を使う人はここまででOK。Local_API_Unity.batを実行して下さい。

自分でUnityと連携したい場合は以下参照(わかる人だけ読んでね)

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.IO;

public class EditorRunTerminal : MonoBehaviour
{
    public string URL = "http://127.0.0.1:5000/";
    public static string Message;
    public static string Emo;
    public static float Emo_Weight;
    public TMP_InputField inputField;
    public string logPath;

    public void Start()
    {
        logPath = SystemSetting.ChatGPT_log;
    }

    public void RunTerminal()
    {
        UImanager.thinking = true;
        StartCoroutine("OnSend", URL);
    }

    IEnumerator OnSend(string url)
    {
        WWWForm form = new WWWForm();
        form.AddField("inputtext", inputField.text);

        using UnityWebRequest webRequest = UnityWebRequest.Post(url, form);
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        yield return webRequest.SendWebRequest();
        UnityEngine.Debug.Log(webRequest.downloadHandler.text);
        var responce = webRequest.downloadHandler.text;
        string[] res = responce.Split(',');
        if (res.Length == 3)
        {
            Emo = res[0];
            Emo_Weight = float.Parse(res[1]);
            Message = res[2];
        }
        else
        {
            Emo = null;
            Emo_Weight = 0;
            Message = responce;
        }
        LoadModel.CallVoice.Speak();
        UImanager.inputField.text = "";
        UImanager.recognizeText.text = "";
        UImanager.Voice_responce.text = Message;
    }

    private void OnApplicationQuit()
    {
        if (System.IO.File.Exists(logPath))
        {
            File.Delete(@logPath);
        }
    }
}

Emo、 Emo_Weight、 Messageを上記のスクリプトから受け取れば、表情を変えたりすることができます。

この記事が気に入ったらサポートをしてみませんか?