JARVISを自作しよう:Memo AIの詳細解説—プライバシー重視のローカル音声エージェント

Dev.to / 2026/4/12

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical Usage

要点

  • この記事では、「Memo AI」を構築する方法を説明します。これは、音声をクラウドへ送らず、プロンプトも送信しないプライバシー重視のローカル音声エージェントで、発話を文字起こしし、意図を検出し、システムタスクを実行します。
  • 5層からなるパイプラインが提示されます。UI/音声取り込み(Streamlit)、文字起こし(静的FFmpegを経由してランタイムFFmpegを使うOpenAI Whisper)、ローカル推論(JSONのみのプロンプトとPydanticによるバリデーションを用いたLlama系モデルのOllama)、およびツール分派のアクション層です。
  • LLMの非決定性にもかかわらず信頼性を高めるために、推論層は出力をJSONに制約し、Pydanticで構造化された意図オブジェクトとして検証します。
  • 安全性のため、パススコーピングなどのセキュリティ制御を導入し、ファイル操作を定義されたルートディレクトリ(例:/output)に制限することで、有害なファイルシステムアクセスのリスクを低減します。
  • システムはモジュール化されており、新しいツールと分派ルールを実装することで、追加の機能(例:Web検索やメール送信)を拡張できるように設計されています。

自分で作るJARVIS:Memo AIを深掘り - プライバシー最優先のローカル音声エージェント

システムタスクを実行し、コードを生成し、メモを取る──クラウドへ1バイトも送信せずに。

動機:なぜローカルへ?

Alexa、Siri、ChatGPT に対するあらゆる音声コマンドが遠隔のサーバに記録される時代において、プライバシーはまさに高級品になっています。私は「Privacy-by-Design(設計段階からのプライバシー)」なエージェントを作りたかった。そこで目指したのが Memo AI です。これは堅牢でフルスタックなアプリケーションで、音声の文字起こし、意図(インテント)検出、そしてシステムレベルの実行を、すべてローカルハードウェア上で完結させます。

アーキテクチャ:5層のパイプライン

ローカルのエージェントを作ることは、単にLLMを組み込むことではありません。人間の発話という「ごちゃごちゃした入力」をうまく扱い、正確なシステムコマンドへ変換できる、信頼できるパイプラインを作ることです。

1. イングェスション層(Streamlit と音声バッファ)

アプリケーションは Streamlit で構築したリアクティブなUIから始まります。streamlit-mic-recorder を使い、音声をブラウザから直接取得しました。生の音声はバイト列としてバッファされるため、初期段階では一時ファイルの後処理を手動で管理する必要がなく、すぐに処理へ回せます。

2. 文字起こし層(OpenAI Whisper)

音声を文字に変換するため、OpenAI の Whisper を組み込みました(速度と精度のバランスのため base モデルを使用)。

  • 問題:Whisper は FFmpeg に依存しており、ユーザがインストールする際に「パス地獄」になりがちです。
  • 解決策:static-ffmpeg を使い、必要なバイナリを実行時に環境へ動的に注入しました。システムレベルでのインストールは不要です。

3. 推論層(Ollama とインテント検出)

文字起こしされたテキストはローカルの Ollama インスタンス(Llama 3.2 または Phi-3 を実行)へ送られます。
ここで多くのエージェントが失敗します。理由は LLM が非決定的だからです。私は次の方法で解決しました:

  • システムプロンプト:モデルに「JSONのみのエンジン」として振る舞わせる。
  • Pydantic による検証:Python の Pydantic ライブラリを使い、LLM の生文字列を、(create_file、write_code などの)特定のインテントを持つ構造化オブジェクトとしてパースする。

4. アクション層(ツールディスパッチャ)

インテントが分類されたら、ディスパッチャがコマンドを Python の「Tool」に割り当てます。

  • スクリプトが必要? → write_code ツールを起動します。
  • 何かを覚えておきたい? → create_file ツールが担当します。 このモジュール構成により、将来的に「Web検索」や「メール送信」のような新機能を追加するのが簡単になります。

5. セキュリティと永続化層

AI の指示に基づいてコードを実行するのは危険です。私は Path Scoping(パススコープ)を実装しました。すべてのファイル操作は厳格なルートディレクトリ(/output)に照らして検証されます。もし LLM が C:/Windows/ へ書き込もうとした場合、システムは ValueError を発生させ、アクションをブロックします。

技術スタック

  • フロントエンド: Streamlit
  • 音声→テキスト: OpenAI Whisper
  • 大規模言語モデル: Ollama(Llama 3.2 1B / 3B)
  • 検証: Pydantic V2
  • システムのつなぎ: Python 3.10+

最大の課題(そしてどう乗り越えたか)

課題1:「FP16」CPU の警告

NVIDIA の専用GPUがない標準的なノートPCで Whisper を動かすと、16ビット浮動小数点(FP16)がサポートされていないことに関する警告がよく出ます。

  • 修正: CUDA が検出されない場合は FP32 をデフォルトにするチェックを stt.py モジュールに実装し、すべてのユーザにとってスムーズな体験を保証しました。

課題2:JSON 内の LLM ハルシネーション

ときどき LLM が JSON の周りに会話の“間”のような文言を付け足します(「もちろん!こちらがあなたのJSONです:…」のように)。

  • 修正: intent.py において、文字列の分割と正規表現風のマーカー( ``` json)を使って、パースの前に余計な部分を取り除く堅牢な抽出ユーティリティを書きました。これにより、LLM がどれだけおしゃべりな性格でも、システムがほぼ「クラッシュしない」状態になります。

課題3:パストラバーサル(Path Traversal)のセキュリティ

AI にファイル名を生成させるのはセキュリティ上のリスクです。悪意あるプロンプトなら、AI をだまして ../../startup_script.py という名前のファイルを作らせることができてしまいます。

  • 修正: os.path.abspath と Path.resolve() を使い、write() コマンドが呼ばれる前に、どのファイルの最終目的地が物理的に自分のプロジェクトの /output サンドボックス内に存在するかを保証しました。

今後の展望

Memo AI はまだ始まりにすぎません。次のステップは以下です:

  1. RAG(Retrieval-Augmented Generation):エージェントがローカルのPDFを「読む」ことを可能にし、質問に答えられるようにする。
  2. 音声フィードバック:Piper や Coqui のようなローカルモデルで Text-to-Speech(TTS)を追加し、エージェントが返事を話せるようにする。
  3. アクティブなツール連携:システムAPIと統合して、音量・明るさの制御やアプリの起動を行えるようにする。

結論

このプロジェクトを作ってわかったのは、強力なAIを作るのに巨大なクラウド予算は不要だということです。ローカルモデルを適切にオーケストレーションできれば、速く動き、無料で使えて、そして—最も重要なのは—完全に自分のものになるパーソナルアシスタントを作れます。

プロジェクトリポジトリへのリンク: https://github.com/priyanshsingh11/Memo-AI

役に立ちましたか? シェアしていただけると嬉しいです!