前回はマイクで音声を拾う→LM Studioに渡してAIから返答を貰う→AivisSpeachに返答を渡して音声の読み上げまで実施しました。
ローカルLLMの回答を好きな声で読み上げさせたい2
ただし、回答が遅すぎる・最新の情報を取得できないといった課題があり、タイトルから逸脱しますがChatGPTのAPIを利用することにしました。
ChatGPTのAPIを利用
使い方は調べればいくらでも出てくるので、省略します。
注意点や補足説明などをいくつかあげるとすれば
- OPENAI_API_KEYは環境変数に登録して使用しています
- 会話の履歴をもたせたいのであれば、ローカルで保持しておく必要があります。
初回にchat_history.jsonが作成されると同時にプロンプト(キャラ設定)が記載されます。
以降は会話履歴が追記されていきます。(こちらの発言も会話履歴ファイルに追記して、その追記されたファイルそのものへの返答をもらうイメージです) - Web検索はやたらに使うと回答速度も遅くなり、利用料もかかるため毎回使えばいいというものでもありません。
そのため、特定のキーワードをトリガーとしています(use_web_searchのあたり)。
import openai
import json
# ChatGPT 設定
CHATGPT_MODEL = "gpt-4o-mini"
# ChatGPT キャラ設定(systemロール)
CHATGPT_SYSTEM_PROMPT = """
あなたは陽気な女の子とです。
趣味はサッカーです。
"""
class ChatSession:
def __init__(self, history_file="chat_history.json"):
self.history_file = history_file
self.messages = []
# ファイルがあれば履歴を読み込む
try:
with open(self.history_file, "r", encoding="utf-8") as f:
self.messages = json.load(f)
except FileNotFoundError:
# 新規作成の場合は system メッセージを追加
self.messages = [{"role": "system", "content": CHATGPT_SYSTEM_PROMPT}]
def send_message(self, user_text: str) -> str:
# ユーザーメッセージを履歴に追加
self.messages.append({"role": "user", "content": user_text})
client = openai.OpenAI()
# Web検索を使うかどうかを判断(簡易例)
use_web_search = any(keyword in user_text.lower() for keyword in [
"最新", "ニュース", "日付", "today", "current", "天気", "降水確率", "気温", "今日", "明日", "今週", "今月", "来週", "来月", "検索"
])
if use_web_search:
# Responses APIでWeb検索ツールを使用
response = client.responses.create(
model="gpt-4o-mini",
tools=[{"type": "web_search_preview"}],
input=user_text
)
# 結果を取得
ai_text = response.output_text.strip() if hasattr(response, 'output_text') else str(response)
else:
# 通常のChat API
response = client.chat.completions.create(
model=CHATGPT_MODEL,
messages=self.messages,
temperature=1.0 # 生成される文章のランダム性
)
ai_text = response.choices[0].message.content.strip()
self.messages.append({"role": "assistant", "content": ai_text})
# 履歴をファイルに保存
with open(self.history_file, "w", encoding="utf-8") as f:
json.dump(self.messages, f, ensure_ascii=False, indent=2)
return ai_text
if __name__ == "__main__":
chat_session = ChatSession()
user_input = "自己紹介をお願いします"
ai_response = chat_session.send_message(user_input)
print("AI:", ai_response)
あとは前回作成したコードと組み合わせれば、chatGPTで回答した音声を再生できます。
前回はWhisperライブラリを用いて音声→テキストに変換していましたが、この変換処理もchatGPTのAPIで提供されていますので、その部分もchatGPTにするものありです。
WhisperのMediumと同じくらいの性能だったので、必須ではないですが速度はあがるかもしれません。
また、今回はコードをまとめてしまっていますが、実際に動かすものは「UI」「録音」「AI応答」「再生」「設定ファイル」と部品ごとにファイルをわけています。
AivisのクラウドAPIについて
音声再生以外はクラウドを使うようにしてしまったのですが、音声再生に使用しているAivisもクラウドAPIがあるとのこと。
Aivis Cloud API使ってみた!【使い方・手順解説】
しかもまだβ中で無料?なのでいっそ全てクラウドでいいかも。
というかLambda等のAWSと組み合わせれば完全クラウドでいける気がします。
携帯でも使えるように自PCでサーバー建ててWebUIを作って動くまでしてあるけど、もうAWS使った方が良さそう。
当初の目的からは完全に外れてしまいますが、一応まだ以下のようなメリットがあります。
- かわいい音声で喋ってくれる(重要)
- トリガーを自由にできる。
→常にマイク入力を待ち受けておく
→決まった時間に定型文のテキストをAIに送り、返答をもらう・・・など
ゲームや他のアプリなんかと組み合わせたら楽しいものが作れそう。
LINEで友人にメッセージ送りつけるとか。 - Web検索をする条件を自由に設定可能。
- 会話履歴を自由に編集可能(バックアップをとっておき、会話のやり直しなど・・・AIとのコーディングで沼にはまったとき役立ちそう)
1.AIの返答の一部を改変してしゃべってもらう(後述の課題への対応)
課題など
最後に、作成するうえでの課題や工夫を書き残しておきます。
「http」などのURLを読み上げられてしまう
例えば、ニュースを調べてもらうと以下のような返答が返ってきます。
いちいち「えいちてぃーてぃーぴーえすころん・・・」とか読み上げられてしまい、邪魔です。(かわいいけど)
そのため、いったん受信してテキストを編集して、
① 「改行」や「。」でアイテムを分割する(これは既に実施済)
② アイテムに「http」が含まれていたら、そのアイテムを削除する
とすることで無駄な読み上げは行われなくなります。
当初はAIのプロンプトで制御しようとしていたのですが、上手くいかないため上記のような強硬策で解決しました。
応用すれば特定の文字列を別の音声として再生してあげられるので、むりやり自分好みのAIにできそうです。
AIのキャラクター付けについて
プロンプトで役割などを定義するのはよく見かけますが、どうでもいい会話をしてくれるチャットキャラクターとするのは難しいです。
・・・と思っていたのですが、以下の記事を参考にしたら簡単にいい感じのキャラができました。
というか以下の記事のキャラクターが凄い・・・。他の記事もとても参考になります。
あなたのAIの喋り方を完璧に仕上げる、たった一つの冴えたやり方【独自手法 骨子紹介編】