要点(最初に結論)
- 同じ小型ローカルLLM(9Bクラス)を使っても、載せるハーネス次第でコーディングエージェントの実効精度は大きく変わります。
- ハーネスには2つの思想があります。フロンティアモデルの賢さを引き出す方向(Claude Code系)と、ハーネス側でモデルの弱さを埋める方向(hermes-agent)です。
- Claude Code系は巨大なシステムプロンプト・広いコンテキスト窓・プロンプトキャッシュ・高い指示追従を前提にしています。9Bでこの前提は崩れます。
- hermes-agentはローカルランタイム(Ollama/llama.cpp)向けの窓確保・ツールの遅延ロード・小さい窓向けの圧縮・壊れた出力の回復を持ち、9Bの弱点を構造的に補います。
- つまり「どのモデルを選ぶか」と同じ重みで「どのハーネスに載せるか」を考える必要があります。
- 本記事は各ハーネスの設計を読み解いて比較したもので、ベンチマークの実測値ではありません(おことわりは後述)。
はじめに
ローカルLLMでコーディングエージェントを動かすと、「モデルは悪くないはずなのに、なぜか作業が進まない」という場面に出会います。
私も、9Bクラスのコーディング特化モデルをローカル(Ollamaやllama.cppのOpenAI互換サーバー)で動かし、複数のエージェントハーネスに載せ替えて挙動を比べていました。
そこで気づいたのは、精度を決めているのはモデル単体の賢さだけではなく、ハーネスがそのモデルの前提で作られているかどうか、という点です。
この記事では、設計思想が対照的な2系統のハーネスを、それぞれの設計を読み解きながら比較します。
- フロンティアモデル向けに最適化された「Claude Code系」
- 小さいモデルの弱さをハーネス側で埋める「hermes-agent」
両者の差を通して、「小型ローカルモデルでハーネスを選ぶ・作るときに何を見るべきか」を整理します。
対象読者
- ローカルLLM(Ollama や llama.cpp)でコーディングエージェントを動かしたい方
- 9B前後の小型モデルで「思ったほど精度が出ない」と感じている方
- 自作のエージェントループやツール実行基盤を設計している方
前提・実行環境
- 想定モデル: 9Bクラスのコーディング特化型ローカルLLM
- 実行基盤: ローカル推論ランタイム。Ollama(
http://localhost:11434/v1)、または llama.cpp のllama-server。いずれも OpenAI 互換エンドポイントを使います。 - 比較対象: Claude Code系ハーネス、hermes-agent
- 調査時点: 2026年6月
なぜ同じ9Bでも精度に差が出るのか
エージェントの実効精度は、ざっくり次の掛け算で決まると考えています。
実効精度 ≒ モデル単体の性能 × ハーネスのモデル適合度
モデル単体の性能が同じでも、右側の「適合度」が低ければ、実際のタスク完遂率は落ちます。
ここで言うハーネスとは、システムプロンプトの組み立て・ツール定義の渡し方・エージェントループ・コンテキスト管理・エラー回復といった、モデルの周りを固める仕組み全体を指します。
モデルがエンジンなら、ハーネスは車体やトランスミッションにあたります。
フロンティアモデルは、長い指示を正確に追え、広いコンテキストを持ち、壊れた出力も少ないので、ハーネスが多少重くても吸収してくれます。
一方で9Bクラスは、指示追従が弱く、コンテキスト窓が狭く、出力フォーマットも崩れやすいです。
この差を理解せずにフロンティア向けのハーネスへ小型モデルを差すと、「モデルが悪いのか、ハーネスが合っていないのか」を切り分けられないまま精度が出ない、という状態に陥ります。
Claude Code系 ― フロンティアモデルの賢さを引き出す設計
Claude Code系は、賢いモデルに豊富な情報と規範を与えて、その判断力を最大化する思想で作られています。
システムプロンプトとツール定義が大きい
まず目立つのは、固定で送られるプロンプトの大きさです。
Claude Code系は、行動規範・安全規範・ツールの使い分け方針を詳しく書いたシステムプロンプトを持ちます。
さらに、各ツールにも個別の詳細な説明文があり、それがリクエストごとにツール定義として送られます。
シェル実行のように使い方の注意が多いツールは、説明文だけでかなりの分量になります。
これらを合わせると、何もしていない初期状態でも、固定のオーバーヘッドだけで1万トークンを優に超える規模になります。
そして、モデルサイズに応じてプロンプトを縮小するような仕組みは見当たりません。
フロンティア向けの大きなプロンプトを、そのまま小型モデルにも送る作りです。
広い窓・キャッシュ・高い追従を前提にしている
この大きさは、Claude Code本来のターゲットでは問題になりません。
理由は3つあります。
- コンテキスト窓が広い。フロンティアモデルの窓は十数万トークン規模で、1万トークン程度のプロンプトは数パーセントの占有にとどまります。
- プロンプトキャッシュが効く。巨大なシステムプロンプトとツール定義はキャッシュされ、毎ターン再送しても課金とレイテンシはほぼ増えません。
- 指示追従が強い。長い規範を正確に守れるモデルでは、規範の量がそのまま品質に寄与します。
つまりClaude Code系の重さは、ターゲット環境では「ほぼ無料」かつ「有益」です。
これは正しい設計判断だと思います。
9Bに載せると前提が崩れる
問題は、この設計を真逆の環境に持ち込んだときに起きます。
- 9Bを現実的に動かす窓は8K〜32Kあたりです。そこに固定で1万トークン超を取られると、肝心の作業(ファイル内容・会話履歴・ツール結果)に使える残りが激減します。
- ローカル経路には、フロンティアAPIのような「課金面でほぼ無料」になるサーバー側プロンプトキャッシュはありません。llama.cppのサーバーは
cache_promptで同じ接頭辞の再計算を省けます。OllamaもKVを内部で使いますが、ホストAPIのプロンプトキャッシュと同等のものとして扱うのは避けた方が安全です。いずれにせよ、巨大プロンプトがコンテキスト窓を占有する事実は変わりません。 - 9Bは長い指示ほど読み飛ばしや取りこぼしが増えます。規範が多いほど守れなくなる側面があります。
サイズの圧迫と追従力の弱さが同時に効くため、フロンティア向けの強みがそのまま弱みに反転します。
Claude Code系の「巨大プロンプト」自体が悪いわけではありません。広い窓・キャッシュ・高い追従というターゲット前提に最適化された結果です。前提が違う9Bに持ち込むとミスマッチになる、というだけの話です。
なお、Claude Code系にも実行時のコンテキスト圧縮(古いツール結果を縮める仕組み)はあります。
ただし、これはツール結果の本文を縮める処理であって、固定の巨大なシステムプロンプト本体は縮みません。
そのため、上で挙げた構造的な圧迫はそのまま残ります。
「窓を広げれば解決では?」への補足
ここで、ひとつ疑問が浮かぶと思います。
ローカルでも窓は広げられます。Ollamaならnum_ctx、llama.cppなら--ctx-sizeで、ランタイムが許容する範囲まで広げられます。
ただし、長い窓での品質は別問題です。
では窓を十分に広げれば、Claude Code系の巨大プロンプトでも問題ないのでしょうか。
半分は解決し、半分は残る、というのが私の見立てです。
解決するのは、窓に収まらず先頭から壊れるという最悪の失敗を避けられる点です。
これは実際に大きな改善です。
残るのは次の2点です。
- ローカルでは窓のコストを自分で払います。窓を広げるとKVキャッシュがその分だけVRAM(やRAM)を消費し、プレフィルにも時間がかかります。ホスト型のフロンティアAPIはこのコストを利用者から隠しますが、手元の9Bでは、環境によっては64K〜128Kへ広げるとVRAMが足りず、CPUオフロードで大幅に遅くなる、ということが起こります。
- 小型モデルは、窓に収まっていても長く密な指示を活かしきれません。いわゆる「中盤を読み落とす(lost in the middle)」傾向や、指示追従の弱さは、窓を広げても残りやすいです。1万トークン超の規範は、フロンティアモデルほど正確には守られない傾向があります。
つまり、窓を広げれば「入らない」失敗は防げますが、「自分のハードウェアでコストを負う」ことと「モデルが長い規範を使いきれない」ことは残ります。
Claude Code系の重さが9Bで最適になりにくい理由は、窓のサイズだけではない、ということです。
hermes-agent ― ハーネスでモデルの弱さを埋める設計
hermes-agentは、小型ローカルモデルを最初から想定し、モデルが苦手なところをハーネス側で吸収する思想で作られています。
ソースを読むと、9Bやローカル実行で実際に起きる「つまずき」への個別対策が、コードに直接埋め込まれていました。
ローカルの「窓の確保」という静かな落とし穴に対処している
最も効いていると感じたのが、ローカルランタイムのコンテキスト窓への対処です。
Ollamaには、OpenAI互換エンドポイント経由でリクエストするとき、コンテキスト長(num_ctx)の既定値が小さく設定される挙動があります。
モデルが本来もっと長い窓を扱えても、既定では小さい値(よく知られた既定値は2048トークン)に落ちます。
これを知らないと、モデルの良し悪し以前に、入力の先頭付近でコンテキストが頭打ちになり、エージェントが破綻します。
hermes-agentは、ローカルエンドポイントを検出すると、初期化処理(agent_init.py)でモデルの実コンテキスト長を問い合わせ、リクエストのnum_ctxに注入します。
GGUFが過大な値を申告するケースに備えて上限でキャップし、コンテキストがツール利用に対して小さすぎるときは「num_ctxを増やすように」と具体的に案内して停止します。
llama.cppを使う場合、コンテキスト長はllama-serverの起動時に--ctx-sizeで指定します。
この場合の落とし穴は、起動フラグで十分な窓を確保し忘れることに移ります。
hermes-agentの自動検出はOllama向けの作りですが、「ローカルではまず窓の確保を疑う」という発想自体は、llama.cppでも同じく効きます。
これは、モデルを差し替えても消えないインフラ層の問題を、ハーネスが先回りで潰している例です。
Claude Code系では、こうした窓の確保はユーザーの手動設定任せでした。
ツールカタログを遅延ロードして窓を守る
ツール定義による圧迫にも、構造的な手当てがあります。
ツールの総量が一定のトークン閾値(既定はコンテキストの約10%)を超えると、tool_searchの仕組みが働きます。
具体的には、検索系の橋渡しツールだけを可視にし、残りのツールカタログは遅延(必要になったときに引く)扱いにします。
遅延の対象は主にMCPや追加プラグインのツールで、中核ツールは常に可視のままです。
これにより、ツールが多くても、固定で送るトークンを小さく保てます。
小型モデルの狭い窓を守るうえで、効果の大きい設計です。
小さい窓を意識した圧縮トリガー
会話が伸びたときのコンテキスト圧縮にも、小型モデル向けの調整が入っています。
コンテキスト圧縮の実装(context_compressor.py)には、最小コンテキストの下限が窓全体に達してしまうような狭い窓(64K級など)のとき、窓の上端付近(おおむね85%)まで使ってから圧縮を始める設定があります。
窓が狭い場合に早すぎる圧縮をかけると、せっかくの容量を無駄にしてしまうためです。
小さい窓ほど積極的に、しかし無駄なく圧縮する、という思想が読み取れます。
壊れた出力の回復が厚い
9Bは、ツール呼び出しのJSONを途中で切ったり、不正な引数を出したりしがちです。
hermes-agentは、こうした「壊れた出力」への回復処理が網羅的でした。
- 存在しないツール名を呼んだら、利用可能なツール一覧を返して数回までやり直させる
- 不正なJSON引数を正規化し、それでも駄目ならエラー内容をモデルに返して自己修復させる
- 引数の末尾が閉じ括弧で終わらない場合は「途中で切れた」と判定し、危険な実行を避ける
- 一部のモデルやサーバが「完了」を返しつつ実は途中で切れているケースを検知して扱いを補正する
- llama.cppのスキーマ変換が正規表現を拒否してエラーになる場合に、該当箇所を外して再試行する(llama.cpp特有の不具合への回復)
弱いモデルが起こしがちな失敗を前提に置き、その回復をループに組み込んでいます。
ここはモデル品質では埋めにくい部分なので、ハーネスが担う価値が大きいところです。
観点別の比較
ここまでの内容を、観点ごとに並べます。
評価は2026年6月時点のソース読解に基づくもので、実測スコアではありません。
| 観点 | Claude Code系 | hermes-agent |
|---|---|---|
| 設計思想 | モデルの賢さを引き出す | ハーネスでモデルの弱さを埋める |
| システムプロンプト | 巨大(規範+全ツールの詳細説明を毎回送る) | 比較的軽く、ツールは遅延ロードで絞れる |
| ローカルの窓確保 | 手動設定任せ | Ollamaはnum_ctxを自動検出・注入・上限キャップ、llama.cppは起動フラグ前提 |
| プロンプトキャッシュ | サーバー側キャッシュで再送コストはほぼゼロ | 課金面の無料キャッシュは無し(llama.cppはcache_promptで接頭辞の再計算を省ける) |
| ツール定義の窓圧迫 | 全ツールを毎回送る | 閾値超でMCP/追加ツールを遅延ロードに切替 |
| コンテキスト圧縮 | ツール結果は縮むがプロンプト本体は固定 | 小さい窓向けの圧縮トリガーあり |
| 壊れた出力の回復 | 切り捨てJSON修復・引数正規化は厚い(無効ツール名の再試行や偽完了の検知は持たない) | 不正名・不正JSON・途中切れ・偽完了・llama.cpp文法エラーまで網羅 |
| 想定環境 | 広い窓・サーバーキャッシュ・高追従モデル | 狭い窓・崩れやすい出力・ローカルランタイム(Ollama/llama.cpp) |
公平に見た共通の弱点
一方に肩入れしすぎないよう、両者が共通して抱える弱点にも触れます。
その弱点を一言でいうと、どちらのハーネスも「モデルがツール呼び出しを安定して出せること」に強く依存している、という点です。
ここが崩れると、ハーネスがいくら優秀でも、ファイル操作やコマンド実行といった実際の処理を行えません。
ツール呼び出しとは何かから、順を追って説明します。
ツール呼び出しとは何か
コーディングエージェントでは、モデルは文章を返すだけでは仕事になりません。
「このファイルを読む」「このコマンドを実行する」といった操作を、ハーネスが実際に実行できる形で指示する必要があります。
この「操作の指示」がツール呼び出しです。
モデルがツール呼び出しを表現する方法は、大きく2通りあります。
1つ目は、ネイティブのfunction callingです。
応答の本文とは別に、専用の構造化されたデータとして「ツール名」と「引数」を出します。
イメージとしては、次のようなJSON(主要なフィールドのみ抜粋)がAPIの応答に含まれる形です。
{
"tool_calls": [
{
"function": {
"name": "read_file",
"arguments": "{\"path\": \"main.py\"}"
}
}
]
}
2つ目は、本文テキストの中に書いてしまう方法です。
たとえば応答本文に、次のように文章として書いてしまうケースです。
では main.py を読みます。read_file({"path": "main.py"}) を呼び出します。
今回想定するローカル(Ollamaやllama.cppのOpenAI互換チャット)経路では、どちらのハーネスも前者だけを読みます。
専用の構造化データに入っているツール呼び出しを拾って実行します。
後者のように本文に文章として書かれたものは、ただのテキストとして扱われ、実行されません。
「安定して出せるか」とは何を指すか
ツール呼び出しの安定性とは、モデルが、呼ぶべき場面で、正しい構造化データのツール呼び出しを確実に出せるか、という意味です。
具体的には、次のような崩れが起きないことを指します。
- 構造化データのほうに出す(本文に文章として書いてしまわない)
- 実在するツール名を使う(存在しない名前を作らない)
- 引数のJSONが壊れていない(途中で切れない、波括弧が閉じている)
フロンティアモデルは、ここがおおむね安定しています。
一方で9Bクラスの小型モデルは、この安定性が機種によって大きくぶれます。
引数のJSONが途中で切れたり、存在しないツール名を呼んだり、ときには操作を本文の文章として書いてしまったりします。
なぜハーネスでは埋めきれないのか
崩れ方には2種類あり、ハーネスが助けられる範囲が違います。
1つ目は、構造化データには出したが、中身が壊れているケースです。
たとえば引数のJSONが途中で切れている、ツール名を間違えている、といった失敗です。
これは両ハーネスとも回復処理を持っています。
特にhermes-agentは、不正なJSONの正規化や、存在しないツール名を返してのやり直しなど、手厚く作られています。
2つ目は、そもそも構造化データに出さず、操作を本文の文章として書いてしまうケースです。
この場合、ハーネスから見えるのは「ただの文章」だけで、実行すべきツール呼び出しがどこにも存在しません。
今回のチャット経路では、どちらのハーネスも本文を解釈し直してツール呼び出しに変換する機能を持たないため、ここは救済できません。
つまり、1つ目はハーネスである程度カバーできますが、2つ目が起きるかどうかは、ほぼモデルの性質で決まります。
ハーネスを変えても直りません。
なお、hermes-agentには別系統のアダプタに限り、本文中の<tool_call>...</tool_call>やJSONを拾い直してツール呼び出しに変換する実装がありました。
これは「本文に書いてしまう」問題への対処にあたりますが、通常のOpenAI互換チャット経路には適用されません。
そのため本記事の前提では、共通の弱点として扱います。
この弱点が意味すること
ここまでをまとめると、ツール呼び出しを安定して出せるモデルかどうかは、ハーネスでは埋めきれない、モデル側の前提条件です。
コーディング特化で関数呼び出しをよく学習したモデルを選べば、この前提条件が整い、エージェントは安定します。
逆に、関数呼び出しが不安定なモデルでは、どちらのハーネスを使っても厳しくなります。
ここまでが、両系統に共通する制約です。
逆に言えば、それ以外の「狭い窓・崩れやすい出力・ローカルランタイム特有の罠」をどれだけ吸収できるかは、ハーネスごとに違います。
Claude Code系とhermes-agentの差は、まさにこの「吸収力」から生まれています。
小型ローカルモデルでハーネスを選ぶ・作るときの指針
ここまでを踏まえると、小型ローカルモデルで精度を出すための着眼点は次のようになります。
- まず、ハーネスがそのモデルの前提で設計されているかを確認します。フロンティア向けの作りに小型モデルを差すと、原因の切り分けが難しくなります。
- 固定で送られるトークン量(システムプロンプト+ツール定義)を見積もります。狭い窓では、ここが小さいほど作業に使える容量が増えます。
- ツールを遅延ロードで絞れるか、不要なツールを外せるかを確認します。
- ローカルランタイムでは、コンテキスト窓が十分に確保されているかを最初に疑います。Ollamaなら
num_ctx、llama.cppなら--ctx-sizeです。既定の小ささは、モデルの実力を測る前に精度を奪います。 - 壊れたツール呼び出しの回復が用意されているかを見ます。9Bは構造化出力を崩しやすいため、ここが効きます。
- 自作する場合は、「賢いモデルに合わせて盛る」のではなく、「弱いモデルでも崩れない最小構成」から始めると扱いやすいです。
逆に言えば、9Bで精度を出したいなら、巨大なプロンプトでも賢いモデルが吸収するという前提を捨てる必要があります。
ツールを遅延ロードで絞り、プロンプトを小さく保ち、num_ctxを確保する、という小型モデル前提の設計に寄せることが近道だと感じています。
まとめ
小型ローカルLLM(9Bクラス)のコーディング精度は、モデル単体の賢さだけでなく、ハーネスがそのモデルの前提で作られているかで大きく変わります。
Claude Code系は、フロンティアモデルの賢さを引き出す設計で、広い窓・キャッシュ・高い追従を前提にしています。
一方のhermes-agentは、ハーネス側でモデルの弱さを埋める設計を採り、ローカルの窓確保(Ollamaのnum_ctx等)・ツールの遅延ロード・小さい窓向けの圧縮・壊れた出力の回復を備えていました。
どのモデルを選ぶかと同じ重みで、どのハーネスに載せるかを考える。
この視点を持つと、ローカルLLM運用の見通しがぐっと良くなるはずです。
参考リンク
おことわり
- 本記事は、2026年6月時点での各ハーネスの設計を読み解き、比較・整理したものです。バージョンによって実装は変わります。
- 精度の優劣は「設計から見た期待値」であり、同一条件でのベンチマーク実測値ではありません。実運用では、必ずご自身の環境で計測してください。
- Claude Code系に関する記述は、公開情報や一般に知られた挙動をもとに整理したものです。内部実装の詳細はバージョンにより異なります。
- hermes-agentに関する記述は、公開ソースの該当ファイルを参照しています。ファイル名・位置は調査時点のもので、厳密にたどる場合は該当コミットを参照してください。
- 本記事の整理にあたっては、生成AIを補助に用い、内容は筆者が検証しています。