Hermes Agent を Fireworks のオープンモデルで動かして Slack から呼び出してみる

Hermes Agent を Fireworks のオープンモデルで動かして Slack から呼び出してみる

Mac miniにHermes Agentを導入し、Slack連携と定期実行で無人で動くAIエージェントを構築しました。オープンモデルを使いながらツール呼び出しを成立させ、毎朝ニュース配信まで実装。つまずきポイントと解決法をまとめます。
2026.08.12

はじめに

こんにちは、クラスメソッド製造ビジネステクノロジー部の嶋田です。

先日、Mac mini を herdr サーバーにして外出先から Claude Code を操作する構成を記事にしました。

https://dev.classmethod.jp/articles/reona-herdr-remote-mac-mini/

記事の結びで、次は Hermes Agent を入れてみたいと予告しましたが、今回はその続きです。

Hermes Agent は Nous Research が開発する OSS のエージェントです。
ライセンスは MIT で、リポジトリは公開されています。
特徴は、ターミナルに常駐するだけでなく、Slack や Telegram などのメッセージングプラットフォームから会話でき、cron による定期実行を内蔵している点にあります。
モデルは固定されておらず、OpenAI 互換のエンドポイントであれば何でも接続できます。

前回の構成は、Mac mini 上の herdr に Tailscale と Mosh で入り、iPhone から承認を飛ばすというものでした。
あれは「手元の端末と同じ操作を、遠くからする」ための構成です。
一方 Hermes が解こうとしているのは、「人が見ていない時間にも動かす」ことのほうです。
同じ常駐でも狙いが違うので、herdr を置き換えるのではなく併設する形で試しました。

本記事では、インストールから Slack 連携、毎朝ニュースを流す cron ジョブの設定までを扱います。
モデルには Fireworks AI 経由のオープンモデルを使いました。
執筆時点のバージョンは v0.20.0(2026.8.3)です。

導入方法

公式ドキュメントはインストール方法としてスクリプトの実行だけを案内しています。

$ curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

このインストーラは ~/.hermes/hermes-agent に git のチェックアウトを作り、シェルの設定ファイルを書き換え、以後は hermes update でそのチェックアウトを更新します。

私は以前書いたとおり、環境構築を mise の宣言に寄せています。

https://dev.classmethod.jp/articles/setup-machine-with-mise-bootstrap/

自前の更新系を持つインストーラは、この方針と噛み合いません。

調べたところ、homebrew-core に formula がありました。

$ brew info hermes-agent
==> hermes-agent: stable 2026.8.3 (bottled), HEAD
Self-improving AI agent that creates skills from experience
https://hermes-agent.nousresearch.com
License: MIT

こちらを使います。
tmux から herdr に移行したときと同じく、brew で入れて dotfiles に宣言する形に乗せられます。

https://dev.classmethod.jp/articles/herdr-tmux-replacement/

$ brew install hermes-agent
$ hermes --version
Hermes Agent v0.20.0 (2026.8.3)
Python: 3.14.6

動くことを確かめたので、mise.toml のパッケージ宣言にも加えました。

"brew:hermes-agent" = "latest"

これで新しいマシンでも mise bootstrap だけで入ります。
宣言が正しく解決されるかは dry-run で確認できます。

$ mise bootstrap -n

デスクトップアプリも hermes-desktop という cask で配布されていますが、今回は CLI と常駐プロセスが目的なので formula だけにしました。

インストール直後に診断コマンドが使えます。

$ hermes doctor

この時点では設定ファイルが未作成である旨と、メッセージング関連の任意パッケージが未導入である旨が出ます。

モデルに Fireworks のオープンモデルをつなぐ

Hermes はモデルを選びません。
Anthropic や OpenAI の API キーも使えますし、OpenRouter やローカルの Ollama も指定できます。

今回はオープンモデルを使いました。
理由は、常駐エージェントの仕事の大半が定型処理だからです。
毎朝ニュースを要約する処理に高価なモデルを充てる必要はありません。
加えて、cron で人が見ていないあいだも動く以上、消費量が読みにくいという事情もあります。

Fireworks AI はオープンモデルを OpenAI 互換の API で提供しています。
Hermes 側にもプロバイダー定義が同梱されていました。

