この章で分かること
- cron 管理が単一ツール
cronjobに統一されていること、そして自然言語で頼むだけでエージェントがジョブを登録してくれる仕組み - スケジュール書式(相対遅延・インターバル・自然な曜日指定・cron 式・ISO タイムスタンプ)とモデル解決順序、発火前のプリフライト検証
- 配信先
deliver:の指定方法と、実際にメッセージを届けるのが第 7 章のゲートウェイであるという役割分担 - LLM を一切呼ばない「no-agent モード」と通常ジョブの使い分け、コスト構造の違い
- 複数ジョブをつなぐ
context_from/continuity、そしてhermes cron doctorなどのヘルスチェックで「無人運転」を安全に保つ方法
これは何か
Hermes は cron によるスケジュール管理を、スケジュール・一覧・削除といった操作ごとに別々のツールへ分けず、cronjob という単一のアクションベースツールに統一している。エージェントはこのツールを次のように呼び分ける。
cronjob(action="create", ...)
cronjob(action="list")
cronjob(action="update", job_id="...")
cronjob(action="pause", job_id="...")
cronjob(action="resume", job_id="...")
cronjob(action="run", job_id="...")
cronjob(action="remove", job_id="...")
人間からは 3 つの入り口で触れる。対話中のスラッシュコマンド /cron add|list|edit|pause|resume|run|remove、独立した CLI hermes cron create|list|edit|pause|resume|run|remove|status|tick|doctor|runs|incidents、そして最も初心者向けなのが自然文でそのまま頼む方法である。
Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.
このように頼むと、Hermes は内部で cronjob ツールに変換してジョブを登録する。CLI のフラグやスケジュール書式を覚えていなくても、会話の続きとして「毎朝これをやっておいて」と言うだけで定期実行が組める設計になっている。
ジョブの実体は ~/.hermes/cron/jobs.json に保存される素の JSON で、hermes update やゲートウェイの再起動、マシンの再起動をまたいで残る。出力は ~/.hermes/cron/output/{job_id}/{timestamp}.md、重複発火を防ぐファイルロックは ~/.hermes/cron/.tick.lock、実行履歴は ~/.hermes/cron/executions.db に記録される。設定キーは config.yaml の cron.*(cron.model・cron.preflight など)にまとまっている。~/.hermes/ 配下のファイル一覧全体は第 2 章の「どのファイルが何をするか」表を参照。
ここで覚えておきたいのは、ジョブを「登録」するのは CLI や対話でもできるが、実際に「発火」させるのは常に第 7 章で扱ったゲートウェイデーモンだという役割分担である。ゲートウェイが起動していなければ、どれだけジョブを登録してもスケジュールどおりには動かない。
いつ使うのか
典型的な使いどころは次の 4 つである。
- 朝のブリーフィング — 毎朝決まった時刻にニュースや PR の状況を要約し、Telegram や Discord に届ける(通常ジョブ +
deliver:) - 夜間バックアップ — 決まったコマンドを毎晩実行して結果だけ報告する。LLM の判断は不要なので no-agent モードが向く
- 定期監視(ヘルスチェック・しきい値アラート) — メモリ使用率やサービスの死活を数分おきに確認し、異常時だけ知らせる。頻度が高いほど no-agent モードの「LLM を呼ばない」性質が効いてくる
- 多段パイプライン — 収集・選別・配信のように工程を分けたジョブを
context_fromでつなぎ、各段を独立したプロンプトで管理する
cron は「時間が来たら自動で」動く点で、第 9 章で扱う delegate_task(今この会話の中でエージェントに委任する)とは駆動のきっかけが異なる。定型の手順そのものを再利用したいならスキル(第 6 章)をジョブにアタッチする。迷ったら次の基準で選ぶ。
| やりたいこと | 選ぶ仕組み |
|---|---|
| 決まった時刻・間隔で自動的に動かしたい | cron(本章) |
| 今この場で、サブタスクとしてエージェントに任せたい | delegate_task(第 9 章) |
| 毎回同じ判断基準・手順を再利用したい | スキルを cron ジョブにアタッチ(第 6 章 + 本章) |
| LLM の判断は不要で、スクリプトの結果だけ届けばよい | no-agent モードの cron ジョブ(本章) |
仕組み① 作成・スケジュール・発火までの流れ
スキルを添付し、作業ディレクトリを指定して作る
ジョブにはスキルを 0 個・1 個・複数個アタッチできる。スキルはロードされた順にシステムプロンプトへ積み上がり、cron の prompt はその上に乗るタスク指示になる。
cronjob(action="create", skill="blogwatcher",
prompt="Check the configured feeds and summarize anything new.",
schedule="0 9 * * *", name="Morning feeds")
cronjob(action="create", skills=["blogwatcher", "maps"],
prompt="Look for new local events and nearby places, then combine them.",
schedule="every 6h")
cron ジョブは既定でどのリポジトリからも切り離されて動く。AGENTS.md や CLAUDE.md は読み込まれず、terminal・read_file・execute_code などのツールはゲートウェイが起動しているディレクトリを基準にする。特定のプロジェクトの文脈で動かしたいときは --workdir(絶対パス必須)を渡す。
hermes cron create "every 1d at 09:00" \
"Audit open PRs, summarize CI health, and post to #eng" \
--workdir /home/me/projects/acme
workdir を指定すると、そのディレクトリの AGENTS.md / CLAUDE.md / .cursorrules がシステムプロンプトへ注入され、ファイル系・実行系ツールもそこを起点に動く。相対パスや存在しないディレクトリは作成・更新時点で拒否される。
スケジュール書式
| 種類 | 書式の例 |
|---|---|
| 相対遅延(一回限り) | in 30m / in 2h / in 1d |
| インターバル(繰り返し) | every 30m / every 2h / every hour |
| 自然な曜日・時刻 | every monday 9am / weekdays at 9am / daily at 7am |
| cron 式 | 0 9 * * * / 0 9 * * MON-FRI / 0 */6 * * * |
| ISO タイムスタンプ(一回限り) | 2026-03-15T09:00:00 |
自然な曜日・時刻表記は内部で cron 式にコンパイルされる(croniter パッケージが標準で同梱されており追加インストール不要)。相対遅延・ISO タイムスタンプは既定で 1 回だけ実行され、インターバルと cron 式は削除するまで繰り返す。
どのモデルで動くか
ジョブが実行時にどのモデルを使うかは、次の優先順位で解決される。
- ジョブ個別のピン留め —
hermes cron create/edit --model … --provider …で明示的に指定した値。一度設定すると変更するまで固定される cron.model/cron.model_provider— cron フリート全体の既定値。hermes modelや/modelで対話用のモデルを切り替えても、ここが別に設定されていれば cron 側は影響を受けない- グローバルデフォルト — 上記どちらも未設定の場合のみ、
hermes modelのグローバル既定に従う
グローバルデフォルトに頼っているジョブは、作成時点のプロバイダ・モデルをスナップショットとして保持する。あとからグローバル既定が変わると、そのジョブはフェイルクローズする——実行をスキップし、LLM 呼び出しを一切行わず、1 回だけアラートを出す。以後は設定が戻るかピン留めするまで、黙ってスキップされ続ける。課金設定を変えたつもりが、放置している間に気づいたら意図せず高額なモデルへ全ジョブが向いていた、という事故を防ぐための設計である。
発火前のプリフライト検証
スケジューラはエージェントの仕組みを一切組み立てる前に、① プロバイダの API キーが解決できるか ② アタッチしたスキルの必須環境変数・コマンド・クレデンシャルファイルが揃っているか ③ 配信先プラットフォームの認証情報が設定済みか(local/origin は対象外)を検証する。ここで失敗すると、そのジョブの last_status は blocked_config になり、LLM 呼び出しは一切行われない。設定ミスのジョブがトークンを浪費することはない。アラートは連続する tick のたびに繰り返されず 1 回だけ届く。
実行の仕組み
cron を実際に発火させているのはゲートウェイデーモンである。ゲートウェイは 60 秒ごとにスケジューラをティックし、期限が来たジョブごとに履歴を持たない新規の AIAgent セッションを起動する。
重複発火を防ぐのが ~/.hermes/cron/.tick.lock というファイルロックで、あるジョブが実行中でも次のティックが二重に同じバッチを走らせないようにする。cron セッション内では既定で cronjob ツール自体が無効化されており、スケジュール実行されたエージェントが自分でジョブを増殖させることはできない(cron.allow_agent_scheduling: true で明示的にオプトインした場合のみ、実行中ジョブが cron テーブルを操作できる)。
仕組み② 通常ジョブと no-agent モードの使い分け
ニュース要約や PR の内容判断のように「LLM の推論」が必要な仕事もあれば、メモリ使用率のしきい値チェックのように「スクリプトの結果をそのまま届ければよい」仕事もある。後者のために Hermes は no-agent モードを用意している。作成時に no_agent=True(CLI では --no-agent)を渡すと、スケジューラはスクリプトを実行して標準出力を配信するだけで、エージェント(LLM)をまったく起動しない。
hermes cron create "every 5m" \
--no-agent --script memory-watchdog.sh \
--deliver telegram --name "memory-watchdog"
no-agent モードの挙動は次のとおりである。
- スクリプトの標準出力(トリム済み)がそのままメッセージとして配信される
- 空の標準出力なら silent tick——何も配信しない。「異常があるときだけ知らせる」監視ジョブの定番パターン
- 非ゼロ終了やタイムアウトはエラーアラートとして配信される(壊れた監視スクリプトが黙って止まることはない)
.sh/.bashは bash で実行、それ以外は現在の Python インタプリタで実行される- スクリプトは
$HERMES_HOME/scripts/配下に解決できるパスでなければならない(パストラバーサルは拒否される)
この一連の設定は、対話で「RAM が 85% を超えたら Telegram で教えて。5 分おきに」と頼むだけでもエージェントが自動で組み立ててくれる。Hermes が write_file でチェックスクリプトを ~/.hermes/scripts/ に書き、続けて cronjob(action="create", schedule="every 5m", script="memory-watchdog.sh", no_agent=True, deliver="telegram") を呼ぶ、という流れである。
通常ジョブでも、頻繁なポーリングでコストを抑えたいときは script に事前チェックスクリプトを添えられる。スクリプトが最終行に {"wakeAgent": false} を出力すると、その回は LLM 呼び出しごとスキップされる(省略時は true 扱い)。状態に変化がないかぎり課金を発生させない「無料の門番」として使える。
仕組み③ 配信先とジョブ連鎖
配信先 deliver:
cron ジョブの最終応答は、エージェント自身がメッセージを送るのではなく、指定した deliver: の値に従って第 7 章のゲートウェイが配信する。主な指定方法は次のとおり。
| 指定 | 意味 |
|---|---|
"origin" | ジョブを作成した場所へ返す(メッセージング経由の既定) |
"local" | ~/.hermes/cron/output/ にファイルとして保存するのみ(CLI 経由の既定) |
"telegram" / "discord" など | 各プラットフォームのホームチャンネルへ |
"telegram:123456" / "discord:#engineering" | 特定のチャット・チャンネルを名指しで指定 |
"bot-chat" | ボット自身のチャットへメッセージとして投入し、ボットに処理させる |
"all" | 接続済みの全ホームチャンネルへファンアウト(発火時に解決される) |
"telegram,discord" | カンマ区切りで複数指定 |
既定では配信メッセージに「これは cron からの応答である」というヘッダー・フッターが付き、返信してもエージェントには届かない旨が明記される。エージェント側が [SILENT] とだけ応答した場合は配信自体が抑制される(出力はローカルには保存される)——「異常があるときだけ報告する」監視ジョブで使う仕掛けである。
ジョブ連鎖 context_from / continuity
cron ジョブは毎回、前回の記憶を持たない孤立したセッションで動く。しかし「あるジョブの出力が、次のジョブにそのまま必要」という場面は多い。context_from は、指定したジョブの直近の出力を次のジョブのプロンプトへ自動的に前置する仕組みである。
cronjob(action="create", name="daily-digest",
schedule="every day 7am",
context_from=["ai-news-fetch", "github-prs-fetch"],
prompt="Write the daily digest using the outputs above.")
context_from にはジョブ ID・ジョブ名のどちらも渡せ、リストで複数指定すると出力が順番に連結される。参照するのは「直近の完了済み出力」であり、同じティックで実行中の上流ジョブを待つことはしない。
もう一つの仕掛けが continuity=true で、これは自分自身の前回出力を毎回のプロンプトに注入する。ニュース監視のようなジョブは何もしなければ毎回同じ話題を再報告してしまうが、continuity を有効にすると「前回すでに報告した内容」を踏まえて重複を避けられる。
仕組み④ ヘルスチェック・障害追跡・セキュリティ
放置して動かす以上、壊れたジョブに気づける仕組みが要る。Hermes は 3 段構えで用意している。
| コマンド | 役割 |
|---|---|
hermes cron doctor | 全アクティブジョブの読み取り専用ヘルスチェック。直近の失敗・配信失敗・次回実行時刻の欠落やズレ・スクリプトの所在不備などを検出し、何かあれば終了コード 1 を返す |
hermes cron runs [job-id] --limit 20 | 実行履歴(別名 history)。個々の試行が成功・失敗・不明のどれで終わったかを追える |
hermes cron incidents | 同じエラーで繰り返し失敗している障害インシデントの一覧。detected → alerted → closed のライフサイクルを持つ |
hermes cron incidents ack <id> で確認済みにすると、そのまったく同じエラー内容についての再通知だけが止まる。実行履歴や失敗回数のカウント自体は変わらず、別の原因で失敗し始めれば新しいインシデントとして再びアラートが飛ぶ。繰り返し失敗するジョブについては、連続失敗数(failure_streak)がしきい値(既定 3 回、cron.failure_nudge_threshold)に達すると、失敗通知に「直せ・止めろ・消せ」を促すレビューナッジが添えられる。
セキュリティ面では、cron ジョブのプロンプトは作成・更新のたびにプロンプトインジェクションやクレデンシャル流出パターンをスキャンされ、不可視 Unicode を使ったトリックや SSH バックドアの試み、明白な秘密情報の持ち出しを含むプロンプトはブロックされる。加えて、cron から起動されたセッションでは既定で cronjob ツール自体が使えない——スケジュール実行されたジョブが自分でジョブを増やし続ける再帰的なスケジューリングを防ぐための設計であり、必要な場合のみ cron.allow_agent_scheduling: true で明示的にオプトインする。
ハンズオン 8-1 対話で朝のブリーフィングジョブを作り、確認・手動実行・削除する
これから作るのは通常ジョブ(LLM 推論あり)で、消さない限り毎朝スケジュールどおりに動き続け、実行のたびに設定したモデルのプロバイダへ課金が発生する。試し終えたら必ず手順 4 まで進めて削除すること。
- 対話セッションで、自然文のまま定期実行を頼む。
Every morning at 9am, check Hacker News for AI news
and send me a short summary on Telegram.
Hermes が内部で cronjob(action="create", schedule="0 9 * * *", prompt="...", deliver="telegram", name="Morning AI Brief") を呼び、ジョブを登録したことを応答してくる。
- 登録されたことと、ジョブ ID または名前を確認する。
hermes cron list
- 朝まで待たずに、その場で一度だけ動かして挙動を確かめる。
hermes cron run "Morning AI Brief"
手動実行は delegate_task と同じくバックグラウンドで走る。コマンドはすぐに制御を返し、完了すると成功・失敗、配信先、次回実行時刻、出力の抜粋が新しいメッセージとして会話に戻ってくる。ジョブ参照は ID の代わりに名前(大文字小文字を区別しない)でも通る。
- 確認できたら後片付けとして削除する。
hermes cron remove "Morning AI Brief"
消さずに残すと、毎朝 9 時に自動でモデルを呼び続ける点を忘れないこと。
ハンズオン 8-2 no-agent の監視ジョブを作り、ヘルスチェックしてから片付ける
no-agent ジョブ自体は LLM を呼ばないため推論コストはかからないが、5 分おきに動き続ける常時実行タスクであることに変わりはない。試し終えたら削除するか pause しておく。
- メモリ使用率が 85% を超えたときだけ標準出力するチェックスクリプトを
~/.hermes/scripts/に置く(内容は自分の監視対象に合わせて用意する。何も出力しなければ silent tick として扱われる)。 - no-agent ジョブとして 5 分おきに登録する。
hermes cron create "every 5m" \
--no-agent --script memory-watchdog.sh \
--deliver telegram --name "memory-watchdog"
- 正しく登録され、次回実行時刻が入っているかをヘルスチェックで確認する。
hermes cron doctor
hermes cron runs memory-watchdog --limit 5
doctor が何も指摘しなければ終了コード 0 で「健全」。しきい値に達していない間は何も配信されない(silent tick)ので、実行履歴上は成功として記録されつつメッセージは届かないのが正常な状態である。
- 確認が終わったら片付ける。
hermes cron remove memory-watchdog
つまずきポイント
グローバルデフォルト任せのジョブは、ある日突然フェイルクローズする —— per-job pin も cron.model も設定していないジョブは、作成時点のモデルをスナップショットしているだけである。あとでグローバルの既定モデルを切り替えると、そのジョブは実行をスキップし、1 回だけアラートを出したあとは黙ってスキップされ続ける。長く回すジョブには --model / --provider で明示的にピン留めしておくと事故を避けられる。
no-agent スクリプトは $HERMES_HOME/scripts/ の外に置けない —— 相対パス・絶対パス・~ 展開のどれを使っても、解決結果がこのディレクトリの外に出るパスは拒否される(パストラバーサル対策)。またスクリプトのサブプロセス環境はサニタイズされ、プロバイダの API キーなど Hermes が管理する秘密情報は継承されない。
cron から起動したセッションは自分でジョブを増やせない —— スケジュール実行されたエージェントは既定で cronjob ツール自体を使えない。「定期的にジョブ一覧を整理して」のような指示を cron 経由で完結させたい場合は、cron.allow_agent_scheduling: true を明示的に設定する必要がある。
cron の配信は既定では「送って終わり」で、会話にならない —— 配信されたメッセージは会話履歴に残らないため、そこに返信してもエージェントには前後関係が分からない。継続した対話にしたい場合は cron.mirror_delivery(あるいはジョブ個別の attach_to_session)を有効にする必要がある——既定は無効である。
理解度チェック
Q1. cron のジョブ管理はどのようなツールに統一されていますか。また、CLI やスラッシュコマンドを使わずにジョブを登録するにはどうすればよいですか。
スケジュール・一覧・削除などの操作をすべて 1 つのアクションベースツール cronjob(action="create" 等)に統一している。CLI やスラッシュコマンドを使わなくても、対話中に「毎朝9時に〜して」のように自然文で頼むだけで、Hermes が内部で cronjob ツールに変換してジョブを登録する。
Q2. per-job pin も cron.model も設定していないジョブが、あとでグローバルデフォルトのモデルが変更されるとどうなりますか。
作成時点のプロバイダ・モデルのスナップショットに従っていたジョブは、グローバル既定が変わるとフェイルクローズする——実行をスキップし LLM 呼び出しを一切行わず、1 回だけアラートを出す。以後は設定を戻すか明示的にピン留めするまで、黙ってスキップされ続ける。
Q3. no-agent モードのジョブは、標準出力が空のときと、スクリプトが非ゼロ終了したときでそれぞれどう振る舞いますか。
標準出力が空なら silent tick として何も配信しない(「異常があるときだけ知らせる」監視の定番パターン)。非ゼロ終了やタイムアウトの場合はエラーアラートとして配信されるため、壊れた監視スクリプトが黙って止まることはない。
Q4. context_from と continuity=true の違いを説明してください。
context_from は他の(あるいは複数の)ジョブの直近の完了済み出力を、自分のプロンプトに前置して受け取る仕組み。continuity=true は自分自身の前回の出力を次回実行のプロンプトに注入する仕組みで、同じ話題の重複報告を避けるのに使う。両者は併用できる。
Q5. hermes cron doctor と hermes cron incidents ack は、それぞれ何のためのコマンドですか。
hermes cron doctor は全アクティブジョブの読み取り専用ヘルスチェックで、失敗・配信失敗・次回実行時刻のズレなどを一覧し、何か問題があれば終了コード1を返す(状態を変更しない)。hermes cron incidents ack <id> は、同一エラーシグネチャの障害インシデントを確認済みにし、まったく同じエラーについての再通知だけを止める操作で、実行履歴や失敗カウントには影響しない。
まとめ
- cron 管理は単一ツール
cronjobに統一されており、CLI・スラッシュコマンドに加えて自然文で頼むだけでもジョブを登録できる。実際に発火させるのは第 7 章のゲートウェイデーモンで、60 秒ごとのティックで期限が来たジョブを新規セッションとして起動する - モデル解決順序(per-job pin → cron.model → グローバルデフォルト)とプリフライト検証により、設定不備のジョブはトークンを浪費せずに blocked_config で止まる
- LLM の判断が要らない仕事は no-agent モードにすると、スクリプトの標準出力を配信するだけで推論コストがかからない。頻度の高い監視ジョブほど効いてくる使い分けである
context_fromでジョブ同士をつなぎ、continuity=trueで自分自身の前回出力を注入すれば、収集・選別・配信のような多段パイプラインや重複を避ける監視ジョブが組めるhermes cron doctor/incidents/runsの 3 つで「動いているはずが実は死んでいる」状態に気づける。次の第 9 章では、時間ではなく要求駆動でエージェントに仕事を任せる delegate_task を扱う