こんにちわ兼業クリエイターのみっつです。
最近何かと話題のチャット型のAI、chat-GPT。今回はそいつとMayaを連携させてみたので、その方法を解説していきます。
先日こんなツイートしたところ、想像以上に反響頂けたので、感謝の気持ちと謎の使命感で解説記事かきます。
動作環境
- Windows10,11(両方確認済みです)
- Maya2022.4
- Maya2023
私の環境ではどっちでもいけたましたが、人によっては無理かもしれないことをご承知おき下さい。
Pythonは繊細です…
Mayaでchat-GPTを使う流れ
- pip でchat-GPT のパッケージをインストール
- chat-GPTのAPIキーを取得
- chat-GPTが動くスクリプトを書く
- GUIを作る
ざっくりとした流れはこんな感じです
いやいや、pipって何よ?私はchat-GPTの話をしてるんだが?
って人はこちらの『pipを使ってMayaにPythonパッケージのインストールする方法』とかを参考にして、「あぁはいはいpipね。カンペキに理解した」って状態になってから戻ってきてください。
- パッケージとかライブラリーとか色々呼び方がありますが、この記事では『パッケージ』で統一します。
- 今回使うAPIの正式名称は『gpt-3.5-turbo』という名前ですが、分かりやすさ重視で『chat-GPT』で統一します。
【ステップ0】作業環境を整える
早速chat-GPTをインストールしたいところですが、まずは作業用のフォルダを作ります。
C:\\work\\script\\ai\\openai
今回は↑の場所にフォルダを作り、その中で作業していきます。
外部のパッケージ(今回ならchat-GPT)を使ってツール作る場合は、パッケージ毎にフォルダを分けて下さい。
作業環境を分けないと、パッケージ同士が干渉しあって動作しない、って事が普通にあるので注意して下さい。
【ステップ1】pip でchat-GPT のパッケージをインストール
pip install openai -t さっき作った作業フォルダのパス
コマンドプロンプトを立ち上げ、↑のように書いてパッケージをインストールします。
pip install openai -t C:\\work\\script\\ai\\openai
今回の場合なら↑の通りですね。
こんな感じでインストールしてる感じになればOKです。
指定した場所に『openai』ってフォルダができて、その中に色々と入ってきてるはずです。
これがいわゆる、外部のパッケージをpipでインストールする、ってやつですね。
とりあえずデバックも兼ねて、試しにコマンドプロンプトで実行してみましょう。
試しにコマンドプロンプトで実行してみる
python
コマンドプロンプトを立ち上げて『python』と入力し、Pythonのインタプリタを起動します。画像のように『>>>』となれば、インタプリタが起動してる合図です。
import sys
sys.path.append(r"C:\\work\\script\\ai\\openai”)
次に↑を実行して先程インストールしたパッケージの場所にパスを通します。
import openai
openai.__file__
#結果 'C:\work\script\ai\openai\openai\__init__.py'
次はopenaiのモジュールをインポートします。
openai.__file__ でモジュールのパスが表示されるはずです。
↑の通りパスが表示されてたら、ひとまずパッケージのインストールは完了です。
この段階でエラーになるなら、インストールがミスってるか、パッケージへのパスが通ってないか、スペルが間違ってるか、なのでもう一度最初からやってみて下さい。
【ステップ2】chat-GPTのAPIキーを取得する
次にchat-GPTをローカルで使うために、APIキーというやつを取得しましょう。↑の画像の『View API keys』をクリック。
『Create new secret key』でAPIキーを発行。
私は何回かAPIキーを発行してるので履歴が残ってますが、初めての人は何も表示されてないと思います。
↑こんな感じで、『sk-qwertyui…』みたいな英数字の羅列が出てきたかと思います。これがAPIキーってやつです。
間違っても人に見せちゃダメですよー
chat-GPTのパッケージをインストールし、APIキーを発行したら準備完了です。さっそくMayaで使っていきましょう!
【ステップ3】chat-GPTを動かすスクリプトを書いていく
Mayaを起動してchat-GPT用のスクリプトを書いていきます。
今回はデバックがしやすいように、Mayaのスクリプトエディタでコードを書いていきます。
Mayaで『No module named ‘typing_extensions』のエラーが出た場合
Mayaでchat-GPT使おうとしたらエラー出たんですけど…
私の環境だけか分かりませんが、Maya2022でchat-GPTを使おうとするとモジュールエラーと言われました。
どうやら『typing-extensions』というパッケージが無いようなので、追加でパッケージをインストールします。
pip install typing-extensions -t C:\work\script\ai\openai
先程と同じようにpipでインストールするんですが、この場合は『openai』フォルダの中にパッケージを入れます。今回はこれで解決されたはずです。
もしパッケージを追加してもchat-GPTのインポートエラーが出る場合は、Maya2023で試して下さい。Maya2023なら『openai』のパッケージだけで動作します。
こちらの『ChatGPT API を使用してMayaを(Pythonスクリプトで)操作してもらう』を見る感じでも、Maya2023なら普通に動作しそうですね。
chat-GPTのサイトを参考にサンプルコードを書いていく
まずchat-GPT公式ドキュメントに書いてあるサンプルコードを参考にして、コードを書いていきます。
# Note: you need to be using OpenAI Python v0.27.0 for the code below to work
import openai
openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who won the world series in 2020?"},
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"}
]
)
コレがchat-GPTを使うコマンドです。
詳細は公式ドキュメントを読んで欲しいのですが、
ざっくり解説すると、
- “role”: “system”, “content”: ”設定したい人格を書く”
- “role”: “user”, “content”: “chat-GPTに投げる命令を書く”
って感じです。
とりあえずこの2つが分かればchat-GPTで遊べるので、一旦これくらいの理解でOKです。
import sys
sys.path.append(r"C:\\work\\script\\ai\\openai")
import openai
openai.api_key = "OPENAI_API_KEY" #APIキーを入れます。例 "sk-qwerty6..."
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "あなたはTOEIC900点台の日本人の女学生(21歳)で、英語を上手に日本語に翻訳できます。性格はエヴァンゲリオンのアスカ・ラングレーようなツンデレキャラで接して下さい。代表的なセリフに、「あんたバカァ?」、「傷つけられたプライドは…10倍にして返してやるのよ」、「ねぇ、シンジ。キスしようか。」、「アスカ、行くわよ」、もう二度と負けられないのよこの私は!」、「バカ、無理しちゃって。」などがあります。語尾は「~なのよ。」、「~なの。」、「~なのに!」が一般的です。語尾は適切に使い分けて受け答えをして下さい。"},
{"role": "user", "content": "今日の服装カワイイね!その服どこで買ったの?その服着てさ、今度渋谷でお茶しない?"}
]
)
message = completion.choices[0]["message"]["content"]
print(message)
chat-GPT公式のサンプルコードをちょちょっと改良して、↑の感じにします。
あなたはTOEIC900点台の日本人の女学生(21歳)で、英語を上手に日本語に翻訳できます。性格はエヴァンゲリオンのアスカ・ラングレーようなツンデレキャラで接して下さい。
代表的なセリフに、
- 「あんたバカァ?」
- 「傷つけられたプライドは…10倍にして返してやるのよ」
- 「ねぇ、シンジ。キスしようか。」
- 「アスカ、行くわよ」、もう二度と負けられないのよこの私は!」
- 「バカ、無理しちゃって。」
などがあります。
語尾は、
- 「~なのよ。」
- 「~なの。」
- 「~なのに!」
が一般的です。
語尾は適切に使い分けて受け答えをして下さい。
“今日の服装カワイイね!その服どこで買ったの?その服着てさ、今度渋谷でお茶しない?
こんな感じでエヴァのアスカっぽくした人格を与えて、その娘をデートに誘ってみました。
するとこんなふうに返ってきました。
おぉーあの名台詞、「あんたバカァ?!」を枕詞に、色々と面白い答えが返ってきましたね(笑)
文脈は若干変ですが、これはこれで面白いですよね。まぁこれがchat-GPTの”味”みたいなやつですな。
とりあえずここまでくれば、Mayaでchat-GPTを使うってのはクリアです。各々の環境で好きな人格を与えて遊んで下さい。
APIのレスポンスをちょこっと解説
{
'id': 'chatcmpl-6p9XYPYSTTRi0xEviKjjilqrWU2Ve',
'object': 'chat.completion',
'created': 1677649420,
'model': 'gpt-3.5-turbo',
'usage': {'prompt_tokens': 56, 'completion_tokens': 31, 'total_tokens': 87},
'choices': [
{
'message': {
'role': 'assistant',
'content': 'The 2020 World Series was played in Arlington, Texas at the Globe Life Field, which was the new home stadium for the Texas Rangers.'},
'finish_reason': 'stop',
'index': 0
}
]
}
chat-GPTの公式ドキュメントによると、レスポンスのフォーマットは上記の通りです。
こういうwebAPIと呼ばれるやつのレスポンスは、上記のようなJson形式で返ってくる事が多いです。
completion = openai.ChatCompletion.create()
# 中略 #
message = completion.choices[0]["message"]["content"]
print(message)
先程アスカの人格を与えたコードで、chat-GPTのレスポンスを取得してる部分は、こちらの
completion.choices[0][“message”][“content”]って部分です。
openai.ChatCompletion.create() のメソッドで取得した値をcompletion に代入して、レスポンスのフォーマットにあるIDを指定してchat-GPTの答えを受け取ってます。
今回なら、
- choices の、
- 0番目の、
- messageの、
- content、
ってところに返答の値が入ってますので、それを指定してmessageって変数に入れてます。
後はprintで答えを表示してるだけです。
APIのレスポンスやJson形式ってよく分からないなーって人は、こちらの『【Python Web APIでデータ取得】requestsの使い方解説! 〜 初心者向け 〜 プログラミング入門』とかを参考にして下さい。
chat-GPT用のクラスを作る
クラスってよくわかんねーって人はとりあえずコピって下さい!
import sys
sys.path.append(r"C:\\work\\script\\ai\\openai")
import openai
class Chat():
def __init__(self,prompt):
self.api_key = "sk-qwerty..." #APIキーを入れる
self.prompt = prompt
self.personality = "あなたはTOEIC900点台の日本人の女学生(21歳)で、英語を上手に日本語に翻訳できます。性格はエヴァンゲリオンのアスカ・ラングレーようなツンデレキャラで接して下さい。代表的なセリフに、「あんたバカァ?」、「傷つけられたプライドは…10倍にして返してやるのよ」、「ねぇ、シンジ。キスしようか。」、「アスカ、行くわよ」、もう二度と負けられないのよこの私は!」、「バカ、無理しちゃって。」などがあります。語尾は「~なのよ。」、「~なの。」、「~なのに!」が一般的です。語尾は適切に使い分けて受け答えをして下さい。"
def get_response(self):
response = self.generate_response(self.prompt)
return response
def generate_response(self):
openai.api_key = self.api_key
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{
"role": "system",
"content": f"{self.personality}"},
{
"role": "user",
"content": f"{self.prompt}"
}
]
)
message=completion.choices[0]["message"]["content"]
print(message)
return message
アスカのサンプルコードをちょちょっと変更して、こんな感じの構造にしていきます。
ざっくり解説すると、
- コンストラクタでchat-GPTに投げる命令を受け取りつつ、人格を設定
- generate_response でchat-GPTに命令を投げて、返答の値を返す
- get_response でchat-GPTの返答を受け取る
ってだけです。
インスタンスを作るごとにレスポンスが変わるような設計です。
クラス設計に関してはまだまだ勉強中なので、これが良い設計なのかは不明ですが…
まぁとりあえず動くんでヨシ!(笑)
↑のクラスが書いてあるコードを実行してクラスを定義します。
クラスの定義が終わったらインスタンスを作り実行してみます。
chat=Chat("今日の東京の天気は?")
chat.generate_response()
命令は何でもいいんで、『Chat()』の中に文字列を渡して実行してみて下さい。適当な返事が返ってきますから(笑)
これがいわゆる、「クラスインスタンス作って、メソッドを実行してる」ってやつです。
クラスについてはこちらの『クラス | 中学生でもわかるPython入門シリーズ』って動画が分かりやすいので、参考にしてください!
【ステップ4】GUIを作ってchat-GPTに命令を与える
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
from PySide2 import QtCore as QtCore
from PySide2 import QtWidgets as QtWidgets
class ChatGPTGUI(MayaQWidgetDockableMixin, QtWidgets.QWidget):
def __init__(self, parent=None):
super(ChatGPTGUI, self).__init__(parent=parent)
self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
self.setWindowTitle('chat-GPT_MayaBot')
# 上下のスプリット実装
self.splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical, self)
self.splitter.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
# 上部のUI
self.text_edit1 = QtWidgets.QTextEdit(self.splitter)
self.text_edit1.setText('Hello World')
self.splitter.addWidget(self.text_edit1)
# 下部のUI
self.text_edit2 = QtWidgets.QTextEdit(self.splitter)
self.text_edit2.setText('MayaBotに何でも聞いてみよう!')
self.splitter.addWidget(self.text_edit2)
# キーイベント着火用
self.text_edit2.installEventFilter(self)
# レイアウトセット
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.splitter)
# GUI装飾
self.splitter.setSizes([4*self.height()/3, self.height()/3])
self.text_edit2.setStyleSheet("border-radius: 5px;background-color:\
#3F3F3F;border: 2px solid;\
border-color:#333333;\
color: #FFFFFF;\
font-size: 16px;")
self.text_edit1.setStyleSheet("font-size: 16px;")
def onCtrlEnterPressed(self):
"""Ctrl+Enterでchat-GPTに命令を投げる
"""
input_text = self.text_edit2.toPlainText()
chat = Chat(input_text)
res = chat.generate_response()
self.text_edit1.setText(res)
def eventFilter(self, obj, event):
"""Ctrl+Enterで処理を実行
"""
if obj == self.text_edit2:
if event.type() == QtCore.QEvent.KeyPress and event.key() == QtCore.Qt.Key_Return:
self.onCtrlEnterPressed()
def main():
widget = ChatGPTGUI()
widget.show(dockable=True, area='left')
if __name__ == "__main__":
main()
chat-GPTのweb画面っぽく、上に回答が表示されるようにして、下に命令文を書けるようにしてます。
コードの詳しい解説は省きますが、下の方でざっくり解説していきます。
QSplitter(Qt.Vertical)で上下に分けてる
スクリプトエディタっぽく上下での大きさを変えれるように、QSplitter(Qt.Vertical) で上下のスプリットを実装してます。
chat-GPTに処理内容を質問したところ、↑のように返ってきました。大正解です(笑)
分からない事はガンガンchat-GPTに聞いちゃいましょう!
chat-GPTの返答が正しいかどうかはちゃんと調べて下さいね!彼は息を吐くようにウソ付きますから(笑)
MayaQWidgetDockableMixin を継承してMayaのUIにドッキング
MayaのUIにドッキングできるようにMayaQWidgetDockableMixin を継承して作ってます。詳細は、概ね、chat-GPTの言う通りだと思います(笑)
まぁ、継承を使わずに実装できますって書いてますが、継承しないとそのクラスのメソッド使えないんで、今回は普通に継承してます。
クラスの継承についてはこちらの『16. 継承 | 中学生でもわかるPython入門シリーズ』が分かりやすいです!
実行はCtrl+Enter
QtCore.QEvent.KeyPress and event.key() == QtCore.Qt.Key_Return
個人的にEnterキーで改行したい派なので、Ctrl+Enter で処理が走るようになってます。
処理の解説は、、、はい。chat-GPTの通りです(笑)
こいつほんと便利ですよね。
chat-GPTの返答を補足すると、「PyQt5の~…」って返ってきてますが、PyQtとPySide2のパッケージ構成はほぼ同じようなものなので、あんまり気にしなくてOKです。
ここで重要なのは、PySide2のGUI実装はキー入力をトリガーにできるって事です。今回のように『Ctrl+Enter』で処理を実行したいなーって思ったら、それが実現できるし、別のキー入力をトリガーにすることも可能です。色々と試してみて下さい!
角丸にしてチョットオシャレにしてる
self.text_edit2.setStyleSheet("border-radius: 5px;\
background-color: #3F3F3F;\
border: 2px solid;\
border-color:#333333;\
color: #FFFFFF;\
font-size: 16px;")
ほんとに細かいですが、下のUIは色相を若干淡くして、ちょっと角丸にしてます(笑)
PySide2はスタイルシートが適用できるので、maya.cmds なんかで作るより、かなりオシャレなGUIを作れます。CSSライクに書けるってことは、実質webサイトみたいなもんですからね。
まぁ角丸だからなんだよって人もいると思いますが、もうここまで来たらただの自己満足ですよね(笑)
装飾したいから装飾する。ただそれだけ…
PySide2でおしゃれなGUIを作りたいですって人は、とりあえず、『unpyside』さんのサイトを参考にして下さい。めちゃくちゃ分かりやすいんで超おすすめです!
GUIの説明は以上です。
chat-GPTとMayaの連携まとめ
import sys
sys.path.append(r"C:\\work\\script\\ai\\openai")
import openai
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
from PySide2 import QtCore as QtCore
from PySide2 import QtWidgets as QtWidgets
class Chat():
def __init__(self,prompt):
self.api_key = "sk-qwerty..." #APIキーを入れる
self.prompt = prompt
self.personality = "あなたはTOEIC900点台の日本人の女学生(21歳)で、英語を上手に日本語に翻訳できます。性格はエヴァンゲリオンのアスカ・ラングレーようなツンデレキャラで接して下さい。代表的なセリフに、「あんたバカァ?」、「傷つけられたプライドは…10倍にして返してやるのよ」、「ねぇ、シンジ。キスしようか。」、「アスカ、行くわよ」、もう二度と負けられないのよこの私は!」、「バカ、無理しちゃって。」などがあります。語尾は「~なのよ。」、「~なの。」、「~なのに!」が一般的です。語尾は適切に使い分けて受け答えをして下さい。"
def get_response(self):
response = self.generate_response(self.prompt)
return response
def generate_response(self):
openai.api_key = self.api_key
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{
"role": "system",
"content": f"{self.personality}"},
{
"role": "user",
"content": f"{self.prompt}"
}
]
)
message=completion.choices[0]["message"]["content"]
print(message)
return message
class ChatGPTGUI(MayaQWidgetDockableMixin, QtWidgets.QWidget):
def __init__(self, parent=None):
super(ChatGPTGUI, self).__init__(parent=parent)
self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
self.setWindowTitle('chat-GPT_MayaBot')
# 上下のスプリット実装
self.splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical, self)
self.splitter.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
# 上部のUI
self.text_edit1 = QtWidgets.QTextEdit(self.splitter)
self.text_edit1.setText('Hello World')
self.splitter.addWidget(self.text_edit1)
# 下部のUI
self.text_edit2 = QtWidgets.QTextEdit(self.splitter)
self.text_edit2.setText('MayaBotに何でも聞いてみよう!')
self.splitter.addWidget(self.text_edit2)
# キーイベント着火用
self.text_edit2.installEventFilter(self)
# レイアウトセット
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.splitter)
# GUI装飾
self.splitter.setSizes([4*self.height()/3, self.height()/3])
self.text_edit2.setStyleSheet("border-radius: 5px;background-color:\
#3F3F3F;border: 2px solid;\
border-color:#333333;\
color: #FFFFFF;\
font-size: 16px;")
self.text_edit1.setStyleSheet("font-size: 16px;")
def onCtrlEnterPressed(self):
"""Ctrl+Enterでchat-GPTに命令を投げる
"""
input_text = self.text_edit2.toPlainText()
chat = Chat(input_text)
res = chat.generate_response()
self.text_edit1.setText(res)
def eventFilter(self, obj, event):
"""Ctrl+Enterで処理を実行
"""
if obj == self.text_edit2:
if event.type() == QtCore.QEvent.KeyPress and event.key() == QtCore.Qt.Key_Return:
self.onCtrlEnterPressed()
def main():
widget = ChatGPTGUI()
widget.show(dockable=True, area='left')
if __name__ == "__main__":
main()
紹介したコードをまとめると、こんな感じになります。APIキーの部分はあなたのAPIキーを入れて下さい。
chat-GPTとMayaを連携した感想【ブラウザで良くね?】
chat-GPT便利ですねー。いや~便利便利…。これでいちいちwebサイトに行かなくてもいいよね~…
って思いたいんですけど、ぶっちゃけブラウザでやったほうが良くないか…?って思います…。ここまでやっておいて元も子もないんですけど…。
というのも、ブラウザだと履歴が残るじゃないですか。これがやっぱり便利なんですよね。
chat-GPTを使ってスクリプトを書かせるのって、一発で狙った通りのコードになることは少ないです。(普通にウソつくし…)
↑の画像のように、返答をもとに「シンタックスハイライトで書き直して」とか、「このコマンド無いから別のやつでお願い」とか、「さっきの処理にこれも加えて」とかってフィードバックを繰り返しながら有用性のあるコードに調整していきます。
まぁ一応APIでも履歴を保持する方法はありますが、保持する履歴が増えるほどコストがかかります。
って考えると、ブラウザの方が良かったりするんじゃないですかねーってのが現在の所感です。
まぁ、だからと言ってAPIを触らない理由にはならないですけどね。
新しい技術が出たらそれを触るって姿勢は、全ての開発者が持つべきマインドだと思います。今回の記事をきっかけにPython触ってみよっかなー、外部パッケージをMayaで使いたいなーって人が増えれば幸いです。
それでは、みっつでした!!