Hermes Agent Wiki 非公式・日本語wiki

WeCom(企業向け WeChat)

目次

Hermes を、Tencent の企業向けメッセージングプラットフォームである WeCom(企业微信)につなぎます。アダプターは WeCom の AI Bot 向け WebSocket ゲートウェイを使って双方向にやり取りするので、公開のエンドポイントも Webhook も要りません。

あわせて読む: 受信用の Webhook の設定は WeCom Callback を参照してください。

前提

  • WeCom の組織アカウント
  • WeCom 管理コンソールで作成した AI Bot
  • Bot の認証情報のページにある Bot ID と Secret
  • Python パッケージ: aiohttphttpx

設定

手順 1: AI Bot を作る

hermes gateway setup

WeCom を選び、WeCom のスマートフォンアプリで QR コードを読み取ります。Hermes が必要な権限を備えた Bot アプリケーションを自動で作り、認証情報を保存します。

設定のウィザードは次のように進みます。

  1. ターミナルに QR コードを表示します
  2. WeCom のスマートフォンアプリで読み取るのを待ちます
  3. Bot ID と Secret を自動で取得します
  4. アクセス制御の設定を案内します

別のやり方: 手動で設定する

スキャンでの作成が使えない場合、ウィザードは手入力に切り替わります。

  1. WeCom 管理コンソールにログインします
  2. ApplicationsCreate ApplicationAI Bot と進みます
  3. Bot の名前と説明を設定します
  4. 認証情報のページから Bot IDSecret をコピーします
  5. hermes gateway setup を実行し、WeCom を選んで、聞かれたら認証情報を入力します

手順 2: Hermes を設定する

hermes gateway setup

WeCom を選び、聞かれた内容に答えていきます。ウィザードは次の項目を案内します。

  • Bot の認証情報(QR コードの読み取り、または手入力)
  • アクセス制御の設定(許可リスト、pairing 方式、または誰でも使える形)
  • 通知の届け先となるホームチャンネル

方法 B: 手動で設定する

~/.hermes/.env に次の内容を書き足します。

WECOM_BOT_ID=your-bot-id
WECOM_SECRET=your-secret

# Optional: restrict access
WECOM_ALLOWED_USERS=user_id_1,user_id_2

# Optional: home channel for cron/notifications
WECOM_HOME_CHANNEL=chat_id

手順 3: ゲートウェイを起動する

hermes gateway

できること

  • WebSocket による通信 — 接続を張り続けるので、公開のエンドポイントは要りません
  • 個別チャットとグループでのやり取り — アクセスの方針を設定できます
  • グループごとの送信者の許可リスト — グループごとに、誰が Bot とやり取りできるかを細かく決められます
  • メディアへの対応 — 画像・ファイル・音声・動画のアップロードとダウンロード
  • AES で暗号化されたメディア — 受け取った添付を自動で復号します
  • 引用の文脈 — 返信のつながりを保ちます
  • マークダウンの表示 — 書式付きの応答
  • 返信の対応づけ — 応答が、届いたメッセージの文脈に結びつきます
  • 自動での再接続 — 接続が切れたときは間隔を広げながらつなぎ直します

設定できる項目

config.yamlplatforms.wecom.extra の下に設定します。

キー 既定値 説明
bot_id WeCom の AI Bot ID(必須)
secret WeCom の AI Bot Secret(必須)
websocket_url wss://openws.work.weixin.qq.com WebSocket ゲートウェイの URL
dm_policy open 個別チャットのアクセス: openallowlistdisabledpairing
group_policy open グループのアクセス: openallowlistdisabled
allow_from [] 個別チャットを許可する利用者 ID(dm_policy=allowlist のとき)
group_allow_from [] 許可するグループ ID(group_policy=allowlist のとき)
groups {} グループごとの設定(後述)
stream_keepalive_enabled false 長いやり取りのとき、WeCom の約 6 分で切れる返信ストリームの枠を延ばすため、一定間隔でキープアライブのフレームを送ります
stream_keepalive_interval_seconds 120 有効にしたときの、キープアライブのフレームを送る間隔
stream_safe_duration_seconds 330 ストリームがこの秒数を過ぎると、締めくくりの送信では確実に届くこちらからの送信を優先します

アクセスの方針

個別チャットの方針

誰が Bot に個別のメッセージを送れるかを決めます。