fireworks = ProviderProfile(
    name="fireworks",
    display_name="Fireworks AI",
    env_vars=("FIREWORKS_API_KEY",),
    base_url="https://api.fireworks.ai/inference/v1",
    auth_type="api_key",
    default_aux_model="accounts/fireworks/models/glm-5p2",
)

API キーを登録します。

$ hermes config set FIREWORKS_API_KEY fw_...

キーは ~/.hermes/.env に保存されます。
シェルの履歴に残したくない場合は、対話ウィザードの hermes setup を使うと入力がマスクされます。

利用できるモデルは API から取得できます。
チャットに使えるもののうち、主なものを挙げます。

モデル Context Tools Image
kimi-k3 1,048,576
deepseek-v4-flash-0731 1,048,576
deepseek-v4-pro 1,048,576
glm-5p2 1,048,576
minimax-m3 512,000
gpt-oss-120b 131,072

accounts/fireworks/models/ のプレフィックスは省略しています)

主要なモデルが 1M の context を持っています。
これは後で効いてきます。

既定のモデルには deepseek-v4-flash-0731 を選びました。
軽量な部類で、かつバージョンが固定されているので挙動を再現しやすいためです。

provider と model は分けて指定する

ここで一度つまずきました。

ドキュメントには hermes config set model anthropic/claude-sonnet-4 のように、プロバイダーとモデルをスラッシュでつないだ一つの文字列を渡す例があります。
同じ形式で Fireworks のモデルを指定すると、リクエストが 404 になります。

$ hermes config set model fireworks/accounts/fireworks/models/deepseek-v4-flash-0731
$ hermes -z 'Reply with exactly: PONG'
HTTP 404: Model not found, inaccessible, and/or not deployed

同じモデル ID を curl で直接叩くと 200 が返るので、Fireworks 側の問題ではありません。
原因は、Fireworks のモデル ID 自体が accounts/fireworks/models/... とスラッシュを含むことにあります。
連結形式ではプロバイダー名とモデル名の境界が決まりません。

config.yaml では model がマッピングになっていて、その下に providermodel を持ちます。
分けて指定すれば通ります。

model:
  provider: fireworks
  model: accounts/fireworks/models/deepseek-v4-flash-0731

コマンドから設定する場合は、ドット記法と --force が必要です。

$ hermes config set model.provider fireworks --force
$ hermes config set model.model accounts/fireworks/models/deepseek-v4-flash-0731 --force
$ hermes -z 'Reply with exactly: PONG'
PONG

ツール呼び出しは通るのか

安価なオープンモデルで気になるのは、ツール呼び出しが成立するかどうかです。
Hermes は多数のツールを持つので、そこから正しいものを選べなければ話になりません。

.md を 2 つと .txt を 1 つ置いたディレクトリで試しました。

$ hermes -z 'Use your tools to count how many .md files are in the current directory. Reply with just the number.'
2

一度で正答しました。

プロンプトの規模も測れます。

$ hermes prompt-size
  System prompt total :   19,633 B  (19.2 KB, 15,641 chars)
  Tool schemas        :   42,634 B  (41.6 KB, 25 tools)

システムプロンプトとツールスキーマを合わせて 62 KB ほどです。
公式は 60 を超えるツールを備えると説明していますが、初期状態で有効なのは 25 でした。
1M の context に対してこの規模なので、ツールを絞る必要はありませんでした。

なお内訳には context (AGENTS.md/cwd files) という項目があり、9.6 KB 計上されていました。
Hermes は作業ディレクトリの AGENTS.md を読み込みます。
リポジトリの規約をそのまま効かせられるということです。

承認まわりを確認する

無人で動かす前に、どのコマンドが自動実行されるのかを把握しておきたいところです。

設定は config.yaml にあり、既定値は次のようになっていました。

設定 既定値 意味
approvals.mode smart 補助モデルがリスクを評価し、低リスクは自動承認、危険は自動拒否、判断が難しいものは利用者に確認する
approvals.cron_mode deny 無人実行時に危険なコマンドをブロックする
command_allowlist []
terminal.backend local コンテナ隔離なし。ホスト上で直接実行する

