何を作ったのか
映画『アイアンマン』のJ.A.R.V.I.S.のように、話しかけるだけで日常のあらゆることを処理してくれるAIアシスタントを自宅に構築した。
スケジュール管理、ニュース収集、タスク管理、技術相談、さらにはこのシステム自体のコード修正まで、すべて音声またはテキストで指示できる。Mac mini上のUbuntu VMで24時間稼働し、家中のどのデバイスからでもアクセスできる。
ChatGPTアプリとの違いは明確だ。ChatGPTは「賢いチャット相手」だが、J.A.R.V.I.S.は自分専用に設計された実行基盤。カレンダーを直接操作し、コードを書き換え、定期的に情報を収集し、自分の行動パターンまで分析して自己改善する。
全体像: 5つのレイヤー
[ブラウザ] ← HTTPS → [nginx] → [FastAPI] → [Claude CLI] → [MCP Tools]
│ │ │ │ │
│ React SPA │ TLS終端 │ API層 │ AI頭脳 │ 外部連携
│ 音声入力/TTS │ 認証 │ SSE配信 │ 常駐プロセス │ カレンダー
│ プラン承認UI │ Tailscale │ ジョブ管理 │ │ タスク管理
上から順に見ていく。
レイヤー1: ブラウザ(React SPA)
フロントエンドはReact + TypeScriptのシングルページアプリケーション。2つのモードを持つ。
- 音声モード: マイクで話しかけ、応答をTTS(テキスト読み上げ)で聞く。スマホやタブレットから「ながら」で使う想定
- テキストモード: 通常のチャットUI。マークダウンでリッチな応答を表示
画面サイズで自動切替される(768px未満は音声、以上はテキスト)が、手動で切り替えもできる。
音声入力にはブラウザのWeb Speech APIを使い、読み上げにはサーバー側のedge-ttsを使う。なぜ読み上げもブラウザ側でやらないのか? ブラウザのSpeechSynthesisは声が機械的すぎる。edge-ttsはMicrosoftの高品質な音声エンジンを無料で使えるため、自然な日本語で応答できる。
レイヤー2: nginx + Tailscale
外部からのアクセスはnginxでTLS終端し、Tailscaleで認証する。
[デバイス] → Tailscale VPN → nginx (HTTPS + auth_request) → FastAPI
ポイントはインターネットに一切公開していないこと。Tailscaleのメッシュ VPN内にいるデバイスだけがアクセスできる。nginxのauth_requestディレクティブでTailscaleの認証サーバーに問い合わせ、自分のtailnet内のデバイスかどうかを検証している。
公開しないのでWAFもDDoS対策も不要。セキュリティの最強の手段は「そもそも攻撃面を作らない」ことだ。
レイヤー3: FastAPI(API層)
PythonのFastAPIが中核のAPI層を担う。主な役割は3つ。
- チャットAPI: ユーザーの入力を受け取り、Claude CLIに渡し、応答をSSE(Server-Sent Events)でストリーミング返却
- TTS API: テキストをedge-ttsで音声に変換し、audio/mpegで返す
- ジョブスケジューラ: 定期実行ジョブ(モーニングルーティン、ニュース収集、自己進化分析)をcron式で管理
SSEを選んだ理由は、WebSocketよりiOSのバックグラウンド復帰に強いから。iPadをスリープから復帰させたとき、WebSocketは再接続のハンドリングが複雑になるが、SSEのEventSourceはブラウザが自動で再接続してくれる。
レイヤー4: Claude CLI(AI頭脳)
ここが設計上の最大の判断ポイント。Claude APIを直接叩くのではなく、Claude CLIをサブプロセスとして常駐させている。
なぜか? Claude CLIには「MCP(Model Context Protocol)」という仕組みがあり、AIが外部ツールを呼び出せる。カレンダーの予定を取得する、タスクを登録する、Webを検索する——これらをAIが自分の判断で選んで実行できる。APIを直接叩く場合、このツール呼び出しの仕組みを自前で実装する必要があるが、CLIならそのまま使える。
さらに「レジデントモード」として、CLIプロセスを起動したまま保持する。通常、CLIは毎回起動→応答→終了するが、これだと起動に数秒かかる。常駐させることで応答開始までの待ち時間をほぼゼロにしている。
レイヤー5: MCPツール(外部連携)
Claude CLIから呼び出されるツール群。自前のMCPサーバーで16個のツールを提供している。
| カテゴリ | ツール例 | 用途 |
|---|---|---|
| スケジュール | get_schedule, add_schedule | Googleカレンダーの読み書き |
| タスク | get_tasks, add_task, complete_task | タスクの管理 |
| 記憶 | remember, get_memories | 長期記憶の保存・検索 |
| ジョブ | get_scheduled_jobs, add_scheduled_job | 定期実行ジョブの管理 |
| Web | WebSearch, WebFetch | 情報検索 |
AIが「明日の予定を確認しよう」と判断すればget_scheduleを呼び、「これは覚えておくべきだ」と判断すればrememberを呼ぶ。どのツールをいつ使うかはAI自身が決める。
設計で最も悩んだこと: 音声の扱い
音声アシスタントで最も厄介なのは、音声認識の精度とレイテンシのトレードオフだ。
ブラウザのWeb Speech APIは無料で使えるが、認識精度はそこそこ。特に技術用語(「Tailscale」「nginx」「MCP」)は誤変換が多い。しかしAI側で文脈から意図を推論できるため、個々の単語の正確さより会話の流れ全体から判断させる方針にした。
読み上げはedge-ttsを採用。リクエストごとにMicrosoftのサーバーで音声合成し、結果をキャッシュする。同じテキストの2回目以降は即座に再生できる。
自己進化: 毎晩の「反省会」
J.A.R.V.I.S.の最もユニークな機能は自己進化だ。
毎晩、その日の全会話ログを3人の「アナリスト」が並列で分析する。
[日次会話ログ]
├── アナリスト1: 自己校正(修正シグナルの検出)
├── アナリスト2: ユーザーモデリング(思考パターンの分析)
└── アナリスト3: システム分析(技術的問題の検出)
│
└── シンセサイザー(3つの分析を統合してレポート生成)
「自己校正」はユーザーが「違う」「そうじゃない」と言った瞬間を全件検出し、なぜその間違いが構造的に発生するのかを掘り下げる。そして「気をつけます」ではなく、コードやルールの変更で再発を防ぐ対策を提案する。
例えば「調査する前に解決策を出すな」という修正が2日連続で発生したら、システムプロンプトの禁止行動リストに追加する。実際にこの対策を入れてから5日間、同じ修正はゼロになった。
インフラ構成
Mac mini (macOS)
└── UTM (仮想化)
└── Ubuntu VM
├── systemd: home-agent.service (自動起動・自動復旧)
├── nginx: TLS終端 + Tailscale認証
├── FastAPI: アプリケーション
└── SQLite: データストア (WALモード)
なぜクラウドではなく自宅Mac miniなのか?
- コスト: 24時間稼働のAPIサーバーをAWSで動かすと月数千円。Mac miniは一度買えば電気代だけ
- レイテンシ: 自宅ネットワーク内なのでLAN内通信は1ms以下
- 自由度: sudoが使える。ファイルシステムに直接触れる。制約なく実験できる
本番のシステムをVMで動かすのは冗長に見えるが、macOSのアップデートでホストが再起動してもVMが自動復旧する構成にしてあるため、可用性の面でもメリットがある。
まとめ
J.A.R.V.I.S.の技術構成を一言でまとめると、**「Claude CLIを常駐させたFastAPIサーバーに、音声UIとMCPツールを被せたもの」**だ。
個々の技術は特別なものではない。FastAPI、nginx、Tailscale、SQLite——どれも定番の技術スタックだ。ポイントはClaude CLIのMCPエコシステムをそのまま活用するという設計判断にある。API直叩きでツール呼び出しを自前実装するより、CLIの仕組みに乗った方が圧倒的に速く、安定して動くものが作れた。