DisPatch:ローカルで動作するAIエージェント用の自己ホスト型チャットアプリ
- カテゴリ
- AIとローカルLLM
- 公開日
- 2026年7月11日
- 更新日
- 2026年10月9日
- 著者
- Jacob Lloyd — プロジェクト完了後、AIの支援を受けて執筆
- 読了時間
- 約23分で読めます
かんたんに言うと: DisPatchは、ご自身のコンピュータ上で動作するチャットアプリであり、家の中にあるあらゆるスマートフォンやノートパソコンから利用できます。このアプリを使って、同じくそのコンピュータ上で動作しているAIアシスタントと会話したり、ファイルを送信してアシスタントに処理させたりすることも可能です。強力な機能を持つAIアシスタントはPINコードによって保護されており、家族共用のデバイスからは安全な範囲内の機能のみが利用できるようになっています。
DisPatchとは、私が自作したアプリケーションです。これにより、自分のAIエージェントと会話するためだけに30個ものコンテナからなる Rocket.Chat 環境を運用する必要がなくなりました。このアプリはPythonのプロセス1つとSQLiteファイル1つだけで動作します。家の中のどのデバイスからでもアクセス可能で、エージェントたちは人間と同様にスレッド形式で返信してくれます。また、私がスマートフォンから送ったファイルも、エージェントが稼働しているマシンにそのまま届きます。バージョン2.0.0は2026年10月9日にリリースされました。ダウンロードできるのは3MB程度のZIPファイルで、その中に含まれるテストスイートでは1,915件のテストがすべて正常に完了します。このページでは、DisPatchの機能や返信内容がどのようにしてユーザーのスマートフォンに届くのか、またPINロック機能がいかにして家族用タブレットから実際に操作可能なエージェントへのアクセスを防いでいるかについて説明します。
要約
- 概要: ローカル環境で動作するAIエージェント用のウェブチャット機能(OpenClawゲートウェイや、任意のモデルAPIも利用可能)に加え、ドラッグ&ドロップによるファイル転送機能も備えており、ホストにアクセスできるどのデバイスからでも利用できます。
- 費用: 0円です。SaaSの利用料やアカウント登録も不要です。
- 必要環境: Python 3.12以降および
uvがインストールされたLinux、またはDockerが必要です。ツールを利用するエージェントを使いたい場合のみOpenClawも必要になります。 - 提供機能: 各ボットごとのスレッド管理、停止ボタン付きのリアルタイム返信表示、再生成・編集機能、ファイルドロップ機能、埋め込み用ツールやアプリ、10種類のテーマ、PINで保護されるサーバー側のセーフモード、そして定期処理用のプッシュAPIなどが利用できます。
- ダウンロード: 2.0.0リリース版であり、公開リポジトリ内の
v2.0.0タグと同一の内容です。ライセンスはMITです。
機能の概要
掲載されているスクリーンショットは、ダミーデータを用いた隔離環境でのものです。最初の4枚はバージョン2.0の画面です。セーフモードおよびファイルサーバーのスクリーンショットは7月版のもので、これらの機能も2.0において同様に動作します。





