Hermes Agent を読み解く — コアの会話ループ
連載「Hermes Agent を読み解く」第2回。
連載「Hermes Agent を読み解く」全10回
- 第1回 全体像と読み方
- [第2回 コアの会話ループ](本記事)
- 第3回 状態管理とコンパクション
- [第4回 記憶アーキテクチャと人格]
- 第5回 ツールシステム
- [第6回 マルチエージェント並列]
- [第7回 Kanban 永続タスクボード]
- [第8回 接続層とインタフェース総覧]
- [第9回 拡張運用]
- [第10回 セキュリティと安全運用]
はじめに — 「1 ターン」を最小単位で見る
エージェントの挙動を理解する最短経路は、ユーザーが 1 メッセージ送ってから 1 応答が返るまで——この 1 ターンを、コードに沿って端から端まで追うことだ。Hermes の場合、その本体は agent/conversation_loop.py。4,836 行ある大物だが、骨格は驚くほど素直な while ループに収まっている。
1. 1 ターンのデータフロー
1 ターンは次の 5 段で流れる。
入力を受け取り、システムプロンプトを組み立て、メインループに入る。ループはツール呼び出しが続く限り回り、脱出したら状態を SQLite に永続化して戻り値を返す。重要なのは、ツール呼び出しが 1 ターンの中で何度も回ること。ユーザーから見た「1 往復」の内側で、API 呼び出し → ツール実行 → 再び API 呼び出し……が複数回起きる。
2. メインループ(while)
ループ 1 周は次の順序で進む。
- API 呼び出し — 現在のメッセージ列をモデルに渡す
- 応答解析 — テキストとツール呼び出しを分離
- ツール検証 — ツール名・引数の妥当性チェック
- ツール実行 — 検証を通ったものを実行
- 圧縮 — context が閾値を超えていればコンパクション(第3回)
- 脱出判定 — ツール呼び出しが無ければループを抜ける
設計上の要点が一つある。messages が正本(source of truth)であり、API 呼び出しはそのコピーに対して行う。なぜか。API に渡す直前には、孤児になったツール結果の除去やスタブ化(第3回 §4)、context 注入といった一時的な加工が入る。これを正本に直接施すと状態が汚れる。だから「正本は手をつけず、送信用のコピーを毎回作って加工する」。地味だが、状態管理の堅牢性を支える判断だ。
3. 4 層リトライ戦略
LLM の出力は不安定だ。Hermes は失敗の種類ごとに 4 通りの回復策を持つ。
| 失敗 | 対処 |
|---|---|
| 不正な JSON | 最大 3 回リトライ |
| truncation(途中切れ) | 切れた分を部分返却して続行 |
| 不正なツール名 | 最大 3 回リトライ |
| 空応答 | nudge(軽い催促)、それでもダメなら fallback |
「とにかく全部リトライ」ではなく、失敗の性質に応じて手を変えているのがポイント。JSON 崩れやツール名の取り違えは「言い直せば直る」類なのでリトライ。truncation は「途中まで正しい」のだから捨てずに部分採用。空応答は催促 → モデル切替、と段階を踏む。
4. 3 つの API モード
Hermes は 3 つの API 形式を内部で吸収する。
-
anthropic_messages— Anthropic Messages API 形式 -
openai— OpenAI Chat Completions 形式 -
codex_responses— OpenAI Responses 形式
呼び出し側のループは共通で、この層がプロバイダ差を吸収する。thinking 系モデルの **reasoning(思考過程)**の扱いもここに含まれる——reasoning ブロックを次ターンに引き継ぐか落とすかは、モデルと API 形式によって分岐する。OpenAI SDK 互換を「軸」と呼びつつ、実際には複数形式を並走させているわけだ。
次回は、ループの中でしれっと呼ばれていた「圧縮」と、その土台になる状態ストア——hermes_state.py(SQLite + FTS5)と agent/context_compressor.py を掘る。
対応マップ章: §2 / 行番号は hermes update でずれうる
クイックイタレート株式会社
IoT / 電力監視 / AI / 衛星・無線通信 / システムインテグレーション/
ローカル LLM・エージェント基盤に関するお問い合わせはお気軽にどうぞ。