前回は声・頭脳・身体の連携に成功しました。が、
プログラムの実行後に一度会話して リアクションしたら、そこでプログラムが終了してしまうので、一回しか会話ができません。ちゃんと会話が続くように、修正します!

LMに進捗を報告
つくる人うまくいきました! だけど、会話の度に都度
main.pyの実行を手動で繰り返しています。
AITuber 開発用LMの回答
うまくいって本当によかったです!AITuberとしての基本システムが完成しましたね。
「会話のたびに main.py を再起動している」という状況は、おそらくエラーが発生してプログラムが落ちているか、あるいはループ処理が途中で止まってしまう設計になっている可能性があります。
本来、AITuberは「一度起動したら、あなたが止めるまで永遠に待機し続ける(常駐する)」のが正しい動きです。
これを解決するために、「エラーが起きても無視して動き続ける(死なない)」 ように main.py を少し改造します。以下の「不死身のループ」版コードに書き換えてください。



不死身のループwww
言葉選びがいつも逸品だな。
修正のポイント
- 1. try-except をループの中に入れる
-
ネットワークエラーや一時的な不具合が起きても、エラーメッセージを表示して、プログラムを停止させずに次のループへ進むようにします。
- 2. 待機時間の調整
-
コメントがない時やエラー時は少し長めに休むことで、PCへの負担とAPIの無駄遣いを防ぎます
このコードに全置換してください
import asyncio
import random
import traceback # エラーの詳細を表示するために追加
from config.settings import settings
# 各種モジュールのインポート
from modules.cognition.llm_client import LLMClient
from modules.expression.vtube_studio import VTubeStudioClient
from modules.expression.voicevox import VoicevoxClient
from modules.perception.youtube import YouTubeCommentFetcher
# キャラクター設定
SYSTEM_PROMPT = """
あなたは「AITuber」のアイです。
元気で明るい、少しおっちょこちょいな性格です。
リスナーのコメントに対して、短くテンポよく返事をしてください。
"""
async def main():
print("🚀 AITuberシステムを起動します...")
print("✨ 'Ctrl + C' を押すまで、永遠に動き続けます!")
# 1. 各モジュールの初期化
brain = LLMClient(system_prompt=SYSTEM_PROMPT)
voice = VoicevoxClient()
body = VTubeStudioClient()
youtube = YouTubeCommentFetcher()
# 2. VTube Studioに接続
# 接続に失敗しても、何度でも再トライするループにする
while True:
try:
if await body.connect():
print("✅ VTube Studio接続成功!")
break
else:
print("❌ VTube Studioが見つかりません。5秒後に再試行します...")
await asyncio.sleep(5)
except Exception as e:
print(f"⚠️ VTS接続エラー: {e}")
await asyncio.sleep(5)
# 会話履歴
history = []
print(f"✅ 準備完了!YouTube配信(ID: {settings.YOUTUBE_VIDEO_ID})のコメントを監視中...")
# --- 無限ループ開始 ---
while True:
try:
# --- 1. YouTubeコメントの取得 ---
comments = youtube.fetch_comments()
# コメントが来ていない場合は待機して次へ
if not comments:
await asyncio.sleep(5) # 負荷軽減のため5秒待つ
continue
# 最新のコメントを取得
last_comment = comments[-1]
user_name = last_comment["user"]
user_text = last_comment["text"]
print(f"\n💬 {user_name}: {user_text}")
# "終了" コマンドでシステムを安全に停止
if user_text == "終了":
print("システムを終了します。")
break
# --- 2. 思考 (Brain) ---
print("🤖 (思考中...)")
prompt_text = f"リスナーの「{user_name}」さんが「{user_text}」と言っています。返事をしてあげて。"
# AI応答生成(ここでもエラーが起きても止まらないようにする)
try:
response_text = await brain.generate_response(prompt_text, history)
except Exception as e:
print(f"❌ AI生成エラー: {e}")
response_text = "ごめんね、ちょっと頭が回らなかったみたい!もう一回言ってくれる?"
print(f"アイ: {response_text}")
# 履歴更新
history.append({"role": "user", "content": f"{user_name}: {user_text}"})
history.append({"role": "assistant", "content": response_text})
# 履歴が長くなりすぎたら古いのを削除(メモリ節約)
if len(history) > 20:
history = history[-10:]
# --- 3. 表現 (Voice & Body) ---
# 口パクのタスク
async def lip_sync_simulation():
while True:
await body.set_parameter("MouthOpen", random.uniform(0.0, 1.0))
await asyncio.sleep(0.1)
# 音声再生と口パクを開始
lip_task = asyncio.create_task(lip_sync_simulation())
# 声を再生(エラーが起きても止まらないように)
try:
await asyncio.to_thread(voice.speak, response_text)
except Exception as e:
print(f"❌ 音声再生エラー: {e}")
# 喋り終わったら口パク停止
lip_task.cancel()
await body.set_parameter("MouthOpen", 0.0)
except KeyboardInterrupt:
# Ctrl+C が押されたらループを抜ける
print("\n🛑 強制終了します")
break
except Exception as e:
# 予期せぬエラーが起きても、ログを出してループを継続(これが重要!)
print(f"⚠️ 予期せぬエラーが発生しましたが、復旧します: {e}")
traceback.print_exc() # エラーの詳しい場所を表示
await asyncio.sleep(3) # エラー連打を防ぐため少し休む
# 終了処理
await body.close()
if __name__ == "__main__":
asyncio.run(main())
この修正で変わること
- 1. エラーでも止まらない
-
try...except Exceptionというブロックで処理を囲んだため、例えば「YouTubeの接続が一瞬切れた」「OpenAIが混雑してエラーを返した」といった場合でも、⚠️ 予期せぬエラーが発生しましたが、復旧しますと表示して、プログラム自体は動き続けます - 2. VTube Studio接続待機
-
起動時にVTube Studioがまだ立ち上がっていなくても、エラー終了せず「5秒後に再試行」しながら待ち続けるようになります。
- 3. メモリ管理
-
会話履歴(
history)が増えすぎるとAIの動作が重くなるため、20ラリーを超えたら古いものを忘れる機能を追加しました
修正という名のコピペ!