- すべてのデバイス間で共通するスレッド機能:スマートフォンでも会話途中から続きを読め、履歴もそのまま維持されます。スレッドはピン留めやアーカイブ、名前変更、検索も可能です。
- ストリーミング形式のMarkdown返信:構文強調付きコードやコピーボタン、ライトボックス付きの画像・動画プレビュー、さらにインストール可能なPWAも備わっています。
- リアルタイム作業パネルと停止ボタン:ステータス欄には現在何を処理中か(モデルの読み込みやツールの使用など)が表示されます。入力中の点々をクリックすればエージェントの思考過程やツール呼び出し状況も確認でき、停止ボタンで処理を中断することも可能です。
- 通常のチャットアプリ機能:再生成機能により以前の返答が維持され、メッセージの編集やそこからの再実行、引用、評価付け、コンテキストからの削除も可能です。各スレッドごとに使用モデルや思考レベルを指定でき、コンテキストの使用率はメーターで確認できます。
- 下書き機能およびオフライン送信キュー:未送信の下書きは該当スレッドに保持され、ネット接続がない状態でも送信待ちとなり、接続復帰後に自動送信されます。
- ツールおよびアプリ機能:「ツール」ボタンを押すとアプリ内で独自プロジェクトを起動できます。静的フォルダやウェブページ、独自バックエンドを持つ信頼できるアプリパッケージも利用可能です。最初のアプリがJob Boardです。
- AIとの対話方法が2通り:設定画面からLM Studio、Ollama、OpenAI、AnthropicなどOpenAI互換のモデルAPIを直接指定することも、OpenClawゲートウェイを介してツールを活用するエージェントを利用することも可能です。
- プッシュ用API:
POST /api/injectにより、定期実行ジョブや他のエージェントからスレッドへメッセージを送信できます。私の場合、朝のブリーフィングもこの仕組みで自動的にスマートフォンに届きます。 - 2段階ロック機能:PINにより通常のエージェント群と「安全」と定義されたサブセットが分離されます。詳細は後述します。
- ホスト用ダッシュボード:システムの健全性、ストレージ状況、データベース整合性やバックアップ状態、さらに設定ミスを指摘するチェックリストも確認できます。
- 8言語対応および10種類のテーマ:右から左へ読むアラビア語も含まれます。
- ローカルファイルビューアー:メッセージ内のファイルパスをタップすると、スマートフォンでもアプリ内でファイルが開きます。ただし事前にフォルダ名を指定する必要があり、機密情報やシステムパスは一切配信されません。
エージェントはスレッド内に画像も投稿できます。ボットが返答文中に [[pic:prompt|caption]] というマーカーを記述すると、即座にプレースホルダーが表示され、レンダリング完了後に実際の画像へと切り替わります。画像はネットワーク上にあるGPUマシンから取得されています。そのマシンが停止している場合は、エラーではなくプレースホルダーにその旨が記載されます。
ファイルサーバー
私が最もよく使う機能です。どのデバイスからでもDisPatchを開き、ファイルをそこにドラッグすると、そのファイルがサーバー上に保存されます。ファイルは日付ごとにグループ化され、画像や動画にはサムネイルも表示されます。

