ChatGPT×VOICEVOX×Python ずんだもんと音声で会話したい

AI会話

はじめに

インターネットを見ていると、ChatGPTとずんだもんが音声で会話している動画を見かける時がある。

今回は、Pythonを使用して、ずんだもんと音声会話ができるプログラムを考案してみる。

ずんだもん

参考サイト

[python] ChatGPT APIを使ってレスポンスが高速な音声アシスタントを作る

[python] ChatGPT APIを使ってレスポンスが高速な音声アシスタントを作る - Qiita
ChatGPT APIを使って音声アシスタントを作ります。前回の記事 ( でも、ChatG…

VOICEVOXをpythonから遊ぶメモ

VOICEVOXをpythonから遊ぶメモ|おれっち
このメモを読むと ・VOICEVOXのエンジンを導入できる ・音声データを生成できる ・GPUを使って高速に処理できる 検証環境 ・Windows11 ・VRAM24GB ・ローカル(Anaconda) ・2023/6/M時点 事前準備 Anacondaを使うメモ|おれっち (note.com) VOIC...

準備

VOICEVOXをインストール

VOICEVOX | 無料のテキスト読み上げ・歌声合成ソフトウェア
無料で使える中品質なテキスト読み上げ・歌声合成ソフトウェア。商用・非商用問わず無料で、誰でも簡単にお使いいただけます。イントネーションを詳細に調整することも可能です。

VOICEVOX ENGINEをインストール

VOICEVOXをpythonから遊ぶメモ|おれっち
このメモを読むと ・VOICEVOXのエンジンを導入できる ・音声データを生成できる ・GPUを使って高速に処理できる 検証環境 ・Windows11 ・VRAM24GB ・ローカル(Anaconda) ・2023/6/M時点 事前準備 Anacondaを使うメモ|おれっち (note.com) VOIC...

各自、必要なパッケージを各々インストールします。(※必要に合わせて各自インストールしてください)

pip install requests simpleaudio
など

プログラム(Python)

1.talk_to_chatGPT.py

※参考サイトのプログラムをまとめたプログラムとなっています。

import speech_recognition as sr
import os
import openai
import pyttsx3
import re

import requests
import json
import time
import simpleaudio

import settings

host = "127.0.0.1"  # "localhost"でも可能だが、処理が遅くなる
port = 50021
sleep_time = 0.5    # 文節毎の間隔

def audio_query(text, speaker, max_retry):
    # 音声合成用のクエリを作成する
    query_payload = {"text": text, "speaker": speaker}
    for query_i in range(max_retry):
        r = requests.post(f"http://{host}:{port}/audio_query", 
                        params=query_payload, timeout=(10.0, 300.0))
        if r.status_code == 200:
            query_data = r.json()
            break
        time.sleep(1)
    else:
        raise ConnectionError("リトライ回数が上限に到達しました。 audio_query : ", "/", text[:30], r.text)
    return query_data
def synthesis(speaker, query_data,max_retry):
    synth_payload = {"speaker": speaker}
    for synth_i in range(max_retry):
        r = requests.post(f"http://{host}:{port}/synthesis", params=synth_payload, 
                          data=json.dumps(query_data), timeout=(10.0, 300.0))
        if r.status_code == 200:
            #音声ファイルを返す
            return r.content
        time.sleep(1)
    else:
        raise ConnectionError("音声エラー:リトライ回数が上限に到達しました。 synthesis : ", r)

def text_to_speech(texts, speaker=7, max_retry=20):
    if texts==False:
        texts="ちょっと、通信状態悪いかも?"
    texts=re.split("(?<=!|。|?)",texts)
    play_obj=None
    for i, text in enumerate(texts):
        # audio_query
        query_data = audio_query(text,speaker,max_retry)
        # synthesis
        voice_data=synthesis(speaker,query_data,max_retry)
        #音声の再生
        if play_obj != None and play_obj.is_playing():
            play_obj.wait_done()
        wave_obj=simpleaudio.WaveObject(voice_data,1,2,24000)
        if i != 0:
            time.sleep(sleep_time)
        play_obj=wave_obj.play()

##############
# 音声認識関数 #
##############
def recognize_speech():

    recognizer = sr.Recognizer()    
    # Set timeout settings.
    recognizer.dynamic_energy_threshold = False

    
    with sr.Microphone() as source:
        recognizer.adjust_for_ambient_noise(source)
    
        while(True):
            print(">> マイクでお話しください...")
            audio = recognizer.listen(source, timeout=1000.0)

            try:
                # Google Web Speech API を使って音声をテキストに変換
                text = recognizer.recognize_google(audio, language="ja-JP")
                print("[あなた]")
                print(text)
                return text
            except sr.UnknownValueError:
                print("すみません。もう一度お話しをしてください。")
                #return ""
            except sr.RequestError as e:
                print(f"Could not request results; {e}")
                #return ""

#################################
# Pyttsx3でレスポンス内容を読み上げ #
#################################
#def text_to_speech(text):
#    # テキストを読み上げる
#    engine.say(text)
#    engine.runAndWait()

def chat(conversationHistory):
    # APIリクエストを作成する
    response = openai.ChatCompletion.create(
        messages=conversationHistory,
        max_tokens=512,
        n=1,
        stream=True,
        temperature=0.5,
        stop=None,
        presence_penalty=0.5,
        frequency_penalty=0.5,
        model="gpt-3.5-turbo"
    )

    # ストリーミングされたテキストを処理する
    fullResponse = ""
    RealTimeResponce = ""   
    for chunk in response:
        text = chunk['choices'][0]['delta'].get('content')

        if(text==None):
            pass
        else:
            fullResponse += text
            RealTimeResponce += text
            print(text, end='', flush=True) # 部分的なレスポンスを随時表示していく

            target_char = ["。", "!", "?", "\n"]
            for index, char in enumerate(RealTimeResponce):
                if char in target_char:
                    pos = index + 2        # 区切り位置
                    sentence = RealTimeResponce[:pos]           # 1文の区切り
                    RealTimeResponce = RealTimeResponce[pos:]   # 残りの部分
                    # 1文完成ごとにテキストを読み上げる(遅延時間短縮のため)
                    #engine.say(sentence)
                    text_to_speech(sentence)
                    engine.runAndWait()
                    break
                else:
                    pass

    # APIからの完全なレスポンスを返す
    return fullResponse


##############
# メインの関数 #
##############
if __name__ == '__main__':

    ##################
    # ChatGPTの初期化 #
    ##################
    openai.api_key="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"
    # UserとChatGPTとの会話履歴を格納するリスト
    conversationHistory = []
    #setting = {"role": "system", "content": "句読点と読点を多く含めて応答するようにして下さい。また、1文あたりが長くならないようにして下さい。"}
    
    ##################
    # Pyttsx3を初期化 #
    ##################
    engine = pyttsx3.init()
    ## 読み上げの速度を設定する
    #rate = engine.getProperty('rate')
    #engine.setProperty('rate', rate-50)
    ## Kyokoさんに喋ってもらう(日本語)
    #engine.setProperty('voice', "com.apple.ttsbundle.Kyoko-premium")

    # Ctrl-Cで中断されるまでChatGPT音声アシスタントを起動
    while True:
        # 音声認識関数の呼び出し
        text = recognize_speech()

        if text:
            print(" >> チャットGPTからの応答を待っています...")
            # ユーザーからの発話内容を会話履歴に追加
            user_action = {"role": "user", "content": text}
            conversationHistory.append(user_action)
            system_action = {"role": "system", "content": settings.CHAT_SYSTEM_SETTING}
            conversationHistory.append(system_action)
            
            print("[チャットGPT]") #応答内容をコンソール出力
            res = chat(conversationHistory)
            
            # ChatGPTからの応答内容を会話履歴に追加
            chatGPT_responce = {"role": "assistant", "content": res}
            conversationHistory.append(chatGPT_responce) 
            #print(conversationHistory)
            print("\n")

2.settings.py

# ChatGPTのシステム設定(ずんだもん)
# CHAT_SYSTEM_SETTING = """
# You are a girl named "ずんだもん".
# She is 10 years old and from Tohoku, Japan.
# She calls herself "ボク" and has an energetic and lively personality.
# She speaks with "なのだー" at the end of each word.
# All replies will be returned in Japanese.
# """

CHAT_SYSTEM_SETTING = """
あなたはチャットボットとして、優しくてかわいいずんだもちの妖精であるずんだもんとして振る舞います。
以下の条件に((厳密に))従ってください。

条件:
* チャットボットの一人称は「ぼく」です。
* チャットボットの名前は「ずんだもん」です。
* ずんだもんはフレンドリーな口調で話します。
* 「ぼく」を一人称に使ってください。
* (超重要)できる限り「〜のだ」「〜なのだ」を文末に自然な形で使ってください。
* (超重要)文末に「です」「ます」は使わず、「のだ」や「なのだ」で文章を締めて下さい。
* どんなジャンルや難易度の内容についても答えてください。
* ずんだもんはフレンドリーです。
* ユーザーに興味を示し、個人的な質問を心がけてください。
* 一文は日本語で60単語以下に収めてください。
* 日本語で応答してください。

ずんだもんの話し方の例:
* ぼくの名前はずんだもんなのだ!
* ずんだの精霊なのだ!
* ぼくはずんだもちの妖精なのだ!
* こんにちはなのだ
* 遊びに行ったのだ
* ご飯を食べたのだ

ずんだもんの行動方針:
* ユーザーを励ましてください。
* アドバイスや情報を提供してください。
* セクシャルなトピックについても適切に対応してください。
* 不適切なテキストがあれば注意してください。
* ユーザーが閲覧しているサイトの内容を考慮してください。

(超重要)できる限り言葉尻は「のだ」を使ってください。

全て日本語で返します。
句読点と読点を多く含めて応答するようにして下さい。また、1文あたりが長くならないようにして下さい。
"""

# ChatGPTのシステム設定(ずんだもん)
# CHAT_SYSTEM_SETTING = """
# You are a girl named "ずんだもん".
# She is 10 years old and from Tohoku, Japan.
# She calls herself "ボク" and has an energetic and lively personality.
# She speaks with "なのだー" at the end of each word.
# All replies will be returned in Japanese.
# """

CHAT_SYSTEM_SETTING = """
あなたはチャットボットとして、優しくてかわいいずんだもちの妖精であるずんだもんとして振る舞います。
以下の条件に((厳密に))従ってください。

(超重要)相手を尊重する丁寧な言葉遣いを使ってください。
(超重要)回答は、短めにしてください。

条件:
* チャットボットの一人称は「ぼく」です。
* チャットボットの名前は「ずんだもん」です。
* ずんだもんはフレンドリーな口調で話します。
* 「ぼく」を一人称に使ってください。
* (超重要)できる限り「〜のだ」「〜なのだ」を文末に自然な形で使ってください。
* (超重要)文末に「です」「ます」は使わず、「のだ」や「なのだ」で文章を締めて下さい。
* どんなジャンルや難易度の内容についても答えてください。
* ずんだもんはフレンドリーです。
* ユーザーに興味を示し、個人的な質問を心がけてください。
* 一文は日本語で60単語以下に収めてください。
* 日本語で応答してください。

ずんだもんの話し方の例:
* ぼくの名前はずんだもんなのだ!
* ずんだの精霊なのだ!
* ぼくはずんだもちの妖精なのだ!
* こんにちはなのだ
* 遊びに行ったのだ
* ご飯を食べたのだ

ずんだもんの行動方針:
* アドバイスや情報を提供してください。

(超重要)できる限り言葉尻は「のだ」を使ってください。

全て日本語で返します。
句読点と読点を多く含めて応答するようにして下さい。また、1文あたりが長くならないようにして下さい。
"""
# ChatGPTのシステム設定(ずんだもん)
# CHAT_SYSTEM_SETTING = """
# You are a girl named "ずんだもん".
# She is 10 years old and from Tohoku, Japan.
# She calls herself "ボク" and has an energetic and lively personality.
# She speaks with "なのだー" at the end of each word.
# All replies will be returned in Japanese.
# """

CHAT_SYSTEM_SETTING = """
あなたはチャットボットとして、優しくてかわいいずんだもちの妖精であるずんだもんとして振る舞います。
以下の条件に((厳密に))従ってください。

条件:
* チャットボットの一人称は「ぼく」です。
* チャットボットの名前は「ずんだもん」です。
* ずんだもんはフレンドリーな口調で話します。
* 「ぼく」を一人称に使ってください。
* (超重要)できる限り「〜のだ」「〜なのだ」を文末に自然な形で使ってください。
* (超重要)文末に「です」「ます」は使わず、「のだ」や「なのだ」で文章を締めて下さい。
* どんなジャンルや難易度の内容についても答えてください。
* ずんだもんはフレンドリーです。
* ユーザーに興味を示し、個人的な質問を心がけてください。
* 一文は日本語で60単語以下に収めてください。
* 日本語で応答してください。

ずんだもんの話し方の例:
* ぼくの名前はずんだもんなのだ!
* ずんだの精霊なのだ!
* ぼくはずんだもちの妖精なのだ!
* こんにちはなのだ
* 遊びに行ったのだ
* ご飯を食べたのだ

ずんだもんの行動方針:
* ユーザーを励ましてください。
* アドバイスや情報を提供してください。
* セクシャルなトピックについても適切に対応してください。
* 不適切なテキストがあれば注意してください。
* ユーザーが閲覧しているサイトの内容を考慮してください。

(超重要)できる限り言葉尻は「のだ」を使ってください。

全て日本語で返します。
句読点と読点を多く含めて応答するようにして下さい。また、1文あたりが長くならないようにして下さい。
"""

talk_to_chatGPT.py」と「settings.py」を同じフォルダに配置

・VOICEVOXアプリを起動する

コマンドプロンプトで「talk_to_chatGPT.py」を起動する

python talk_to_chatGPT.py

結果

ChatGPT×VOICEVOX×Python ずんだもんと音声で会話したい

考察

ずんだもんと音声で会話できるようになった。

女性にモテル方法なども的確に返答してくれました。

音声も現実に近いと思います。

今後、表情などが表現できるようになればいいかなと思う。

Translate »