Skip to content
Published on

AI harness 解剖 — モデルを agent に変える scaffolding(loop・tool・context、そして自作する)

Authors

プロローグ — 同じモデル、違う harness、違う結果

2026 年によく見る光景: 2 つのチームが同じ frontier モデルを使っている。あるチームの agent はチケットを受け取って PR を開き、もう一方のチームの agent は 30 分間同じファイルを空回りしている。

違いはモデルではない。harness だ。

モデルは engine だ。harness は車だ。 同じ engine をレーシングシャーシに載せるか、壊れたトラックに載せるかで、走行体験はまったく変わる。

2023〜2024 年の AI engineering は「どのモデルを使うか」だった。2026 年の AI engineering はほとんどが**「そのモデルをどう取り囲むか」**だ。モデル提供者は数社に収束したが、harness は爆発的に多様化した — Claude Code、Cursor、Codex CLI、Aider、OpenClaw… これらは同じモデル API を呼ぶ。違うのは harness だ。

この記事は harness を解剖する。agent loop、tool execution 層、context 管理、System Prompt、制御フロー、失敗モード。そして40 行の最小 harness を自作し、harness をどう評価するかまで見る。


1章 · harness とは何か

harness = LLM を取り囲み、それを「agent」に変えるすべてのコード。

LLM 自体は無状態関数だ — テキストが入ればテキストが出てくる。それだけだ。ファイルを読むことも、コマンドを実行することも、記憶することもできない。harness がそのすべてをやる。

        ┌──────────────────────────────────────┐
        │              harness                 │
        │  ┌────────────┐   ┌────────────────┐ │
        │  │ System     │   │ context 管理    │ │
        │  │ Prompt     │   │ (window・圧縮)  │ │
        │  └────────────┘   └────────────────┘ │
        │  ┌────────────┐   ┌────────────────┐ │
        │  │ agent      │──▶│  モデル (LLM)   │ │
        │  │ loop       │◀──│  = engine      │ │
        │  └─────┬──────┘   └────────────────┘ │
        │        ▼                              │
        │  ┌────────────┐   ┌────────────────┐ │
        │  │ tool       │   │ 制御フロー・hook・│ │
        │  │ execution  │   │ 権限ゲート       │ │
        │  └────────────┘   └────────────────┘ │
        └──────────────────────────────────────┘

harness が所有するもの:

  • loop — モデルを 1 回ではなく繰り返し呼ぶ。
  • tool — モデルが「行動」できるようにする。
  • context — 何をモデルに見せるかを決める。
  • System Prompt — モデルのアイデンティティとルール。
  • 制御フロー — いつ止め、いつ人を呼び、いつ並列化するか。
  • 安全装置 — 無限 loop・コスト暴走・誤った tool 呼び出しを防ぐ。

モデルを変えると推論の質が変わる。harness を変えると agent の行動そのものが変わる。 だから両者を分けて考えなければならない。


2章 · agent loop — harness の心臓

harness の核心は驚くほど単純だ。while loop だ。

1. モデルを呼ぶ (現在のメッセージ群を渡す)
2. モデルの応答を見る:
     - 最終回答か?       → loop 終了
     - tool 呼び出しか?  → 3 へ
3. tool を実行する
4. tool 結果をメッセージに追加する
5. 1 に戻る

これが ReAct パターンの本質だ — think → act → observe を繰り返す。モデルが「思考」(推論)し、「行動」(tool 呼び出し)し、harness が「観察」(結果の注入)を与える。

終了条件 — loop は必ず止まらなければならない

while で最も重要なのは脱出だ。終了条件が弱いと agent は永遠に回る。

終了条件意味
最終回答モデルが tool なしでテキストのみ返す
step capN 回繰り返したら強制終了 (例: 40)
トークン予算累積トークンが上限を超えたら終了
明示的シグナルモデルが done のような終了 tool を呼ぶ
エラー復旧不能な失敗
人の介入ユーザーが中断

step cap とトークン予算は選択肢ではない。 この 2 つがない harness はコスト爆弾だ。


3章 · tool execution 層

モデルは「行動」できない。モデルがやるのは**「この tool をこの引数で呼び出したい」という構造化された出力を出すこと**だけだ。実際の実行は harness がやる。

tool 呼び出しのライフサイクル

