概要
この文書では、任意のメタバース(デモとして、VKet Cloudを使用)で、AIエージェントをつかって自分のアバターを音声で会話を行うAIアバター化する仕組みをつくりましたことの説明を行います。
一般的なAIアバターの仕組みは、生成AI部分がChatGPTですが、この仕組みではAIエージェントにつなげることで、ChatGPTよりも専門性の高い回答や、AIエージェントをローカル(localhost)やプライベートクラウドで運用することで、機密性の高い情報を扱い、音声会話ログを自分の手元に残すことができます。
IBM Granite 4.0 を使った取り組みの公開記事としては世界最速かもしれない
仕組みイメージ
デモ動画
他のプレイヤーの音声をテキストに変換し、テキストをもとに、AIエージェントがテキストを生成し音声で発話して返している様子です。どんな処理をしているかわかりやすいように、右上に今回の仕組みが動く様子を重ねています。
メタバースをつかった理由
いつくかあります。
- 人間が操作する場合と、AIに対応させる場合、共存できる
- YouTubeのような動画と異なり、コラボしやすい。突発的な飛び入りコラボもできる
- 表現の幅が広い
- 背景の絵を描くよりも、3Dの方が楽だから(この感覚は賛否両論かも)
使っているクラウドサービス・ソフトウェア
この記事で紹介するプログラム
- Python 3.12
- Pythonライブラリ
- ibm-watson
- pyaudio
- SpeechRecognition
- python-dotenv
- requests
音声認識
音声をテキストに変換しています。
音声合成
テキストを音声に変換し読み上げています。
サウンドデバイス
AIエージェント
音声認識で変換したテキストに対するレスポンスを生成します。
-
Dify Community版
- Difyは、自分のパソコンやプライベートクラウドで運用しているものがおすすめです。
- プライベートクラウド運用に興味がある方は、「中規模プライベートクラウドまで拡張できるMicroCloudを、IBM Cloud Virtual Server for VPCに入れてみたをご覧ください。」
- Difyは、自分のパソコンやプライベートクラウドで運用しているものがおすすめです。
LLM/SLM
AIエージェントに使用するLLM/SLMは、主に下記を使っています。OpenAIのGPT5などでも動くことは確認済みです。
-
IBM Granite 4.0
- 2025年10月2日に発表されたばかり
- IBM Granite 3.3
AIエージェントとLLM/SLMの接続には、Ollamaを使っています。LM Studioでも構いません。
その他Difyで対応している、OpenAI API GPT5 や Google Geminiなども使うことはできます。
メタバースサービス
動作確認済みは下記です。今回の仕組みはとてもシンプルなので、他のメタバースサービスでも動きます。
- Vket Cloud
- Spatial
- Roomiq(以前のNTT DOOR)
- Cluster
準備
Pythonのインストール
お使いのパソコンで、Pythonを使えるようにします。Pythonの古いバージョンは使わないようにしましょう。サポートが提供されいてるバージョンを使うようにしてください。
サウンドデバイス
Virtual Audio Cableをお使いのパソコンにインストールします。インストール後、下記の2つがサウンドデバイスとして認識されていれば良いです。
- 出力(スピーカー):CABLE Input
- 入力(マイク):CABLE Output
IBM Cloud
IBM Cloudのカタログ画面にアクセスします。
IBM Watson Speech To TextとIBM Watson Text To Speechの2つのサービスを無料のLiteプランで有効化します。
それぞれのサービスについて、リソースリストに表示されたサービス名をクリックし、「Service credentials」画面でAPIキーと接続先のURLを取得します。
LLM/SLM
お使いのパソコン、またはクラウド(パブリッククラウドまたはプライベートクラウド)上のサーバーに、OllamaとLLM/SLMを導入します。
Ollamaのインストール後、Ollamaを起動し、LLM/SLMを、ollamaを使って取得してください。
AIエージェント
モデル
例としてDifyを使います。お使いのパソコン、またはクラウド(パブリッククラウドまたはプライベートクラウド)上のサーバーに、Dify Community版を導入します。
- OllamaのモデルをDifyにシステムモデルとして設定します。
DifyをDockerを使って制御している場合は、モデルの追加時に、Base URLを、http://host.docker.internal:11434/ を入力する必要があります。
Difyにアプリを用意
単純なもので良いので、Dify内に、追加したモデルを使ったアプリを用意します。
単純なチャットボットをつくるには、「最初から作成」をクリックし、下記の組み合わせで作ると簡単です。
アプリタイプを選択 | 初心者向けの基本的なアプリタイ |
---|---|
チャットフロー | チャットボット |
APIキーの取得
アプリのオーケストレート画面で、「APIアクセス」をクリックし、画面右上にある「APIキー」のボタンをクリックします。APIキーを発行します。
APP IDの取得
画面左側の「オーケストレート」をクリックし、画面右上の「公開する」-->「更新を公開」-->「アプリを実行」の順にクリックします。
表示されたアプリのURLのうち、/chat/以降の文字列が、APP IDになります。
BASE URLの取得
表示されたアプリのURLのうち、/chat/の前のFQDNが、BASE URLになります。たとえば http://localhost や https://agent.example.com などになります。
Pythonを使って、メタバース内のアバターをAIアバター化する仕組みを作る
すべての準備を終え、Pythonプログラムを用意するために、Pythonプログラムで、ここまで準備した各値を読み込むための環境変数ファイルを作成し、それからメインのプログラムをつくります。
作業用ディレクトリ(フォルダ)の作成
次のコマンドを実行します。
mkdir testapp-voiceapp
cd testapp-voiceapp
Pythonの仮想環境を用意する。
次のコマンドを実行します。
python -m venv myvenv
Pythonの仮想環境を起動
Windows環境では、次のコマンドを実行します。
.\myvenv\Scripts\Activate.ps1
Linux環境では、次のコマンドを実行します。
source myvenv/bin/activate
ライブラリのインストール
pipのアップグレード
ライブラリをインストールするツールである pip を最新版にアップグレードします。
Windows環境では、次のコマンドを実行します。
pip install --upgrade pip
python.exe -m pip install --upgrade pip
Linux環境では、次のコマンドを実行します。
pip install --upgrade pip
ライブラリのインストール
次のコマンドを実行します。
pip install ibm-watson pyaudio SpeechRecognition python-dotenv requests
環境変数ファイル .envの用意
ここまでに用意した各値をセットしています。
# --- Dify 設定 ---
## DifyのグローバルAPIキー
DIFY_API_KEY="Difyのアプリで発行したAPIキーの値"
## 作成したChat AppのApp ID
DIFY_APP_ID="DifyのアプリURLから確認したAPP ID"
## DifyのベースURL
DIFY_BASE_URL="DifyのアプリURLから確認したBASE URL"
## セッションを識別するためのユーザーID(任意のユニークな文字列)
DIFY_USER_ID="英数字で任意のもの"
# --- IBM Watson Speech to Text (STT) ---
## STTサービスインスタンスの資格情報を設定
WATSON_STT_API_KEY="IBM Cloudの画面で確認したWatson Speech To TextのAPIキー"
WATSON_STT_URL="IBM Cloudの画面で確認したWatson Speech To TextのAPIに接続するためのURL"
## 言語モデルの指定
WATSON_STT_MODEL="ja-JP_BroadbandModel"
# --- IBM Watson Text to Speech (TTS) ---
## TTSサービスインスタンスの資格情報を設定
WATSON_TTS_API_KEY="IBM Cloudの画面で確認したWatson Text To SpeechのAPIキー"
WATSON_TTS_URL="IBM Cloudの画面で確認したWatson Text To SpeechのAPIに接続するためのURL"
## 音声モデルの指定
WATSON_TTS_VOICE="ja-JP_EmiV3Voice"
# --- 会話制御設定 ---
# 会話をスタートさせるためのキーワードを設定します (例: コグ、AI、ユイ など)
WAKE_WORD="AI "
WAKE_WORDは、音声で会話する際に、AIアバターに回答して欲しい内容であるかどうか判別するためのものです。
スマートスピーカーで、「エーアイ!明日の天気について調べて」と話すように、エーアイ!の部分が、WAKE_WORDにあたります。
Pythonプログラム「conversation_agent.py」の作成
作成にあたっては、Google Gemini 2.5 Flashをサポートに使っています。また各APIのサンプルコードなどを参考にしています。一応、動くという代物なので、もっと洗練することができるかもしれませんが、今後の課題としておきます。
import os
import time
import io
import wave
import sys
import requests
# ライブラリのインポート
from dotenv import load_dotenv
import pyaudio
import speech_recognition as sr
from ibm_watson import SpeechToTextV1, TextToSpeechV1
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator
# .envファイルから環境変数をロード
load_dotenv()
# --- 設定と初期化 ---
# 1. 環境変数のチェックと設定
try:
# Dify 設定
DIFY_API_KEY = os.getenv("DIFY_API_KEY")
DIFY_APP_ID = os.getenv("DIFY_APP_ID")
DIFY_BASE_URL = os.getenv("DIFY_BASE_URL")
DIFY_USER_ID = os.getenv("DIFY_USER_ID")
# Watson 設定
WATSON_STT_API_KEY = os.getenv("WATSON_STT_API_KEY")
WATSON_STT_URL = os.getenv("WATSON_STT_URL")
WATSON_TTS_API_KEY = os.getenv("WATSON_TTS_API_KEY")
WATSON_TTS_URL = os.getenv("WATSON_TTS_URL")
# モデル設定を環境変数から読み込み
WATSON_TTS_VOICE = os.getenv("WATSON_TTS_VOICE")
WATSON_STT_MODEL = os.getenv("WATSON_STT_MODEL")
# 会話制御設定
WAKE_WORD = os.getenv("WAKE_WORD")
# 必須API認証情報とモデル設定のチェック
if not all([DIFY_API_KEY, DIFY_APP_ID, DIFY_BASE_URL,
WATSON_STT_API_KEY, WATSON_STT_URL, WATSON_TTS_API_KEY, WATSON_TTS_URL,
WATSON_TTS_VOICE, WATSON_STT_MODEL]): # モデル変数をチェック対象に追加
raise ValueError("必須の設定が不足しています。")
except ValueError as e:
print(f"致命的エラー: {e}")
print("プログラムを終了します。")
sys.exit(1)
# 2. 録音した音声を一時保存
TEMP_AUDIO_FILE = "user_input.wav"
# マイク録音のためのRecognizerとPyAudio
r = sr.Recognizer()
p = pyaudio.PyAudio()
# 3. サービスの初期化
try:
# IBM Watson Speech to Text (STT) の初期化
stt_authenticator = IAMAuthenticator(WATSON_STT_API_KEY)
stt_service = SpeechToTextV1(authenticator=stt_authenticator)
stt_service.set_service_url(WATSON_STT_URL)
# IBM Watson Text to Speech (TTS) の初期化
tts_authenticator = IAMAuthenticator(WATSON_TTS_API_KEY)
tts_service = TextToSpeechV1(authenticator=tts_authenticator)
tts_service.set_service_url(WATSON_TTS_URL)
except Exception as e:
print(f"致命的エラー: IBM Watson サービスの初期化に失敗しました。詳細: {e}")
p.terminate()
sys.exit(1)
# --- 関数定義 ---
def text_to_speech(text):
"""Watson TTS を使用してテキストを音声として読み上げる (デフォルトスピーカーを使用)"""
print(f"AI応答: {text}")
try:
response = tts_service.synthesize(
text,
voice=WATSON_TTS_VOICE,
accept='audio/wav'
).get_result()
audio_stream = io.BytesIO(response.content)
with wave.open(audio_stream, 'rb') as wf:
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
data = wf.readframes(1024)
while data:
stream.write(data)
data = wf.readframes(1024)
stream.stop_stream()
stream.close()
except Exception as e:
print(f"警告: TTS/音声再生に失敗しました。詳細: {e}")
def recognize_speech_from_mic():
# ... (変更なし) ...
"""マイクから音声を録音し、WAVEファイルとして保存する (デフォルトマイクを使用)"""
with sr.Microphone() as source:
r.adjust_for_ambient_noise(source)
print("\nどうぞ、話しかけてください (終了する場合は「さようなら」)。")
try:
audio = r.listen(source, timeout=5, phrase_time_limit=10)
except sr.WaitTimeoutError:
print("タイムアウト: 音声入力がありませんでした。")
return None
try:
with open(TEMP_AUDIO_FILE, "wb") as f:
f.write(audio.get_wav_data())
return TEMP_AUDIO_FILE
except Exception as e:
print(f"警告: 録音ファイル作成に失敗しました。詳細: {e}")
return None
def watson_speech_to_text(audio_file_path):
"""Watson STT を使用して音声ファイルからテキストに変換する"""
if not audio_file_path or not os.path.exists(audio_file_path):
return None
try:
with open(audio_file_path, 'rb') as audio_file:
response = stt_service.recognize(
audio_file,
content_type='audio/wav',
model=WATSON_STT_MODEL # <--- 環境変数を使用
).get_result()
if response.get('results'):
user_input = response['results'][0]['alternatives'][0]['transcript']
print(f"ユーザー入力: {user_input}")
return user_input
else:
print("Watson STT: 音声が聞き取れませんでした。")
return None
except Exception as e:
print(f"警告: Watson STT通信または認証に失敗しました。詳細: {e}")
return None
finally:
if os.path.exists(TEMP_AUDIO_FILE):
os.remove(TEMP_AUDIO_FILE)
def get_dify_response(prompt):
"""Dify API (Chat App)にリクエストを送信し、応答を取得する"""
chat_url = f"{DIFY_BASE_URL.rstrip('/')}/v1/chat-messages"
headers = {
"Authorization": f"Bearer {DIFY_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"inputs": {},
"query": prompt,
"response_mode": "blocking",
"user": DIFY_USER_ID,
"conversation_id": ""
}
try:
response = requests.post(chat_url, headers=headers, json=payload, timeout=60)
response.raise_for_status()
data = response.json()
if data.get('answer'): return data['answer'].strip()
return "Difyからの応答が空でした。Appの設定を確認してください。"
except requests.exceptions.RequestException as e:
print(f"警告: Dify API通信に失敗しました。詳細: {e}")
return "ごめんなさい、Difyサービスに接続できません。URL、APIキー、App ID、またはDifyサービスの状態を確認してください。"
except Exception as e:
print(f"警告: Dify応答処理に失敗しました。詳細: {e}")
return "Dify応答を処理中にエラーが発生しました。"
# --- メイン処理 ---
def main():
print("📢 デバイス設定: 既定の録音/再生デバイスを使用します。")
text_to_speech("会話プログラムを開始しました。起動キーワードで話しかけてください。")
while True:
audio_path = recognize_speech_from_mic()
if audio_path is None:
time.sleep(0.5)
continue
user_text = watson_speech_to_text(audio_path)
if user_text is None:
time.sleep(0.5)
continue
# 起動キーワードのチェック
processed_text = user_text.lower().strip()
wake_word_lower = WAKE_WORD.lower().strip()
if processed_text.startswith(wake_word_lower):
prompt = processed_text[len(wake_word_lower):].strip()
if "さようなら" in processed_text or "おわり" in processed_text:
text_to_speech("さようなら。またお話ししましょう!")
break
if not prompt:
ai_response_text = "何かご用でしょうか?"
else:
ai_response_text = get_dify_response(prompt)
text_to_speech(ai_response_text)
else:
print(f"待機中: キーワード'{WAKE_WORD}'が検出されませんでした。")
time.sleep(0.5)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nプログラムを中断しました。")
finally:
if 'p' in locals():
p.terminate()
動作確認
パソコン上での準備
デフォルトのサウンドデバイスに、次の2つを指定します。
- 出力(スピーカー):CABLE Input
- 入力(マイク):CABLE Output
メタバース(仮想空間)の用意
例として、Vket Cloudを使っています。
メタバースサービスを使って、任意の空間を用意します。下記は、勤務先の大学で授業やイベント向けに構築した空間です。
Pythonプログラムの起動
「conversation_agent.py」を起動します。
python conversation_agent.py
実行結果
📢 デバイス設定: 既定の録音/再生デバイスを使用します。
AI応答: 会話プログラムを開始しました。起動キーワードで話しかけてください。
どうぞ、話しかけてください (終了する場合は「さようなら」)。
と表示されます。
別のアカウントで、用意したメタバース(仮想空間)に接続
例として、Vket Cloudを使っています。
別のデバイス(スマートフォンやタブレット端末など)から、用意したメタバースに接続します。
接続後、マイクをオンにし、WAKE_WORDをつけて、話しかけてみましょう。WAKE_WORDが無事に認識されれば、デモ動画のように返答を音声で返してくれます。
参考資料
- Difyドキュメント
- Ollamaのドキュメント
- LM Studioのドキュメント
- IBM Watson Speech To Text:IBM Cloud資料
- IBM Watson Text To Speech:IBM Cloud資料
- IBM Granite 資料
- IBM Granite 4.0のモデルをファインチューニングする場合は、Unslothのドキュメントを参照のこと