マルチノードアーキテクチャ
概要
マルチノードアーキテクチャは、複数のデバイスを連携させ、**「A デバイスで対話し、B デバイスで実行する」**という分散型のエクスペリエンスを実現します。Gateway は中央スケジューリングハブとして、すべてのノードの登録、検出、およびインテリジェントルーティングを管理します。
アーキテクチャ全体像
┌─────────┐ WebSocket ┌─────────────┐ WebSocket ┌──────────┐
│ Web 端 │ ◄────────────► │ Gateway │ ◄────────────► │ APP 節点 A│
│(Browser) │ │ (Node.js) │ │ (macOS) │
└─────────┘ │ │ └──────────┘
│ - 節點註冊 │
│ - 智能路由 │ ┌──────────┐
│ - 消息轉發 │ ◄────────────► │ APP 節點 B│
│ - 權限驗證 │ │(Windows) │
└─────────────┘ └──────────┘

ノードタイプ
| タイプ | 識別 | 説明 | 実行能力 |
|---|---|---|---|
| Human | Web ブラウザ | Web 側ユーザーノード | Agent Engine なし |
| Agent | APP デスクトップ | 完全な Agent 実行ノード | あり(ローカル Sidecar) |
| Action | 自動化ノード | 無人実行 | あり |
| Monitor | 監視ノード | ステータス監視 | なし |
ノード登録情報
各ノードは Gateway に接続する際、以下の情報を登録します:
| フィールド | 説明 |
|---|---|
nodeId |
一意のノード識別子 |
displayName |
表示名 |
platform |
オペレーティングシステム(macOS / Windows / Linux / Browser) |
version / coreVersion / uiVersion |
バージョン情報 |
deviceFamily / modelIdentifier |
デバイス情報 |
caps |
能力文字列の配列 |
tools |
利用可能なツールの説明リスト |
commands |
実行可能なコマンドリスト |
description |
ノード能力の説明テキスト |
scope |
可視範囲(account / enterprise) |
ノードの可視範囲
| 範囲 | 可視ルール | 説明 |
|---|---|---|
| account | 同一 userId のみ可視 | 個人ノード、組織を跨いで利用可能 |
| enterprise | 同一 orgId のすべてのメンバーが可視 | 企業ノード、組織内で共有 |
account レベルのノード:ユーザーが複数のデバイスでログインし、任意のデバイス間でタスクをスケジューリングできます。
enterprise レベルのノード:組織内の共有実行リソースであり、すべてのメンバーが Gateway を通じて利用できます。
Gateway インテリジェントルーティング
Web 側のユーザーが対話を開始すると、Gateway は3 段階のフォールバック戦略を用いて、最も適切な実行ノードを選択します:
第 1 段階:LLM セマンティックルーティング
OpenAI API を使用してユーザーの意図を分析し、最適なノードをマッチングします:
| 入力 | 説明 |
|---|---|
| ユーザーメッセージ | ユーザーが送信した対話内容 |
| ノードリスト | 各ノードの名称、説明(最大 500 文字)、ツールリスト(最大 15 項目) |
LLM の戻り値:{nodeId, confidence, reason}
セキュリティ対策:
- ノードの説明は DATA として処理し、命令として実行しない
- ノードの説明を介したプロンプトインジェクションを防止
注意:現在、LLM ルーティングはまだモデルが構成されておらず、自動的に第 2 段階へフォールバックします。
第 2 段階:BM25 キーワードマッチング
BM25 アルゴリズムに基づき、ユーザーのクエリとノードの説明に対してキーワードマッチングを行います:
| パラメータ | 値 |
|---|---|
| k1 | 1.5 |
| b | 0.75 |
| 中国語サポート | 文字レベルの分かち書き(一-鿿) |
スコアが最も高いノード、または null(マッチなし)を返します。
第 3 段階:最新接続によるフォールバック
connectedAtMs タイムスタンプに従って最後に接続されたノードを選択し、常にフォールバック結果があることを保証します。
リモート実行
対話フロー
1. Web 端發送 chat.send 到 Gateway
2. Gateway 執行智能路由,選擇目標節點
3. Gateway 轉發消息到 APP 節點
4. APP 節點啟動 Agent Loop 執行任務
5. 執行過程中的流式事件通過 chat.event 回傳
6. Web 端實時渲染執行過程
リモートツール呼び出し
1. 主 Agent 通過 dispatch_multi_node_agent 指定遠程節點
2. Gateway 發送 node.invoke.request 到目標節點
3. 遠程節點啟動獨立 Agent Loop
4. 完成後通過 node.invoke.result 返回結果
添付ファイルの Hoisting(2026-04 更新)NEW
ノード間ディスパッチ時、メッセージにインライン base64 添付ファイル(画像、ドキュメントなど)が含まれる場合、システムは自動的にクラウドストレージへアップロードし、URL 参照に変更します:
- 理由:Gateway RPC メッセージが過大になり転送に失敗するのを回避するため
- タイミング:リモートディスパッチ前に自動実行され、ユーザーは意識しない
- OS パスの正規化:オペレーティングシステム(macOS/Windows/Linux)を跨いでディスパッチする際、パス区切り文字を自動変換
権限ポップアップの配信ルール
ノード間実行時、ツール権限ポップアップを「どちら側で表示するか」は重要なプロダクト上の意思決定です —— 「ユーザーが速やかに応答できること」と「権限を超えた機密操作のトリガーを防ぐこと」との間でバランスを取る必要があります。
同一アカウントのシナリオ(呼び出し側と実行側が同一アカウント)
| シナリオ | ポップアップの位置と利用可能なアクション | ステータス |
|---|---|---|
| node-A APP → node-B APP(同一アカウントで両端にログイン) | Gateway を通じて node-A にポップアップ情報をプッシュし、node-A がポップアップを表示して「許可 / 常に許可」をクリック可能 | ⚠️ クロス端配信は未実装 |
| node-C Web → node-B APP(同一アカウントで Web から APP を呼び出し) | ポップアップは node-C Web 側で表示・応答する | ✅ 実装済み |
| IM チャネルメッセージ(デフォルトターゲット node が同一アカウントで発起、DingTalk/Feishu/Telegram など) | channel がフォーム/ボタンのインタラクションをサポートする場合、権限ポップアップは当該 channel のインタラクティブスタイルに変換される(AskUserQuestion により実現)。サポートしない場合はデフォルトで拒否(5 分待たない) |
✅ 実装済み |
異なるアカウントのシナリオ(enterprise node のアカウント間呼び出し)
- node-D は enterprise タイプのノード(同一組織内で他ユーザーから発見可能)で、現在 user001 でログイン中
- node-E(user002 でログイン、APP または Web 側いずれも可)が Gateway を通じて node-D にメッセージを送信
- node-D の実行中にツール権限が必要になった場合:
- ポップアップは node-D 本体のインターフェースにのみ表示され、node-E へクロス端配信されない
- 誰も応答しないまま 5 分を超えるとデフォルトで拒否
設計の動機
| ルール | 動機 |
|---|---|
| 同一アカウントの複数端はクロス端で認可可能 | ユーザーはいずれの端でも認可リクエストを処理でき、ある端が手元にないためにタスクがブロックされるのを回避 |
| アカウント間の enterprise はクロス端配信しない | 被呼び出し側の本体に厳格に限定し、外部アカウントがリモートメッセージを介して機密操作(ローカルファイルの読み書き、Bash 実行など)をトリガーするのを防止 |
| IM チャネルがインタラクションをサポートしない場合はデフォルトで拒否 | IM 側でポップアップを表示できない場合、リクエストが宙吊りになり Agent フローをブロックするのを回避 |
| アカウント間の 5 分タイムアウト | 「ユーザーが一時的に離席している可能性」と「タスクが長期間宙吊りになるのを回避すること」のバランスを取る |
実装上の注意:node-A APP → node-B APP の同一アカウントによるクロス端配信リンクは、現時点では存在しません。このパスに関わる要件では、Gateway ルーティング + APP 側の受信/表示ロジックを新規追加する必要があり、すでに利用可能であると想定しないでください。
アカウント間 Gateway 分離 NEW
権限ポップアップの位置ルールに加えて、データアクセス自体にも分離があります:
個人記憶の分離
ノードが enterprise 範囲でアカウントを跨いで呼び出される場合:
- アカウントレベルの記憶(userId 紐付け):❌ アクセス不可
- 企業レベルの記憶(orgId 紐付け):✅ アクセス可能
- セッションレベルの記憶(今回のセッション):✅ アクセス可能
isRemoteSession フラグにより記憶クエリ時に強制的にフィルタリングされます —— アカウント間の呼び出し元はmemory_query を通じてターゲットノード所有者の個人記憶を読み取ることはできません。
なぜこのように設計するのか
- プライバシー保護:あなたの個人的な好み、作業習慣、アカウント情報は、同僚があなたのノードを呼び出すことで読み取ることはできない
- 企業共有:組織の技術スタック、規範などの企業ナレッジは正常に共有され続け、コラボレーションに影響しない
- コンプライアンス要件:GDPR などのプライバシー法規が求める「最小権限」の要求を満たす
対話アイコンの区別
セッションリストでは、異なるソースの対話に異なるアイコンが表示されます:
| アイコン | 意味 |
|---|---|
| コンピューターアイコン(LocalComputerIcon) | ローカル APP ノードが発起または実行 |
| サーバーアイコン(NodeIcon) | リモートノードが実行 |
| プラットフォームアイコン(Telegram/WeChat など) | IM チャネル由来 |
isLocalInitiated、targetNodeId、sourceChannel フィールドに基づいて判定します。
WebSocket プロトコル
接続ハンドシェイク
1. Client → Gateway: connect.challenge
2. Gateway → Client: challenge(含 nonce)
3. Client → Gateway: connect(含簽名的 JWT)
4. Gateway → Client: hello-ok(確認連接)
ハートビートによる保持
- tick メカニズム:定期的なハートビートで接続を維持
- 切断時の再接続:指数バックオフでリトライ
- 期限切れのクリーンアップ:Gateway が定期的に期限切れのノードセッションをクリーンアップ
フロー制御
- Delta スロットリング:SSE ストリーミングイベントを 150ms 集約し、WebSocket フレーム数を削減
- Run TTL:10 分でタイムアウト、1 時間ごとに期限切れの Run をクリーンアップ
ユーザーにとっての意味
マルチノードアーキテクチャにより、どこからでもタスクを開始し、最も適切なデバイスに実行させることができます。
典型的なシナリオ:
- カフェでスマートフォンのブラウザ(Web 側)から、オフィスのコンピューターのファイルにアクセスする必要があるタスクを開始 → Gateway が自動的にタスクをオフィスの APP ノードにルーティングして実行
- チーム内の 1 台の高性能サーバー(macOS Pro)で常に APP を起動し enterprise に設定 → すべてのチームメンバーが自分のブラウザから重い処理をディスパッチできる
あなたが感じられるエクスペリエンス:
- Web 側で対話を開始すると、システムがオンラインの APP ノードを自動的に選択して実行する
- セッションリストに異なるアイコンが表示され、対話がローカル実行かリモート実行かがわかる
- APP ノードがオンラインでない場合、Web 側は Agent 能力を必要とするタスクを実行できないと通知する
- 複数のコンピューターに APP がインストールされている場合、どのコンピューターで実行するかを手動で選択できる
注意が必要な点:
- Web 側は Agent タスクを単独で実行できず、少なくとも 1 つの APP ノードがオンラインである必要がある
- リモート実行の対話にはネットワーク遅延がある(Gateway とノード間のネットワーク品質に依存)
- ノードを enterprise 範囲に設定すると、チームメンバーがあなたのデバイスにタスクをディスパッチできる —— ただしあなたの個人記憶にはアクセスされない
関連ドキュメント
- Work ノード管理 — 管理画面でオンラインノードを確認
- ランタイムセキュリティ — ノード — ノードのセキュリティ設定
- サブエージェント — ノード間ディスパッチ — dispatch_multi_node_agent
- 三次元記憶システム — アカウント間の記憶分離
