Hermes Agent Wiki 非公式・日本語wiki

Hermes Relay(コネクタ)

目次

Hermes Relay はそれ自体がチャットのプラットフォームではありません。コネクタの しくみ であり、ゲートウェイが実在のメッセージングのプラットフォーム (Discord、Telegram、Slack、WhatsApp など)を 1 つ以上、プラットフォームの 認証情報をいっさい持たずに 表から受け持てるようにします。プラットフォームの ボットトークンとソケットを持つのは、*コネクタ* という別のサービスです。 ゲートウェイは認証済みの WebSocket 1 本でコネクタへ 外向きに つなぎ、 ハンドシェイクで能力の一覧を受け取り、以後はそのソケット越しに正規化された メッセージのイベント(受信)と操作(送信)をやり取りします。

要点は次のとおりです。

  • 通信は外向きだけ。 ゲートウェイは受信用のポートを一切開きません。届いた

メッセージは、ゲートウェイ自身がつないだ同じ WebSocket を通って戻ってきます。 そのため NAT の内側でも、公開 IP のないホストでも relay は動きます。

  • プラットフォームの秘密情報をゲートウェイに置かない。 ボットトークンは

コネクタ側にあります。認証が要るプラットフォームのメディアの URL はコネクタ 側で置き直されるので、プラットフォームの認証情報が通信路に出ることはありません。

  • プラットフォームに依存しない。 表に立っているプラットフォームに何ができるか

(メッセージ長の上限、markdown の方言、編集・スレッド・ストリーミングへの対応、 使える操作の正確な一覧)を、ゲートウェイはハンドシェイクの記述から学びます。 プラットフォームごとの処理を埋め込んでいるわけではありません。

ゲートウェイ ⇄ コネクタ間の正式な取り決めは、リポジトリの docs/relay-connector-contract.md にあります。

Relay を使う場面

Relay は、ホスティングされた、あるいは共有のコネクタのサービスがプラットフォーム 側を受け持つ構成のためのものです。たとえば 1 つの共有ボットが多くの利用者の エージェントを表から受け持つマルチテナントのホスティングや、ゲートウェイの端末に ボットトークンを置きたくない構成です。自前のボットを直接動かしているなら、 プラットフォームごとの標準のアダプタ(Telegram、 Discord など)を使ってください。

登録

自前で運用するゲートウェイは、ゲートウェイごとの秘密鍵でコネクタに認証されます。 hermes gateway enroll は、1 回だけ使える登録トークン(テナントの経路が用意 されたときにコネクタが発行し、ゲートウェイの設定と一緒に渡されます)を、その 秘密鍵と引き換えます。

hermes gateway enroll \
  --token <enrollment-token> \
  --connector-url wss://connector.example.com/relay

このコマンドがすることは次のとおりです。

  1. 既存のログイン情報(~/.hermes/auth.json)から Nous Portal のアクセストークンを

取り直します。これで、どの Nous の組織(テナント)の持ち物かが示されます。 gateway.idp.token_url が設定してあれば、Nous Portal ではなく自前の IdP が 使われます(外部と切り離した環境や自前 IdP の経路で、Nous Portal は関わりません)。 client_id と client_secret が設定されていれば一般的な OAuth2 の client-credentials 方式で取得し、どちらも設定されていなければその URL を 環境から渡されるトークンの取得口として扱います(応答の本文がそのまま トークンになる素の GET で、メタデータサーバー方式です。たとえば Domino の $DOMINO_API_PROXY/access-token)。片方だけを設定するのは誤りです。

  1. 登録トークンとゲートウェイの id を、コネクタの /relay/enroll へ TLS 越しに

POST します。

  1. コネクタがトークンを検証し(署名、1 回限り、テナントの一致)、ゲートウェイごとの

秘密鍵とテナントごとの配送鍵を発行して、その 1 回だけ返します。

  1. 認証情報を ~/.hermes/.env に保存します。

GATEWAY_RELAY_ID、GATEWAY_RELAY_SECRET、GATEWAY_RELAY_DELIVERY_KEY (渡された場合は GATEWAY_RELAY_URL と GATEWAY_RELAY_WAKE_URL も)。

新しい環境変数を読ませるため、そのあとゲートウェイを再起動してください。

オプション:

オプション 説明
--token 1 回だけ使える登録トークン。GATEWAY_RELAY_ENROLL_TOKEN でも指定できます。
--connector-url コネクタの基点、または relay の URL(wss://…/relay か https://…)。GATEWAY_RELAY_URL や config.yaml の gateway.relay_url でも指定できます。
--gateway-id このゲートウェイに固定で付ける id(停止の指示をどの単位で効かせるかに使います)。既定は gw-<hostname> です。
--wake-url 任意。ゲートウェイが休んでいる間に処理待ちが溜まったとき、コネクタがつつきに来る到達可能な URL(中身のない GET)。GATEWAY_RELAY_WAKE_URL として保存されます。設定しなくても、次につなぎ直したときに溜まったメッセージは流れます。

設定

コネクタの relay の URL が設定されていると relay が有効になります。環境側が URL を 差し込んでくる場合でも、特定のプロファイルを relay に乗せたくないときは、 config.yaml でこのプラットフォームを無効にします。

platforms:
  relay:
    enabled: false
  • 明示的な無効が勝ちます。 enabled: false のとき、ゲートウェイは本人確認の

トークンを取りにいかず、GATEWAY_RELAY_* の認証情報を用意も書き換えもせず、 relay のアダプタを登録せず、関連性の方針も送りません。gateway.relay_url や GATEWAY_RELAY_URL が設定されていても同じです。標準のメッセージングの アダプタは relay の URL が無いときと同じようにつなぎ、cron の配送も relay が 表に立っているプラットフォームは無いものとして扱います。

  • enabled を書かなければ、URL があるかどうかで有効になります。 enabled: true

にしても、コネクタの URL は別途必要です。gateway.json の enabled: false は ほかのプラットフォームと同じく参考扱いなので、無効にしたいときは config.yaml (利用者側でも管理側でも)に書いてください。

判定は、ゲートウェイの読み込みが使うのと同じファイルと併合の手順(最上位または gateway.platforms のブロック、管理側の上書き)から出て、有効化の時点で適用されます。 変更したらゲートウェイを再起動してください。開いている relay のソケットは切断されません。 実行中の relay を無効にしていても、hermes gateway enroll は使えます。

設定 置き場所 意味
GATEWAY_RELAY_URL 環境変数(~/.hermes/.env) コネクタの relay の WebSocket の URL。プラットフォームの設定で明示的に無効にしていない限り、relay が有効になります。
gateway.relay_url config.yaml 上と同じものを設定ファイルで書く形(環境変数が優先されます)。
GATEWAY_RELAY_ID 環境変数 このゲートウェイの id(enroll が書きます)。
GATEWAY_RELAY_SECRET 環境変数 WebSocket の接続時に認証されるゲートウェイごとの秘密鍵(enroll が書きます)。
GATEWAY_RELAY_DELIVERY_KEY 環境変数 テナントごとの配送鍵(enroll が書きます。将来の互換のために保持されます)。
GATEWAY_RELAY_WAKE_URL / gateway.relay_wake_url 環境変数 / config.yaml 任意。休止中や停止中のゲートウェイをつつく先。
GATEWAY_RELAY_PLATFORMS 環境変数 このゲートウェイが 1 本の接続で表から受け持つプラットフォームのカンマ区切りの一覧(例: discord,telegram)。ふつうは配備側や統括側が書き込みます。
GATEWAY_RELAY_BOT_IDS 環境変数 プラットフォームごとのボットの識別子を並べた JSON。例: {"discord": {"botId": "…"}}。GATEWAY_RELAY_PLATFORMS と対で使います。
gateway.idp.token_url config.yaml 設定すると、登録と払い出しの認証先が Nous Portal ではなく自前の IdP になります。gateway.idp.client_id/client_secret も設定してあれば OAuth2 の client-credentials 方式、そうでなければ環境から渡されるトークンの取得口(素の GET で、そのままか {"access_token": …} の形でトークンを返すもの)として扱います。

対応している機能

relay の接続で実際に何が動くかは、ハンドシェイクで擦り合わされます。コネクタが supported_ops の一覧を示し、ゲートウェイはコネクタが明示した操作しか使いません (古いコネクタの場合は send/edit/typing/follow_up という昔ながらの組み合わせに 落ちます)。プラットフォームごとの機能の有無(編集を使ったストリーミング、スレッド、 下書きのストリーミング、markdown の方言、メッセージ長の上限)も、ハンドシェイクの 記述から来ます。この擦り合わせの範囲で、relay は次に対応します。

  • テキストのメッセージとストリーミング — 送信、返信、そして表に立っている

プラットフォームがメッセージの編集に対応していれば、編集を重ねる形での ストリーミング。対応していない場合は、区切りごとに 1 通ずつ送る形に落ちます。

  • メディアの双方向 — 送信する画像・音声・オーディオ・動画・書類は、コネクタに

アップロードされるか公開 URL で参照され、プラットフォームごとの標準の アップロード経路で説明文付きで届きます。受け取った添付はエージェント向けに ファイルとして手元に落とされます。認証が要るプラットフォームの URL はコネクタ側で 置き直されるので、プラットフォームの認証情報がゲートウェイに届くことはありません。 置き直されるメディアは 25 MB が上限で、期限(およそ 1 時間)があります。

  • プラットフォーム標準の対話パーツ — 実行の承認、確認、確かめの質問は、番号付きの

文章に落とす代わりに プラットフォーム標準の操作部品(Discord のボタン、Telegram の インラインキーボード、Slack の Block Kit の操作、WhatsApp のボタンやリストの メッセージ)で表示されます。ボタンを押した結果は、実際に押した本人からの認証済みの 応答として返るので、ゲートウェイの権限の判定は打ち込まれた返信とまったく同じように 効きます。問いかけの期限はゲートウェイ側で管理されます。

  • 反応による進行の合図 — ボットが処理の状況を示す反応(作業中は 👀、終わったら

✅ か ❌)は relay 越しでも動きます。反応は最善を尽くす扱いで、付けられなくても そのやり取り自体は失敗しません。

  • スレッドの扱い — 引き継ぎ用のスレッドの作成と、スレッドの名前の変更(LLM が

内容から名前を付け直す場合を含む)を、プラットフォームに依存しない thread_create / thread_rename の操作で行います。人が手で付け直した名前を 上書きしない仕掛けも入っています。使えるかどうかはプラットフォーム次第です (たとえば Slack のスレッドは名前を変えられず、WhatsApp にはスレッドがありません)。

  • 入力中の表示 — 処理している間、ゲートウェイはコネクタ越しに入力中(と入力停止)を

送り出します。

  • チャットの情報 — get_chat_info の問い合わせは、コネクタが対応を示していれば

そちらへ中継されます。

  • 溜め置きの配送と起こし役 — ゲートウェイが休んだり切れたりしている間、コネクタは

届いたメッセージを消えない形で溜め、つなぎ直したときに順番どおり流し直します (受領の確認を待つ方式なので、取りこぼしも重複もありません)。起こすための URL が 登録してあれば、眠っているゲートウェイ宛ての処理待ちが来たときにコネクタがそこを つつきます。

複数のプラットフォームを同時に表から受け持てます。1 つのゲートウェイが relay の 接続 1 本で複数のプラットフォーム(たとえば Discord *と* Telegram)を受け持ち、 送信するメッセージには宛先のプラットフォームの印が付きます。

困ったときは

登録が 401 で失敗する — 本人確認のトークンをコネクタが検証できませんでした。 hermes auth add nous(または hermes setup)でログインし直してから、もう一度試して ください。

登録が 403 で失敗する — 登録トークンが不正か、期限切れか、すでに使われたか、 別のテナントのものです。登録トークンは 1 回きりなので、テナントの経路を用意した 相手に新しいものを出してもらってください。

「Could not reach the connector」と出る — コネクタの URL を確かめてください。 つなぎ先の wss://…/relay でも、基点の https://… でも貼れます。CLI が自動で 読み替えます。

enroll が実行を断る — 管理された環境やホスティングされた環境にいます。そこでは relay の秘密鍵はホスティングする側が用意します。自分での登録は、自前で運用する ゲートウェイのためのものです。

動いていた relay のプラットフォームが無効と表示される — ハンドシェイクが成功した *あとで* WebSocket がコード 4401 で閉じた場合、そのゲートウェイの秘密鍵が取り消された ということです(インスタンスの割り当てが解かれた場合など)。ゲートウェイはあえて つなぎ直しをやめ、relay を無効として報告します。ハンドシェイクが一度も成功していない *うちの* 4401 は、まだ用意が済んでいないだけの一時的なすれ違いとみなして、ふつうに やり直します。

登録したのに何も変わらない — ゲートウェイは GATEWAY_RELAY_* を起動時に読みます。 再起動してください(hermes gateway restart)。

ある機能(ボタン、メディア、スレッドなど)が黙って素のテキストに落ちる — その プラットフォームのコネクタが、ハンドシェイクの supported_ops にその操作を挙げて いません。ゲートウェイは、コネクタが扱えない操作を送るのではなく、意図して テキストの挙動に落としています。