こんにちは!今回は、PythonでOpenAI、Groq、そしてYahoo APIを利用して、音声で対話できるチャットボットを作成した経験を共有したいと思います。このプロジェクトでは、最新のAI技術を組み合わせて、ユーザーと自然な会話ができるシステムを構築しました。

プロジェクトの概要

このチャットボットは以下の機能を持っています:

  1. ユーザーの音声入力を録音
  2. 音声をテキストに変換(音声認識)
  3. AIによる応答生成
  4. テキストを音声に変換(音声合成)
  5. 応答音声の再生

さらに、Yahoo APIを使用して応答テキストにふりがなを付ける機能も実装しました。

ファイル構成

プロジェクトは以下のファイル構成になっています:

.env
app.py
chat.py
speech_to_text.py
text_to_speech.py
user_voice_recording.py
yahoo_api.py

必要なライブラリ

このプロジェクトには以下のライブラリが必要です:

pip install numpy requests python-dotenv openai groq PyAudio Wave gTTS pygame

主要な実装ポイント

1. 音声録音(user_voice_recording.py)

PyAudioを使用してユーザーの音声を録音します。無音検出機能を実装し、ユーザーが話し終わったら自動的に録音を停止します。

2. 音声認識(speech_to_text.py)

Groq APIを使用して、録音した音声をテキストに変換します。Whisperモデルを利用しています。

3. チャット処理(chat.py)

OpenAI APIを使用して、ユーザーの入力に対する応答を生成します。gpt-4o-miniモデルを使用しています。

4. 音声合成(text_to_speech.py)

Google Text-to-Speech(gTTS)を使用して、AIの応答をテキストから音声に変換します。

5. ふりがな付与(yahoo_api.py)

Yahoo APIを使用して、AIの応答テキストにふりがなを付けます。これにより、難しい漢字を含む応答でも理解しやすくなります。

実行方法

  1. 必要なAPIキーを.envファイルに設定します。
  2. app.pyを実行すると、チャットボットが起動します。
  3. マイクに向かって話しかけると、AIが音声で応答します。

コード

.env

OPENAI_API_KEY=your_openai_api_key_here
GROQ_API_KEY=your_groq_api_key_here
YAHOO_CLIENT_ID=your_yahoo_client_id_here

app.py

# 必要なライブラリをインポート
import os
from dotenv import load_dotenv
from openai import OpenAI
from groq import Groq

# 環境変数を読み込む
load_dotenv()

# 自作モジュールをインポート
import user_voice_recording
import speech_to_text
import chat
import text_to_speech
import yahoo_api

# 音声ファイルの名前を定義
USER_FILE = "user.wav"
AI_FILE = "ai.mp3"

# APIクライアントを初期化
openai_client = OpenAI()
groq_client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
yahoo_client_id = os.getenv("YAHOO_CLIENT_ID")


def main():
    # 会話履歴を保持するリスト
    chat_history = []

    while True:
        try:
            # ユーザーの音声を録音
            user_voice_recording.record_audio(USER_FILE)
            # 音声をテキストに変換
            user_input = speech_to_text.whisper_api(USER_FILE, groq_client)

            # デバッグ用:キーボード入力を使用する場合
            # user_input = input("user_input: ")

            print(f"人間: {user_input}")

            # 特定のフレーズで会話を終了
            if "ご視聴ありがとうございました" in user_input:
                continue

            # 会話履歴に追加
            chat_history.append(f"人間: {user_input}")
            # AIの応答を生成
            response = chat.process_chat(openai_client, "gpt-4o-mini", chat_history)

            print(f"AI: {response}")
            chat_history.append(f"AI: {response}")

            # ふりがなを取得
            furigana = yahoo_api.yahooapis_furigana(yahoo_client_id, response)

            print(f"ふりがな: {furigana}")

            # テキストを音声に変換
            text_to_speech.gtts_func(furigana, AI_FILE)

            # 会話履歴を最新の10往復に制限
            if len(chat_history) > 20:
                chat_history = chat_history[-20:]

        except KeyboardInterrupt:
            break
        except Exception as e:
            print(f"エラー: {e}")


