クイックファクト
- フルスタックの総VRAM: ~15 GB(Whisper 3 GB + Gemma 4 E4B 6 GB + Qwen3 8B 6 GB)。PiperはCPU上で動作。
- 簡略化スタック(Qwen3-VL 8B): ~8 GB VRAM — ビジョンとテキスト推論の両方を1つのモデルで処理。
- 音声レイテンシ(Whisper small、RTX 4070): STT ~200〜500 ms。LLM最初のトークン500〜1500 ms。Piper TTS 100 ms。
- 画像処理レイテンシ(Gemma 4 E4B、RTX 4070): 解像度とプロンプトにより画像1枚あたり~2〜5秒。
- リアルタイムビデオ非対応: VLMは個別フレームを処理し、継続的なビデオストリームは処理しません。ビデオの場合は1 FPSでフレームを抽出して各フレームを処理。
- VLM + LLMに同じOllamaインスタンス: OllamaはQwen3-VLをビジョンモデルとテキストモデルの両方として提供でき、VRAMを節約。
- 全コンポーネントがMIT、Apache 2.0、またはGPL-3.0ライセンス(whisper.cpp MIT、Gemma 4 Apache 2.0、Qwen3 Apache 2.0、Piper GPL-3.0)。元のrhasspy/piperリポジトリは2025年にアーカイブされ、開発はOHF-Voice/piper1-gpl(v1.8.0)で継続されています。ライセンスはGPL-3.0のため、クローズドソース製品に組み込む前にライセンスを確認してください。
マルチモーダルAIパイプラインとは?
マルチモーダルAIシステムは複数の入力タイプ(音声、画像、テキスト)を受け付け、複数の出力タイプ(テキスト、音声)を生成します。クラウド版はGPT-5.5 — 音声、画像、テキストを任意の組み合わせで受け付ける単一モデルです。
- クラウドアプローチ(GPT-5.5): 全モダリティで同時に訓練された巨大なモデル。クロスモーダル推論は訓練中に学習 — 画像コンテンツと音声クエリの関係をネイティブに推論できます。
- ローカルアプローチ(このガイド): オーケストレーターで接続された各モダリティ向けの個別特化モデル。よりモジュラーで運用コストが低いですが、「継ぎ目」が生じます — ビジョンモデルの出力はLLMに渡される前にテキストにシリアライズされます。
- ローカルで構築する理由: プライバシー(医療画像、プロプライエタリ文書、機密スクリーンショット)、コスト(クエリごとの費用ゼロ)、オフライン機能(モデルダウンロード後はインターネット不要)、カスタマイズ性(任意のコンポーネントの交換)。
- モジュラーの利点: 任意の1つのコンポーネントを独立してアップグレードできます。より良いローカルSTTモデルが登場したら、STTレイヤーのみを置き換えます。
コスト:ローカルパイプライン vs クラウドAPI(月次)
中程度の使用量(1日100件以上のクエリ)では、ローカルマルチモーダルパイプラインは3〜6ヶ月で元が取れます。
📍 一文で説明
ローカルマルチモーダルパイプラインは一回限りのハードウェア投資(600〜3,500ドル)後、API費用が0ドル/月となり、GPT-5.5 APIコスト(135〜225ドル/月)に対して3〜18ヶ月(クエリ量により異なる)で損益分岐点に達します。
用途 | GPT-5.5 API | Google Cloud | ローカル |
|---|---|---|---|
| 音声クエリ100件/日 | $90〜150/月 | $60〜120/月 | $0 |
| 画像分析50件/日 | $45〜75/月 | $30〜60/月 | $0 |
| 複合(典型的) | $135〜225/月 | $90〜180/月 | $0 |
| ハードウェア(一回限り) | $0 | $0 | $600〜3,500 |
| 損益分岐点 | — | — | 3〜18ヶ月 |
ローカルパイプラインは中程度の使用量(1日100件以上のクエリ)で3〜6ヶ月で元が取れます。軽い使用量(1日10件のクエリ)では損益分岐点が12〜18ヶ月に延びます。
アーキテクチャの概要
ローカルマルチモーダルパイプラインはルーター・オーケストレーターパターンを使用します:入力は境界でタイプ分けされ、適切なモデルにルーティングされ、最終応答を生成する前にオーケストレーターが出力を結合します。
- 入力タイプ: マイク音声(音声)、カメラまたはファイル画像(ビジョン)、キーボードテキスト(テキスト)。
- ルーターロジック: 境界で入力タイプを検出。音声 → STTモデル。画像 → VLM。テキスト → 直接LLM。音声と画像が同時に来た場合は並列処理して結合。
- モデルレジストリ: 各入力タイプは適切なモデルを呼び出してテキスト説明/転写を返すハンドラー関数にマッピングされます。
- オーケストレーター: 全モデル出力を収集し、テキストLLM用の単一プロンプトに結合し、LLM応答を取得し、音声出力のTTSまたは画面テキストにルーティングします。
- 出力タイプ: 音声応答(Piper TTS)、画面上のテキスト、または他のシステムとの統合用構造化データ(JSON)。
- 並列処理: STTとVLMは同時処理が可能 — 画像に関する音声クエリは両方を並列処理でき、シーケンシャル処理と比較してレイテンシを40〜60%削減します。
コンポーネントスタック
VRAM要件と各コンポーネントの役割を含むフルスタック。
📍 一文で説明
フルローカルマルチモーダルスタックは~15 GB VRAMを使用:Whisper large-v3(3 GB)+ Gemma 4 E4B(6 GB)+ Qwen3 8B(6 GB);Piper TTSはVRAMコストゼロでCPU上で動作します。
💬 簡潔に説明
Qwen3-VL 8Bをビジョンモデルとテキストモデルとして両方使用することで、VRAMを8 GBに削減できます — 写真と会話の両方を1つのモデルで処理します。
レイヤー | ツール | モデル | VRAM | 役割 |
|---|---|---|---|---|
| STT | whisper.cpp | Whisper large-v3 | ~3 GB | 音声 → テキスト転写 |
| ビジョン | Ollama | Gemma 4 E4B | ~6 GB | 画像 → テキスト説明 |
| 推論 | Ollama | Qwen3 8B Q4 | ~6 GB | テキスト → テキスト応答 |
| TTS | Piper | en_US-lessac-medium | CPUのみ | テキスト → 音声出力 |
| 合計(個別モデル) | ~15 GB | フルパイプライン |
💡ヒント: 個別のGemma 4 E4B + Qwen3 8Bの代わりにQwen3-VL 8Bを使用してVRAMを~8 GBに削減できます。
💡ヒント: 代替VLM:Qwen3-VL 8B(~6 GB VRAM)— 多言語OCRとドキュメント理解向けに設計されています。ご自身のドキュメントでGemma 4 E4Bと比較してください。
マルチモーダル向けハードウェア階層
5つのハードウェア構成、能力とVRAMの順に並べています。
ティア | GPU | RAM | 実行可能 | レイテンシ(音声クエリ+画像) |
|---|---|---|---|---|
| エントリー | RTX 3060 12 GB | 16 GB | STT + Phi-4(ビジョンは別途、シーケンシャル) | 5〜10秒 |
| ミッド | RTX 4070 12 GB | 32 GB | 7Bモデルでフルスタック(Gemma 4 E4B + Qwen3 8B、ぎりぎり) | 3〜6秒 |
| ハイ | RTX 4090 24 GB | 64 GB | 12B VLM + 8B LLM同時のフルスタック | 2〜4秒 |
| Appleミッド | M5 Pro 36 GB | 36 GB統合 | Metal経由で8Bモデルのフルスタック(推奨) | 2〜4秒 |
| Appleハイ | M5 Max 128 GB | 128 GB統合 | 70Bモデルのフルスタック — 最高のローカル品質 | 1〜3秒 |
レイテンシは音声クエリの終了からTTS再生の開始まで測定され、画像が存在する場合は画像処理を含みます。
💡ヒント: 128 GB統合メモリのM5 Maxは究極のローカルマルチモーダルプラットフォームです。Whisper large-v3(3 GB)+ Gemma 4 31B(~20 GB)+ Piper TTSを同時に実行できます。
ユースケース1:音声制御ドキュメントアナライザー
ドキュメント画像について質問を話しかけると、パイプラインが音声を転写し、ドキュメントを視覚的に処理し、回答を読み上げます。
- 例: 請求書を撮影して「合計金額と支払期限はいつですか?」と尋ねます。
- パイプライン: Whisperが質問を転写 → 画像がGemma 4 E4BまたはQwen3-VLに送信 → VLMが請求書テキストと構造を抽出 → LLMが質問 + VLM出力を結合 → Piperが回答を読み上げます。
- プロンプト: 「こちらが画像です:[VLM説明]。ユーザーが尋ねています:[転写]。画像の内容に基づいて質問に答えてください。」
- 最適なVLM: 請求書/ドキュメントOCR精度にはQwen3-VL 8BまたはGemma 4 E4B。
- プライバシーの価値: 医療記録、法的文書、財務諸表 — データがマシンを離れることなく完全にローカルで処理されます。
ユースケース2:ビジュアルQ&Aアシスタント
カメラをオブジェクトやシーンに向け、口頭で質問し、音声回答を受け取ります。
- アプリケーション: 倉庫在庫、現場検査、視覚障害者向けアクセシビリティ。
- 実装: カメラフレームを取得(OpenCV)、JPEGとして保存、Whisper転写とともにVLMに渡します。
- 最適なモデル: 一般的なオブジェクト/シーン理解にはGemma 4 E4BまたはQwen3-VL 8B。
- レイテンシ: RTX 4070で画像取得 + VLM処理 + LLM + TTSに3〜6秒。
ユースケース3:会議転写 + スライド分析
会議中継続してWhisperを実行して転写を構築しながら、定期的にスライドのスクリーンショットをVLM分析のために取得します。終了時に転写 + スライドコンテンツを組み合わせてローカルサマリーを作成 — クラウドゼロ、データ露出ゼロ。
- STT: 会議中にfaster-whisperをストリーミングモードで実行します。
- ビジョン: 新しいスライドが表示されるたびにスクリーンショットを取得し、Gemma 4 E4Bに説明を依頼します。
- 結合: 会議終了時に転写 + スライド説明をQwen3 8Bに渡してサマリーとアクションアイテムを作成します。
- 出力: 音声読み上げサマリー(Piper TTS)+ ローカル保存テキストファイル。
- GDPRの価値: 会議処理全体がローカル。音声、転写、スライドはいかなるクラウドサービスにも送信されません。
ユースケース4:ローカルアクセシビリティツール
ローカルマルチモーダルパイプラインは、視覚または運動障害のあるユーザー向けのスクリーンリーダーおよび音声制御UIアシスタントとして機能できます — クラウドアクセシビリティサービスのプライバシーの懸念なしにオフラインで動作します。
- スクリーンリーダー: 2秒ごとにスクリーンショットを取得 → Gemma 4 E4Bが画面の内容を説明 → Piperが読み上げます。
- 音声ナビゲーション: Whisperが音声コマンドを転写 → LLMが意図を解釈 → pyautoguiでキーボード/マウスアクションを実行。
- プライバシーの利点: 障害のあるユーザーはしばしば敏感なコンテキストでアクセシビリティツールを使用します。ローカルツールは画面コンテンツがサードパーティに送信されないことを保証します。
- アクセシビリティ向けモデル選択: 高速な画面説明にはMoondream 2(2 GB VRAM)。より豊かな説明にはGemma 4 E4B(6 GB VRAM)。
ユースケース5:ローカルセキュリティカメラ分析
IPカメラからフレームを取得し、動作検出をローカルで実行し、動きが検出された場合にのみVLM分析をトリガーします — クラウドカメラサービスやサードパーティビデオストレージなしで。
- フレーム取得: OpenCVを使用してIPカメラからRTSP経由で5〜10秒ごとにフレームを取得します。
- 動作検出: 連続するフレーム間の差分を計算します。動作しきい値以下のフレームをスキップします。
- VLM分析: 動きが検出されたら、フレームをVLMに送信します:「何が起きているか説明してください。人はいますか?」
- アラート出力: 人が検出されたら、ローカルデスクトップ通知とPiper TTS告知をトリガーします。
- プライバシーの利点: RingとNestはそれぞれAWSとGoogleサーバーにビデオを送信します。このセットアップはすべての映像をあなたのハードウェアに保持します。
- 速度向けの最適なVLM: Moondream 2(フレームあたり~1秒、~2 GB VRAM)またはGemma 4 E4B(~3秒、~6 GB VRAM)。
Pythonオーケストレーターの構築
非同期Pythonオーケストレーターが入力を適切なモデルにルーティングし、出力を結合します。asyncioを使用することでSTTとビジョン処理を並列実行できます。
#!/usr/bin/env python3
"""Local multimodal orchestrator: voice + vision + text, all offline."""
import asyncio
import base64
import subprocess
import tempfile
import sounddevice as sd
import soundfile as sf
import numpy as np
import requests
OLLAMA_URL = "http://localhost:11434/api/generate"
WHISPER_BIN = "./whisper.cpp/build/bin/whisper-cli"
WHISPER_MODEL = "./whisper.cpp/models/ggml-small.bin"
VISION_MODEL = "gemma4:e4b"
TEXT_MODEL = "qwen3:8b"
PIPER_VOICE = "voices/en_US-lessac-medium.onnx"
SAMPLE_RATE = 16000
async def transcribe_audio(audio: np.ndarray) -> str:
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
sf.write(f.name, audio, SAMPLE_RATE)
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, lambda: subprocess.run(
[WHISPER_BIN, "-m", WHISPER_MODEL, "-f", f.name, "--no-timestamps", "--no-prints"],
capture_output=True, text=True
))
return result.stdout.strip()
async def describe_image(image_path: str) -> str:
with open(image_path, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode("utf-8")
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(None, lambda: requests.post(
OLLAMA_URL,
json={"model": VISION_MODEL, "prompt": "Describe the content of this image in detail.", "images": [image_b64], "stream": False},
))
return response.json()["response"]
async def reason(transcript: str, image_description: str | None = None) -> str:
if image_description:
prompt = f"The user asked (via voice): {transcript}\n\nThe image shows: {image_description}\n\nAnswer based on the image. Be concise."
else:
prompt = transcript
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(None, lambda: requests.post(
OLLAMA_URL, json={"model": TEXT_MODEL, "prompt": prompt, "stream": False},
))
return response.json()["response"]
async def speak(text: str) -> None:
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
await asyncio.get_event_loop().run_in_executor(None, lambda: subprocess.run(
f'echo "{text}" | piper --model {PIPER_VOICE} --output_file {f.name}', shell=True, check=True
))
data, sr = sf.read(f.name)
sd.play(data, sr)
sd.wait()
async def process_query(audio: np.ndarray, image_path: str | None = None) -> None:
if image_path:
transcript, image_desc = await asyncio.gather(transcribe_audio(audio), describe_image(image_path))
else:
transcript = await transcribe_audio(audio)
image_desc = None
if not transcript or len(transcript) < 3:
return
response = await reason(transcript, image_desc)
await speak(response)
async def main():
while True:
audio = sd.rec(int(5 * SAMPLE_RATE), samplerate=SAMPLE_RATE, channels=1, dtype="int16")
sd.wait()
await process_query(audio)
if __name__ == "__main__":
asyncio.run(main())パフォーマンス最適化
フルマルチモーダルスタックで許容可能なレイテンシを実現するための主要な最適化:
📍 一文で説明
2つの最大の最適化:(1)音声と画像の両方が利用可能な場合にasyncioでSTTとVLMを並列実行する、(2)LLMが終わる前に音声を開始できるようにLLM出力をTTSに文ごとにストリーミングする。
💬 簡潔に説明
並列処理なし:STT(0.5秒)+ VLM(3秒)+ LLM(1秒)+ TTS(0.1秒)= 4.6秒。並列STT + VLMあり:max(0.5秒、3秒)+ LLM(1秒)+ TTS(0.1秒)= 4.1秒。ストリーミングTTS追加 → ユーザーは3.5秒で音声を聞きます。
- 並列STT + VLM:
asyncio.gather(transcribe_audio(), describe_image())を使用して両方を同時に実行します。0.3〜2秒を節約します。 - モデルをウォームに保つ: Ollamaはリクエスト間でモデルをVRAMに自動的に保持します。クエリ間で再ロードしないでください。
- LLM → TTSストリーミング: LLM出力の文境界を検出します。LLMが生成を続けている間に各完成した文をPiperに渡します。
- VRAM管理: VRAMが限られている場合、テキストLLMをロードする前に画像処理後にVLMをアンロードします。~2〜3秒追加されますが、8 GB GPUでフルスタックを処理できます。
- Qwen3-VLをVLM + LLMとして使用: モデル切り替えのオーバーヘッドを完全に排除 — 1つのモデルがビジョンとテキスト推論の両方を処理します。
- TTS最初の音声目標: Piperはテキストを受信してから50〜100 ms以内に最初の音声を生成します。1文ずつストリーミングしてサブ秒の知覚レイテンシを実現します。
制限と正直な評価
ローカルマルチモーダルパイプラインはGPT-5.5ではありません。 ギャップを明確にすることで、フラストレーションを防ぎ、制限を回避した設計ができます。
- モダリティの継ぎ目: ビジョン出力はテキストLLMに渡される前にテキストにシリアライズされます。LLMは画像の特徴について直接推論できません — 画像のテキスト説明について推論します。
- リアルタイムビデオなし: ローカルVLMは単一フレームを処理し、継続的なビデオは処理しません。ビデオの場合は0.5〜2 FPSでフレームを抽出してシーケンシャルに処理します。
- VLM品質のギャップ: ローカルビジョンモデル(Gemma 4 E4B、Qwen3-VL 8B)は複雑なインフォグラフィック、手書きテキスト、曖昧なシーンでGPT-5.5 Visionより劣っています。
- VRAMプレッシャー: 1つのGPUで3つのモデルを同時実行するには慎重なVRAM管理が必要です。12 GB GPUではモデルサイズを慎重に選択する必要があります。
- レイテンシ vs クラウド: クラウドマルチモーダル呼び出し(GPT-5.5)は1〜3秒かかります。ローカルパイプラインは同等のハードウェアで3〜8秒かかります。
- 一貫性: ローカルモデルはクラウドモデルより変動しやすい出力品質を生成します。ビジョン説明とLLM応答の両方で時折ハルシネーションが発生することを想定してください。
よくある質問
GPT-4Vの代替で、完全にオンデバイスで動くものは?
OpenAI GPT-4Vに代わる完全オンデバイスの有力な選択肢は、Ollama経由で動かすオープンウェイトのビジョン言語モデルです。8〜12 GBのGPUではQwen3-VL 8B(ダウンロード約6.2 GB、256Kコンテキスト)やGemma 4 E4B(約6.6〜9.5 GB)、24 GBのカードや32 GB以上のユニファイドメモリを持つApple SiliconではGemma 4 31B(約20 GB)やQwen3-VL 32B(約21 GB)が候補です。画像とテキストを1回のリクエストで処理でき、クラウドAPIは不要です。複雑なインフォグラフィック、手書き文字、幅広い世界知識が必要なタスクでは、最先端のクラウドモデルより精度が下がるため、ご自身の画像で検証してください。音声の入出力は、本ガイドのとおりwhisper.cppとPiperを組み合わせます。
Qwen3-VL だけで音声、ビジョン、テキストを処理できますか?
はい。Qwen3-VL 8B は画像とテキストを処理できます。これを単一モデルとして使用し、whisper.cpp を音声入力に、Piper を出力に追加します。VRAM を ~15 GB から ~8 GB に削減します。
ビジョンとテキスト LLM を一緒に実行すべきですか、それとも別々に?
12+ GB VRAM がある場合は一緒に。8 GB の場合は別々(スワップ)— VLM をロード、説明を保存、アンロード、LLM をロード。別々は ~2–3 秒のレイテンシオーバーヘッドですが、VRAM を節約。
ビジョンモデルの選択:Gemma 4 E4B と Qwen3-VL 8B のどちらを選ぶべきですか?
Gemma 4 E4B(Apache 2.0)はテキストと画像を扱う汎用モデルで、Ollamaでのダウンロードは約6.6〜9.5 GBです。Qwen3-VL 8B(約6.2 GB、256Kコンテキスト)はビジョンに特化し、多言語OCRや文書処理に向いています。どちらも画像理解とテキスト推論を1つのモデルで担えるため、自分の画像で両方を試し、ハードウェア上の精度と速度で選んでください。
パイプラインはリアルタイム音声アシスタントに十分に高速ですか?
最適化されていれば はい。whisper ~1–2秒、LLM ~1–2秒、TTS ~0.1秒 = 計 ~2–4秒。クラウド API より高速ですが、人間の会話ほど同期的ではありません。非同期アシスタントに最適。
RTX 3060 12GB でパイプラインを実行できますか?
4つ全て同時実行は不可(~15 GB 必要)。解決策:Qwen3-VL 8B(8 GB)のみ、または INT4 量子化(~10 GB)。入力タイプに応じてモデルをスワップ。
医療・法的用途で GDPR 準拠ですか?
ネットワーク通信が発生しないローカルパイプラインはデータ転送のリスクを下げますが、GDPR準拠かどうかは、データの保存場所だけでなく、適法性の根拠、DPIA、保存期間、漏えい時の手順に左右されます。Wiresharkでパイプラインのプロセスから外向きパケットがないことを確認してください。会話履歴や画像ファイルを保存する場合、その保存先には厳格な保持期間とアクセス制御が求められます。医療・法的用途では、法務・コンプライアンス部門にご相談ください。
パイプラインにウェブ検索を追加できますか?
はい。オーケストレーターとテキスト LLM の間に検索ステップを追加。DuckDuckGo API またはローカル RAG を使用。0.5–2 秒のレイテンシを追加するが、時事問題に対応可能。
24/7 で消費する電力量は?
アイドル時:~50–80 W(GPU)、~15–25 W(Mac M5 Pro)。処理中:~150–300 W(GPU)、~30–60 W(Mac)。コスト:月 $5–15(Mac)または $15–35(デスクトップ)。
外部 GPU なしのラップトップで実行できますか?
CPU のみ:非常に遅い(~30+ 秒レイテンシ)。統合 GPU(Metal、Arc):良好だが理想的ではない。推奨:外部 GPU またはクラウド。
専門 VLM vs 汎用モデル?
専門(Gemma 4 E4B):より高速、より軽量。汎用(Qwen3-VL):複雑なシーン向け。実践的:専門 + 推論 LLM が大規模汎用モデル単体より優れた結果。
ソース
- Ollama — ローカル LLM オーケストレーション、ビジョンモデルとチャット API をサポート。
- whisper.cpp on GitHub — 高速 CPU ベース音声認識、Metal/CUDA アクセラレーション。
- Gemma 4 E4B on Ollama — ビジョン言語モデル、オープンソース。
- Qwen3-VL — マルチモーダル LLM、8B。
- Piper TTS (piper1-gpl) on GitHub — ローカル テキスト音声変換、高速。