1. harness がモデルに「tool 一覧」を伝える (名前・説明・引数 schema)
2. モデルが tool 呼び出しブロックを出力する (tool 名 + 引数)
3. harness がそのブロックを parse する
4. harness が引数を検証する (schema 違反 → エラーをモデルに返す)
5. harness が tool を実行する (多くは sandbox 内で)
6. harness が結果(stdout・stderr・戻り値・エラー)を捕捉する
7. harness が結果を「tool 結果」メッセージにして会話に追加する
8. loop に戻ってモデルを再び呼ぶ

核心: モデルは tool の存在を「説明」でしか知らない。 tool の説明が貧弱ならモデルは tool を誤って使う。tool 定義は事実上 prompt の一部だ。

tool execution で harness が責任を持つもの

  • 検証 — モデルが送った引数が schema に合うか。合わなければ実行せずエラーを返す(モデルに自己修正させる)。
  • sandboxing — tool 実行は隔離された環境で。ホスト・本番と分離する。
  • エラー捕捉 — tool が死んでも harness は死なない。エラーを捕まえてモデルに「観察」として与える。
  • タイムアウト — すべての tool 呼び出しにタイムアウト。止まった tool に loop を止めさせるな。
  • 結果のフォーマット — 巨大な出力(ログ 10 万行)をそのまま入れると context が破裂する。切り詰めるか要約する。

MCP — tool 層の標準

2026 年には tool を harness ごとに新しく実装しない。**MCP(Model Context Protocol)**サーバーが「tool の束」を標準インターフェースとして公開し、どの harness もそれに接続する。harness の tool 層は次第に「MCP クライアント」になっている。


4章 · context 管理 — 最も難しい部分

モデルの context window は有限だ。そして agent loop は毎 step メッセージを積み上げる — tool 呼び出し、tool 結果、推論。100 step の作業は context を圧倒する。

何を入れ何を抜くかを決めることが harness の最も難しい責任だ。

Context Rot — context は腐る

context が長くなるほどモデルの性能はただ「維持」されるわけではない — 低下する。古い tool 結果、破棄された計画、無関係な探索の痕跡が積もると、モデルは信号と雑音を区別しにくくなる。これを **context rot(context の腐敗)**と呼ぶ。

window に「入る」からといってすべて同じではない。前方・後方が中間よりよく使われる。 だから harness は単に「全部入れる」ではなく、何を、どこに置くかを設計しなければならない。

context を扱う技法

技法説明
Pruning古い・無関係なメッセージを切り捨てる
Compaction古い会話を要約版に圧縮する
Retrieval必要なときだけ外部から関連する断片を取ってくる (RAG)
Sub-agent 隔離下位作業を別 context で回して本文を汚染しない
構造化tool 結果を原文ではなく構造化・要約された形で
外部メモリファイル・DB に状態を置き、context にはポインタだけ

核心の洞察: context window は RAM ではなく作業台だ。 無限に積む場所ではなく、今の作業に必要なものだけを載せておく場所だ。harness の仕事はこの作業台をきれいに保つことだ。


5章 · System Prompt — harness の憲法

System Prompt は harness がモデルに与える憲法だ。毎 loop ごとにモデルが見る、変わらない土台。

ここに入るもの:

  • アイデンティティ — 「お前は何で、何のために存在するのか」。
  • ルール・制約 — 絶対にやらないこと、常にやること。
  • tool 使用の指針 — tool をいつ・どう使うか。
  • 出力形式 — 応答の構造。
  • 安全境界 — 拒否すべきリクエスト、人を呼ぶべき状況。

ここにcontext ファイルCLAUDE.mdAGENTS.md など)が加わる — プロジェクトごとのルール・アーキテクチャ・慣習。System Prompt が「harness の憲法」なら context ファイルは「このプロジェクトの地域法」だ。

核心: 同じモデル + 同じ tool でも、System Prompt が違えばまったく違う agent だ。 System Prompt は harness がモデルの「性格」を形作る場所だ。


6章 · 制御フロー — loop の上のオーケストレーション

基本 loop(2章)の上に、成熟した harness は制御フローを載せる。

Sub-agent — context 隔離

下位作業を新しい context で回す別の agent。「このディレクトリ構造を調査して」のような探索作業を sub-agent に任せれば、数十回のファイル読み込みが本文 context を汚染しない。 sub-agent は結果の要約だけを返す。context rot 防御の核心ツール。

Hook — 決定論的な割り込み

agent loop の特定の地点(tool 実行前、応答後など)で回る決定論的なコード。モデルの判断ではなく固定されたルール。例:「どの tool でも実行前に linter を回す」「commit 前にテストを強制する」。

権限ゲート — 行動する前に止まる