cron_mode が最初から deny なのは安心できます。
rm -rf / やフォークボムのように、設定で緩めても実行できないハードラインも別に定義されています。

ひとつ留意すべき点があります。
smart モードのリスク判定は補助モデルが行うので、オープンモデルを使う構成ではその判断力に安全性が依存します。
今回の default_aux_modelglm-5p2 でした。

保険として、恒久的にブロックするパターンを追加しました。

approvals:
  deny:
    - "git push --force*"
    - "*rm -rf *"
    - "*curl*|*sh*"

捨ててよいディレクトリを作って試したところ、期待どおり止まりました。

The command was blocked, and I'm not going to work around it.

Your config.yaml has a deny rule matching `*rm -rf *` (under approvals.deny).
That's a hard block — it can't be run even with /yolo or approvals.mode=off,
and the policy says not to retry or rephrase it. So I won't try alternatives
like `rm -r` or `find -delete` to sneak around it; that would violate your
own configuration.

rm -rfind -delete という代替手段を自分で挙げたうえで、それを使わないと明言しています。
ディレクトリは残っていました。
単発の指示に対する反応なので、長い自律セッションの途中でも同じように振る舞うかはまだ分かりません。

Slack につなぐ

Hermes は Telegram、Discord、Slack、WhatsApp、Signal などに対応しています。
私は個人の Slack ワークスペースを使いました。

組み上がる構成は次のようになります。

前回の構成では、外から入るために Tailscale と Mosh が必要でした。
今回は Slack を経由するので、接続はゲートウェイ側から外向きに張られます。

必要なトークンは 2 種類です。
ボットトークン(xoxb-)と、アプリレベルトークン(xapp-、スコープは connections:write)を用意します。
後者を使うということは Socket Mode で動くということで、公開 URL やトンネリングは要りません。
ゲートウェイ側から外向きに接続するので、ノート PC や NAT 内部のマシンでもそのまま動きます。

アプリの作成は manifest で済みます。

$ hermes slack manifest --write

~/.hermes/slack-manifest.json が生成されます。
スラッシュコマンドが 50 個登録済みで、socket_mode_enabled も有効になった状態です。
Slack のアプリ作成画面で「From an app manifest」を選び、この中身を貼り付ければ設定は完了します。

トークンを登録します。

$ hermes config set SLACK_BOT_TOKEN xoxb-...
$ hermes config set SLACK_APP_TOKEN xapp-...

依存パッケージを自分で入れる

Slack 連携には追加のパッケージが要ります。
formula にはこれが同梱されていません。
公式インストーラであれば .[slack] という extra として解決される部分です。

必要なものは Hermes 側の定義から分かります。

slack-bolt==1.29.0
slack-sdk==3.43.0
aiohttp==3.14.1

formula の venv に直接入れます。

$ /opt/homebrew/opt/hermes-agent/libexec/bin/python3 -m pip install \
    'slack-bolt==1.29.0' 'slack-sdk==3.43.0' 'aiohttp==3.14.1'

このパスは Homebrew の Cellar 配下なので、brew upgrade hermes-agent を実行すると消えます。
後述する dotfiles 側の仕組みで復元するようにしました。

アクセス制御

誰がどのチャンネルから話しかけられるかは、環境変数で絞れます。

SLACK_HOME_CHANNEL=C...      # cron や通知の既定の配信先
SLACK_ALLOWED_CHANNELS=C...  # 非空なら、これ以外はメンションされても無視する
SLACK_ALLOWED_USERS=U...     # 会話を許可するメンバー ID

既定値も確認しておきました。

環境変数 既定値 効果
SLACK_REQUIRE_MENTION true チャンネルではメンションが必須
SLACK_ALLOW_BOTS none ボットやアプリ由来の投稿を処理しない
SLACK_DISABLE_DMS false DM はチャンネル制限とは独立に制御される

私は RSS を流し込んでいるチャンネルがあるので、そこにボットを入れて暴走しないかが気になっていました。
フィードの投稿はアプリ由来なので SLACK_ALLOW_BOTS で弾かれ、人間の発言もメンションしない限り届きません。
二重に守られているので、同じワークスペースに置いておいても問題ありませんでした。

ゲートウェイが Slack につながらない

