0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GPUひとつで「イタリア語で喋る家族AIアシスタント」を完全オフラインで作った話

0
Posted at

クラウド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?」と思うかもしれない。選定理由は明確:

  1. Python標準ライブラリ — 追加依存ゼロ
  2. フルスクリーン対応root.attributes("-fullscreen", True)一行
  3. スレッドセーフな更新root.after()でバックグラウンドスレッドから安全に更新
  4. 起動が速い — ElectronやWebUIのような重いランタイム不要
  5. 本当に必要な機能はステータス表示だけ — 凝った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

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?