ありがとうLM!
さあ、コピペで直すぞ!





完了! はやいはやい!
ファイルを保存したら実行だ!
修正結果は・・・
ターミナルに実行コードを打ち込んで起動します。
実行コード
python main.py


2026-02-06 12:29:06 | ERROR | modules.expression.vtube_studio:connect:57 – VTube Studio接続エラ ー: [WinError 1225] リモート コンピューターによりネットワーク接続が拒否されました。
❌ VTube Studioが見つかりません。5秒後に再試行します…



あ、 VTube Studio立ち上げるの忘れてた。
ちゃんとエラーが表示されたのでOK!
仕切りなおして。。。





各アプリ起動準備よし!
実行します!!
やっぱり一個ずつしか実行しません。





🚀 AITuberシステムを起動します…
✨ ‘Ctrl + C’ を押すまで、永遠に動き続けます!



いいえ、一回で止まりました
シンプルにプログラムを繰り返す命令を追加するだけでいい気もするのでLMに聞いてみます。



main.py に繰り返しの文
入れるだけでいいんじゃないの?
AITuber 開発用LMの回答
はい、その通りです!大正解です。 main.py の処理を while True などのループに入れるだけで、起動しっぱなしでコメントを待ち受ける「常駐型」のプログラムになります。



あ、うん
なんかありがと
LM回答の続き
ただし、単純にループさせるだけだと3つの問題が発生します。それを防ぐための「おまじない」を加えた**「完成版・無限ループコード」**を用意しました。
単純なループで起きる3つの問題と対策
- 1. PCが固まる(CPU暴走)
-
対策: コメントがない時に
await asyncio.sleep()で「休憩」を入れます。これを入れないと、プログラムが全速力で「コメントまだ?まだ?まだ?」と確認し続けてPCが重くなります。 - 2. エラーで即死する
-
対策: ネットが一瞬切れたり、APIがエラーを返したりしただけで止まらないよう、
try-exceptでエラーを無視してループを続けるようにします - 3. 記憶があふれる
-
対策: 会話履歴(
history)が無限に増えると、AIの記憶容量(トークン)オーバーでエラーになります。古い記憶を捨てる処理を追加します
【コピペでOK】完成版・常駐型 main.py
このコードに全書き換えしてください。これで「Ctrl+C」を押して強制終了するまで、永遠に動き続けます。
コード
import asyncio
import random
import traceback # エラーの詳細を表示するために追加
from config.settings import settings
# 各種モジュールのインポート
from modules.cognition.llm_client import LLMClient
from modules.expression.vtube_studio import VTubeStudioClient
from modules.expression.voicevox import VoicevoxClient
from modules.perception.youtube import YouTubeCommentFetcher
# キャラクター設定
SYSTEM_PROMPT = """
あなたは「AITuber」のアイです。
元気で明るい、少しおっちょこちょいな性格です。
リスナーのコメントに対して、短くテンポよく返事をしてください。
"""
async def main():
print("🚀 AITuberシステムを起動します...")
print("✨ 'Ctrl + C' を押すまで、永遠に動き続けます!")
# 1. 各モジュールの初期化
brain = LLMClient(system_prompt=SYSTEM_PROMPT)
voice = VoicevoxClient()
body = VTubeStudioClient()
youtube = YouTubeCommentFetcher()
# 2. VTube Studioに接続(失敗してもリトライし続ける)
while True:
try:
if await body.connect():
print("✅ VTube Studio接続成功!")
break
else:
print("❌ VTube Studioが見つかりません。5秒後に再試行します...")
await asyncio.sleep(5)
except Exception as e:
print(f"⚠️ VTS接続エラー: {e}")
await asyncio.sleep(5)
# 会話履歴
history = []
print(f"✅ 準備完了!YouTube配信(ID: {settings.YOUTUBE_VIDEO_ID})のコメントを監視中...")
# --- 無限ループ開始 ---
while True:
try:
# --- 1. YouTubeコメントの取得 ---
comments = youtube.fetch_comments()
# コメントが来ていない場合は休憩して次へ
if not comments:
await asyncio.sleep(5) # 5秒待機(負荷軽減のため重要!)[1]
continue
# 最新のコメントを取得
last_comment = comments[-1]
user_name = last_comment["user"]
user_text = last_comment["text"]
print(f"\n💬 {user_name}: {user_text}")
# "終了" コマンドでシステムを安全に停止
if user_text == "終了":
print("システムを終了します。")
break
# --- 2. 思考 (Brain) ---
print("🤖 (思考中...)")
prompt_text = f"リスナーの「{user_name}」さんが「{user_text}」と言っています。返事をしてあげて。"
# AI応答生成(エラーが起きても止まらないようにガード)
try:
response_text = await brain.generate_response(prompt_text, history)
except Exception as e:
print(f"❌ AI生成エラー: {e}")
response_text = "ごめんね、ちょっと頭が回らなかったみたい!もう一回言ってくれる?"
print(f"アイ: {response_text}")
# 履歴更新
history.append({"role": "user", "content": f"{user_name}: {user_text}"})
history.append({"role": "assistant", "content": response_text})
# 【重要】記憶の整理:履歴が20個を超えたら古いものを忘れる[4]
if len(history) > 20:
history = history[-10:] # 最新10個だけ残す
# --- 3. 表現 (Voice & Body) ---
# 口パクのタスク
async def lip_sync_simulation():
while True:
await body.set_parameter("MouthOpen", random.uniform(0.0, 1.0))
await asyncio.sleep(0.1)
# 音声再生と口パクを開始
lip_task = asyncio.create_task(lip_sync_simulation())
# 声を再生
try:
await asyncio.to_thread(voice.speak, response_text)
except Exception as e:
print(f"❌ 音声再生エラー: {e}")
# 喋り終わったら口パク停止
lip_task.cancel()
await body.set_parameter("MouthOpen", 0.0)
except KeyboardInterrupt:
# Ctrl+C が押されたらループを抜ける
print("\n🛑 強制終了します")
break
except Exception as e:
# 予期せぬエラーが起きても、ログを出してループを継続(これが重要!)
print(f"⚠️ 予期せぬエラーが発生しましたが、復旧します: {e}")
traceback.print_exc() # エラーの詳しい場所を表示
await asyncio.sleep(3) # エラー連打を防ぐため少し休む
# 終了処理
await body.close()
if __name__ == "__main__":
asyncio.run(main())
リトライ!でも。。。