ここが今回いちばん時間を使ったところです。

常駐させるには、メッセージングとスケジューラを担うゲートウェイをサービスとして登録します。

$ hermes gateway install
$ hermes gateway status
 Gateway is supervised by launchd (PID 4075)

launchd の管理下に入るので、ログイン時の自動起動とクラッシュ時の復帰が効きます。

ところが、メンションしても返事がありません。
cron からの投稿は届いているので、送信はできているのに受信だけが動いていない状態です。

ログを見ると原因が書いてありました。

WARNING gateway.run: No adapter available for slack
WARNING gateway.run: No adapter could be created for any of the 1 configured platform(s).
Gateway will continue for cron job execution.

Slack のアダプタが読み込まれておらず、ゲートウェイが cron 専用として動いていました。

依存パッケージは入っています。
Hermes 内部の判定関数を直接呼んでも不足なしと返りますし、トークンも有効です。
そして CLI からは問題なく動きます。

違いはプロセスの起動方法にありました。
/opt/homebrew/bin/hermes は実体ではなくラッパースクリプトで、実行前に環境変数を設定しています。

#!/bin/bash
HERMES_BUNDLED_SKILLS=... HERMES_BUNDLED_PLUGINS=... HERMES_WEB_DIST=... \
exec "/opt/homebrew/Cellar/hermes-agent/2026.8.3_1/libexec/bin/hermes" "$@"

一方、hermes gateway install が生成した plist は、このラッパーを経由せず Python を直接起動していました。

<key>ProgramArguments</key>
<array>
    <string>/opt/homebrew/Cellar/hermes-agent/2026.8.3_1/libexec/bin/python</string>
    <string>-m</string><string>hermes_cli.main</string>
    <string>gateway</string><string>run</string><string>--replace</string>
</array>
<key>EnvironmentVariables</key>
<dict>
    <key>PATH</key>...<key>VIRTUAL_ENV</key>...<key>HERMES_HOME</key>...
</dict>

環境変数は 3 つしか設定されておらず、HERMES_BUNDLED_PLUGINS がありません。
Slack のアダプタは share/hermes-agent/plugins/platforms/slack/ に置かれた同梱プラグインなので、ゲートウェイからは存在自体が見えていませんでした。

ラッパー経由で起動するように書き換えます。

<key>ProgramArguments</key>
<array>
    <string>/opt/homebrew/bin/hermes</string>
    <string>gateway</string><string>run</string><string>--replace</string>
</array>

/opt/homebrew/bin/hermes はバージョンを含まないパスです。
元の plist が Cellar のバージョン入りパスを直書きしていた問題も、これで一緒に解決します。

反映して再読み込みします。

$ launchctl bootout gui/$(id -u)/ai.hermes.gateway
$ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.hermes.gateway.plist
INFO gateway.run: Connecting to slack...
INFO adapter: [Slack] Socket Mode connected (1 workspace(s))
INFO gateway.run: ✓ slack connected
INFO gateway.run: Gateway running with 1 platform(s)

メンションすると返事が来るようになりました。

この問題は formula と gateway install の組み合わせでのみ起きます。
公式インストーラを使っている場合はチェックアウトの構成が違うので、同じ症状にはならないはずです。
なお hermes gateway install を再実行すると plist が再生成されるため、この修正が失われる可能性があります。

Slack から話しかける

つながったので、Slack から仕事を頼んでみます。
DevelopersIO に今日投稿された NVIDIA 関連の記事を調べてもらいました。

Hermes に Slack 上で依頼している様子

返事はスレッドに入り、作業の途中経過が実況されます。

DevelopersIO(クラスメソッドのブログ)で今日投稿された NVIDIA 関連記事を調べますね。

検索結果は関連順のようです。今日(2026/08/12)投稿の記事に絞るため、日付順で確認します。

NVIDIA タグの記事一覧で今日(2026/08/12)投稿の記事が確認できました。

サイト内検索を使ってみたものの、並び順が関連度だと気付いて、タグ一覧の日付順に切り替えています。
指示していない判断です。

結果は 3 本挙がりました。

