📝

Raspberry PiとChatGPTでつくるボイス・アシスタント・ロボット #8

2023/09/20に公開

thumbnail_1
ChatGPTにpromptを送るロボットのイメージ

ChatGPT API

https://openai.com/blog/introducing-chatgpt-and-whisper-apis

ChatGPTは2022年11月30日に公開され、その有効性が確立された高度な言語モデルです。ChatGPT APIは、この強力なモデルをプログラムやアプリケーションに統合するためのツールとして、2023年3月1日に公開されました。

Pythonライブラリ

https://pypi.org/project/openai/

PythonライブラリはPyPiからインストールでき、詳細な情報は公式ドキュメンテーションで確認することができます。ライブラリの利用により、多くの機能を簡単に活用できます。

実験ロボットを操作には、以下のモデルとテキスト生成機能を使用しました。

  • モデル: gpt-3.5-turbo-0613
  • テキスト生成機能: ChatCompletion

次章からは「Function calling(関数呼び出し)」機能を導入します。そのため、関数呼び出しに最適化されたgpt-3.5-turbo-0613モデルを使用します。テキスト生成には様々な機能がありますが、今回は「ChatCompletion(チャット)」機能を採用します。

実験用チャットテンプレート

実験を開始する前に、テキストベースのチャットテンプレートを作成します。このテンプレート使ってChatGPT APIの基本操作を確認します。

tree
.
├── .env
└── ex_bot_gpt_analyzer_1.py

ChatGPT APIのキーは環境変数に記述しても良いですが、ここでは.envファイルを用意して実行ファイルと同じ階層に配置します。

.env
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # OpenAI API KEY 

APIキーはクウォートで囲まず、直書きします。

ex_bot_gpt_analyzer_1.py
# ライブラリのインポート ---(※1)
import openai, os, dotenv

# .envファイルから環境変数をロード ---(※2)
dotenv.load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

# ChatGPTのAPIにユーザー入力を送る関数を定義 ---(※3)
def chat_with_gpt(text):
    try:
        # 会話履歴を管理する変数 ---(※4)
        messages = [
            {"role": "system", "content": """
                            あなたは優秀なボイス・アシスタント・ロボット。名前は「ゆっくり霊夢」です。
                            """},
            {"role": "user", "content": text} # ---(※5)
        ]
        # ChatGPTのAPIを呼び出す ---(※6)
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo-0613",
            messages=messages)
        # 応答からChatGPTの返答を取り出して返す ---(※7)
        return response.choices[0]["message"]["content"]

    except Exception as e: # エラーハンドリング ---(※8)
        print(f"🖥️. SYSTEM: エラーが発生しました: {e}")
        return None

if __name__ == "__main__":
    print("🖥️. SYSTEM: チャットを開始します。終了するには '/exit' を入力してください。")

    # ターミナルで連続して対話するループ ---(※9)
    while True:
        user_input = input("😀 USER: ")
        if user_input == "/exit":
            print("🖥️. SYSTEM: チャットを終了します。")
            break

        # ChatGPTによる応答を取得 ---(※10)
        assistant_reply = chat_with_gpt(user_input)

        # ChatGPTの応答を表示
        print("🤖 GPT: " + assistant_reply)

(※1)で必要なライブラリをインポートし、(※2)で.envファイルからChatGPTのAPIキーををロードします。ここでは読み込みにdotenvモジュールを使用します。

(※3)でChatGPTのAPIにユーザー入力を送る関数を定義します。

(※4)でChatGPTに送信するパラメータのひとつ、チャットメッセージリストをmessagesを定義します。チャットメッセージは3種類あり、roleで指定します。[1]

  • system:チャットAIの振る舞いに対する指示
  • user:人間の発話
  • assistant:AIの発話

usercontentパラメーター(※5)は、(※3)で定義した関数の引数を代入します。

(※6)でChatGPTのAPIを呼び出します。テキスト生成の機能は「ChatCompletion(チャット)」を使用し、モデルは「gpt-3.5-turbo-0613」、チャットメッセージリストは(※4)で指定した「massages」になります。それらをパラメータに設定し、ChatGPT APIへ送信します。

ChatGPT APIからの応答はJSON形式で返るので、テキスト部分を取り出します(※7)。