高リスクの tool(ファイル削除、デプロイ、外部送信)は実行前にポリシー検査または人の承認を経る。harness は「読み取り tool は自動、書き込み tool はゲート」のようなポリシーを強制する。

並列化

互いに依存性のない tool 呼び出しは同時に。独立したファイル読み込み 3 つを直列にする理由はない。ただし、状態を触る呼び出しは直列で。

Human-in-the-loop

取り返しのつかない決定の前で harness は止まり、人に尋ねる。自律性と安全のダイヤルは harness が握っている。


7章 · 失敗モードと復元

成熟した harness とデモ harness の違いは失敗をどう扱うかで分かれる。

失敗モード症状harness の防御
無限 loop同じ行動を繰り返す、進展なしstep cap、loop 検知(反復行動の探知)
tool エラーtool が死ぬ、400 応答エラーを捕まえてモデルに「観察」として伝える、リトライ上限
幻覚された tool存在しない tool 名を呼ぶレジストリと照合、モデルに「そんな tool はない」と返す
Context rot長くなった context で品質低下圧縮、sub-agent 隔離、pruning
コスト暴走トークンが静かに積もるトークン予算、自己修正回数の上限
誤った自信間違った答えを確信を持って検証 tool(テスト・型チェック)を loop に
スコープ逸脱指示された以上のことをやるSystem Prompt の制約、権限ゲート

核心原則: agent は失敗する — harness がその失敗を吸収し、復旧可能なら復旧し、そうでなければきれいに止まらなければならない。 失敗を防ぐのではなく、失敗を扱うのが harness の仕事だ。


8章 · 実際の harness たち — 何が違うのか

同じモデル API を呼んでいるのに、体験が違う。harness が違うからだ。

harness表面特徴
Claude CodeCLIsub-agent、hook、skill、MCP、権限モード、CLAUDE.md
CursorIDEエディタがそのまま harness の表面、background agent、diff 中心
Codex CLICLIsandbox 実行、AGENTS.md
AiderCLIgit ネイティブ、repo map、commit 中心
OpenClawメッセージングアプリローカル gateway、Skills/ClawHub、マルチチャネル
Claude Agent SDKライブラリharness 自体を作るためのツール

これらの違いはほぼすべてharness の設計判断だ:

  • 表面(CLI vs IDE vs メッセージング)
  • context ファイルの慣習(CLAUDE.md vs AGENTS.md
  • tool execution モデル(sandbox の強度)
  • 制御フロー(sub-agent・hook のサポート有無)
  • 権限モデル(どれだけ自律的か)

Claude Agent SDK が特に興味深い — それは完成された harness ではなく、harness を作るためのライブラリだ。loop・tool・context 管理を提供し、あなたはその上に自分の harness を建てる。


9章 · 自作する — 40 行の最小 harness

harness の本質は単純だ。最小 harness を自作してみると明確になる。

import anthropic

client = anthropic.Anthropic()

# tool 定義 — モデルはこの「説明」でしか tool を知らない
TOOLS = [{
    "name": "run_bash",
    "description": "Run a bash command and return its output.",
    "input_schema": {
        "type": "object",
        "properties": {"command": {"type": "string"}},
        "required": ["command"],
    },
}]

def execute_tool(name, args):
    # 実際の実行 — 現実では sandbox 内で
    import subprocess
    if name == "run_bash":
        r = subprocess.run(args["command"], shell=True,
                           capture_output=True, text=True, timeout=30)
        return (r.stdout + r.stderr)[:4000]   # 結果を切り詰めて context を保護
    return f"Unknown tool: {name}"            # 幻覚された tool への防御

def agent_loop(task, max_steps=20):           # step cap = 終了保証
    messages = [{"role": "user", "content": task}]
    for step in range(max_steps):
        resp = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=2048,
            system="You are a coding agent. Use tools to complete the task.",
            tools=TOOLS,
            messages=messages,
        )
        messages.append({"role": "assistant", "content": resp.content})

        # tool 呼び出しがなければ → 最終回答 → loop 終了
        tool_uses = [b for b in resp.content if b.type == "tool_use"]
        if not tool_uses:
            return resp.content

        # tool を実行し結果をメッセージに注入
        results = []
        for tu in tool_uses:
            output = execute_tool(tu.name, tu.input)
            results.append({
                "type": "tool_result",
                "tool_use_id": tu.id,
                "content": output,
            })
        messages.append({"role": "user", "content": results})

    return "Step limit reached"               # fail-closed