https://dev.classmethod.jp/articles/nvidia-nemotron-3-5-lightning-dspark-dflash-mtp-dgx-spark/
https://dev.classmethod.jp/articles/dgx-spark-nemotron-3-5-lightning-first-touch/
https://dev.classmethod.jp/articles/nvidia-nemo-switchyard-v020-rust-first-touch/

今日(2026年8月12日)DevelopersIO で投稿された NVIDIA 関連の記事は、以下の *3本* です。

1. *NVIDIA Nemotron 3.5 Lightning の 3 種類の投機的デコーディング(DSpark / DFlash / MTP)を
   DGX Spark で比較してみた* — 投稿: Guri / Hajime Oguri
2. *NVIDIA Nemotron 3.5 Lightning 30B-A3B-NVFP4 を試してみた* — 投稿: 森茂洋 / Hiroshi Morishige
3. *Rust に生まれ変わった NeMo Switchyard v0.2.0 を試してみた* — 投稿: 森茂洋 / Hiroshi Morishige

いずれも NVIDIA 関連のモデル・ツールを DGX Spark 環境で試した内容ですね。
リンク先(記事URL)も取得しましょうか?

URL もお願いすると、取得方法をひとつ変えてから返してきました。

アクセシビリティスナップショットにはURL表示がないので、DOM からリンク先を抽出します。

URL 取得を依頼した後の応答(記事 3 本の URL が並んだ状態)

ここで使われているのはブラウザ操作のツールです。
前述のとおり検索 API のキーは設定していないので web_search は無効ですが、ページを開いて読む側のツールは鍵なしで動きます。

やりとりを通して感じたのは、手が止まったときに黙って失敗しない点です。
検索の並び順が違えば別のページに切り替え、必要な情報がスナップショットに出ていなければ DOM を見る。
そのつど何をしているかが流れてくるので、待っているあいだも状況が分かります。

毎朝ニュースを流す

cron を試します。
NVIDIA に関するニュースを毎朝 6 時に投稿させることにしました。