振る舞い
open 誰でも Bot に個別のメッセージを送れます(既定)
allowlist allow_from に載っている利用者 ID だけが個別のメッセージを送れます
disabled 個別のメッセージをすべて無視します
pairing pairing 方式(最初の設定用)
WECOM_DM_POLICY=allowlist

グループの方針

どのグループで Bot が応答するかを決めます。

振る舞い
open すべてのグループで応答します(既定)
allowlist group_allow_from に載っているグループ ID でだけ応答します
disabled グループのメッセージをすべて無視します
WECOM_GROUP_POLICY=allowlist

グループごとの送信者の許可リスト

細かく決めたい場合は、特定のグループの中で Bot とやり取りできる利用者を絞り込めます。設定は config.yaml で行います。

platforms:
  wecom:
    enabled: true
    extra:
      bot_id: "your-bot-id"
      secret: "your-secret"
      group_policy: "allowlist"
      group_allow_from:
        - "group_id_1"
        - "group_id_2"
      groups:
        group_id_1:
          allow_from:
            - "user_alice"
            - "user_bob"
        group_id_2:
          allow_from:
            - "user_charlie"
        "*":
          allow_from:
            - "user_admin"

仕組み:

  1. まず group_policygroup_allow_from で、そのグループを扱うかどうかが決まります。
  2. グループが最初の判定を通ったら、groups.<group_id>.allow_from の一覧(あれば)で、そのグループの中の誰が Bot とやり取りできるかをさらに絞ります。
  3. ワイルドカードの "*" のグループ項目は、明示的に書かれていないグループの既定として使われます。
  4. 許可リストの項目では * のワイルドカードで全員を許可でき、大文字と小文字は区別されません。
  5. 項目には wecom:user:wecom:group: の接頭辞を付けても構いません。接頭辞は自動で取り除かれます。

グループに allow_from を設定していない場合、そのグループにいる全員が許可されます(グループ自体が最初の判定を通っていることが前提です)。

メディアへの対応

受信

アダプターは、利用者から届いた添付メディアを受け取り、エージェントが扱えるよう手元に保存します。

種類 扱われ方
画像 ダウンロードして手元に保存します。URL 形式と base64 形式の両方に対応します。
ファイル ダウンロードして保存します。ファイル名は元のメッセージのものを保ちます。
音声 音声メッセージの文字起こしがあれば取り出します。
混在したメッセージ WeCom の複数の種類が混ざったメッセージ(テキスト + 画像)を解析し、含まれる要素をすべて取り出します。

引用されたメッセージ: 引用(返信元)のメッセージに含まれるメディアも取り出すので、エージェントは何に対する返信なのかを把握できます。

AES で暗号化されたメディアの復号

WeCom は、受信するメディアの添付の一部を AES-256-CBC で暗号化します。アダプターはこれを自動で処理します。

  • 届いたメディアの項目に aeskey のフィールドが含まれている場合、アダプターは暗号化されたデータをダウンロードし、PKCS#7 のパディングを伴う AES-256-CBC で復号します。
  • AES の鍵は aeskey フィールドを base64 で復号した値です(ちょうど 32 バイトである必要があります)。
  • IV は鍵の先頭 16 バイトから作られます。
  • この処理には Python パッケージの cryptographypip install cryptography)が必要です。

設定は要りません。暗号化されたメディアを受け取ると、復号は裏側で自動的に行われます。

送信

メソッド 送るもの 大きさの上限
send マークダウンのテキストメッセージ 4000 文字
send_image / send_image_file WeCom 本来の画像メッセージ 10 MB
send_document 添付ファイル 20 MB
send_voice 音声メッセージ(本来の音声として送れるのは AMR 形式のみ) 2 MB
send_video 動画メッセージ 10 MB

分割してのアップロード: ファイルは 512 KB ごとに区切り、3 段階の手順(init → chunks → finish)でアップロードします。この処理はアダプターが自動で行います。

自動での切り替え: メディアがその種類本来の上限を超えていても、全体の上限である 20 MB に収まっている場合は、一般的な添付ファイルとして自動的に送ります。

  • 10 MB を超える画像 → ファイルとして送信
  • 10 MB を超える動画 → ファイルとして送信
  • 2 MB を超える音声 → ファイルとして送信
  • AMR 以外の音声 → ファイルとして送信(WeCom 本来の音声は AMR にしか対応していません)