これがすべてだ。while loop + tool execution + メッセージ注入。 Claude Code も Cursor も、この骨格の上に — context 管理、sub-agent、hook、権限、より良い tool、より良い System Prompt を — 載せたものだ。

上の 40 行で抜けているものこそが「プロダクション harness」の仕事だ: context 圧縮(4章)、権限ゲート(6章)、失敗復元(7章)、ストリーミング UX、可観測性、コスト追跡。


10章 · harness を評価する — モデル eval とは違う

「このモデルは良い?」はモデル eval だ。「この harness は良い?」は別の問いだ。

harness eval の核心: モデルを固定し、harness だけを変えて測定する。

モデル eval:   harness 固定、モデル A vs モデル B
harness eval: モデル固定、harness A vs harness B

測定する指標:

  • 作業完了率 — 同じ作業の束を harness A/B で回したときの成功比率。
  • step 数 — 完了まで何回の loop か? 少ないほど効率的。
  • 作業あたりのコスト — トークン・時間。harness が context をうまく管理すれば安くなる。
  • エラー復旧率 — tool が失敗したとき harness が復旧させる比率。
  • 逸脱率 — スコープを外れたり無限 loop に陥る比率。

同じ frontier モデルでも harness が悪ければ、弱いモデル + 良い harness より実務作業で負ける。 だから harness eval はモデル eval と同じくらい重要だ — ところがほとんどの場合測定されていない。


11章 · harness がモデルと同じくらい重要な理由

整理しよう。2026 年に「AI engineering」と呼ぶ仕事のほとんどはharness engineering だ。

  • モデル提供者は少数に収束した — モデルは事実上**コモディティ(commodity)**に近づいた。
  • 差別化はそのモデルをどう取り囲むかから生まれる — loop、tool、context、制御フロー。
  • 同じモデル、違う harness → 一方は PR を開き、一方は空回りする。
  • frontier モデル + 悪い harness < 弱いモデル + 良い harness(実務作業の基準で)。

これは 1章の比喩に戻る。engine(モデル)は次第に似てくる。レースは車(harness)で分かれる。

そして良い知らせ: harness はengineering 可能だ。モデルはあなたが変えられない。しかし loop、終了条件、tool の説明、context 管理、権限ゲート、失敗復元 — これは全部あなたが設計するコードだ。AI engineer が実際にコントロールできる表面こそが harness だ。


エピローグ — harness を意識せよ

この記事の一文要約: モデルを選ぶときと同じくらい harness を意識せよ。

ツールを使うときは「この harness が loop をどう回すか、context をどう管理するか、失敗をどう扱うか」を見る。harness を作るときは — 40 行の骨格の上に — context 管理・権限・復元を載せる。harness を評価するときはモデルを固定し harness だけを測る。

次の 10 年の AI engineering はより大きなモデルを待つことではない。より良い harness を建てることだ。

12項目チェックリスト

  1. harness の agent loop に終了条件が明確か(step cap・トークン予算)?
  2. tool の引数を実行前に検証するか?
  3. tool execution が sandbox 内で回るか?
  4. すべての tool 呼び出しにタイムアウトがあるか?
  5. 巨大な tool 出力を切り詰めるか要約するか?
  6. context 圧縮・pruning の戦略があるか?
  7. 探索的な下位作業を sub-agent で隔離するか?
  8. 高リスクの tool に権限ゲートがあるか?
  9. 無限 loop 検知(反復行動の探知)があるか?
  10. 幻覚された tool 名をレジストリで弾くか?
  11. 自己修正 loop に回数の上限があるか?
  12. harness を(モデルを固定して)別途評価するか?

アンチパターン 10 個

  1. 終了条件のない loop — コスト爆弾。
  2. tool の引数を検証なしで実行。
  3. tool execution をホストで直接(sandbox なし)。
  4. 巨大な出力を context にそのまま注入。 5.「全部入れる」context 戦略 — context rot。
  5. 探索作業を本文 context で — 汚染。
  6. 高リスクの tool に権限ゲートなし。
  7. tool エラーで harness が一緒に死ぬ。
  8. モデルだけ評価して harness は評価しない。
  9. harness を「モデル呼び出しラッパー」としてだけ扱う — そこで止まる。

次の記事の予告

次の記事の候補: sub-agent オーケストレーション — マルチ agent harness の設計context 圧縮の深掘り — 何を捨て何を残すかharness eval スイートの構築 — モデルを固定して harness を測定する

「モデルは engine だ。だがレースは車で分かれる。2026 年 AI engineering の重心はモデルではなく harness だ。」

— AI harness 解剖、おわり。