main.pyを差し替えてそのまま保存!
いざ、リトライ!
実行コード
python main.py




コードを実行したあと、
(.venv) PS D:\02-my-app\00_AIvtube\my-aituber>
こちらのコード「次の命令待ってます」状態になるので、問題が全く解決していません。



解決方法がLMにはないようだな。こんな時は
たのむぜGPT!
別のAI(チャットGPT)にファイルと、ターミナルのログを見せて問題を解消してもらいます。
助っ人GPTの活躍!
ChatGPTにコードを共有して、解決方法を訊きます。
質問の内容
ログを拾ってコメントを返すプログラムだけど
ターミナルでプログラムを一周実行するたびに終了してしまう。
ちゃんと繰り返してほしい
import asyncio
import random
import traceback # エラーの詳細を表示するために追加
from config.settings import settings
各種モジュールのインポート
from modules.cognition.llm_client import LLMClient
from modules.expression.vtube_studio import VTubeStudioClient
from modules.expression.voicevox import VoicevoxClient
from modules.perception.youtube import YouTubeCommentFetcher
キャラクター設定
SYSTEM_PROMPT = “””
あなたは「AITuber」のアイです。
元気で明るい、少しおっちょこちょいな性格です。
リスナーのコメントに対して、短くテンポよく返事をしてください。
“””
async def main():
print(“🚀 AITuberシステムを起動します…”)
print(“✨ ‘Ctrl + C’ を押すまで、永遠に動き続けます!”)
# 1. 各モジュールの初期化
brain = LLMClient(system_prompt=SYSTEM_PROMPT)
voice = VoicevoxClient()
body = VTubeStudioClient()
youtube = YouTubeCommentFetcher()
# 2. VTube Studioに接続(失敗してもリトライし続ける)
while True:
try:
if await body.connect():
print("✅ VTube Studio接続成功!")
break
else:
print("❌ VTube Studioが見つかりません。5秒後に再試行します...")
await asyncio.sleep(5)
except Exception as e:
print(f"⚠️ VTS接続エラー: {e}")
await asyncio.sleep(5)
# 会話履歴
history = []
print(f"✅ 準備完了!YouTube配信(ID: {settings.YOUTUBE_VIDEO_ID})のコメントを監視中...")
# --- 無限ループ開始 ---
while True:
try:
# --- 1. YouTubeコメントの取得 ---
comments = youtube.fetch_comments()
# コメントが来ていない場合は休憩して次へ
if not comments:
await asyncio.sleep(5) # 5秒待機(負荷軽減のため重要!)[1]
continue
# 最新のコメントを取得
last_comment = comments[-1]
user_name = last_comment["user"]
user_text = last_comment["text"]
print(f"\n💬 {user_name}: {user_text}")
# "終了" コマンドでシステムを安全に停止
if user_text == "終了":
print("システムを終了します。")
break
# --- 2. 思考 (Brain) ---
print("🤖 (思考中...)")
prompt_text = f"リスナーの「{user_name}」さんが「{user_text}」と言っています。返事をしてあげて。"
# AI応答生成(エラーが起きても止まらないようにガード)
try:
response_text = await brain.generate_response(prompt_text, history)
except Exception as e:
print(f"❌ AI生成エラー: {e}")
response_text = "ごめんね、ちょっと頭が回らなかったみたい!もう一回言ってくれる?"
print(f"アイ: {response_text}")
# 履歴更新
history.append({"role": "user", "content": f"{user_name}: {user_text}"})
history.append({"role": "assistant", "content": response_text})
# 【重要】記憶の整理:履歴が20個を超えたら古いものを忘れる[4]
if len(history) > 20:
history = history[-10:] # 最新10個だけ残す
# --- 3. 表現 (Voice & Body) ---
# 口パクのタスク
async def lip_sync_simulation():
while True:
await body.set_parameter("MouthOpen", random.uniform(0.0, 1.0))
await asyncio.sleep(0.1)
# 音声再生と口パクを開始
lip_task = asyncio.create_task(lip_sync_simulation())
# 声を再生
try:
await asyncio.to_thread(voice.speak, response_text)
except Exception as e:
print(f"❌ 音声再生エラー: {e}")
# 喋り終わったら口パク停止
lip_task.cancel()
await body.set_parameter("MouthOpen", 0.0)
except KeyboardInterrupt:
# Ctrl+C が押されたらループを抜ける
print("\n🛑 強制終了します")
break
except Exception as e:
# 予期せぬエラーが起きても、ログを出してループを継続(これが重要!)
print(f"⚠️ 予期せぬエラーが発生しましたが、復旧します: {e}")
traceback.print_exc() # エラーの詳しい場所を表示
await asyncio.sleep(3) # エラー連打を防ぐため少し休む
# 終了処理
await body.close()
if __name__ == "__main__":
asyncio.run(main())
GPTの回答
原因はほぼコレです。
**await body.close() が while True: の中に入っちゃってる(インデント崩れ)**ので、1周回ったタイミングでVTube Studio接続を閉じて、そのまま処理が終わったように見えます。
(さらに if __name__ == "__main__": asyncio.run(main()) があるのに、この断片だけだと main() 定義が見えないので、実コード側での構造も要確認)
ここもチェック(よくある“1周で終わる”原因)
- await body.close() がループ内にある(今回の症状ドンピシャ)
- 例外で落ちてるのに、上位で握れてない(main() の外で落ちてる等)
- youtube.fetch_comments() が例外を投げて、その例外が外側で終了してる(この修正版なら継続)
直し方
- 無限ループの外で close する(
finally:に入れるのが安全) - ついでに、口パクtaskは
cancel()後に 握りつぶす(CancelledError対策)
“このまま置き換えできる形” の例です。(これだけだと直りません!)
import asyncio, random, traceback
async def main():
# 1. 各モジュールの初期化
brain = LLMClient(system_prompt=SYSTEM_PROMPT)
voice = VoicevoxClient()
body = VTubeStudioClient()
youtube = YouTubeCommentFetcher()
# 2. VTube Studioに接続(失敗してもリトライし続ける)
while True:
try:
if await body.connect():
print("✅ VTube Studio接続成功!")
break
else:
print("❌ VTube Studioが見つかりません。5秒後に再試行します...")
await asyncio.sleep(5)
except Exception as e:
print(f"⚠️ VTS接続エラー: {e}")
await asyncio.sleep(5)
history = []
print(f"✅ 準備完了!YouTube配信(ID: {settings.YOUTUBE_VIDEO_ID})のコメントを監視中...")
try:
# --- 無限ループ開始 ---
while True:
try:
comments = youtube.fetch_comments()
if not comments:
await asyncio.sleep(5)
continue
last_comment = comments[-1]
user_name = last_comment["user"]
user_text = last_comment["text"]
print(f"\n💬 {user_name}: {user_text}")
if user_text == "終了":
print("システムを終了します。")
break
print("🤖 (思考中...)")
prompt_text = f"リスナーの「{user_name}」さんが「{user_text}」と言っています。返事をしてあげて。"
try:
response_text = await brain.generate_response(prompt_text, history)
except Exception as e:
print(f"❌ AI生成エラー: {e}")
response_text = "ごめんね、ちょっと頭が回らなかったみたい!もう一回言ってくれる?"
print(f"アイ: {response_text}")
history.append({"role": "user", "content": f"{user_name}: {user_text}"})
history.append({"role": "assistant", "content": response_text})
if len(history) > 20:
history = history[-10:]
# --- 口パク ---
async def lip_sync_simulation():
try:
while True:
await body.set_parameter("MouthOpen", random.uniform(0.0, 1.0))
await asyncio.sleep(0.1)
except asyncio.CancelledError:
# cancelされたら静かに終了
return
lip_task = asyncio.create_task(lip_sync_simulation())
try:
await asyncio.to_thread(voice.speak, response_text)
except Exception as e:
print(f"❌ 音声再生エラー: {e}")
finally:
lip_task.cancel()
# cancel完了を待つ(CancelledErrorを握りつぶす)
try:
await lip_task
except asyncio.CancelledError:
pass
await body.set_parameter("MouthOpen", 0.0)
except KeyboardInterrupt:
print("\n🛑 強制終了します")
break
except Exception as e:
print(f"⚠️ 予期せぬエラーが発生しましたが、復旧します: {e}")
traceback.print_exc()
await asyncio.sleep(3)
finally:
# ★終了処理は「無限ループの外」で必ず実行
try:
await body.close()
except Exception as e:
print(f"⚠️ close中にエラー: {e}")
if __name__ == "__main__":
asyncio.run(main())
NotebookLMはユーザーが入れたデータのみを情報源として推論を行うので、特定の分野に特化したAIとして大活躍しますが、反面。閉鎖的な環境なので応用力や修正作業に制限が出てしまいがち。
それを補うためにはChatGPTなど、情報源をネット全体から取得してくれるAIに問いを投げるのが有効です。 こちらは膨大な情報源から推論を行うため、ハルシネーション(誤情報やウソ)が混在しやすくなりますが、それは応用や大胆な方針転換の提案もしてくれるため。場合によっては問題解決の突破口を開いてくれます



