Skip to content

アーキテクチャ概要

源码版本v2026.7.20

Hermes Agent は Nous Research がオープンソース(MIT)で開発するデスクトップ AI エージェントで、「共に成長する永続的エージェント」を位置付けている。メッセージが Telegram/Discord/Slack などから届き、コアループがモデル+ツール (tool) を呼び出してタスクを完遂し、その経験を記憶とスキルに蓄積して次回再利用する。

レイヤー構成

各層の一言解説

  • UI 層: TUI / CLI / Web / ACP(エディタプロトコル) — 単なる入口、業務ロジックは持たない。
  • ゲートウェイ (gateway): gateway/run.py が統一アクセス、各プラットフォームはアダプタ (adapter)、セッション (session) と配信は分離、ストリーム (stream) と hooks は差し替え可能。
  • コアループ: run_conversation が「モデル呼び出し → ツール呼び出し (tool dispatch) → 結果反映 → 予算 (budget) 減少」を編成し、失敗時はフォールバック (fallback)。
  • 機能層: Tool はアトミック関数(tools/registry.py)、Skill は高階で進化可能なフロー(学習ループが書き換え)、Provider/MCP/Plugins がモデルと外部機能を供給。
  • スケジュール: Cron が定時駆動、Subagent は隔離委譲(コンテキスト (context) コストゼロ)、6 つのサンドボックス (sandbox) バックエンドが実行を隔離。
  • 学習ループ: 対話の経験をスキルとユーザーモデルに蓄積し、「使うほど良くなる」フライホイールを形成。

トップダウン:各層の代表的なシグネチャ

Hermes のレイヤー構成を理解する一番の近道は、各層がコード上でどんな「シグネチャ」を露出しているかを見ることだ。

UI 層 → コアループへの転送器。すべてのシェルは AIAgent.run_conversation を呼ぶが、これは単なる転送だ(run_agent.py:6350)。

python
def run_conversation(self, user_message, system_message=None,
                     conversation_history=None, ..., moa_config=None) -> Dict[str, Any]:
    """Forwarder — see ``agent.conversation_loop.run_conversation``."""
    from agent.conversation_loop import run_conversation
    token = set_conversation_context(self._conversation_root_id())
    with scoped_runtime_main({}):
        return run_conversation(self, user_message, ...)

会話 ID は ContextVar 経由で伝達される。これにより、メインループ内のすべての LLM 呼び出し(圧縮 (compression)、ビジョン、MoA スロット、バックグラウンドレビューの fork)が自動的にタグ付けされる。

ゲートウェイ層 → プラットフォームアダプタの総司GatewayRunner は mixin で認証/カンバン/スラッシュコマンド機能を組み立てる(gateway/run.py:22392)。エントリの start_gateway(replace=True) は古いインスタンスを先に殺す——systemd 再起動時に前のプロセスが残ったままになるケースへの抜け道だ。

python
class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, GatewaySlashCommandsMixin):
    """Main gateway controller. Manages the lifecycle of all platform adapters
    and routes messages to/from the agent."""

コアループ → 本当に仕事をする場所run_conversationagent/conversation_loop.py にあり、「モデル呼び出し → ツール呼び出し → 結果反映 → 予算減少」を編成し、失敗時はフォールバックする。シグネチャは前述の通り。

