クラウドAPI不要、月額課金ゼロ、個人情報も外に出さない。RTX搭載のWindowsノートPCだけで、日本語で話しかけるとイタリア語で答えてくれる音声AIアシスタントを作りました。
はじめに — なぜ「イタリア語」なのか
「AIアシスタント作ってみた」という記事は山ほどある。ChatGPT APIを叩いて、Whisper APIで音声認識して、ElevenLabsで喋らせる — 確かに動く。でも毎月のAPI代がかかるし、音声データはクラウドに送られるし、オフラインでは使えない。
自分の場合、もうひとつ事情があった。イタリア語を話す勉強をするのだ。
子どもが日本語で「今日の天気は?」と聞いたら、イタリア語で返してほしい。妻が英語で質問しても、イタリア語で返してほしい。もちろんイタリア語で話しかけたら、文法の間違いをさりげなく直してほしい。
そして最も重要な制約は — 完全オフライン。家族の会話がクラウドに送信されるのは論外だ。
🎯 何を作ったのか
CasaAI(カーザAI)— 完全オフラインで動作するイタリア語音声アシスタント。
「Casa!」(ウェイクワード)
↓
🎤 音声録音
↓
🧠 faster-whisper(GPU) → テキスト化 + 言語自動検出
↓
🌍 日本語/英語ならイタリア語に翻訳しつつ回答
↓
🤖 Ollama + Qwen 2.5 7B(ローカルLLM)
↓
🔊 Piper TTS → イタリア語で音声出力
↓
💾 会話メモリに保存(JSON、直近10件)
↓
🖥️ フルスクリーンUIを更新
↓
🔁 ループ
技術スタック全体がローカルで完結している:
| コンポーネント | 技術 | 動作場所 |
|---|---|---|
| 音声認識(STT) | faster-whisper + CTranslate2 | GPU(CUDA) |
| 言語モデル(LLM) | Ollama + Qwen 2.5 7B Q4 | GPU |
| 音声合成(TTS) | Piper | CPU |
| UI | Tkinter フルスクリーン | CPU |
| メモリ | JSON ファイル | ローカルディスク |
GPU VRAM使用量は約6GB。8GB VRAMのノートPC用GPUで問題なく動作する。
🏗️ アーキテクチャ — 「動けばいい」ではなく
個人プロジェクトであっても、後から手を入れられない設計は技術的負債を生む。CasaAIはClean Architectureに基づいて設計した。
CasaAI/
├── config/settings.json ← 全設定を外部化
├── core/ ← ドメインロジック層
│ ├── audio_manager.py ← 録音・再生
│ ├── wake_word.py ← ウェイクワード検出
│ ├── stt_engine.py ← 音声認識エンジン
│ ├── language_detector.py ← 言語判定
│ ├── llm_client.py ← LLMクライアント
│ ├── tts_engine.py ← 音声合成エンジン
│ └── memory_manager.py ← 会話メモリ管理
├── infrastructure/ ← インフラ層
│ ├── logger.py ← 構造化ログ
│ └── system_monitor.py ← システム監視
├── ui/ ← インターフェース層
│ └── main_window.py ← Tkinter UI
└── main.py ← オーケストレーター
各レイヤーの依存は内側にのみ向かう。LLMをOllamaから別のものに差し替えたい? llm_client.pyだけ変えればいい。TTSをPiperからVOICEVOXに変えたい? tts_engine.pyだけ。UIをTkinterからWebに変えたい? main_window.pyだけ。
設定はsettings.jsonに完全外部化。ハードコードされた値はゼロ。
🔥 一番苦労したこと — RTX 5050との戦い
開発で最も時間を食われたのは、アルゴリズムでもアーキテクチャでもなく、NVIDIA RTX 5050(Blackwell)との互換性問題だった。
問題1: cublas64_12.dll is not found
faster-whisperの裏側にあるCTranslate2は、CUDA演算にcuBLASライブラリを使う。WindowsではこのDLLがシステムにインストールされている前提で動作する。
しかし、CUDAフルツールキットをインストールしていない環境(ドライバだけの環境)ではDLLが見つからない。
解決策: nvidia-cublas-cu12をpipでインストールし、main.pyの最初の処理としてDLLパスをPATH環境変数に注入する。
# main.py の冒頭 — 他のimportより前に実行
def _register_nvidia_dll_paths() -> None:
venv_sp = Path(sys.prefix) / "Lib" / "site-packages" / "nvidia"
dll_dirs = []
for bin_dir in venv_sp.rglob("bin"):
if bin_dir.is_dir() and any(bin_dir.glob("*.dll")):
dll_dirs.append(str(bin_dir))
# PATHに追加(os.add_dll_directory()だけでは不十分)
os.environ["PATH"] = ";".join(dll_dirs) + ";" + os.environ.get("PATH", "")
ポイントは**os.add_dll_directory()だけでは動かない**こと。CTranslate2はC言語レベルでLoadLibrary()を呼ぶため、Pythonのadd_dll_directory()は効かない。PATH環境変数への追加が必須。
問題2: cuBLAS_STATUS_NOT_SUPPORTED
RTX 50XXシリーズ(Blackwell)では、CTranslate2のint8系compute typeが動作しない。
# これらは全部ダメ(RTX 50XX)
int8 → FAIL
int8_float16 → FAIL
int8_float32 → FAIL
int8_bfloat16 → FAIL
# これらはOK
float16 → WORKS ✅
float32 → WORKS ✅
bfloat16 → WORKS ✅
compute_type="auto"もダメ。autoはint8_float16を選んでしまう。
解決策: STTエンジン内で安全なcompute typeへの強制フォールバックを実装。
@staticmethod
def _ensure_safe_compute_type(compute_type: str) -> str:
unsafe = {"int8", "int8_float16", "int8_float32", "int8_bfloat16", "auto"}
if compute_type.lower() in unsafe:
return "float16"
return compute_type
RTX 50XXを使っている人はこのハマりどころに確実に遭遇する。記事を読んでいるあなたが同じGPUなら、この情報だけでも数時間のデバッグが節約できるはず。
問題3: Windows cp932エンコーディング地獄
PiperのTTSにイタリア語テキストをsubprocess.run()で渡す際、Windowsのデフォルトエンコーディング(cp932 / Shift-JIS)が使われて、ùやèなどのイタリア語特殊文字でクラッシュする。
# ❌ ダメ — Windowsはcp932で送信しようとする
proc = subprocess.run(cmd, input=text, text=True, ...)
# ✅ 正解 — UTF-8バイトを直接送信
proc = subprocess.run(cmd, input=text.encode("utf-8"), ...)
さらに、LLMが応答にCJK文字を含めることがある(日本語入力を翻訳する文脈で)。Piperのイタリア語モデルはこれを処理できないので、TTS入力前にサニタイズする。
# CJK文字を除去してイタリア語モデルが処理できるテキストのみ残す
text = re.sub(r'[\u3000-\u9fff\uac00-\ud7af\uff00-\uffef]', '', text)
🎤 ウェイクワード — 「Casa」を認識させる難しさ
当初、ウェイクワード検出は単純だった。短い音声を録音して、Whisperで文字起こしして、"casa"が含まれるか判定する。
しかし実際に試すと全く動かない。
Whisperは短い発話(1-2秒)が苦手で、しかも言語自動検出が入るため、「カーザ」と言っても日本語として認識して「かさ」「バーザー」「あざ」といった意味不明な文字列を返してしまう。
3つの対策を組み合わせた:
① 言語強制: ウェイクワードSTTのみイタリア語を強制指定。ユーザー発話のSTTは自動検出のまま。
# ウェイクワード時のみ言語を強制
wake_result = self._stt.transcribe(
wake_audio, sample_rate,
language_override="it" # ← ここがポイント
)
② ファジーマッチング: "casa"の音声認識ゆらぎパターンをリスト化。
_FUZZY_VARIANTS = {
"casa": [
"casa", "kasa", "cassa", "caza", "causa",
"かさ", "かーさ", "カサ", "カーザ", # 日本語誤認識
"kasar", "kassar", # 英語誤認識
],
}
③ 録音時間延長: 2秒から3秒に。Whisperは長い音声の方が文脈を掴みやすい。
この3つの対策で、ウェイクワード認識率は体感で90%以上に改善した。
🧠 LLMシステムプロンプト — 「家族安全なイタリア語家庭教師」
LLMのシステムプロンプトは、CasaAIの「人格」を定義する最も重要な部分だ。
設計方針は:
- 常にイタリア語で応答(日本語/英語入力には翻訳を添える)
- イタリア語入力には文法修正(優しく)
- 家族安全(政治・暴力・不適切コンテンツを排除)
- 簡潔(3-4文以内)— 長い応答はTTS待ちが長くなる
Qwen 2.5 7B(Q4量子化)は、このプロンプトに従って安定的にイタリア語を出力してくれる。7BパラメータのモデルでもQ4量子化なら約4GB VRAMで動作し、応答は3-5秒程度。家庭用アシスタントには十分なレスポンス。
📊 パフォーマンス実測
RTX 5050 Laptop(8GB VRAM)での実測値:
| 処理 | 時間 | VRAM使用量 |
|---|---|---|
| Whisper small モデルロード | ~3秒 | ~1.5GB |
| STT(3秒の音声) | ~0.5秒 | +0.2GB |
| LLM応答生成 | ~3-5秒 | ~4GB(Ollama) |
| Piper TTS合成 | ~1秒 | CPU処理 |
| 合計VRAM | — | ~6GB / 8GB |
ウェイクワードから音声応答開始まで、合計約5-7秒。人間同士の会話のテンポには及ばないが、「ちょっと考えてから答える」くらいの感覚で、家族は違和感なく使っている。
🖥️ UI — なぜTkinterなのか
「2026年にTkinter?」と思うかもしれない。選定理由は明確:
- Python標準ライブラリ — 追加依存ゼロ
-
フルスクリーン対応 —
root.attributes("-fullscreen", True)一行 -
スレッドセーフな更新 —
root.after()でバックグラウンドスレッドから安全に更新 - 起動が速い — ElectronやWebUIのような重いランタイム不要
- 本当に必要な機能はステータス表示だけ — 凝ったUIは不要
ダークテーマのフルスクリーン画面に、状態・入力テキスト・応答テキスト・システムモニターを表示する。キーボードのESCで終了。マウス不要。
家族が使うデバイスにおいて、**「常に画面に表示されていて、今何をしているかが分かる」**ことが最も重要だ。
📝 学んだこと
1. ローカルAIは「動かす」までが大変
モデルの性能や精度より、DLLの配置、エンコーディング、GPU互換性に時間を取られる。特にWindows。
2. 短い音声の認識は難しい
Whisperは長い音声には強いが、1-2秒の単語レベルの認識は苦手。ウェイクワードシステムを自作する場合、ファジーマッチングは必須。
3. Clean Architectureは個人プロジェクトでも効く
開発中に何度もコンポーネントを差し替えた(TTS、STT設定、UI表示内容など)。レイヤー分離していなかったら、毎回全体に影響が出ていたはず。
4. RTX 50XXはまだエコシステムが追いついていない
2026年3月時点で、CTranslate2のint8がBlackwellで動かない問題は未修正。最新GPUを使うなら、float16を明示指定することを強く推奨。
🚀 今後の展望
- VOICEVOXとの統合 — 日本語TTS対応で多言語出力
- ウェイクワードの専用モデル化 — Whisper依存からの脱却
- 会話メモリの強化 — ベクトル検索による長期記憶
- Raspberry Pi対応 — 小型デバイスでの常時稼働
おわりに
CasaAIは「完全オフラインの多言語音声AIアシスタント」という、ニッチだが実用的なプロジェクトだ。クラウドに依存せず、プライバシーを守りながら、家族の言語学習をサポートしてくれる。
ソースコードはGitHubで公開している。RTX 50XX対応のハマりどころも含めて、同じようなことをやりたい人の参考になれば幸いだ。
🔗 GitHub: github.com/Rikiza89/CasaAI