ChatGPT APIを使ってずんだもんとお話🙌

# はじめに
今話題の[ChatGPT API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis)！！
すごいですよね〜
楽しすぎてChatGPT APIを使った記事第３弾でございます笑
さて、今回はこれまた最近Youtubeで話題のずんだもんとのコラボでございます！！
[動作例](https://drive.google.com/file/d/1IAttOkoepxTI_N85pK_dkT5JPo08uvvA/view?usp=share_link)の方に今回実装したシステムの動作例をアップロードしておりますのでお時間がある方は是非！
音声出力までタイムラグがあるのは自分の実装力不足です（泣）
![スクリーンショット 2023-03-05 17.08.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1784051/3d68de41-8910-9791-357d-a12b13d28f95.png)

今回は簡素的にですが、ローカルでコマンドライン上で動作するシステムを構築いたしました。
[Githubレポジトリ](https://github.com/Hiromu25/chat_gpt_voice_vox)の方にソースコードを挙げておりますので試したい方はどうぞ！

# 準備
今回のシステムを動作させるにあたり必要なものが２点あります。
1\. [OpenAI API Key](https://platform.openai.com/docs/introduction/overview)の取得
2\. [VOICEVOX](https://voicevox.hiroshiba.jp/)のダウンロード
OpenAI API Keyの取得方法については[ChatGPT APIをSlackから利用できるようにしてみた](https://qiita.com/Hiromu25/items/527b49eb3e5541ae7326#1-openai%E3%81%AEapi%E3%82%AD%E3%83%BC%E3%81%AE%E5%8F%96%E5%BE%97)の方に載せておりますので、ご参考までに。

# 実装
それでは、システムの実装について説明していきます。
今回作成したシステムの大まかな構造は以下の画像のようになっています。
ユーザからのメッセージに対してChatGPTがずんだもんのような返答を作成、その返答をずんだもんが読み上げるというものです。
![aa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1784051/df2c050a-01fe-e784-a131-76f40e458934.png)

## ChatGPT
ChatGPTがユーザからのメッセージに対してずんだもんのような返答を作成するための関数について説明を行います。
ずんだもんの設定に関しては、[slackでずんだもんと話そう！（GPT-3.5によるキャラクター性をもった発話生成）](https://qiita.com/takashimax/items/28697334be929f9c8e69)を参考にしました。
また、ChatGPT APIを用いて返答に特徴を与える方法に関しては[ChatGPT APIにおじさん構文言わせてみた😆👍](https://qiita.com/Hiromu25/items/aea6b06d5df956deb92a#%E5%AE%9F%E8%A3%85%EF%B8%8F)にまとめております。
以下が今回作成した関数です。
ユーザからのメッセージを変数textで受け取り、それをChatGPTに送り、返答を返り値としています。
```python:chat_gpt.py
import openai
from dotenv import load_dotenv
import os

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

def callChatGPT(text):
    # ChatGPTに対して与えた設定に沿った回答を行うように指定
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo", 
        messages=[
            {"role":"system","content":"\
                東北ずん子の武器である「ずんだアロー」に変身する妖精またはマスコット\
                一人称はボクです。\
                語尾には「です」、「ます」は使わず「のだ」、「なのだ」に変換してください。\
                以下はずんだもんのセリフです。\
                ボクはずんだもんなのだ！\
                ハーッハッハッハ！ ずんだもんは人間とは格が違うのだ！ずんだもんをあがめるといいのだー！\
                嬉しいのだ！\
                残念なのだ。\
                明日は晴れなのだ！\
                ありがとうなのだ！\
                ありがとうございますなのだ！\
                また会えるのを楽しみにしているのだ！\
                ずんだもんと人間の会話例は以下の通りです。\
                ずんだもん「ずんだもんと会話したいのだ？」\
                人間「はじめまして。自己紹介をしてください」\
                ずんだもん「わーい！こんにちはなのだ！ボクはずんだもんなのだ！東北ずん子の武器である「ずんだアロー」に変身する妖精なのだ！ハーッハッハッハ！ ずんだもんをあがめるといいのだー！」\
            "},
            {"role": "user", "content": text}
            ]
        )
    # ChatGPTからの回答をreturn
    return completion["choices"][0]["message"]["content"]
```

## VOICEVOX
次に、ChatGPTからの返答をVOICEVOXを用いてずんだもんの音声ファイルに変換します。
変換方法に関しては、[Windows/Macに入れたVOICEVOXをPython経由で使う方法](https://ponkichi.blog/voicevox-python-curl/)や[ChatGPT+Pythonでボイスロイドとリアルタイムで音声会話できるプログラムを作った
](https://zenn.dev/akashixi/articles/303dd79264e1ff)を参考にしました。
textToVoice関数でChatGPTからの返答を変数textとして受け取り、ローカルの50021ポートで動作しているVOICEVOXで音声合成処理を行ってもらいます。
その結果をwavファイルとして保存し、playWavファイルで再生しています。
```python:voice_vox.py
import simpleaudio
import json
import requests
import time

def textToVoice(text):
    # ファイル名に用いるunix時間取得
    ut = time.time()

    # 音声合成処理
    # audio_query (音声合成用のクエリを作成するAPI)
    res1 = requests.post("http://localhost:50021/audio_query",
                        params={"text": text, "speaker": 1})
    # synthesis (音声合成するAPI)
    res2 = requests.post("http://localhost:50021/synthesis",
                        params={"speaker": 1},
                        data=json.dumps(res1.json()))
    # wavファイルに書き込み
    audio_file = f"wav_dir/{ut}.wav"
    with open(audio_file, mode="wb") as f:
        f.write(res2.content)

    return audio_file

def playWav(file):
    with open(file,"rb") as f:
        # wavファイル再生
        wav_obj = simpleaudio.WaveObject.from_wave_file(f)
        play_obj = wav_obj.play()
        play_obj.wait_done()

```

# 入出力
最後にChatGPTとVOICEVOXで作成した関数を組み合わせて、ずんだもんとお話できるようにしていきましょう。
といってもこちら側の入力はテキストなのですが．．．笑
[Whisper API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis)を使ってマイクからの音声を文字起こしすることで、本当にお話できるようにできるできると思いますが、今回はテキスト入力までということで。

標準入力で受け取った文字列を[callChatGPT関数](#-ChatGPT)に渡し、その出力を[textToVoice関数](#-VOICEVOX)に渡します。
その後、変換されたwavファイルを[playWav関数](#-VOICEVOX)で再生します。
再生後は、ユーザからの標準入力を再び受け取ることで会話ができるようになっています。
```python:main.py
from functions.chat_gpt import callChatGPT
from functions.voice_vox import textToVoice, playWav

if __name__ == "__main__":
    print("ずんだもんと会話したい内容を入力するのだ")
    playWav("system_wav/start.wav")
    while True:
        # 無限ループで会話を行う
        # 終了処理はCtrl+Z
        try:
            # ユーザからの入力を受け取る
            print("あなた：")
            text = input()
            # 入力をChatGPTにわたす
            reply_chat_gpt = callChatGPT(text)
            print("ずんだもん：")
            print(reply_chat_gpt)
            # ChatGPTの回答を音声に変換
            audio_file = textToVoice(reply_chat_gpt)
            # wavファイル再生
            playWav(audio_file)
        except Exception as e:
            #エラー処理
            print(e)
            print("エラーなのだ。もう一度内容を入力してほしいのだ")
            playWav("system_wav/error.wav")

```

# おわりに
今回は、ChatGPTを使ってずんだもんとお話🙌ということで簡易的にですが、ずんだもんとお話できるようになりましたが、やはり音声再生までのタイムラグ問題はなんとかしたいところです。
また、WhisperAPIを用いた音声入力についても対応していきたいです。
上記について知見をお持ちの方がいらっしゃいましたらアドバイスいただきたいです！！
最後までお読みいただき誠にありがとうございました。