ふむ、コピペしたがうまくいかんな。。。
何度もmain.pyを修正 → ターミナルで実行を繰り返して、そのたびに結果(ターミナルのログをコピペ)をGPTに報告。繰り返して解決にたどりつけました。
最終的に安定したmain.pyのコード
import asyncio
import random
import traceback
import uuid
import multiprocessing as mp
import signal
import sys
from config.settings import settings
from modules.cognition.llm_client import LLMClient
from modules.expression.vtube_studio import VTubeStudioClient
from modules.expression.voicevox import VoicevoxClient
from modules.perception.youtube import YouTubeCommentFetcher
SYSTEM_PROMPT = """
あなたは「AITuber」のアイです。
元気で明るい、少しおっちょこちょいな性格です。
リスナーのコメントに対して、短くテンポよく返事をしてください。
"""
MAX_LIP_SECONDS = 12
def drain_queue(q: mp.Queue, max_items: int = 50):
"""
キューに溜まっている古い音声を捨てる(最新優先)
※ mp.Queue の empty/qsize はWindowsで信用できないので get_nowait で抜く
"""
for _ in range(max_items):
try:
q.get_nowait()
except Exception:
break
def voice_worker(req_q: mp.Queue):
voice = VoicevoxClient()
while True:
item = req_q.get()
if item is None:
break
_req_id, text = item
try:
voice.speak(text)
except KeyboardInterrupt:
# Ctrl+C が worker に飛んでも main で止めたいので、ここでは無視して継続
continue
except BaseException:
# worker側が落ちても main は動かし続ける
traceback.print_exc()
def restart_voice_worker(old_proc: mp.Process, req_q: mp.Queue) -> mp.Process:
try:
if old_proc is not None and old_proc.is_alive():
old_proc.terminate()
old_proc.join(timeout=2)
except Exception:
pass
proc = mp.Process(target=voice_worker, args=(req_q,), daemon=True)
proc.start()
return proc
async def wait_or_stop(seconds: float, stop_event: asyncio.Event):
try:
await asyncio.wait_for(stop_event.wait(), timeout=seconds)
except asyncio.TimeoutError:
return
async def lip_sync_for(body, seconds: float, stop_event: asyncio.Event):
end = asyncio.get_running_loop().time() + seconds
try:
while not stop_event.is_set() and asyncio.get_running_loop().time() < end:
await body.set_parameter("MouthOpen", random.uniform(0.0, 1.0))
await wait_or_stop(0.1, stop_event)
finally:
try:
await body.set_parameter("MouthOpen", 0.0)
except Exception:
pass
async def connect_vts_forever(body, stop_event: asyncio.Event):
while not stop_event.is_set():
try:
if await body.connect():
print("✅ VTube Studio接続成功!")
return True
print("❌ VTube Studioが見つかりません。5秒後に再試行します...")
await wait_or_stop(5, stop_event)
except Exception as e:
print(f"⚠️ VTS接続エラー: {e}")
await wait_or_stop(5, stop_event)
return False
async def stop_listener(stop_event: asyncio.Event):
"""
Windows/multiprocessing環境で Ctrl+C が不安定なので、
「q + Enter」で確実に止める。
"""
print("🛑 停止したいときは 'q' を入力して Enter(Ctrl+Cは環境により不安定)")
while not stop_event.is_set():
try:
line = await asyncio.to_thread(sys.stdin.readline)
except Exception:
await asyncio.sleep(0.5)
continue
if not line:
continue
if line.strip().lower() in ("q", "quit", "exit"):
stop_event.set()
return
async def main():
print("🚀 AITuberシステムを起動します...")
print("✨ 'Ctrl + C' を押すまで、永遠に動き続けます!")
stop_event = asyncio.Event()
loop = asyncio.get_running_loop()
# 可能ならSIGINTも stop_event にする(Windowsでは効かない場合あり)
try:
loop.add_signal_handler(signal.SIGINT, stop_event.set)
except NotImplementedError:
pass
# 停止入力リスナー(確実に止める保険)
asyncio.create_task(stop_listener(stop_event))
brain = LLMClient(system_prompt=SYSTEM_PROMPT)
body = VTubeStudioClient()
youtube = YouTubeCommentFetcher()
req_q: mp.Queue = mp.Queue()
vp = restart_voice_worker(None, req_q)
history = []
try:
ok = await connect_vts_forever(body, stop_event)
if not ok:
return
print(f"✅ 準備完了!YouTube配信(ID: {settings.YOUTUBE_VIDEO_ID})のコメントを監視中...")
last_seen_key = None
last_alive = asyncio.get_running_loop().time()
while not stop_event.is_set():
try:
comments = youtube.fetch_comments()
if not comments:
now = asyncio.get_running_loop().time()
if now - last_alive > 15:
print("…待機中(コメントなし)")
last_alive = now
await wait_or_stop(5, stop_event)
continue
last_comment = comments[-1]
user_name = last_comment.get("user", "unknown")
user_text = last_comment.get("text", "")
comment_id = last_comment.get("id")
key = comment_id if comment_id is not None else (user_name, user_text)
if key == last_seen_key:
await wait_or_stop(1, stop_event)
continue
last_seen_key = key
print(f"\n💬 {user_name}: {user_text}")
if user_text.strip() == "終了":
print("システムを終了します。")
stop_event.set()
break
print("🤖 (思考中...)")
prompt_text = f"リスナーの「{user_name}」さんが「{user_text}」と言っています。返事をしてあげて。"
try:
response_text = await brain.generate_response(prompt_text, history)
except Exception as e:
print(f"❌ AI生成エラー: {e}")
response_text = "ごめんね、ちょっと頭が回らなかったみたい!もう一回言ってくれる?"
if stop_event.is_set():
break
print(f"アイ: {response_text}")
history.append({"role": "user", "content": f"{user_name}: {user_text}"})
history.append({"role": "assistant", "content": response_text})
if len(history) > 20:
history = history[-10:]
if not vp.is_alive():
print("⚠️ 音声ワーカーが停止していました。再起動します。")
vp = restart_voice_worker(vp, req_q)
# ★古い音声を捨てて、最新だけ喋らせる(テキストと音声のズレ防止)
drain_queue(req_q)
req_q.put((str(uuid.uuid4()), response_text))
await lip_sync_for(body, MAX_LIP_SECONDS, stop_event)
print("✅ ループ継続:次のコメント取得へ")
except KeyboardInterrupt:
stop_event.set()
break
except Exception as e:
print(f"⚠️ ループ内エラー(継続します): {e}")
traceback.print_exc()
await wait_or_stop(3, stop_event)
finally:
try:
await body.close()
except Exception:
pass
try:
req_q.put(None)
except Exception:
pass
try:
if vp is not None and vp.is_alive():
vp.terminate()
vp.join(timeout=2)
except Exception:
pass
print("👋 終了しました")
if __name__ == "__main__":
mp.freeze_support()
asyncio.run(main())
助っ人につくってもらったコードは、念のためLMの方にも入れて共有しておきます。(プログラムは読んでくれないのでtxt形式で!)



