LoginSignup
0
1

ティラノスクリプトで動的にキャラクターと会話する(GPT3.5)

Last updated at Posted at 2024-01-29

創作物への生成AIの利用については様々な議論がなされており、扱いには注意が必要です。

ティラノスクリプトでGPT-3.5のAPIと連携し、自由入力でキャラクターに応答してもらえるようにしてみました。

しくみ

image.png

ティラノスクリプトではJavaScriptが使えますので、GPTのAPIも叩けます。

(GPT-3.5は、俗にいうChatGPT……と、だいたいおんなじ!)

JavaScriptを使って、ゲームからサーバーにリクエストを送り、サーバー側で整えて、GPT-3.5のAPIを叩いて、お返事を出力しています。

なお、サーバーを挟まずに直でクライアントからGPT-3.5のAPIを叩きに行ってもいいんですが、OpenAIのキーをゲーム側においとくわけにもいかないのと、うまくいったらデータベースを持たせてやりたかったりとか、用があるので泣く泣くサーバーを立てました。

公開しない、ないしOpenAIのキーを各自で用意してもらう場合はサーバーはなくても大丈夫だと思います。

サーバーを立てるの、自宅の水道を出しっぱなしにするくらいおっかねぇ~~~……。

実装

クライアント側(前後とスクリプト部分)

#青年
今日はティラノスクリプトに
JavaScriptでGPT3.5のAPIを組み込んでみました
自由にしゃべってみましょう[p]
#青年
というわけで何か御用ですか[p]
*input
[cm]
[edit name="tf.test" top=100 left=100 width="200"]
[button x=400 y=100 target="*ok" graphic="arrow_next.png"]
[s]
*ok
[commit]
[cm]
[jump target="*next"]
[s]
*next
[iscript]
var API_URL = "http://【IP・ポート】/get_response/";
// XMLHttpRequest オブジェクトの生成 (毎回新しく生成)
var xhr = new XMLHttpRequest();
// リクエストの設定
xhr.open("POST", API_URL, true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
// イベントハンドラの設定
xhr.onreadystatechange = function() {
// レスポンスが完了した場合
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var responseData = JSON.parse(xhr.responseText);
tf.response = responseData.response;
console.log(responseData);
// 非同期処理完了後のターゲットを実行
tyrano.plugin.kag.ftag.startTag("jump", {target: "*after_api"});
} else {
tf.response = "失敗しちゃったみたい。";
console.error("Error:", xhr.statusText);
// エラー時のターゲットも設定可能
tyrano.plugin.kag.ftag.startTag("jump", {target: "*error"});
}
}
};
// データの送信(これがプロンプトだよ!)
var data = JSON.stringify({
system_input: "ユーザーを「わが師」と呼びます。返答は50字以内です。名前はケレス。一人称は僕。慇懃で丁寧です。手に持っているのは名刺です。好きなものは我が師。例「これは技術デモです。我が師」",
user_input: tf.test
});
xhr.send(data);
[endscript]
[s] ; この部分でスクリプトを止める
*after_api
[emb exp="tf.response"]
; 次へボタンを表示してクリックされたら *input へジャンプ
[button graphic="arrow_next.png" x="100" y="150" target="*input"]
; ここで処理は停止しボタンがクリックされるのを待つ
[s]

「純粋なJavaScriptでやってたときと比べたら、なんか応答を待ってくれない~」、

とぼやいてたらChatGPTさんがいい感じに待機する処理を書いてくれました。

意外とティラノスクリプトに堪能です。

(tyrano.plugin.kag.ftag.startTagってなんだ……!?)

[p]タグでは止まってくれないので、ボタン操作にしたりしていて、
なんていうか、まあ、このあたりが「とりあえず」の部分ですね。
ひとのおうちで動くんだろうか……?

サーバー側

こっちはPythonで、GCPの格安インスタンスにのっけたFastAPIを稼働させています。

(とっても雑です)

from fastapi.middleware.cors import CORSMiddleware
from fastapi import FastAPI
from pydantic import BaseModel
import os
import openai
import time
from datetime import datetime


app = FastAPI()

 

【※ここでCORSとか設定したりするといいみたい……※】

 

# モデルクラスの定義(入力データの形式を指定)
class InputText(BaseModel):
    system_input: str
    user_input: str


# ChatGPTにテキストを送って受け取る処理
def get_gptText(system_input, user_input, max_retries=0, model_name="gpt-3.5-turbo"):
    openai.api_key = os.getenv("OPENAI_API_KEY")  # 環境変数

    retries = 0
    while retries <= max_retries:
        try:
            # テキストを生成するためのAPIリクエストを作成する
            start_time = time.time()
            start_time_str = datetime.fromtimestamp(start_time).strftime('%Y-%m-%d %H:%M:%S')

            max_tokens_value = 60

            # GPT-3.5 Turbo model
            response = openai.ChatCompletion.create(
                model=model_name,
                messages=[
                    {"role": "system", "content": system_input},
                    {"role": "user", "content": user_input},
                ],
            )

            end_time = time.time()
            end_time_str = datetime.fromtimestamp(end_time).strftime('%Y-%m-%d %H:%M:%S')
            response_time = end_time - start_time

            return response.choices[0]['message']['content'], start_time_str, end_time_str, response_time

        except Exception as e:
            print(f"Error: {e}")
            retries += 1
            print(f"Retrying... {retries}/{max_retries}")
            time.sleep(20)
            if retries > max_retries:
                print("Max retries exceeded. Exiting...")
                return None


@app.post("/get_response/")
async def get_response(text: InputText):
    response, start_time, end_time, response_time = get_gptText(text.system_input, text.user_input)
    if not response:
        raise HTTPException(status_code=500, detail="Failed to get response from GPT")
    return {
        "response": response,
        "response_time": response_time
    }


@app.get("/")
def read_root():
    return {"Hello": "World"}

コードは上記のようなものですが、そのまえにGCPでいろいろインストールしたり、コマンドラインで順番にビンタする必要があります。

とくにブラウザで動かしたいわけじゃなく、おうちでお話するbotが欲しいだけだったら直でAPIをどつくのもありです。

コスト

これをやると、サーバー費+OpenAIのChatのAPIのぶんコストがかかります。

サーバー

とりあえずGCPの無料枠で動かしてみました。
数円かかっていました。

GPT-3.5のAPI使用料金

応答を作成するのに使うお金です。
GPT-3.5 Turboの4K contextは
1000トークンあたり、入力$0.0015、出力$0.002です。
ドル払いなので、円高/円安の影響を受けます。
参考:https://openai.com/pricing

トークンは文字数ってわけじゃなくて、tokenizerで数えることができますが、日本語だと、まあ、だいたい文字数(以下くらい)です。
トークナイザー:https://platform.openai.com/tokenizer

日本語は英語よりも損で、プロンプト(命令文)は英語だけど、回答は日本語でね、なーんてやったりすることもあるようです。
でも、まじめな用途ならともかくも、「俺/オレ」「僕/ぼく/ボク」とかのニュアンスは捨てがたいものがあるよね。
正規表現や置換でやる方法もあるようです。

このトークンってやつには、ユーザーの入力+命令文も含まれるので、
ざっくり入力200、出力100くらい使うとして、
今日(2023/8/18)のレートだと、0.073 円くらい。
10回やりとりしたら0.7円、100回やりとりしたら7円くらいになります。

1人でおしゃべりしてる分には何ら問題ないのですが、これが100人とかになってくると、ねっ!

回数もね、1日にひとり10回とか、合計して1日1000回とか、何かしらは制限をかけたいところですね。
それか、キーを自前で用意してもらわないとちょっともたないですね。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1