この記事では、最近ほぼ完成間近となったあるプロジェクトについて記録しておこうと思います:weixin-household-gateway。
プロジェクトの所在地:
github.com/thekfjie/weixin-household-gateway
簡単に言えば、これはCodex/AIのバックエンドをWeChatに接続するホームゲートウェイです。
これはウェブベースのエージェントでも、単なるチャットボットでもなく、自社のサーバーに展開されたWeChatへの入り口です。WeChat内で、サーバーやコード、ファイル、添付ファイルの処理を任せることができるほか、家族が比較的安全な方法で一般的なAIとの会話を行うことも可能です。
すごく複雑だ……
1. なぜこれを行うのか?
当初の考えは実に単純なものでした。普段、ブログやAPI、音楽解析、その他さまざまなサービスなど、多くのものをサーバー上で動かしています。外出先で、AIにログの確認や設定の変更、ファイルの分析などを依頼したい場合、最も自然な入り口はウェブページではなく、WeChatなのです。
さらに、家族にも自分のモデルを共有したいという考えもあります。ネットワーク環境や操作・学習のハードルなどを気にすることなく、家族が特定の大型モデルを最も手軽に利用できるようにしたいのです……当然ながら、WeChatが最適な解決策となりました。
WeChatは常にオンラインだからです。
一方、WeChatは今年の初めに、公式API「WeChat OpenClaw(🙏)」の提供を開始しました。
しかし、openclawのようなトークンを大量に消費するサービスは使いたくない。私の週ごとの利用上限では到底足りていないだろう(🙏)。それに、エージェントをそのままWeChatに投入するのは明らかに無理だ。WeChatでのチャット体験は、ウェブコンソールとは全く別物だからだ:
- WeChatでは、1つのメッセージをその場で更新することはできません。
- WeChatにはストリーミング出力機能がありません。
- 情報が断片的すぎると、タイムラインが埋め尽くされてしまう。
- 家族が使用する際は、admin と同じ権限を持つことはできません。
- サーバーの操作、ファイルの読み取り、sudo、Dockerといったものについては、その境界を明確に区別する必要があります。
したがって、このプロジェクトの最終的な目標は、「ウェブエージェントをWeChatに移植する」ことではなく、WeChat環境により適したAIゲートウェイを構築することにある。
2. 今はどんな姿をしているのでしょうか?
全体としては、Linux サーバー上にデプロイされた TypeScript / Node.js による単一サービスとなっています。主な構成は概ね以下の通りです:
flowchart TB
USER["微信用户"] --> ILINK["iLink 轮询"]
ILINK --> WORKER["WechatWorker"]
WORKER --> ROUTE{"角色与任务路由"}
ROUTE -->|admin| ADMIN["Admin ACP 持久会话"]
ROUTE -->|family 普通聊天| API["Family API 直连"]
ROUTE -->|family 复杂任务| FACP["Family ACP 非持久任务"]
ADMIN --> CODEX["Codex / ACP / CLI"]
API --> MODEL["OpenAI-compatible API"]
FACP --> CODEX
CODEX --> FILTER["输出过滤与分段"]
MODEL --> FILTER
FILTER --> SEND["iLink 回复微信"]
WORKER --> DB["SQLite 会话与消息"]
WORKER --> FILES["inbox / office / outbox 工作区"]
おそらく次のようなことを行う:
- iLinkのWeChatメッセージをポーリングする。
- アカウントの役割の識別:
admin/family。 - タスクの種類に応じて、API または ACP を選択してください。
- WeChatの添付ファイルをダウンロードし、管理対象ワークスペースに保存します。
- Codex を呼び出してタスクを処理する。
- 結果を、WeChatのユーザーが受け入れやすい形で返信してください。
- SQLite を使用して、アカウント、セッション、メッセージ、添付ファイルの記録を保存します。
デプロイ先ディレクトリも、現在ではほぼ固定されています:
/opt/weixin-household-gateway
/var/lib/weixin-household-gateway
inbox/
office/
outbox/
~/.codex
その中でも inbox 受信メールの添付ファイルで、office 中間処理エリアであり、outbox WeChatに送信できる完成ファイルです。
3. なぜ「admin」と「family」を分ける必要があるのですか?
これは、このプロジェクトにおいて私が最も重要だと考えている点です。
admin これは私自身が使うためのものです。デフォルトではACPの永続セッションを採用しており、サーバーの運用・保守、コードの修正、長時間かかるタスク、多段階のツール呼び出しに適しています。より多くのコンテキストを把握できるほか、私が明示的に設定すれば、より高度な操作を実行することも可能です。
family これは家族向けのものだ。デフォルトでは直接接続APIが優先され、通常のチャット、画像認識、簡易なQ&Aなどはすべてこの経路を通る。圧縮ファイル、Officeファイル、複雑な添付ファイル、ツールによる処理が必要なタスクに遭遇した場合にのみ、ACPにエスカレーションされるが、その際は非永続的なACPとなる。
その理由は極めて現実的なものです:
- 家族がサーバーの運用管理権限に触れる必要はありません。
- 通常のチャットにおいて、「エージェントのように振る舞う」という理由だけで、わざわざツールチェーンを迂回する必要はない。
- API パスの方が高速であり、コンテキストキャッシュの維持も容易です。
- ACPはツールタスクに適していますが、ファミリーのデフォルトのセッションモードにしてはいけません。
最終的には、比較的使いやすいルールになりました:
| シーン | デフォルトのパス | 説明 |
|---|---|---|
| admin 運用・保守/コード/長期タスク | ACP 持続セッション | 自分用に |
| family 一般的なチャット | API 直接接続 | より速く、より安全に |
| family ファイル/添付ファイルの複雑なタスク | ACP 非永続的 | 使用後はAPIメイントラックに戻る |
この設計は途中で何度も変更されました。特に「family」のコンテキストに関しては、当初は直近の数件のメッセージしか取得しておらず、ユーザー体験が非常に悪く、キャッシュも機能しませんでした。その後、独立したAPIコンテキストトラックに変更し、文字数の上限を約10万文字に設定しました。これにより、プロンプトキャッシュキーを可能な限り安定させ、毎回新しいゲームを始めるような状態にならないようにしました。
4. WeChatには真のストリーム出力機能はない
これもまた、かなり悩ましい点だ。
ウェブページではトークン単位で更新ができ、端末では一行ずつスクロールできます。しかし、WeChatはそうではありません。WeChatではメッセージを送信することしかできず、同じメッセージをスマートに編集することはできません。無理にトークンごとに送信しようとすると、画面がメッセージで埋め尽くされてしまいます。
そこで最終的に採用したのは、WeChatにより適した「準ストリーミング型」でした:
- admin ACP では、プロセス出力がデフォルトで有効になっています。
- family ACP では、デフォルトでプロセス出力は行われません。
- family API では、デフォルトで事前セグメンテーションは有効になっていません。
- 最終的な回答は、やはり長さに応じて区切り分けられます。
現時点での大まかな戦略は以下の通りです:
| モード | デフォルトのプロセス出力 | 説明 |
|---|---|---|
| admin-acp | 開 | 何をしているかが一目でわかるため、長時間の作業に適している |
| family-acp | 関 | デフォルトでは最終結果のみを表示します |
| family-api | 関 | 通常のチャットを優先するなど、完全な回答 |
この場所について、当初私も「本当にストリーム形式にできるだろうか」と考えたことがありましたが、その後、WeChatのチャットにおいて最も重要なのは、技術的にストリーム形式に似ているかどうかではなく、ユーザーにとってその操作感が自然かどうかであることに気づきました。
例えば、admin の長期タスクでは、一定の間隔を置いて1つずつ送信されることを許容できます:
我看完主链路了,现在项目是一个 TypeScript/Node 单服务。
30秒間「考え中」と表示されたままただ待つよりも、ずっと気楽だ。
しかし、family側でも同じように投稿すると、まるでロボットが内部の施工ログをすべて吐き出しているかのように、不自然に見えてしまいます。
そこで、最終的には役割ごとに分類することになりました。
5. ACP、このルートが一番きつい
このプロジェクトで本当に苦労する部分は、基本的にACPに集中している。
ACPは、単に「モデルを1回呼び出して、文字列を1つ取得する」だけのものではありません。ACPは絶えずイベントを出力します:
- status
- thinking
- responding
- tool progress
- visible message run
- usage update
- prompt result
その中でも最も厄介なのは:visible_message_run 必ずしも最終的な答えとは限りません。それは手順の説明である場合もあれば、最終的にユーザーにとって役立つ文章である場合もあります。
そこで、プロジェクトでは専用のコレクターを実装し、ACPイベントを振り分け、どれをWeChatに送信すべきか、どれを内部状態として扱うべきか、どれを最終的な回答とするべきかを判断するようにしました。
その後、ある典型的な問題にも直面しました。それは、ACPの最終ステップの結果が必ずしも適時に出力されるとは限らないということです。もしプロジェクトがタイムアウトになった時点で直ちに例外として判定されてしまうと、フロントエンドはこの処理を待たずに終了してしまい、その後の結果を受け取ることができなくなってしまいます。結局、タイムアウト、キャンセル、処理完了といった動作を見直し、ユーザーに「突然消えてしまった」という印象を与えないよう、少なくとも明確なステータスを表示できるようにする必要がありました。
こういう問題は厄介だけど、現実にはよくあることだ。
AIをWeChatに組み込んだ瞬間、それは単なる関数の呼び出しではなく、人間と会話している存在になるからです。AIが固まったり、沈黙したり、同じことを繰り返したり、文が不自然に区切られたりすると、ユーザーはそれを敏感に感じ取ります。
6. 権限やサンドボックスに関しても、かなり多くの失敗を経験した
このプロジェクトでは、実際にサーバーを扱うことになります!!!
そのため、権限はいくつかの階層に分かれています:
- プロジェクト独自の役割戦略。
- family の小規模モデルの権限審査。
- Codex ACP への権限リクエスト。
- Linux ユーザーと sudoers。
- Codex独自のサンドボックス。
これらの要素が重なり合うと、ある典型的な状況が生じます:
プロジェクトではすでに admin の sudo 権限が許可されているはずなのに、Codex 自身のサンドボックスではまだブロックされている。
後で気づいたのだが、Linux 上の Codex では、デフォルトのサンドボックスとして bubblewrap/bwrap が使用されており、さらに NoNewPrivs=1。この場合、たとえsudoersのユーザー設定が緩和されていても、sudoが失敗する可能性があります。
そのため、現在のインストーラーでは以下の処理が行われます:
- 検査
bubblewrap/bwrap。 - 必要に応じて、互換性のあるシンボリックリンクを作成する。
- admin 運用モードでの設定
danger-full-access。 - family はこのパラメータを解放しない。
- Codex trusted project は、プロジェクトディレクトリと admin runtime のみを信頼し、それ以外は信頼しません。
/。
ここでの原則も非常に単純です:
「admin」は私の運用管理用の入り口であり、強力であることが求められる。「family」は家庭用の入り口であり、安定性と抑制が求められる。
7. 書類業務:できるが、むやみにやってはいけない
WeChatでは、ファイルを送信するという場面も非常に一般的です。
例えば、圧縮ファイルやWord、Excel、画像などを送信して、AIにその中身を確認してもらいたい場合などです。このプロジェクトでは、添付ファイルを inbox、その後、タイプに応じてACPへのアップグレードを行うかどうかを決定します。
しかし、ここにも落とし穴がある。
例えば、ある時はZIPのようなファイルに見えたものの、実際の内容はRAR5に近いもので、手元のPCには適切なRAR読み取りツールがなかった。モデルはひと回り考えてから、こう考えた。 apt-get update ツールをインストールしたところ、当然ながら「family」ポリシーは許可されませんでした。
そのため、現在の「family」の設計は、より次のような方向性を重視しています:
- 既存のツールで処理できるなら、そうする。
- システムレベルのインストールや、sudo、docker、systemctl などの操作が必要な場合は、むやみに手を加えないでください。
- ユーザーに表示するコンテンツからは、パス、コマンドの断片、および内部推論と見なされる部分をフィルタリングする必要があります。
そうでなければ、家族が単に書類を見たいだけなのに、サーバーの設置現場の写真ばかり送られてきてしまっては、あまりにも抽象的すぎるでしょう。
8. このプロジェクトは最終的に何を解決したのか?
一言でまとめると:
これにより、CodexはWeChat経由で利用可能で、役割分担が可能、展開可能、ファイルや長時間のタスクを処理できる家庭用AIゲートウェイへと変貌を遂げました。
現在、その主な機能は以下の通りです:
- WeChatの複数アカウント連携。
- admin / family の権限設定。
- family 通常チャット優先 API。
- family 複雑なタスクのACPを自動的にアップグレードする。
- admin ACP の永続セッション。
- WeChatに適した段落分けでの出力。
- プロセス出力を設定可能です。
- ファイルのダウンロードとファイルの送信。
- SQLite セッションの保存。
- Codex の構成生成。
- Linux インストールスクリプト、systemd サービス、doctor による自己診断。
- プロンプトテンプレートを外部に配置し、後で変更しやすいようにしています。
また、一見目立たないものの、実際には体験に大きな影響を与える要素もいくつかあります:
/new新しい会話。/outputプロセスの出力を制御する。- APIのコンテキスト予算とキャッシュヒット。
- ACPの最終解答の抽出。
- タイムアウト後のキャンセルと終了処理。
- family の出力フィルタリング。
これらを個別にみるとそれほど華やかではありませんが、それらを組み合わせることで、WeChatでの体験は「使える」というレベルから、徐々に「我慢できる、むしろ使い続けたい」というレベルへと変わっていくのです。
9. まだ不十分な点
もちろん、まだ完璧な製品というわけではありません。
現時点で私が比較的明確に把握している未解決の問題は以下の通りです:
- WeChatのメッセージには、本当の配信確認機能がありません。(ACPのワークフローはやはり少し奇妙です)
- メッセージの取り消し・編集機能はまだ実装されていません。(WeChatはいつこの機能を公開するのでしょうか?!)
- ACPの事象は今後も変化し続ける可能性があるため、それに合わせて対応していく必要があります。
- 「family」のセキュリティポリシーについては、実際の使用状況をさらに観察していく必要がある。(いつも何かしらの奇妙な問題が発生する)
- プロンプトは少しずつ調整していく必要があり、一度で書き上げることは不可能です。(私はプロモの達人ではありませんq)
しかし、少なくともそれは「当然と思われる考え」から、導入・更新・説明・保守が可能なシステムへと変化した。
この道のりは、まさに山登りのようだった。
その過程で、些細だが厄介な落とし穴がたくさんあった:セグメントごとの敵、コンテキストの消失、ACPの終了処理漏れ、sudoがサンドボックスにブロックされる、bubblewrapの名称不一致、APIとACPの切り替えを繰り返す、familyキャッシュへのアクセス失敗……
朗報なのは、そのほとんどがすでに一つずつ押されてしまったということだ。
10. 最後に
とにかく、weixin-household-gateway そろそろ締めくくりに入る段階に差し掛かっていると言えるでしょう。
今後もし開発を続けるなら、メッセージの取り消し、ファイル処理機能、ACPイベントへの対応、そしてより快適な出力体験を中心に、少しずつ改良していこうと思う。基礎部分はもう大丈夫だろう?そう願いたい……
多くの境界を処理しました:
権限の境界、文脈の境界、出力の境界、家族間の利用の境界、そして私自身がこれを維持し続けられるかという境界。
ひとまずここまで。
ようやくこの未完成プロジェクトに小さな看板を立てることができた。