# スクリプトが直接実行された場合にmain関数を呼び出す
if __name__ == "__main__":
    main()

chat.py

# システムメッセージの定義
# AIアシスタントの基本的な振る舞いを設定
system_content = "あなたはいつも日本語で返答するアシスタントです。いつも短い返答しかしません。"

# ユーザーメッセージの定義
# AIと人間の会話の文脈や制約を設定
user_content = """以下は人間と AIのフレンドリーな会話である。 
音声でチャットするので、絵文字やマークダウン記法は使わないでください。
AIが質問の答えを知らない場合は、正直に「知らない」と答える。

会話履歴:
{}

AI:

"""


def process_chat(client, model, chat_history):
    # 会話履歴を1つの文字列に結合
    history = "\n".join(chat_history)

    # OpenAI APIを使用してチャット応答を生成
    chat_completion = client.chat.completions.create(
        messages=[
            {"role": "system", "content": system_content},  # システムメッセージ
            {
                "role": "user",
                "content": user_content.format(history),
            },  # ユーザーメッセージ(会話履歴を含む)
        ],
        model=model,  # 使用するAIモデル
        temperature=1,  # 応答の多様性を制御(0-1の範囲、高いほど創造的)
        max_tokens=256,  # 生成する最大トークン数
    )

    # 生成された応答を取得
    response = chat_completion.choices[0].message.content

    # 改行を空白に置換して返す(音声出力のため)
    return response.replace("\n", " ")

speech_to_text.py

def whisper_api(filename, client):
    """
    音声ファイルをテキストに変換する関数

    Args:
    filename (str): 処理する音声ファイルのパス
    client: Groq APIクライアントオブジェクト

    Returns:
    str: 音声から変換されたテキスト
    """

    # 音声ファイルをバイナリモードで開く
    with open(filename, "rb") as file:
        # Groq APIを使用して音声をテキストに変換
        transcription = client.audio.transcriptions.create(
            file=(filename, file.read()),  # ファイル名とファイルの内容をタプルで指定
            model="whisper-large-v3",  # 使用する音声認識モデル
            language="ja",  # 日本語を指定
        )

    # 変換されたテキストを返す
    return transcription.text

text_to_speech.py

# pygameは複数のOSで動作するので、音声再生もクロスプラットフォームで実現できます
import pygame
from gtts import gTTS


def gtts_func(text, file):
    """
    テキストを音声に変換し、再生する関数

    Args:
    text (str): 音声に変換するテキスト
    file (str): 生成された音声ファイルの保存先パス

    この関数は、Google Text-to-Speech (gTTS) を使用してテキストを音声に変換し、
    pygameを使用して音声を再生します。
    """

    # Google Text-to-Speech (gTTS) を使用してテキストを音声に変換
    tts = gTTS(text=text, lang="ja")  # 言語を日本語に設定
    tts.save(file)  # 音声ファイルを保存

    # pygameのミキサーを初期化
    pygame.mixer.init()

    # 音声ファイルをロード
    pygame.mixer.music.load(file)

    # 音声の再生を開始
    pygame.mixer.music.play()

    # 再生が終わるまで待機
    while pygame.mixer.music.get_busy():
        pygame.time.Clock().tick(10)  # CPUの使用率を下げるために短い待機を挿入

    # 明示的に音声ファイルをアンロード
    pygame.mixer.music.unload()

user_voice_recording.py

import numpy as np
import pyaudio
import wave

# 音声録音の設定
CHUNK = 1024  # バッファサイズ
FORMAT = pyaudio.paInt16  # 音声フォーマット(16ビット整数)
CHANNELS = 1  # モノラル
RATE = 44100  # サンプリングレート(Hz)
THRESHOLD = 3000  # 音声検出の閾値
SILENCE_LIMIT = 2  # 無音と判断する秒数
SILENT_CHUNKS = int(SILENCE_LIMIT * RATE / CHUNK)  # 無音と判断するチャンク数