情報源は、実際に取得できることを確認したうえで 3 つ選びました。

  • NVIDIA Newsroom(https://nvidianews.nvidia.com/releases.xml):公式のプレスリリース
  • NVIDIA Developer Blog(https://developer.nvidia.com/blog/feed/):技術寄りの記事
  • Hacker News(https://hn.algolia.com/api/v1/search_by_date?query=nvidia):コミュニティでの話題

Google News の RSS も候補でしたが、記事の URL が news.google.com/rss/articles/CBMi... というリダイレクトのラッパーになり、出典が判別できないため外しました。

ジョブを作ります。

$ hermes cron create '0 6 * * *' "<プロンプト>" --name nvidia-daily --deliver slack:C...
Created job: abe9239b4898
  Next run: 2026-08-13T06:00:00+09:00

--deliver には配信先を指定します。
このほか、手順を固定するためにスキルを添付する --skill、モデルを通さずスクリプトの出力をそのまま配信する --no-agent、ジョブ単位でモデルを固定する --model などがあります。

手動で実行できます。

$ hermes cron run abe9239b4898
  Ran now: succeeded.

Slack には次のように届きました。

ジョブを手動実行して Hermes Agent が Slack に NVIDIA のニュース記事一覧を投稿している画面

- ジェンセン・ファンCEOが、AIファクトリーのコンピューティングが投資可能な資産クラスに
  なりつつあると述べ、金融大手と5,000億ドル超のAIインフラ資金調達枠組を発表した件を解説
  https://blogs.nvidia.com/blog/nvidia-ai-factory-compute/
- 長期実行エージェント向けに高効率なオープンMoEモデル「Nemotron 3.5 Lightning」と
  モデル間ルーティングの「NeMo Switchyard」を発表
  https://blogs.nvidia.com/blog/nemotron-lightning-switchyard-rtx-dgx/
(以下略)

検索 API がなくても動く

ひとつ意外だったことがあります。

Hermes には web_search というツールがありますが、これは検索バックエンドの API キーを必要とします。
今回はどれも設定していないので、このツールは無効でした。

それでもニュースは集まりました。
モデルがターミナルのツールから curl を使って RSS を取得していたからです。
最初に動作を確認したとき、応答にこう書かれていました。

I searched via Google News' RSS feed (curl) because this session has no
dedicated web_search tool — that's a working search, not a failure.

使えるツールがないと投げ出さずに代替手段を見つけ、しかもその経緯を申告しています。

ブロックされても「成功」する

ただし、この自力での回避には注意すべき側面もありました。

初回の実行ログには、3 種類のブロックが記録されていました。

BLOCKED: Security scan — [HIGH] Plain HTTP URL in execution context:
  URL 'http://hn.algolia.com/api/v1/search_by_date?...'
BLOCKED: execute_code runs arbitrary local Python ... Cron jobs run without
  a user present to approve it.
BLOCKED: Command flagged as dangerous (script execution via heredoc) but cron
  jobs run without a user present to approve it.

私が Hacker News の URL を http:// で指定していたため、平文 HTTP としてセキュリティスキャンに弾かれていました。
execute_code とヒアドキュメントによるスクリプト実行は、cron_mode: deny によって無人実行時に一律で止められます。

問題は、これらがブロックされてもジョブが失敗扱いにならないことです。
エージェントは別の手段を探して完走し、実行結果は succeeded になります。
実際、初回は Hacker News から 1 件も取れないまま、残り 2 つの情報源だけで結果が出ていました。

出力を見ただけでは気付けません。
無人で回すジョブについては、ログを確認する習慣が要ります。

プロンプトに「HTTPS のみを使う」「ヒアドキュメントと execute_code を使わない」と明示したところ、ブロックは解消しました。

宣言で表せない部分を dotfiles に逃がす

Hermes 本体は mise.toml に宣言済みですが、Slack の依存パッケージは brew の管理外なので宣言では表せません。
mise の bootstrap タスクに逃がしました。

py=/opt/homebrew/opt/hermes-agent/libexec/bin/python3
if [ -x "$py" ] && ! "$py" -c 'import slack_bolt' >/dev/null 2>&1; then
  "$py" -m pip install --quiet 'slack-bolt==1.29.0' 'slack-sdk==3.43.0' 'aiohttp==3.14.1'
fi

未導入のときだけ実行するので、何度流しても問題ありません。
brew upgrade でパッケージが消えても、bootstrap を流せば戻ります。

今回の収穫と確かめられなかったこと

動くようになったのは次の範囲です。

  • Slack から会話でき、外出先でもスマートフォンから同じチャンネルで続けられる
  • 毎朝 6 時に指定した情報源からニュースを集めて投稿する
  • 安価なオープンモデルでもツール呼び出しが成立し、検索 API なしでも情報を集められる
  • 環境構築は dotfiles の宣言に収まっている

一方、確かめられていないことも残っています。

Hermes の売りは、作業の経験からエージェントが自分でスキルを作り、使いながら改善していく仕組みにあります。
今回は導入までで、この学習ループがオープンモデルでも成立するかは試せていません。
安全側の挙動についても、単発の指示を拒否することは確認しましたが、長い自律セッションの途中で同じように振る舞うかは分かっていません。
この辺りは今後検証していきたいところですね。

コストも測っていません。
定型処理に安価なモデルを充てるという判断自体は、実際の消費量を見てから評価すべきものです。
今後も継続的に稼働させ、一定期間でのコストの評価を実測してみたいです。

herdr との連携にも手を付けていません。
前回の記事では、既存の herdr 連携と moshi-hook の転送をそのまま使えそうだと書きました。
今回は Slack から動かすところまでで区切ったので、こちらは別途試します。

おわりに

Hermes Agent を導入し、Fireworks のオープンモデルで動かして、Slack と cron につなぐところまでを書きました。

つまずいたのは 2 箇所です。
プロバイダーとモデルを連結した文字列は Fireworks では使えないこと、そして Homebrew の formula と gateway install の組み合わせでは同梱プラグインが読み込まれないことです。
どちらも公式ドキュメントには書かれていないので、同じ構成を試す方の役に立てば幸いです。

前回の記事では、Mac mini に環境を集約して外から操作できるようにしました。
今回はそこに、人がいない時間にも動くエージェントが加わりました。

Hermes Agent を活用して色んな角度から検証ができそうなので、今後の記事で書いていく予定です。

参考資料

この記事をシェアする

関連記事