機能層 → ツール登録。各 tools/*.py はモジュール import 時に registry.register を呼ぶ(tools/registry.py:365)。override=True のときだけプラグインが組み込みツールを上書きできる。

python
def register(self, name, toolset, schema, handler, check_fn=None,
             requires_env=None, is_async=False, description="", emoji="",
             max_result_size_chars=None, dynamic_schema_overrides=None, override=False):
    """Register a tool.  Called at module-import time by each tool file.
    ``override=True`` is an explicit opt-in for plugins ... Without it,
    registrations that would shadow an existing tool are rejected."""

プラグイン (plugin) が組み込みツールを差し替えるには、明示的に override=True を指定し、allow_tool_override 設定を通す必要がある——プラグインがこっそり browser ツールをすり替えるのを防ぐためだ。MCP ツールの同名上書きは別ルートで処理される。

スケジュール → cron の ticktick(cron/scheduler.py:3888)はファイルロックで保護された 1 回のスキャンで、gateway は 60 秒ごとにバックグラウンドスレッドから呼ぶ。adapters パラメータにより、cron タスクは gateway が既に張ったプラットフォーム接続を再利用し、自分で再接続しなくて済む。

学習ループ → 学習グラフの集約build_learning_graph(agent/learning_graph.py:248-328)は、学習された(組み込みでない)スキルと記憶をノード/エッジに組み立てる。まず build_skill_nodes(_skill_roots()) で base と profile の 2 つのルートを走査し、source != "base" かつ created_by == "agent" または use_count > 0 のスキルを「学習された」として抽出する——組み込みスキルは学習グラフに入れないことで、進化シグナルがノイズに埋もれないようにしている。

レイヤー構成の動機

なぜこの分割か?一言で言えば:変えられる層で変えられない層を汚さないため

  • UI 層はシェルの張り替えが頻繁(TUI/Web/IDE プロトコルそれぞれの反復リズムがある)。シェルの差し替えで業務を変えさせてはいけない——だからシェルはコールバック(step_callback/event_callback)を渡すだけで run_conversation の中には入らない。
  • ゲートウェイ層では新プラットフォームの追加が日常(QQbot、Enterprise WeChat、新しい Discord スタイル)。しかしコアループはプラットフォームを意識すべきでない——だからプラットフォームロジックはすべてアダプタで、ゲートウェイはメッセージと agent 呼び出しを疎結合に保つだけだ。
  • 機能層は「アトム」、学習ループは「進化」——アトムは進化パスに汚染されてはいけない(さもないとバックグラウンドレビューのバグがツールを壊し、メインループまで連鎖する)。だから learning_mutations が書き換えるのは tools/ ではなく skills/ だ。
  • スケジュール拡張は横断的:Cron と Subagent はどちらも「コアループの外からコアループを起動する」もので、run_conversation を共有するが agent の状態を直接は持たない。

よくある誤読

  • 「Hermes は agent フレームワークだ」——違う。これは具体的なデスクトップ agent の実装で、コアループ、ツールセット、スキルディレクトリはどれも Hermes 自前のもの。フレームワークとしての能力(MCP、Plugin、Provider の抽象)は、拡張できるように開いた境界であって、本体ではない。
  • 「学習ループがプロンプト (prompt) を自動最適化する」——しない。学習ループが書き換えるのは skills/ の SKILL.md と ~/.hermes/skills/ の記憶だけで、システムプロンプト (system prompt) のテンプレートは変えない。プロンプトテンプレートはリポジトリ内の固定ファイルで、変えるにはリリースが必要だ。
  • 「Subagent は分散」——違う。Subagent は同じプロセス内で agent を fork(runtime を共有)する。サンドボックスは実行の隔離(コードが Docker/SSH/Modal/Daytona で走る)であって、agent 状態の分散ではない。
  • 「ゲートウェイ層 = メッセージプラットフォーム接入」——半分だけ正しい。ゲートウェイはセッション状態(session/)、配律重複排除(delivery_ledger)、ストリーム配信(stream_*)、スラッシュコマンド、kanban watchers まで担う——これらはすべて「メッセージプラットフォーム ↔ agent」間の職責で、単なる接入にとどまらない。

推奨される読書順序

  1. 起動とエントリ — コマンドがどうやって稼働中のエージェントになるか
  2. メインループ — メッセージ受信後に何が起きるか
  3. 機能層 — ツールとスキルの違い
  4. ゲートウェイ — マルチプラットフォーム配信の仕組み
  5. スケジュール — 自動化と隔離
  6. 学習ループ — 自己改善の仕組み

非公式コミュニティ学習サイト。MIT ライセンスの NousResearch/hermes-agent ソースに基づく。