def record_audio(OUTPUT_FILE):
    # PyAudioオブジェクトとストリームの初期化
    p = pyaudio.PyAudio()
    stream = p.open(
        format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK
    )

    print("話し始めてください...")

    audio_buffer = []  # 録音データを格納するリスト
    silent_chunks = 0  # 連続した無音チャンクのカウンター
    audio_started = False  # 音声検出フラグ

    try:
        while True:
            # チャンクごとに音声データを読み取る
            data = stream.read(CHUNK)
            audio_data = np.frombuffer(data, dtype=np.int16)
            current_volume = np.mean(np.abs(audio_data))  # 現在の音量を計算

            if not audio_started:
                # 音声開始の検出
                if current_volume >= THRESHOLD:
                    print("音声を検出しました。")
                    audio_started = True
                else:
                    continue

            audio_buffer.append(data)

            # 無音検出
            if current_volume < THRESHOLD:
                silent_chunks += 1
                if silent_chunks >= SILENT_CHUNKS:
                    break  # 一定期間無音が続いたら録音終了
            else:
                silent_chunks = 0  # 音声が検出されたらリセット

    finally:
        # ストリームとPyAudioオブジェクトのクリーンアップ
        stream.stop_stream()
        stream.close()
        p.terminate()

    print("録音が終了しました")
    save_audio(p, audio_buffer, OUTPUT_FILE)


def save_audio(p, audio_buffer, OUTPUT_FILE):
    # 録音データをWAVファイルとして保存
    with wave.open(OUTPUT_FILE, "wb") as wf:
        wf.setnchannels(CHANNELS)
        wf.setsampwidth(p.get_sample_size(FORMAT))
        wf.setframerate(RATE)
        wf.writeframes(b"".join(audio_buffer))
    print(f"録音データを '{OUTPUT_FILE}' として保存しました。")


if __name__ == "__main__":
    OUTPUT_FILE = "user.wav"
    record_audio(OUTPUT_FILE)

yahoo_api.py

import requests


def yahooapis_furigana(yahoo_client_id, text):
    """
    Yahoo!のふりがなAPIを使用して、テキストにふりがなを付ける関数

    Args:
    yahoo_client_id (str): Yahoo! APIのクライアントID
    text (str): ふりがなを付けたい日本語テキスト

    Returns:
    str: ふりがなが付けられたテキスト
    """

    # Yahoo!ふりがなAPIのエンドポイントURL
    url = "https://jlp.yahooapis.jp/FuriganaService/V2/furigana"

    # リクエストヘッダーの設定
    headers = {
        "Content-Type": "application/json",
        "User-Agent": f"Yahoo AppID: {yahoo_client_id}",
    }

    # リクエストボディの設定
    data = {
        "id": "1",
        "jsonrpc": "2.0",
        "method": "jlp.furiganaservice.furigana",
        "params": {"q": text, "grade": 6},  # 小学6年生レベルのふりがなを指定
    }

    # APIにPOSTリクエストを送信
    response = requests.post(url, headers=headers, json=data)
    result = response.json()

    # レスポンスからふりがな付きテキストを生成
    text = ""
    for word in result["result"]["word"]:
        if "furigana" in word:
            text += word["furigana"]  # ふりがながある場合はそれを使用
        elif "surface" in word:
            text += word["surface"]  # ふりがながない場合は元の表記を使用

    return text

これらのファイルを適切に配置し、必要なライブラリをインストールした上でapp.pyを実行することで、音声対話チャットボットを起動できます。APIキーは.envファイルに正しく設定してください。

このコードは基本的な機能を実装していますが、実際の使用にあたっては、エラーハンドリングやセキュリティ面での改善が必要かもしれません。また、各APIの利用規約を確認し、適切に使用してください。

まとめ

このプロジェクトでは、複数のAPIと技術を組み合わせることで、高度な機能を持つ音声対話チャットボットを作成しました。音声認識、自然言語処理、音声合成を組み合わせることで、より自然でインタラクティブなユーザー体験を提供できます。

今後の改善点としては、ノイズ除去の強化、多言語対応、さらにはカスタムAIモデルの導入などが考えられます。

皆さんも是非、このプロジェクトを参考に、独自の音声対話システムを作ってみてください!

ご質問やフィードバックがありましたら、コメント欄でお待ちしています。Happy coding!