この機能の利点は、ファイルがエージェントが動作している同じマシン上に保存される点です。例えば、スマートフォンからアップロードした写真も、数秒後にはエージェントがディスクから読み取り可能になります。そのため、クラウドストレージやケーブルなしで、ファイルに説明を付けたり整理したり、ウェブサイトに掲載することも可能です。
コード側では、以下のような障害発生パターンを防ぐ仕組みが備わっています:
- FastAPIがデータを受け取り始める前にアップロード可能なサイズをチェックするため、スマートフォン側からホストのメモリを使い果たすことはありません。
- デフォルトでは総保存容量は20GBに制限されています(
DISPATCH_FILES_TOTAL_MAX。0に設定すると制限が解除されます)。 - 各ファイルは一旦一時ファイルとして書き込まれ、同期された後に正しい場所へ名前が変更されるため、接続が切れても不完全な状態のファイルが残ることはありません。
- ロックされているデバイスからは一方方向のみでファイルを送信できます。そのデバイスからはファイルサーバー内のコンテンツを閲覧したりダウンロードしたりすることはできません。
なぜ既存のUIを使わずに自作したのか
Rocket.Chatも機能しましたが、「テキストを送信して返答を得て、それをあらゆる場所で同期する」というだけの用途のために、データベースや多数のコンテナを用意するのは過剰なインフラ負荷でした。
「自己ホスティング可能なAIチャット」という目的であれば、Open WebUIやLibreChatが一般的な選択肢であり、モデルとの対話機能も優れています。しかし私のニーズは異なっていました。家族全員が共有するスレッドにエージェントも参加させたい、子供が手に取る端末に対してサーバー側で制限をかけたい、スクリプトからHTTPエンドポイントにデータを送信できるようにしたい、そしてファイルがエージェントが読み取り可能な場所に保存されるようにしたい――そうした要件の組み合わせこそがDisPatchです。もし単にモデル用の見栄えの良いフロントエンドが欲しいだけであれば、上記のツールを使う方が良いでしょう。
また、DisPatchにはビルド手順が一切ありません。フロントエンドは純粋なJSモジュールおよびシンプルなCSS/HTMLで構成されており、バンドラーも不要で、npm run buildの実行も必要ありません。当初から「後からAIエージェントが簡単に修正できること」を要件としていました。ファイルを編集してページをリロードするだけで完了するのです。今でも同じ判断を下すでしょう。
ダウンロード内容について
配布されているZIPファイルは dispatch-2026-10.zip という名前で、これが DisPatch 2.0.0 のリリース版です。このファイルは 公開リポジトリ内の v2.0.0 タグとバイト単位で同一内容となっています。今回のバージョンについて計測した結果は以下の通りです。
| 項目 | ZIP内の状況 |
|---|---|
| ダウンロードサイズ | 2,992,647バイト(2.9 MB)。ファイル数は319個で、解凍後は7.9 MBになる |
| SHA-256 | e9824d3c13bb3acd2d2eebcf2b162b14ae26eb86f56ca0e4ac8aa20f686a2398 |
| バックエンド | Python 3.12以降、FastAPI と uvicornを使用。SQLite データベースファイル1つも含まれており、uv で管理されている。backend/app/ ディレクトリ内には36,501行のコードがあり(main.py は12,331行)、同梱されているジョブボードアプリ用にも5,497行が存在する |
| テスト | Pythonによるテストコードが30,726行ある。pytest では 1,915件のテストが成功した。また node --test を用いて473件のフロントエンドテストも実施されている |
| フロントエンド | ビルド不要の純粋なJavaScriptを使用。外部ライブラリやテストコードを除くと約21,000行のコードがあり、さらにCSSおよびHTMLが約9,300行存在する |
| 同梱ライブラリ | marked、highlight.js、DOMPurify。CDN呼び出しは一切ない |
| データ | コードとは別に、デフォルトでは ~/.local/share/local-chat/ ディレクトリ(プロジェクトの旧名称)に保存される。ここにはデータベース、config.yaml、security.yaml、メディアファイルやファイル類、定期的に作成されるデータベースバックアップなどが含まれる |
| ライセンス | MIT |
8月に公開された1.0.0版のZIPファイルからの変更点は以下の通りです。
- 応答処理が高速化され、メッセージの紛失もなくなった。 これまで各メッセージごとに新たな
openclawプロセスを起動していましたが、現在は既に開かれているWebSocket経由でエージェントゲートウェイへ直接メッセージが送信されるようになりました。計測の結果、応答時間は約1秒短縮され(固定オーバーヘッドが2.26秒から1.12秒に)、モデルがテキストを生成するごとに逐次配信されるようになっています。接続が切れた場合やサーバ再起動後でも、ゲートウェイが受け付けたすべての応答が確実に届く仕組みです。 - ライセンスの変更。 1.0.0版ではAGPL-3.0でしたが、2026年8月30日にプロジェクトはMITライセンスへ再ライセンスされ、2.0.0も同様にMITライセンスとなっています。7月および8月版のZIPファイルは従来の場所に残されており、それぞれ独自の
LICENSEファイルが付属しています。 - コーディング用ターミナルが廃止された。 1.0.0版ではブラウザ内で利用可能なオプションのターミナル機能(サーバ側のPTYをxterm.jsへストリーム配信する仕組み)が搭載されていました。私はこれを自ら退職させたコーディングエージェント用に使用していましたが、2.0ではその機能および同梱ライブラリも削除されました。
dshがインストールされている環境では DeepSeek Harness パネルが利用可能となり、同様の用途に使えるほか、ライブセッションの起動・監視・停止も行えます。 - 新機能: 会話制御機能、下書きや送信待ちメッセージ管理、各種ツールおよびアプリ、チェックリスト表、ローカルビューア、画像マーカー、明るい/暗いモード切り替えに代わる10種類のテーマ、さらに静かな印象を与えるメッセージ行などが追加されました。詳細な一覧はリポジトリ内の
CHANGELOG.mdに記載されています。
設定方法
これは、ダウンロードしたZIPファイル内にある README.md および docs/ の内容を要約したものです。これは通常の uv プロジェクトです。
unzip dispatch-2026-10.zip
cd DisPatch-2.0.0/backend
uv sync --frozen
uv run pytest -q # 1,915 passed on my machine
uv run uvicorn app.main:app --host 0.0.0.0 --port 8765
あるいは、Pythonを使わずに設定することも可能です。解凍後のディレクトリの最上位から cp .env.example .env && docker compose up -d と実行してください。deploy/systemd/ には常時稼働用のユニットファイルが含まれており、docs/deploy-bare-metal.md には多くの人が見落としがちな手順、すなわち loginctl enable-linger についても記載されています。この処理を行わないと、SSHセッションを閉じた際にユーザーサービスが停止してしまいます。
- まずPINを設定してください(歯車アイコン→「セキュリティ」)。上記の両コマンドは
0.0.0.0でリスニングしているため、PINが設定されていない間はネットワーク上の誰でも完全にアクセス可能になります。試用時には--host 127.0.0.1(Docker利用時はBIND_ADDR=127.0.0.1)を指定し、インターネット向けにポートを開放しないようにしてください。 - データディレクトリ内の
config.yamlを編集し、ボットのidがご自身の OpenClaw エージェントのIDと一致するように設定してください。このファイルが変更されると自動的に再読み込みされます。 - 設定値は
DISPATCH_という接頭辞を持つ環境変数から取得され、その一覧は.env.exampleに記載されています。具体的にはHOST、PORT、DATA_DIR、AGENT_TIMEOUT(900秒)、MAX_CONCURRENCY(3)、GATEWAY_WSなどです。 DISPATCH_MAX_CONCURRENCYは同時に実行できるエージェントの数を制限します。デフォルト値の3はローカルモデルを動かしているマシンを保護するための設定です。4件目以降の処理も破棄されることはなく、順番待ちとなります。
DisPatchにはOpenClawが同梱されていません。エージェント用には、同一ホスト上に OpenClawゲートウェイ が存在することを前提としています。もしゲートウェイが存在しなくてもアプリケーションは起動し、警告ログを出力するだけです。スレッド処理、インジェクトAPI、ファイルサーバー、PINロック機能、各種ツール、モデルAPIなどはすべて正常に動作します。私自身もこのZIPファイルの動作確認には「新しく解凍→uv sync→空きポートを利用→ゲートウェイなし」という手順を用いています。
返信がユーザーに届くまでの流れ
本システムでは特に障害時の対応に力を入けて設計しました。例えば返信中に接続が切断された場合、サーバーが再起動した際に処理中のデータがあった場合、あるいはサブエージェントが10分後に返答した場合などです。DISPATCH_GATEWAY_WSの設定によって通信手段が決まります。この値を設定しない場合は各メッセージごとにopenclaw agentプロセスが起動され、処理終了後に返信内容が送り返されます。shadowモードでは実際には配信せずに配信内容を記録するだけです。1と設定するとメッセージはゲートウェイのソケット経由で送られ、返信も同様にそこから届きます。私は1を使用しています。配布されているZIPファイルではこの機能は無効になっているため、設定を有効化するまで返信の到着方法は変わりません。
ソケット機能を有効にすると、ゲートウェイが受け付けたすべての処理は「進行中」テーブルに記録され、ゲートウェイ自身が提供する「この処理の完了を待つ」機能によって状態が更新されます。ライブストリームが途切れても処理終了時には返信内容が届きますし、サーバー停止時に進行中だった処理も再起動時に復元されます。ゲートウェイが一度も受け付けていない処理のみが再送されるため、同じ返信が二重に送られることはありません。
安全策としてのトランスクリプトスキャンについて補足します。この機能はOpenClawのセッションファイルを読み取りますが、新しいバージョンのOpenClawではセッション情報がデータベースに保存されるようになっています。その場合DisPatchは起動時に「ファイルベースの安全策は利用できません」とログ出力し、ソケット経由での配信が用いられます。この点もソケット機能を有効化すべき理由の一つです。
どちらの経路から返信が届こうとも、すべての候補となる返信は共通のパイプラインを通過します。そこでは既に配信済みかどうかが確認され、重複して検出された場合でも一度だけ表示される仕組みになっています。CLI処理でエラーが発生した際にも、スキャン処理は実行されます。なぜならモデルはプロセスが異常終了する前に既に回答内容をトランスクリプトに書き込んでいるケースが多いからです。
二段階ロック:通常モードとセーフモード
ロック解除された側は単なるおもちゃではありません。そこに存在するエージェントたちはコードを編集したり、私の公開中のウェブサイトに変更内容を反映させたり、各種ツールを実行したり、そのマシン自体を管理したりできます。一方でロックされている側では一般的なチャットのみが可能です。質問やサポート目的には役立ちますが、上記のような操作は一切できません。家族用の電話機は必ずしも大人の手に渡るわけではないため、サーバー側でこのロックを強制しています。リクエストがどのように処理されるかは以下の通りです。
デバイスを記憶する機能は、セキュリティ設定で有効化しない限りオフ状態です。有効化すると記憶期間が30日間となります。「ロック」ボタンを押すと記憶済みのデバイス情報が消去され、PINを変更するとすべての記憶済みデバイス情報が失われます。サーバー側では最大10台分の記憶済みデバイス情報のみ保持可能です。もし信頼済みデバイス情報が保存されたファイルに不具合が生じても、どのデバイスも信頼対象とはならず、それでもPIN認証は引き続き利用できます。
万が一ロック状態になってしまった場合でも、復帰方法は二通りあります。リカバリーコードファイルを使うか、security.yamlファイルを直接編集する方法です。もしsecurity.yamlファイル自体が破損している場合には、LAN上の誰に対しても全機能が利用可能になるのではなく、アプリは引き続きロック状態を維持します。これは意図的にそう設計された仕様です。
注意点
- inject APIでは
contentというフィールドを使用します。POST /api/injectでは{"bot_id": "...", "content": "..."}の形式が求められます。私自身も含め、textを送信してしまうことが最も一般的な統合時のミスでした。 - 長いメッセージはCLI引数として渡さないでください(これはフォールバック処理においても重要です)。Linuxでは単一の引数のサイズが128 KB(
MAX_ARG_STRLEN)までに制限されており、長いメッセージを渡すとE2BIGエラーで処理が失敗します。そのため、コードではメッセージを一時ファイルに書き込み、--message-fileを指定しています。この仕組みを引数渡しに「簡略化」してはいけません。 - セッションキーは小文字ですが、ボットIDはそうではありません。 OpenClawではセッションキーを小文字化しますが、ボットIDには大文字・小文字の混在も許容されます(
Alpha、My_Botなど)。大文字小文字を区別する検索を行う場合、他の機能は正常に動作していても、パネルの動作や返答の送信が静かに停止してしまいます。対処法としては、完全一致を優先し、それがなければ小文字化した値を使用します。 - CLIでは最後のブロックのみが返されます。 ソケットが利用できない場合、ツール呼び出しの間にエージェントが発言した内容はトランスクリプト内にのみ存在します。このためトランスクリプトの処理機能があり、ソケットが有効な状態でも念のため実行されます。
- WebSocketではOriginを確認してください。 HTTPミドルウェアはWebSocketルートでは実行されません。DisPatchでは
OriginのホストがHostと一致するかどうかを即座に検証し、一致しない場合はハンドシェイクを拒否します。これによりクロスサイトでのWebSocket乗っ取りを防ぎます。ブラウザ以外のクライアントからはOriginが送信されないため問題なく通過します。 - アップロードされたSVGファイルを画像として配信してはいけません。 SVGファイルを直接開くと、自身のオリジン内でスクリプトが実行可能となり、結果的に保存型XSSの脆弱性につながります。DisPatchではSVGをメディアとして受け付けず、サンドボックス化されたCSPおよび
nosniff属性付きで配信します。 - サービスワーカーの更新により、古いタブでは旧バージョンのJavaScriptが使われる場合があります。 これはまるで不具合のように見えます。対処法としては
controllerchangeイベント発生時に自動的にページを再読み込みするようにします。
アイデアの再現方法
たとえ私の提供するzipファイルを実行しなくても、このアーキテクチャは4つの交換可能なコンポーネントから成り立っています。
- チャットバックエンド:会話履歴を管理する部分です。小さなウェブサーバー(ここではFastAPIおよびWALモードのSQLiteファイル)と、リアルタイム更新用のWebSocket、そしてメッセージを送信するための通常のHTTPエンドポイントから構成されます。これによりデータベースや多数のコンテナが不要になります。
- エージェントゲートウェイ:バックエンドが通信する部分です。ここではOpenClawを使用していますが、セッションIDとメッセージを受け取りテキストを返す機能さえあれば何でも構いません。別プロセスとして動作するため、クラッシュしたりアップグレードされたりしてもチャットアプリ自体は停止しません。
- モデルサーバー:ゲートウェイの背後で動作するもので、LM Studio、vLLM、Ollamaなどが該当します。これらはゲートウェイ側の責任範囲であり、チャットアプリ側の問題ではありません。
- 任意のメディアサービス:例えば別マシン上で動作する画像生成ツールなどで、エージェントがツールとして呼び出すものです。バックエンドはその結果を単に保存するだけです。
これらの知見は、使用している技術スタックに関係なく応用可能です:
- サーバー側でロックモードを適用し、CSSでは設定しないこと。
- 「返信がユーザーに一度だけ届く」ことを配信上の課題として捉え、重複防止処理を実装すること。
- AIエージェントがフロントエンドのメンテナンスを行う場合は、ビルド不要な状態を維持すること。
関連記事:OpenClawを利用した私のローカルAIエージェントスタック · DeepSeek Harnessの初見 · ローカルAIエージェントボックスを使った私のウェブサイト管理 · LLMアシスタントの設定方法
ダウンロード
個人利用は無料です。役に立ったら、コーヒーをおごってもらえると嬉しいです。