ありがとうGPT!
解決したのでLMに報告とコードの共有
LMのソースにも入れましたが、進捗状況としてmain.py内のコードをチャット画面で共有して、次にすすみます。



main.pyの中身は最後はこちらにしたよ!
ファイル名:main.pyの中身(一例).txt
コードをチャット欄に貼り付けると長いので、新しく入力したソースファイルの名前で共有しています。
AITuber 開発用LMの回答
共有いただいた main.py のコードを確認しました。 非常に素晴らしい完成度です!
単に動くだけでなく、実運用で必ず直面する「音声生成中のフリーズ」や「終了時のトラブル」まで考慮された、プロ仕様に近い設計になっています。
特に以下の3点が、初心者レベルを超えていて素晴らしいです。
- 1. マルチプロセス化 (multiprocessing
-
重たい音声合成処理(VOICEVOX)を別プロセス(
voice_worker)に逃がしているため、アイちゃんが喋っている最中でも、メインループ(コメント取得やVTube Studioとの通信)が止まらずスムーズに動きます。 - 2. キューのドレイン処理 (drain_queue)
-
コメントが殺到したときに、古い音声生成待ちを捨てて「最新の話題」に即座に反応できる仕組みが入っています。これにより、会話のラグ(遅延)を最小限に抑えられます
- 3. Windows対応の停止処理 (stop_listener)
-
Windows環境での
Ctrl+Cの不安定さを考慮し、「q + Enter」で安全に終了できる仕組みを入れているのは、非常に実戦的な配慮です。
さあ、次のステージへ!(Next Steps)
システム(脳・声・体・耳)の統合は完璧です。次は、このアイちゃんを**「配信者」としてデビューさせるための環境構築と、「表現力」の強化**に進みましょう。
次回、いよいよ配信環境の構築!



ChatGPTと作ったコードを
LMがベタ褒めしてくれたぜ。頑張った甲斐があったよ
どうやら次回は配信環境を整える作業になりそうです。まだまだ改善点はありますが。テストで生配信いきたいですね!