Hermes Agent 内部構造ノート¶
Hermes Agent (NousResearch) の内部実装を読むための勉強メモ。手を動かしながら確認するハンズオン側は README.md。
このノートのスタンス:
- 「会話ループ」「永続メモリ」「自動スキル生成」「MCP」「Gateway」の 5 つの中核機構をMermaid + 3 層解説で紐解く
- 行レベルの工夫が肝になるところは実ファイル名・行番号を併記する(agent/conversation_loop.py:558-587 のような形式)
- 「LLM がやっていること」と「Python コードがやっていること」を明確に分けて書く
1. 全体像: 4 つの責務を 1 プロセスで抱える¶
Hermes Agent は単一の hermes プロセスが4 つの責務を担う:
flowchart TB
User[ユーザー]
subgraph HermesProcess["hermes プロセス(単一)"]
REPL[CLI REPL<br/>cli.py: HermesCLI]
Loop[会話ループ<br/>agent/conversation_loop.py: run_conversation]
Tools[ツール群<br/>tools/ + tools/registry.py]
BG[background review<br/>fork した AIAgent]
end
subgraph Storage["~/.hermes/ 配下"]
DB[(state.db<br/>SQLite + FTS5)]
Mem[memories/<br/>MEMORY.md / USER.md]
Skills[skills/<br/>SKILL.md 群]
Cfg[config.yaml]
end
subgraph External["外部"]
LLM[LLM プロバイダ<br/>OpenAI / Anthropic / OpenRouter / ...]
MCP[MCP サーバ群]
GW[Gateway adapter<br/>Telegram / Discord / Slack / ...]
end
User -->|stdin| REPL
REPL --> Loop
Loop -->|chat.completions| LLM
Loop -->|tool_call| Tools
Tools --> MCP
Loop -->|persist| DB
Loop --> BG
BG -->|write| Skills
BG -->|write| Mem
GW <-->|websocket / http| Loop
1-1. ディレクトリ構造(要点だけ)¶
| パス | 役割 |
|---|---|
cli.py |
対話 CLI 本体(HermesCLI クラス、Rich + prompt_toolkit ベース TUI) |
run_agent.py |
AIAgent クラス本体。会話ループの「親」 |
agent/conversation_loop.py |
run_conversation() の中身(ここが心臓部) |
agent/agent_init.py |
AIAgent.__init__ の中身(60+ 引数を整理) |
agent/memory_manager.py |
メモリプロバイダ統括 |
agent/curator.py |
長期メンテ(archive / consolidate) |
agent/background_review.py |
各ターン後のスキル/メモリ自動レビュー |
tools/ |
個別ツール(60+)。registry.py 経由で自動登録 |
hermes_state.py |
SQLite + FTS5 のセッションストア (SessionDB) |
hermes_constants.py |
~/.hermes 解決 (get_hermes_home()) |
gateway/run.py |
Gateway エントリ |
gateway/platforms/ |
Telegram / Discord / Slack / WhatsApp 他のアダプタ |
plugins/memory/ |
外部メモリプロバイダ (honcho / hindsight / mem0 等) |
skills/ |
同梱の built-in skill |
HERMES_HOME 環境変数で ~/.hermes を別パスに移せる(hermes_constants.py:42)。
2. AIAgent の会話ループ¶
agent/conversation_loop.py:run_conversation() がエージェントの心臓。LLM とのやり取りを ツール呼び出しが無くなるまで 回し続ける。
2-1. 動的フローチャート¶
flowchart TD
Start([ユーザー入力到着])
SP[システムプロンプト構築<br/>prompt_builder.py + skills + MEMORY.md/USER.md snapshot]
PF[memory provider prefetch<br/>memory_manager.prefetch_all]
Loop{api_call_count < max_iterations<br/>かつ interrupt 未要求?}
API[client.chat.completions.create<br/>messages + tool_schemas]
Stream[stream 受信 + think タグ除去<br/>think_scrubber.py]
TC{tool_calls あり?}
Exec[handle_function_call<br/>tools/registry 経由でディスパッチ]
Append[messages に tool_result を追加]
Persist[messages を SQLite + FTS5 に書く]
BR[spawn_background_review_thread<br/>fork した AIAgent で memory/skill review]
Done([最終応答を返す])
Start --> SP --> PF --> Loop
Loop -->|Yes| API --> Stream --> TC
TC -->|Yes| Exec --> Append --> Loop
TC -->|No| Persist --> BR --> Done
Loop -->|No| Done
2-2. コード本体(要約)¶
実コードを簡略化すると、こうなっている(agent/conversation_loop.py):
while (api_call_count < self.max_iterations
and self.iteration_budget.remaining > 0) \
or self._budget_grace_call:
if self._interrupt_requested:
break
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tool_schemas,
)
if response.tool_calls:
for tool_call in response.tool_calls:
result = handle_function_call(
tool_call.name, tool_call.args, task_id
)
messages.append(tool_result_message(result))
api_call_count += 1
else:
return response.content
ここで押さえるべき行レベルの工夫:
iteration_budget.remaining— 1 ターンで使えるツール呼び出し回数の上限。暴走防止。_budget_grace_call— 上限ギリギリで 1 回だけ「最終応答」のためのチャンスを与える。_interrupt_requested— ユーザーが Ctrl+C を押すと立つフラグ。次のループで break する。stream—client.chat.completions.create(stream=True)で受け取り、think_scrubberが<think>...</think>を即座に除去(モデルが reasoning を出すタイプ用)。- messages の永続化は最終応答後だけ — ループ中はメモリ上、応答が確定してから一括で SQLite に書き込む(書き込み 1 回で済むのは FTS5 トリガーのコストを抑えるため)。
2-3. プロバイダ抽象化¶
LLM プロバイダごとの差異は agent/*_adapter.py に押し込まれている:
| アダプタ | 役割 |
|---|---|
agent/anthropic_adapter.py |
Anthropic 直叩き(tool_use / tool_result の形式変換) |
agent/bedrock_adapter.py |
AWS Bedrock |
agent/gemini_*.py |
Google Gemini(OpenAI 互換でない部分のラッパ) |
agent/codex_responses_adapter.py |
OpenAI Codex |
agent/azure_identity_adapter.py |
Azure OpenAI (Entra ID 認証) |
会話ループ自体は OpenAI 形式({role, content, tool_calls})で書かれていて、各 adapter が往復で形式変換する設計。
3. 永続メモリの 3 層¶
「永続メモリ」と一言で言っても、Hermes Agent には3 つの異なるレイヤがある。混乱しがちなので明示的に分ける:
| レイヤ | 保存場所 | 何を | プロンプトへの注入 |
|---|---|---|---|
| MEMORY.md / USER.md | ~/.hermes/memories/*.md |
エージェントの個人ノート + ユーザーモデル | セッション開始時に system prompt にfrozen snapshot として焼き込む |
| Session DB | ~/.hermes/state.db |
全セッションの message + metadata | セッション内は履歴として、横断検索は session_search ツール経由 |
| External memory provider(任意 1 つ) | プロバイダ次第 | ユーザーモデル・知識グラフ等 | 各ターン前に prefetch(query) で注入 |
3-1. SQLite + FTS5 のスキーマ¶
hermes_state.py:186-310 の SCHEMA_SQL から要点だけ:
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
source TEXT NOT NULL, -- 'cli' | 'telegram' | 'discord' | 'cron' | ...
user_id TEXT,
model TEXT,
system_prompt TEXT,
parent_session_id TEXT, -- 圧縮で分割した親セッション
started_at REAL NOT NULL,
ended_at REAL,
message_count INTEGER DEFAULT 0,
input_tokens INTEGER DEFAULT 0,
output_tokens INTEGER DEFAULT 0,
cache_read_tokens INTEGER DEFAULT 0, -- プロンプトキャッシュのヒット計測
cache_write_tokens INTEGER DEFAULT 0,
estimated_cost_usd REAL,
title TEXT,
-- ...
);
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL REFERENCES sessions(id),
role TEXT NOT NULL, -- system | user | assistant | tool
content TEXT,
tool_call_id TEXT,
tool_calls TEXT, -- JSON 配列
tool_name TEXT,
timestamp REAL NOT NULL,
-- ...
);
-- FTS5 仮想テーブル + triggers で自動同期
CREATE VIRTUAL TABLE messages_fts USING fts5(content);
CREATE TRIGGER messages_fts_insert AFTER INSERT ON messages BEGIN
INSERT INTO messages_fts(rowid, content) VALUES (
new.id,
COALESCE(new.content, '') || ' ' ||
COALESCE(new.tool_name, '') || ' ' ||
COALESCE(new.tool_calls, '')
);
END;
-- CJK / 日本語の部分一致用に trigram tokenizer も別途
CREATE VIRTUAL TABLE messages_fts_trigram USING fts5(
content, tokenize='trigram'
);
3-2. なぜ FTS5 を採用したか¶
- 横断検索が高速: 何万ターンの過去ログでも ms オーダーで全文検索可能
- LLM 不要: 検索パスに LLM 呼び出しが入らない(コスト 0、レイテンシ ms)
- CJK 対応: 既定の tokenizer は欧米語向けだが、trigram tokenizer 用に 別テーブル を持つことで日本語/中国語の部分一致を実現
- WAL モード: 読み取りは並列、書き込みは排他。CLI ↔ Gateway の同時アクセスを想定
3-3. session_search ツールの 3 モード¶
tools/session_search_tool.py:1-30。1 つのツールで 3 つの呼び出しモードを引数で判別:
| モード | トリガ | 動作 |
|---|---|---|
| DISCOVERY | query= を渡す |
FTS5 で検索 → セッション系統で dedupe → 各ヒットの ±5 メッセージウィンドウ + bookend (先頭/末尾 3 ユーザー+アシスタント) を返す |
| SCROLL | session_id + around_message_id |
前後 ±window をめくる |
| BROWSE | 引数なし | db.list_sessions_rich() で時系列に最近のセッションを返す |
全部 SQL/FTS5 から実メッセージを返すので LLM 呼び出しは発生しない。エージェント側は「過去ログを覚えていなくてもツールで思い出せる」前提で動く。
3-4. MEMORY.md / USER.md の frozen snapshot パターン¶
tools/memory_tool.py:1-30。これが面白いので別途解説する:
sequenceDiagram
participant U as ユーザー
participant A as AIAgent (新セッション)
participant M as MEMORY.md / USER.md
participant SP as system_prompt (frozen)
M->>SP: セッション開始時に snapshot を焼き込む
U->>A: 「私の名前は山本健」
A->>M: memory_tool(action=add, ...)
Note over M: ファイルには追記されるが<br/>当該セッションの system_prompt は変わらない
U->>A: 「次の質問」
A->>SP: 同じ snapshot を使い続ける<br/>(プレフィックスキャッシュ温存)
Note over A: ─── /quit ───
M->>SP: 新セッションで再 snapshot<br/>新しい内容が反映される
なぜこの設計?:
- プロンプトキャッシュを温存するため。同じセッション中に system_prompt が変わるとキャッシュが無効化されてコストが跳ねる
- 次セッションから反映なので、即時性は犠牲にしている
- 即時に思い出させたいなら memory ツールで action=read を呼べばその場で読める
実装の安全装置:
- 書き込みは tempfile + os.replace で原子的
- fcntl / msvcrt ロックで並行書き込みを防ぐ
- 内容は tools/threat_patterns.py の strict スコープでプロンプトインジェクション検査(system prompt にそのまま入るので)
3-5. 外部メモリプロバイダ¶
agent/memory_provider.py の ABC を実装すれば追加可能。同梱プラグイン:
| プラグイン | 何 |
|---|---|
honcho |
dialectic user modeling(会話から user representation を作る) |
hindsight |
knowledge graph + entity resolution(cloud / local embedded / local external) |
mem0 |
mem0.ai |
supermemory, byterover, holographic, openviking, retaindb |
その他 |
MemoryManager は外部プロバイダを同時に 1 つだけ許可(ツールスキーマ肥大化と挙動衝突防止)。設定は ~/.hermes/config.yaml の memory.provider。
ライフサイクルフック:
- initialize(session_id, hermes_home, platform, ...) — 起動時
- system_prompt_block() — system prompt 静的部分
- prefetch(query) — 各ターン前
- sync_turn(user_msg, assistant_response) — 各ターン後(async write)
- get_tool_schemas() / handle_tool_call() — モデル向けに memory ツールを公開
4. スキル自動生成の 3 経路¶
「成功体験から再利用可能 skill を作る」の実体は 3 系統:
flowchart LR
subgraph Sources["生成源"]
BR[background_review<br/>各ターン後の自動レビュー]
CR[curator<br/>長期メンテ・整理]
User[ユーザー手動<br/>~/.hermes/skills/ に直接書く]
end
subgraph Storage["~/.hermes/skills/"]
SK[my-skill/<br/>├ SKILL.md<br/>├ references/<br/>├ templates/<br/>├ scripts/<br/>└ assets/]
end
subgraph Activation["呼び出し経路"]
Slash[/skill-name で明示ロード/]
List[skills_list + skill_view<br/>段階的開示]
Bundle[/bundle-name/ で複数一括/]
end
BR --> SK
CR --> SK
User --> SK
SK --> Slash
SK --> List
SK --> Bundle
4-1. ターン後の background review(自動生成の主役)¶
agent/background_review.py:558-587。仕掛けが秀逸なので展開する:
- fork した AIAgent を daemon thread で起動
- fork は親と同じ provider・model・base_url・credential を継承 → プロンプトキャッシュにヒット
- fork は memory + skill 管理ツールだけ許可(ホワイトリスト方式)
- ユーザーメッセージとして
_SKILL_REVIEW_PROMPTを投入 - fork が
skill_manageツールで skill 作成/編集/新ファイル追加を実行
_SKILL_REVIEW_PROMPT の要点(agent/background_review.py:46-):
- ターゲットは「クラスレベル」の skill(umbrella + references/ で構造化、一発限りの粒度は避ける)
- 反応するシグナル:
- ユーザーがスタイル/手順/工程を修正した
- 新しいテクニックが出現した
- 既存 skill が間違っていた
- ユーザーが「remember this」と言った
- フラストレーション表現(「stop doing X」「too verbose」)
- 行動順序:
- すでにロード済の skill を patch
- 既存 umbrella skill を update
- 既存 umbrella の下に support file (
references/<topic>.md,templates/<name>.<ext>,scripts/<name>.<ext>) を追加 - それでも合うものがなければ 新規クラスレベル skill を作成
- 「何もしないターン」は中立ではなく学習機会の損失と定義(積極的に生成させる文化)
4-2. スキル形式¶
tools/skills_tool.py:6-66。YAML frontmatter + Markdown:
---
name: skill-name # 必須、64 文字以下
description: Brief description # 必須、1024 文字以下
version: 1.0.0 # 任意
license: MIT # 任意 (agentskills.io)
platforms: [macos] # 任意 — macos / linux / windows
prerequisites: # 任意 — 必要 env var / コマンド
env_vars: [API_KEY]
commands: [curl, jq]
metadata: # agentskills.io 互換
hermes:
tags: [fine-tuning, llm]
related_skills: [peft, lora]
---
# Skill Title
実際の手順や知識…
ディレクトリ構造:
~/.hermes/skills/
├── my-skill/
│ ├── SKILL.md # 本体(必須)
│ ├── references/ # 補足ドキュメント(調査メモ、エラートランスクリプト)
│ ├── templates/ # 雛形ファイル
│ ├── scripts/ # 再実行可能なスクリプト
│ └── assets/ # その他
└── category-name/
└── another-skill/
└── SKILL.md
4-3. Curator: 長期メンテナンス¶
agent/curator.py:1-25。background_review とは別の機構:
| 項目 | background_review | curator |
|---|---|---|
| 起動 | 各ターン後(即時) | inactivity-triggered(7 日アイドル) |
| 役割 | 新規 skill 作成・既存 patch | lifecycle 自動遷移(active → stale → archived) |
| 対象 | 全 skill | agent-created skill のみ(user / hub / bundled は不可侵) |
| 削除 | しない | archive 止まり(auto-delete なし、復元可能) |
寿命設定:
- DEFAULT_STALE_AFTER_DAYS = 30
- DEFAULT_ARCHIVE_AFTER_DAYS = 90
実行コマンド:
4-4. 呼び出しの 3 経路¶
- スラッシュコマンド —
/skill-nameで明示ロード。SKILL.md がユーザーメッセージとして注入される(system prompt ではない理由はプロンプトキャッシュ温存) - skills_list / skill_view — モデルが
skills_listで利用可能 skill のメタデータ(name + description)だけを progressive disclosure 第 1 段として確認し、必要ならskill_viewで本体を読む - Skill bundle —
~/.hermes/skill-bundles/<name>.yamlで複数 skill を 1 つの slash command にまとめる。/<bundle-name>で全部一括ロード
4-5. usage telemetry¶
tools/skill_usage.py:1-30。~/.hermes/skills/.usage.json に skill 名キーで使用回数・最終使用時刻を記録。skill_view / skill_manage がインクリメント。curator が読んで lifecycle 判定。
原子的書き込み(tempfile + os.replace)で並行アクセス安全。
5. MCP 対応¶
5-1. 設定¶
~/.hermes/config.yaml の mcp_servers キー(tools/mcp_tool.py:1-30):
mcp_servers:
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
env: {}
timeout: 120
connect_timeout: 60
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_..."
supports_parallel_tool_calls: true
remote_api:
url: "https://my-mcp.example.com/mcp"
headers:
Authorization: "Bearer sk-..."
timeout: 180
searxng:
url: "http://localhost:8000/sse"
transport: sse
sampling: # サーバから LLM 逆呼び出しを許可
enabled: true
model: "gemini-3-flash"
max_tokens_cap: 4096
5-2. アーキテクチャ¶
flowchart LR
subgraph HermesProcess["hermes プロセス"]
Loop[会話ループ]
Reg[tools/registry.py]
MCPLoop[_mcp_loop<br/>専用 daemon thread]
end
subgraph MCPServers["MCP サーバ群"]
S1[filesystem<br/>stdio: npx]
S2[github<br/>stdio: npx]
S3[remote_api<br/>HTTP]
S4[searxng<br/>SSE]
end
Loop -->|tool_call| Reg
Reg -->|run_coroutine_threadsafe| MCPLoop
MCPLoop <-->|asyncio Task| S1
MCPLoop <-->|asyncio Task| S2
MCPLoop <-->|HTTP| S3
MCPLoop <-->|SSE stream| S4
ポイント:
- 専用バックグラウンド event loop を daemon thread で 1 つ持ち、各 MCP サーバを長寿命 asyncio Task として起動
- ツール呼び出しは run_coroutine_threadsafe() で同期 → 非同期の橋渡し
- 自動再接続: 指数バックオフで最大 5 回
- credential ストリッピング: エラーメッセージから API キーを削除して LLM に返す
- 自動 discovery: 接続後、MCP サーバが宣言しているツールを tools/registry.py に登録 → エージェントは組み込みツールと区別なく呼べる
5-3. 3 つのトランスポート¶
| 方式 | 設定キー | 用途 |
|---|---|---|
| stdio | command + args |
ローカルプロセス(npx ... が定番) |
| HTTP / Streamable HTTP | url(http/https) |
リモート MCP サーバ |
| SSE | url + transport: sse |
Server-Sent Events 専用サーバ |
5-4. Sampling¶
MCP サーバから LLM を逆呼び出しできる機能(MCP プロトコルの sampling/createMessage)。tools/mcp_tool.py で許可リスト形式で制御:
sampling:
enabled: true
model: "gemini-3-flash"
max_tokens_cap: 4096
timeout: 30
max_rpm: 10 # rate limit
allowed_models: [] # 空ならクライアント側で固定
max_tool_rounds: 5
6. Gateway: マルチプラットフォーム対応¶
gateway/run.py:start_gateway()。CLI と同じ AIAgent / 同じ DB / 同じ skill 群を共有する別エントリポイント。
6-1. アーキテクチャ¶
flowchart LR
subgraph User["外部"]
TG[Telegram User]
DC[Discord User]
SL[Slack User]
end
subgraph Gateway["hermes gateway start"]
TGA[TelegramAdapter]
DCA[DiscordAdapter]
SLA[SlackAdapter]
Router[ChatRouter<br/>session_id を解決]
Agent[AIAgent インスタンス]
end
subgraph Storage["~/.hermes/state.db"]
DB[(sessions<br/>messages)]
end
TG -->|webhook/poll| TGA
DC -->|websocket| DCA
SL -->|websocket| SLA
TGA --> Router
DCA --> Router
SLA --> Router
Router --> Agent
Agent --> DB
6-2. プラットフォーム一覧¶
gateway/platforms/ 配下(組み込み):
| カテゴリ | プラットフォーム |
|---|---|
| メッセンジャー | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Line, Feishu, WeCom, WeChat |
| ビジネス | DingTalk, MS Graph (Teams), QQ Bot, Yuanbao |
| その他 | Email, SMS, BlueBubbles (iMessage), HomeAssistant, webhook 汎用, REST API server |
プラグイン側にもさらに line, irc, teams, google_chat などがある。
6-3. プラットフォーム追加方法¶
gateway/platforms/ADDING_A_PLATFORM.md に詳しい。BasePlatformAdapter を継承して以下を実装:
| メソッド | 役割 |
|---|---|
connect() |
接続確立 |
disconnect() |
切断 |
send(chat_id, content) |
テキスト送信 |
send_typing(chat_id) |
typing インジケータ |
send_image(chat_id, image) |
画像送信 |
get_chat_info(chat_id) |
チャットメタデータ取得 |
セッション管理は Gateway 側が自動で行う。Adapter は「メッセージを受け取って Router に渡す」「Router から渡されたものを送信する」だけに集中。
6-4. クロスプラットフォーム検索¶
すべてのプラットフォームが同じ state.db に書き込むため、session_search ツール一つで Telegram の会話と CLI の会話を横断検索できる:
SELECT id, source, title FROM sessions WHERE source IN ('telegram', 'cli')
ORDER BY started_at DESC;
7. 対応 LLM プロバイダ¶
35+ プロバイダ。代表例:
provider |
認証 | 備考 |
|---|---|---|
openrouter |
OPENROUTER_API_KEY |
万能、推奨デフォルト |
nous |
hermes login (OAuth) |
Nous Portal サブスク。Tool Gateway 込み |
nous-api |
NOUS_API_KEY |
Nous Portal API |
anthropic |
ANTHROPIC_API_KEY |
要 pip install .[anthropic] |
openai-codex |
hermes auth |
OpenAI Codex |
gemini |
GOOGLE_API_KEY |
Google AI Studio OpenAI 互換 |
copilot |
GITHUB_TOKEN |
GitHub Models / Copilot |
azure-foundry |
API key or Entra ID | Azure OpenAI |
custom (= ollama / vllm / llamacpp) |
任意 | base_url 手書き |
lmstudio |
任意 | http://127.0.0.1:1234/v1 |
auto を指定すると認証情報から自動推測。
設定ファイル: ~/.hermes/config.yaml:
model:
default: "anthropic/claude-sonnet-4.5"
provider: "openrouter"
base_url: "https://openrouter.ai/api/v1"
context_length: 200000
max_tokens: 8192
hermes model で対話的に切替可能。
8. プロンプトキャッシュとコスト管理¶
Hermes Agent はあちこちで プロンプトキャッシュを死守する設計になっている。なぜなら長セッション + 多ツールでトークン数が膨れ上がるため、キャッシュなしでは API コストが爆発する:
| 設計判断 | キャッシュ保護の意図 |
|---|---|
| MEMORY.md / USER.md は frozen snapshot | セッション中に system_prompt を変えない |
| skill は user message として注入 | system_prompt を変えない |
| background_review の fork は親と同じ provider/credential | キャッシュキー共有 |
| session_search は LLM 不要 | キャッシュを汚さずに過去を引ける |
| context_compressor.py で長セッションを圧縮 | キャッシュ可能サイズに収める |
sessions テーブルの cache_read_tokens / cache_write_tokens カラムでセッション単位の効率を計測できる:
SELECT
id, title,
cache_read_tokens, cache_write_tokens,
ROUND(cast(cache_read_tokens as float) / NULLIF(cache_read_tokens + cache_write_tokens, 0), 2) AS hit_ratio
FROM sessions ORDER BY started_at DESC LIMIT 10;
9. 実ファイル参照早見表¶
ノートを書きながら原典に当たりたい人向け:
| トピック | ファイル + 関数 |
|---|---|
| CLI エントリ | hermes_cli/main.py |
| 対話 REPL 本体 | cli.py:HermesCLI |
| AIAgent クラス | run_agent.py:327 (class AIAgent) |
__init__ 詳細 |
agent/agent_init.py:init_agent |
| 会話ループ | agent/conversation_loop.py:run_conversation |
| ツール登録 | tools/registry.py, model_tools.py:handle_function_call |
| SQLite + FTS5 スキーマ | hermes_state.py:186-310 |
| セッション検索 | tools/session_search_tool.py:1-30 |
| MEMORY.md / USER.md | tools/memory_tool.py:1-100 |
| メモリプロバイダ ABC | agent/memory_provider.py:1-100 |
| 自動スキル生成 | agent/background_review.py:558-587 |
| Skill review プロンプト | agent/background_review.py:46-120 |
| Curator | agent/curator.py |
| Skill 形式 | tools/skills_tool.py:1-66 |
| Skill 作成ツール | tools/skill_manager_tool.py:1-37 |
| MCP クライアント | tools/mcp_tool.py:1-90 |
| Gateway エントリ | gateway/run.py:start_gateway |
| BasePlatformAdapter | gateway/platforms/base.py |
| プラットフォーム追加方法 | gateway/platforms/ADDING_A_PLATFORM.md |
| プロバイダ一覧 | cli-config.yaml.example (model.provider のコメント) |
| 環境変数一覧 | .env.example |
| ホーム解決 | hermes_constants.py:42 |
| Setup wizard | hermes_cli/setup.py:1-50 |
| Install スクリプト | scripts/install.sh, setup-hermes.sh |
10. 連載コードとの比較¶
連載で扱ったツール群と Hermes Agent の対応表:
| 機能 | 連載で扱った例 | Hermes Agent の該当 |
|---|---|---|
| ReAct エージェント | 第 11 回 (create_react_agent) |
agent/conversation_loop.py:run_conversation |
| マルチエージェント (Supervisor) | 第 23 回 | tools/delegate_tool.py + sub-agent spawning |
| 共有メモリ + TODO | 第 24 回 (Claude Code 風文章執筆) | ~/.hermes/memories/ + tools/todo_tool.py |
| MCP サーバ/クライアント | 第 19, 20 回 | tools/mcp_tool.py |
| LangGraph Functional API | 第 21, 22 回 | (対応物なし。Hermes はワンパスループに寄せた設計) |
| プロンプト最適化 | 第 25-28 回 (DSPy) | (Hermes は LLM 自己レビューで近似。DSPy のような optimizer は無い) |
| Middleware | 第 29, 31 回 (LangChain v1) | tools/registry.py のディスパッチ + adapter 層 |
「LangGraph で組むより一発で動くものが欲しい」場合の選択肢。逆にフローを細かく制御したいなら LangGraph / DSPy の方が向く。
まとめ: Hermes Agent の設計上の主張¶
- 学習ループは中央集権でなくバックグラウンド分散: メインの会話に介入せず、別スレッドの fork が自律的にメモリ/スキルを更新する
- 永続化は SQLite 一本化: マイクロサービス的に分けず、
state.db一つに全プラットフォーム・全セッション・全ツール呼び出しを集める - プロンプトキャッシュ最優先: frozen snapshot, user-message injection, fork sharing 等、あらゆる設計判断がキャッシュヒット率を守る方向
- プロバイダ非依存: adapter 層で OpenAI 形式に統一、35+ プロバイダを差し替え可能に
- 拡張は MCP / Plugin / Skill の 3 層: 動的(MCP)/ Python コード(Plugin)/ 知識(Skill)の使い分け
これらを踏まえた上で、ハンズオン側(README.md)に戻って実体験するのが理解の近道。
作成: 2026-05-27 / 最終更新: 2026-05-27