(※8)はAPI送信のエラーハンドリングです。もしエラーが出たならNoneを返します。

連続してチャットを行えるよう(※9)でメインループ行います。/exit入力でチャットループとともにプログラムを終了します。

(※10)でChatGPTによる応答を取得します。

プログラムを実行すると以下のようにターミナルでチャットを行えます。

terminal
🖥️ SYSTEM: チャットを開始します。終了するには '/exit' を入力してください。
😀 USER: こんにちは
🤖 GPT: こんにちは!私はゆっくり霊夢と申します。どのようにお手伝いいたしましょうか?
😀 USER: 右を向いてください
🤖 GPT: 申し訳ありませんが、私はボイス・アシスタント・ロボットであり、物理的な動作はできません。音声による応答や情報提供ができるのが私の機能です。何かお手伝いできることがありますか?
😀 USER: /exit
🖥️ SYSTEM: チャットを終了します。

ChatGPT APIのプロンプトを使う

ChatGPTでは、プロンプトと呼ばれるテキストが、ユーザーの入力や指示を自然言語処理モデルに伝えるために使用されます。プロンプトは、モデルに対して尋ねる内容や実行させるタスクを指定するためのキー要素です。

この節では、実験用のチャットテンプレートを使って、ChatGPTに音声コマンドを実行させる実験を行います。具体的な指示として、「指示された方向に合わせた水平・垂直の角度をJSON形式で生成する」というタスクをプロンプトとしてチャットメッセージに直接記述します。

ex_bot_gpt_analyzer_2.py
# 会話履歴を管理する変数 ---(※4)
messages = [
    {"role": "system", "content": """
	    あなたは垂直方向と水平方向に移動するカメラを搭載した音声チャットロボットです。
	    名前は「ゆっくり霊夢」です。
	    次のテキストはあなたに対する指示です。これを指定されたパラメータで数値化してください。
	    結果は指定のJSON形式で出力してください。

	    ###数値化するパラメータ###
	    - pan(水平): -90 < pan <90
	    - tilt(垂直): -90 < tilt <90

	    ### 出力形式 ###
	    {
		"pan": int(pan),
		"tilt": int(tilt),
		"reply": "ここに自由な回答をJSON形式で指定"
	    }

	    ###出力の例###
	    Q: 右を向いて
	    A: 
	    {
		"pan": -90,
		"tilt": 0,
		"reply": "了解しました。右に向きます。"
	    }

	    Q: 上を向いて
	    A: 
	    {
		"pan": 0,
		"tilt": -90,
		"reply": "了解しました。上を向きます。"
	    }

	    Q: 左を向いて
	    A:
	"""},
    {"role": "user", "content": text} # ---(※5)
]

チャットテンプレートのチャットメッセージリストに指示を書き込みました。プログラムを実行して回答を確認してみましょう。

terminal
🖥️ SYSTEM: チャットを開始します。終了するには '/exit' を入力してください。
😀 USER: こんにちは
🤖 GPT: こんにちは!何かご質問やご要望はございますか?
😀 USER: 右を向いて
🤖 GPT: {
    "pan": -90,
    "tilt": 0,
    "reply": "了解しました。右に向きます。"
}
😀 USER: 少し下を向いてください
🤖 GPT: {
    "pan": 0,
    "tilt": 10,
    "reply": "了解しました。少し下を向きます。"
}
😀 USER: /exit
🖥️ SYSTEM: チャットを終了します。

上手く行きました。実験の最初期はJSON形式の出力をパースしてサーボモーター制御を行って、制御することは出来たのですが、プロンプトの指定だけでは出力形式が安定しません。

terminal
😀 USER: 天気予報は調べられますか
🤖 GPT: 申し訳ありませんが、私は天気予報を調べる機能は持っておりません。他のことでお手伝いできることがあればお知らせください。

これは一例ですが、学習頻度の高い回答を生成する際は、プロンプトで指示したJSON形式での返答を無視しています。また、JSON形式で返答した後にテキストでも返答するケースも発生します。

複数の指示をプロンプトで指定すると更に生成の精度が落ちることもあり、別の方法が必要だと感じました。

脚注
  1. 参照 OpenAI GPT-4/ChatGPT/LangChain 人工知能プログラミング実践入門 / 布留川 英一 (著), 佐藤 英一 (編集) ↩︎

Discussion