自己紹介
私は大学三年生で、大学を卒業するまでに開発のスキルを身に着けたくpythonの勉強を始めました。高校でCの経験は3年ほどあり、プログラミングの基礎概念はある程度理解しています。
開発はclaude codeを使用しながら必要な点は説明をしてもらい学習をしながら進めています。
はじめに
今回は「ずんだもんに自分の画面を見てもらいたい」「マイクで話しかけたら返事してほしい」という動機から、完全ローカル動作のAIアシスタントを作りました。
- マイクで話しかける → ずんだもんが声で返答(Phase 2)
- スクリーンショットを送って、ずんだもんの声でリアクション(Phase 1)
Phase1に関してはマシンスペックの問題でVRAMが足りず断念しました。またいい方法が思いついたら考えます。
使用技術
| 役割 | ツール |
|---|---|
| 音声認識(STT) | faster-whisper(small, CUDA) |
| AI返答生成 | LM Studio gemma-4-e4b-it / qwen 3.5 9B※もっといいモデルが見つかれば変更予定 |
| 音声合成(TTS) | VOICEVOX(ずんだもん) |
| 画面キャプチャ | Pillow(ImageGrab) |
| UI | Python / PyQt6(予定) |
動作環境
- Windows 11
- RTX 3070 Ti(VRAM 8GB)RAM 32GB
- Python 3.12
システム構成
案1:画面監視版
スクリーンショット → LM Studio(Vision) → VOICEVOX → 音声再生
画面の変化を検知して自動でコメントする構成。
# 変化検知の仕組み
def calc_diff(frame_a, frame_b):
return np.mean(np.abs(frame_a - frame_b))
# 閾値を超えたら反応
if diff > CHANGE_THRESHOLD:
image_base64 = capture_full_base64()
reply = ask_lm_studio(image_base64)
speak(reply)
案2:マイク会話版
マイク入力 → Whisper(STT) → LM Studio → VOICEVOX → 音声再生
ストリーミングで最初の文が揃い次第すぐ読み上げることで体感レイテンシを削減。
# LM Studioをストリーミングで呼び出し、文末ごとにVOICEVOXへ送る
for line in response.iter_lines():
delta = json.loads(line[6:])["choices"][0]["delta"].get("content", "")
buffer += delta
if buffer and buffer[-1] in SENTENCE_END: # 。!?で区切る
synthesize_and_queue(buffer)
buffer = ""
ハマったポイント
1. Whisperの音声がブツブツになる
sd.rec() を0.1秒ずつ呼ぶと録音の隙間ができて音が途切れる。
解決策: sounddevice.InputStream + queue で途切れない連続録音に変更。
def callback(indata, frames, time, status):
q.put(indata.copy().flatten())
with sd.InputStream(samplerate=SAMPLE_RATE, channels=1,
blocksize=chunk_size, callback=callback):
# キューから取り出して処理
2. faster-whisperのCUDAエラー(cublas64_12.dll)
CUDAツールキットが入っていないと発生。PyPI経由でDLLを追加インストールして解決。
pip install nvidia-cublas-cu12 nvidia-cudnn-cu12
# コードでPATHに追加
for pkg in ["nvidia/cublas/bin", "nvidia/cudnn/bin"]:
path = os.path.join(site.getusersitepackages(), pkg)
if os.path.exists(path):
os.add_dll_directory(path)
3. Qwen3.5系はReasoning(思考)モデルだった
max_tokens: 80 に設定していたら思考トークンで全部使い切ってしまい返答が空文字に。
解決策: enable_thinking: False を付けてmax_tokensを増やす。
4. VOICEVOXの合成がとても遅い(4〜5秒)
audio_query・synthesisそれぞれ2秒ずつかかっていた。AivisSpeechでは同条件で0.87秒。
VOICEVOXでは理想のイメージであるリアルタイムでの会話感がなくなってしまう。
現状: ずんだもんはVOICEVOX専用キャラのため、AivisSpeechへの移行が難しい。
思考中アニメなど演出でラグをカバーする方向で検討中。これが最大の課題。
現在のシステムプロンプト
キャラクターの語尾(のだ・なのだ)を守らせるには、Q&A形式の例示が最も効果的だった。
これはトレーニングされたAIの学習したデータの中に「ずんだもん」というキャラクターがいなかったのが大きいと思う。おそらくクラウドAIのAPI等を使用したらうまくいくのではなかろうか。
SYSTEM_PROMPT = """あなたは「ずんだもん」というキャラクターです。
## 絶対ルール
- 語尾を「のだ」または「なのだ」で終える
- 2文以内で短く返す
## 口調の例
ユーザー: ゲームで負けた
ずんだもん: くやしいのだ…次は勝つのだ!
ユーザー: お腹すいた
ずんだもん: ずんだもちを食べるのだ!おいしいのだ!
"""
速度計測結果
| ステップ | 時間 |
|---|---|
| 音声認識(Whisper small, CUDA) | 約0.5秒 |
| AI返答生成(gemma-4-e4b-it) | 約2.5秒 |
| VOICEVOX音声合成 | 約4〜5秒 |
| 合計 | 約7〜8秒 |
ボトルネックはVOICEVOX。ストリーミング化により体感レイテンシは改善。
でも並列で処理するとそれはそれで一文目と二文目の時間差が生まれて会話のテンポ間が損なわれる
ここも要検討かもしれません。
今後の予定
- PyQt6でオーバーレイGUI(常に最前面表示)
- 立ち絵の状態切り替え(待機・思考中・発話中)
- ある程度の時間無音で自動話しかけ機能
- 能動的にずんだもんから世間話をさせることで友達感UP
まとめ
- 完全ローカルで音声会話AIアシスタントは作れる
- ボトルネックはVOICEVOXの合成速度(GPUモードでも改善せず)
- Whisperはfaster-whisper + CUDAで十分実用的な速度
- 小さいモデルへのキャラクター指示はQ&A例示形式が効く
- リアルタイム性・完全なキャラクターを求めるならAPI(Claude/GPT-4o)が現実的
※ずんだもんの音声はVOICEVOX利用規約に基づき個人利用の範囲で使用しています。