全体の上限である 20 MB を超えるファイルは送られず、その旨を知らせるメッセージがチャットに投稿されます。

返信方式での応答

WeCom のコールバック経由でメッセージを受け取ると、アダプターは届いた要求の ID を覚えておきます。その要求の文脈がまだ生きているうちに応答を送る場合、アダプターは WeCom の返信方式(aibot_respond_msg)を使い、応答を元のメッセージに直接結びつけます。WeCom のクライアント上で、より自然な会話に見えます。

返信のストリームが生きている間、応答は返信方式の msgtype: "stream" フレームで少しずつ流れていきます。届いた要求の文脈が期限切れになっているか使えない場合(あるいはストリームのフレームの送信に失敗した場合)は、aibot_send_msg によるこちらからの送信に切り替えます。

返信方式はメディアにも使えます。アップロードしたメディアも、元のメッセージへの返信として送れます。

接続と再接続

アダプターは、WeCom のゲートウェイ wss://openws.work.weixin.qq.com への WebSocket 接続を張り続けます。

接続の流れ

  1. 接続: WebSocket 接続を開き、bot_id と secret を載せた aibot_subscribe の認証フレームを送ります。
  2. ハートビート: 接続を保つため、アプリケーション層の ping フレームを 30 秒ごとに送ります。
  3. 待ち受け: 届いたフレームを読み続け、メッセージのコールバックへ渡します。

再接続の動き

接続が切れると、アダプターは待ち時間を延ばしながら再接続します。

試行 待ち時間
1 回目 2 秒
2 回目 5 秒
3 回目 10 秒
4 回目 30 秒
5 回目以降 60 秒

再接続に成功するたび、待ち時間の数え直しが行われます。切断時には処理待ちの要求をすべて失敗させるので、呼び出し側が延々と待たされることはありません。

重複の除去

受け取ったメッセージは、メッセージ ID をもとに 5 分の枠、最大 1000 件のキャッシュで重複を除きます。再接続やネットワークの乱れで、同じメッセージが二重に処理されるのを防ぎます。

環境変数の一覧

変数 必須 既定値 説明
WECOM_BOT_ID WeCom の AI Bot ID
WECOM_SECRET WeCom の AI Bot Secret
WECOM_ALLOWED_USERS _(空)_ ゲートウェイ全体の許可リストに使う利用者 ID のカンマ区切りの一覧
WECOM_HOME_CHANNEL 定期タスクや通知の出力先となるチャット ID
WECOM_WEBSOCKET_URL wss://openws.work.weixin.qq.com WebSocket ゲートウェイの URL
WECOM_DM_POLICY open 個別チャットのアクセスの方針
WECOM_GROUP_POLICY open グループのアクセスの方針

困ったときは

症状 対処
WECOM_BOT_ID and WECOM_SECRET are required 両方の環境変数を設定するか、設定のウィザードで指定します
WeCom startup failed: aiohttp not installed aiohttp を入れます: pip install aiohttp
WeCom startup failed: httpx not installed httpx を入れます: pip install httpx
invalid secret (errcode=40013) Bot の認証情報と secret が一致しているか確かめます
Timed out waiting for subscribe acknowledgement openws.work.weixin.qq.com へのネットワーク接続を確かめます
グループで Bot が応答しない group_policy の設定を見直し、そのグループ ID が group_allow_from に入っているか確かめます
グループ内の特定の利用者が無視される groups の設定にあるグループごとの allow_from の一覧を確かめます
メディアの復号に失敗する cryptography を入れます: pip install cryptography
cryptography is required for WeCom media decryption 受け取ったメディアが AES で暗号化されています。pip install cryptography で入れてください。
音声メッセージがファイルとして送られる WeCom 本来の音声は AMR 形式にしか対応していません。他の形式は自動でファイルに切り替わります。
File too large のエラーが出る WeCom はすべてのファイルのアップロードに 20 MB の上限を設けています。圧縮するか分割してください。
画像がファイルとして送られる 10 MB を超える画像は本来の画像の上限を超えるため、添付ファイルへ自動で切り替わります。
Timeout sending message to WeCom WebSocket が切れている可能性があります。再接続のログを確認してください。
WeCom websocket closed during authentication ネットワークの問題か、認証情報の誤りです。bot_id と secret を確かめてください。