はじめに
以前、Raspberry Pi にしゃべらせた者です。今回は本丸。Raspberry PiでAIによる応答が出来る様にしたいと思います。
使用する機材
使用する機材は、以前の記事にある環境そのまま使用します。
前回と違う箇所
前回は、「ただいま」に対して「おかえりなさい」と定型の返事をするだけでしたが、今回は、「ただいま」に対する返答内容をAIに考えてもらう、思いっきり他力本願柔軟な会話システムにします。ついでに、語尾は以前、明言させてもらった「にゃっ」をつけるにゃっ!!
AIを使うためにOpenAI API Key取得
AIをRaspberry Piで使用するためにOpenAIのAPI Keyを取得しておきます。
1. OpenAI にアクセスしてログイン
まずOpenAIのページに行きます。https://platform.openai.com/api-keys
- ChatGPTアカウント(またはOpenAIアカウント)でログイン
- アカウントを持っていない場合:Sign up(アカウント作成)
2.「Create new secret key」をクリック
ログイン後 → API keys の画面が表示されるはずです。ここでCreate new secret key(新規API Key作成) をクリック
3.Keyをコピーして保存
生成されたKeyが表示されるのでメモしておきます。
重要:この画面でしか見られないのでKeyをコピーして 安全な場所に保存しておきましょう。
4.(必要なら)支払い情報を設定
APIの利用には 支払い情報(Billing)の登録 が必要になる場合があり、クレジットカード情報を登録しておくと、従量課金で使えるようになります。
5.Raspberry Piに登録
Raspberry Piで常に使用できる様、.bashrcに登録しておきます。
~/.bashrcを編集
nano ~/.bashrc- ファイルの一番下に以下を追加
export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxx"※ sk-xxxxxxxxxxxxxxxx の部分を、取得したAPI Keyに置き換えてください。
- 保存して反映
source ~/.bashrc- 設定されているか確認
echo $OPENAI_API_KEYAPI Keyが表示されれば設定完了です。
6.OpenAIインストール
今回コードでOpenAIを使用するにあたりインストールしておきます。
pip install openaiコード
今回は、前回に作成したコードを変更する形で進めるため、前回のコードと今回作成したコードの違う箇所を抜粋してみます。
1. AIのため、OpenAI 追加
# 新規追加:OpenAI クライアント初期化
self.openai_client = OpenAI()環境変数のチェックも追加
if not os.environ.get('OPENAI_API_KEY'):
print("OPENAI_API_KEY 環境変数が設定されていません")
raise Exception("OpenAI API認証情報が見つかりません")2. 会話コンテキストの管理
AIとの会話履歴を保持することで、文脈を理解した応答が可能になります。以下ポイントです。
- システムプロンプトで「にゃっ」語尾を指定(親しみやすさ重視)
- 絵文字禁止ルール(音声合成で読み上げられてしまうため)
- 応答は1〜2文に制限(長すぎると聞き取りづらい)
self.conversation_history = [
{
"role": "system",
"content": """あなたは家庭用のスマートスピーカーのアシスタントです。
以下のルールに従って応答してください:
- 親しみやすく、温かみのある口調で話す
- 応答は短く簡潔に(1〜2文程度)
- 「ただいま」には「お帰りなさい」系の返事をする
- 「お帰りなさい」系の返事には質問をする
- 日本語で応答する
- 絵文字は使わない(音声読み上げされるため)
- 語尾には必ず「にゃっ」をつける(例:「お帰りなさいにゃっ」「わかったにゃっ」)"""
}
]3. AI応答生成機能
新しく追加されたget_chatgpt_response()メソッドで、ユーザー入力に対する応答を生成します。以下ポイントです。
- モデル選択:
gpt-4o-miniを使用してコスト削減 - 会話履歴の管理: 20メッセージを超えたら古いものを削除(システムプロンプトは維持)
- temperature=0.7: 適度にクリエイティブだが安定した応答
def get_chatgpt_response(self, user_input):
"""AIから応答を取得"""
# ユーザー入力を会話履歴に追加
self.conversation_history.append({
"role": "user",
"content": user_input
})
# AIのAPI呼び出し
response = self.openai_client.chat.completions.create(
model="gpt-4o-mini", # コスト効率の良いモデル
messages=self.conversation_history,
max_tokens=150,
temperature=0.7
)
assistant_message = response.choices[0].message.content
# 応答を会話履歴に追加
self.conversation_history.append({
"role": "assistant",
"content": assistant_message
})
# 会話履歴が長くなりすぎたら古いものを削除
if len(self.conversation_history) > 20:
self.conversation_history = [self.conversation_history[0]] + self.conversation_history[-10:]
return assistant_message主な修正は以上になります。ということで、今回作成したコードの全容はこちら!!
コード
import time
import sys
import os
import tempfile
import pyaudio
from google.cloud import speech
from google.cloud import texttospeech
import pygame
from openai import OpenAI
class ContinuousVoiceGreeting:
def __init__(self):
# Google Cloud認証確認
if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS'):
print("GOOGLE_APPLICATION_CREDENTIALS 環境変数が設定されていません")
raise Exception("Google Cloud認証情報が見つかりません")
# OpenAI認証確認
if not os.environ.get('OPENAI_API_KEY'):
print("OPENAI_API_KEY 環境変数が設定されていません")
raise Exception("OpenAI API認証情報が見つかりません")
# OpenAI クライアント初期化
self.openai_client = OpenAI()
# Google Cloud クライアント初期化
self.speech_client = speech.SpeechClient()
self.tts_client = texttospeech.TextToSpeechClient()
# AI会話履歴(コンテキスト維持用)
self.conversation_history = [
{
"role": "system",
"content": """あなたは家庭用のスマートスピーカーのアシスタントです。
以下のルールに従って応答してください:
- 親しみやすく、温かみのある口調で話す
- 応答は短く簡潔に(1〜2文程度)
- 「ただいま」には「お帰りなさい」系の返事をする
- 「お帰りなさい」系の返事には質問をする
- 日本語で応答する
- 絵文字は使わない(音声読み上げされるため)
- 語尾には必ず「にゃっ」をつける(例:「お帰りなさいにゃっ」「わかったにゃっ」)"""
}
]
# Raspberry Pi最適化設定
self.speech_config = speech.RecognitionConfig(
encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16,
sample_rate_hertz=16000,
language_code="ja-JP",
model="latest_short", # 短時間モデルが安定
use_enhanced=True,
enable_automatic_punctuation=True,
)
# 自然な日本語音声設定
self.tts_voice = texttospeech.VoiceSelectionParams(
language_code="ja-JP",
ssml_gender=texttospeech.SsmlVoiceGender.FEMALE,
name="ja-JP-Neural2-B"
)
self.tts_config = texttospeech.AudioConfig(
audio_encoding=texttospeech.AudioEncoding.MP3,
speaking_rate=0.9, # 少し遅めが聞き取りやすい
pitch=0.0
)
# PyAudio設定(Raspberry Pi最適化)
self.audio_format = pyaudio.paInt16
self.channels = 1
self.rate = 16000
self.chunk = 1024
self.audio = pyaudio.PyAudio()
# システム状態管理
self.is_running = True
self.is_speaking = False
# pygame初期化(3.5mmジャック用最適化)
os.environ['SDL_AUDIODRIVER'] = 'alsa' # ALSA強制使用
pygame.mixer.pre_init(frequency=22050, size=-16, channels=1, buffer=512)
pygame.mixer.init()
print("Raspberry Pi + AI 初期化完了!")
def get_chatgpt_response(self, user_input):
"""AIから応答を取得"""
try:
# ユーザー入力を会話履歴に追加
self.conversation_history.append({
"role": "user",
"content": user_input
})
# AIのAPI呼び出し
response = self.openai_client.chat.completions.create(
model="gpt-4o-mini", # コスト効率の良いモデル
messages=self.conversation_history,
max_tokens=150,
temperature=0.7
)
# 応答を取得
assistant_message = response.choices[0].message.content
# 応答を会話履歴に追加
self.conversation_history.append({
"role": "assistant",
"content": assistant_message
})
# 会話履歴が長くなりすぎたら古いものを削除(システムプロンプトは維持)
if len(self.conversation_history) > 20:
self.conversation_history = [self.conversation_history[0]] + self.conversation_history[-10:]
print(f"ChatGPT応答: 「{assistant_message}」")
return assistant_message
except Exception as e:
print(f"ChatGPTエラー: {e}")
return "すみません、うまく応答できませんでしたにゃっ"
def listen_for_voice(self):
"""音声入力を待機・取得する"""
try:
print("音声入力待機中... (5秒間録音)")
stream = self.audio.open(
format=self.audio_format,
channels=self.channels,
rate=self.rate,
input=True,
frames_per_buffer=self.chunk,
input_device_index=None
)
frames = []
for _ in range(0, int(self.rate / self.chunk * 5)):
try:
data = stream.read(self.chunk, exception_on_overflow=False)
frames.append(data)
except OSError:
print("音声入力でオーバーフローが発生しました")
continue
stream.stop_stream()
stream.close()
audio_data = b''.join(frames)
print("Google Cloudで音声認識中...")
# Google Cloud Speech-to-Text
audio = speech.RecognitionAudio(content=audio_data)
response = self.speech_client.recognize(
config=self.speech_config,
audio=audio
)
if response.results:
text = response.results[0].alternatives[0].transcript
confidence = response.results[0].alternatives[0].confidence
print(f"認識結果: 「{text}」(信頼度: {confidence:.2f})")
return text
else:
print("音声を認識できませんでした")
return None
except Exception as e:
print(f"音声認識エラー: {e}")
return None
def speak_response(self, message):
"""音声出力(3.5mmジャック)"""
if self.is_speaking:
return
self.is_speaking = True
print(f"3.5mmジャックから音声出力: 「{message}」")
try:
# Google Cloud Text-to-Speech
synthesis_input = texttospeech.SynthesisInput(text=message)
response = self.tts_client.synthesize_speech(
input=synthesis_input,
voice=self.tts_voice,
audio_config=self.tts_config
)
# 3.5mmジャック用音声再生
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as tmp_file:
tmp_file.write(response.audio_content)
tmp_file_path = tmp_file.name
# pygame音声再生(3.5mmジャック向け最適化)
try:
pygame.mixer.music.load(tmp_file_path)
pygame.mixer.music.set_volume(0.8) # 音量調整
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
time.sleep(0.1)
except pygame.error as e:
print(f"pygame音声エラー: {e}")
os.unlink(tmp_file_path)
print("音声出力完了")
except Exception as e:
print(f"音声出力エラー: {e}")
finally:
self.is_speaking = False
def check_exit_command(self, text):
"""終了コマンドチェック"""
if text is None:
return False
exit_patterns = ["終わり", "やめて", "ストップ", "またね", "exit", "quit", "シャットダウン"]
text_lower = text.lower()
for pattern in exit_patterns:
if pattern.lower() in text_lower:
return True
return False
def run(self):
"""メインループ"""
try:
while self.is_running:
recognized_text = self.listen_for_voice()
if recognized_text:
if self.check_exit_command(recognized_text):
# 終了時もChatGPTに別れの挨拶を生成させる
farewell = self.get_chatgpt_response("終了します。さようなら。")
self.speak_response(farewell)
print("終了コマンドを検出")
break
# ChatGPTで応答を生成
response = self.get_chatgpt_response(recognized_text)
self.speak_response(response)
time.sleep(0.5)
print("\n" + "-"*40)
except KeyboardInterrupt:
print("\n Ctrl+Cで終了")
except Exception as e:
print(f"\n システムエラー: {e}")
finally:
self.cleanup()
def cleanup(self):
"""終了処理"""
self.is_running = False
if self.is_speaking:
print("音声出力完了を待機中...")
time.sleep(2)
self.audio.terminate()
pygame.mixer.quit()
def main():
print("=" * 50)
print("ChatGPT音声アシスタント(にゃっ語尾付き)")
print("=" * 50)
print("必要な環境変数:")
print(" - GOOGLE_APPLICATION_CREDENTIALS")
print(" - OPENAI_API_KEY")
print("=" * 50)
try:
greeting_system = ContinuousVoiceGreeting()
greeting_system.run()
except Exception as e:
print(f"Raspberry Pi初期化エラー: {e}")
print("セットアップガイドを確認してください")
sys.exit(1)
if __name__ == "__main__":
main()話しかけてみよう
音が出ます。
すごい。めっちゃ会話してくれる!!!オレニトモダチデキターーーーー!!!!
ただ、やはりレスポンスに少し間が空くのは気になりますが、用途として帰宅後に上着を脱ぎ靴を脱ぐ間の会話を目的としているので、以下の点に工夫を入れることで解消出来そうな気がします。
- 応答のラリー回数を減らす
- 会話開始をRaspberry Piが行うことで、会話ラリーの待ちが少なくなる
返答の音量は、設定等で制御できますが、接続するスピーカーのつまみで調整するのが手軽かな。(横着)
最後に
今までRaspberry Piを使用して、以下の機能を試してきました。
- 人が近づいたことを検知する人検知機能
- 顔を判断する顔認証機能
- 声を認識し返答を返す音声入出力機能
- 返答を柔軟にするためのAI機能(今回の実装)
これをまるっとまとめると、以前のRaspberry Piに顔を覚えてもらうという記事にある、やりたいことに挙げた以下の項目が全て実現できる算段になったと思います。
- 帰宅したことに反応する → 人検知機能
- 誰が帰宅したか判別する → 顔認識機能
- 2の結果、自分なら「おかえりなさい」他の人なら「どちらさまですか?」と話しかける
奥さんの場合、処理終了(うるさいって言われるから) → 音声入出力機能 - 応答に合わせ、粋な返しをする → AIによる会話機能
- 2回くらいの会話ラリーの後、「ゆっくりお休みください」と言って処理終了 → AIによる会話機能
これが全て実現できるわけです。次回はその集大成となるお帰りなさいマシーン完成を目指して突き進んでいきます!!!
きっと帰宅が楽しみになる様な装置が出来るはずにゃっ!!!








