JCARVIS 連携設計
スタックちゃんは JCARVIS の 物理インターフェース として位置づけられる。 Discord・Slack と並ぶ第3のインターフェースとして、Stack-chan を声・顔・物理通知の出力デバイスとして使う。
全体像
┌────────────────────────┐
│ JCARVIS Brain │
│ (Claude Code (Fable 5 deep tier 選択 / Opus 4.8))│
└────────────┬───────────┘
┌────────┼────────┐
▼ ▼ ▼
┌───────────┐ ┌──────┐ ┌──────────┐
│ Slack │ │Discord│ │ Stack-chan│ ← 本プロジェクト
│ connector │ │ conn. │ │ connector │
└───────────┘ └──────┘ └─────┬─────┘
│ WSS / MCP
▼
┌────────────────┐
│ スタックちゃん本体│
│ (CoreS3+Module-LLM)│
└────────────────┘役割分担
| レイヤ | 担当 |
|---|---|
| 思考・判断 | JCARVIS Brain(タスク種別判定 → Claude/Gemma 振り分け) |
| 音声・顔・物理出力 | スタックちゃん本体 |
| テキストチャット | Slack / Discord |
| タスク管理・メール・ニュース | JCARVIS 側の Skill / MCP ツール |
JCARVIS 側に既に「Smart Routing」「Skill レゴブロック」があるので、スタックちゃん プロジェクト独自の Backend Router は JCARVIS に委譲する形に統合できる。
CommanderEngine — Gemma4 司令官 (2026-07-14, JCARVIS#324/#328)
デバイス経由 (source=stackchan) の応答は ローカル常駐 Gemma4 (E4B, ollama) が最初に受ける:
質問 → Gemma4 が1コールで {"difficulty":"easy|hard","text":"…"} を生成 (ウォーム TTFB 0.3〜1.7s)
├─ easy: text をそのまま SSE 送出 — Agent SDK 不使用 (課金ゼロ)・1〜2秒で発話開始
└─ hard: text = Donack人格フィラー「調べて、分かったら声でお伝えしますね」を即送出
→ Agent SDK (Claude) が JCARVIS 内のプライベート情報 (予定/メール/タスク) で正式推論
→ 完了後 `push.js speak` (bridge MCP `stackchan.speak`) で 100字以内の話し言葉を音声プッシュ- 背景: Agent SDK コールドスタート ≈5〜11s/リクエストが体感遅延の支配項だった (JCARVIS#321 実測)
- フェイルセーフ: Gemma 5s タイムアウト / ollama 停止時は分類スキップで Agent SDK 直行
- デバイス発の対話は往復とも音声で完結 (#donack への本回答投稿はしない。読み上げ失敗時のみ代替投稿)
- チャット経路 (discord/channels) は従来どおり ClaudeEngine 直 (挙動不変)
接続点(MCP / WSS)
状態 (2026-06-10): 下記はすべて実装済み・E2E 疎通確認済み (#86)。 JCARVIS 側
connectors/stackchan/server.js(HTTP/SSE, JCARVIS #114) とconnectors/stackchan/bridge-client.js+scripts/donack/push.js(能動 push, JCARVIS #115) が存在する。
ポートマップ
| ポート | プロセス | 用途 |
|---|---|---|
| 7780 | stackchan-bridge (WSS) | 本体 ⇔ bridge。Tailscale Funnel で /stackchan/ws として公開 |
| 8787 | stackchan-bridge (MCP HTTP) | JCARVIS → stackchan.* ツール呼び出し |
| 8181 | JCARVIS connectors/stackchan/server.js | bridge → JCARVIS Brain (HTTP POST + SSE) |
| 8788 | JCARVIS 管理用 WSS (stack-chan-backend) | 管理画面・デバッグ用に温存 (JCARVIS #112 で 8787→8788 退避) |
起動手順(Mac 側)
# JCARVIS :8181 (Claude サブスクモード)
cd AIEO_JCARVIS
AGENT_PROFILE=donack DONACK_ENGINE=claude node connectors/stackchan/server.js
# 注意: DONACK_ENGINE 未指定だと EchoEngine (テスト用) になる。
# 注意: ユーザーデフォルトモデルが alias (例 "fable") だと SDK が解釈できないことがある。
# その場合 ANTHROPIC_MODEL=claude-haiku-4-5-20251001 等で明示する(音声経路は低レイテンシ優先)。
# bridge :7780 (#72 構成: 発話は firmware on-device melotts)
cd aieo-stack-chan/mac-studio/bridge
TTS_ENGINE=none DEFAULT_BACKEND=jcarvis npm run start
# Funnel 公開
bash ../scripts/tailscale-funnel-setup.sh配信ポリシー(JCARVIS #125/#130)
JCARVIS は source フラグで配信方式を切り替える。bridge が送る source: 'stackchan' は delivery_policy.sources.stackchan.tts: true に該当し、文境界チャンクで SSE 配信される (受信側の逐次発話対応は #87)。
JCARVIS の connectors/stackchan/ を窓口として:
スタックちゃん本体
│ WSS (Tailscale Funnel)
▼
[Mac Studio / stackchan-bridge]
│ HTTP or stdio MCP
▼
[JCARVIS Brain]
│ Skill / Provider Routing
▼
Claude / Gemma 4 / その他必要な実装
1. Mac Studio 側 stackchan-bridge(本プロジェクト)
- WSS サーバー(本体からの接続を受ける、Tailscale Funnel で公開)
- 受信した
agent.runを JCARVIS に投げる - ストリーミング応答を WSS で本体に返す
- 同時に Backend Router 抽象を維持し、JCARVIS が落ちていれば Claude/Codex/Local に直接フォールバック
2. JCARVIS 側 connectors/stackchan/(先方リポに追加が必要)
connector.yaml 例:
name: stackchan
type: physical
interface: http
endpoint: http://localhost:8181/agent
capabilities:
- voice_input # スタックちゃんから声で受け取る
- voice_output # TTS でスタックちゃんから返す
- face_display # Donack キャラ表情で状態表現
- led_notify # LED で通知
- ir_control # 家電操作
description: "M5Stack スタックちゃん物理インターフェース"mcp-servers.json 例:
{
"mcpServers": {
"stackchan": {
"url": "http://localhost:8787/mcp",
"tools": [
"stackchan.speak",
"stackchan.set_expression",
"stackchan.notify",
"stackchan.ir_send",
"stackchan.get_presence"
]
}
}
}3. Backend Router の挙動
スタックちゃん側 Backend Router (#24) は以下を順に試す:
payload.backendが指定されていればそれ- JCARVIS が起動中 → JCARVIS にすべて委譲
- JCARVIS 不在時 → 直接 Claude / Codex / Local LLM
これにより、JCARVIS が使えれば JCARVIS のスマートルーティングを享受できるし、JCARVIS が止まっていてもスタックちゃん単体で動作する。
連携 MCP ツール仕様(JCARVIS → スタックちゃん)
JCARVIS の Skill から呼べるよう、Mac Studio 側で MCP サーバーを立てる。
| Tool | 引数 | 動作 |
|---|---|---|
stackchan.speak | { text, expression? } | スタックちゃんに TTS で発話させる。内部配送は agent.delta+agent.done(会話応答と同一タイプ・#148。firmware は notify を発話処理しないため)。デバイス発 run 進行中は発話が連結され得る |
stackchan.set_expression | { expression: idle|listening|thinking|talking|happy|sad|sleeping } | 表情切替 |
stackchan.notify | { kind, summary, speak?, led? } | LED・キャラ・TTS で通知 |
stackchan.ir_send | { device, action } | Unit IR で家電操作 |
stackchan.get_presence | {} | TMOS PIR の在席状態を返す |
stackchan.get_status | {} | 動作モード・接続状況を返す |
連携 MCP ツール仕様(スタックちゃん → JCARVIS)
スタックちゃんから JCARVIS の機能を呼ぶ:
| Tool | 引数 | 動作 |
|---|---|---|
jcarvis.ask | { prompt, context? } | 自然言語の依頼を JCARVIS Brain に投げる |
jcarvis.tasks | { filter? } | タスク一覧取得(GitLab連携) |
jcarvis.calendar | { range } | 予定取得 |
jcarvis.translate | { text, target } | 翻訳(Gemma 4 local) |
jcarvis.summarize | { text } | 要約 |
JCARVIS は既に Skill レゴブロックを持っているので、これらは JCARVIS 側の既存 Skill を MCP 経由で叩く形になる。
Slack / Discord との一貫性
ユーザーが Slack で「テレビ消して」と書くのと、スタックちゃんに「テレビ消して」と話しかけるのは、JCARVIS から見て同じ意図 として扱える。
JCARVIS の Skill Registry に 発話元コネクタを引数として渡す ことで、応答の戻り先を切り替えられる:
# JCARVIS skill 呼び出し例
skill: ir-control
args:
device: tv
action: off
connector: stackchan # スタックちゃんからの呼び出し → 音声+表情で返す
# slack なら #channel に返す
# discord なら DM に返すデプロイ順序
JCARVIS と本プロジェクトの開発を交互に進める必要があるので、最初はスタンドアロンで動かしてから繋ぐ:
- スタックちゃん本体スタンドアロン — Module-LLM だけで雑談、Mac Studio 不要
- Mac Studio + Backend Router 直結 — Claude/Codex/Local を直接呼ぶ
- JCARVIS bridge を入れて切替 — Backend Router が JCARVIS にも投げ分け可能に
- JCARVIS 側に stackchan connector を実装 — 双方向 MCP 連携完成
- Slack/Discord と同等の一級コネクタ化 — JCARVIS のダッシュボードからスタックちゃんに発話指示できる