ハーネスエンジニアリング入門: 60行の自作エージェントでClaude Codeの中身を理解する
ハーネスエンジニアリングとは何かを馬具のたとえと3層の進化で整理し、Python約60行の最小ハーネスを自作して、エージェントループ・ツール実行・ガードレールの仕組みをコードで理解します。
Claude Code も Codex CLI も Cursor も、その正体は LLM の周りに組まれた「ハーネス」だ。本稿では、まずハーネスとハーネスエンジニアリングという言葉を短く整理し、そのうえで Python 約60行で動くハーネスを実際に書いて、Claude Code の中で毎日起きていることをコードのレベルで理解する。読み終わる頃には、while ループ1個とツール2個が「エージェント」と呼ばれるものの骨格だと腹落ちしているはずだ。
この記事を3行で
- LLM単体は「テキストを1回受けて1回返す」だけ。ファイルも読めずコマンドも打てない。それを仕事する存在に変える外側の機構がハーネス
- 心臓部は約25行の while ループ。「モデルに聞く → ツール要求を実行 → 結果を見せてまた聞く」を
stop_reasonが変わるまで回すだけ - 自作ハーネスと Claude Code の差分(コンテキスト管理・権限・メモリ)を見ると、普段触っている設定項目の意味が全部つながる
1. ハーネスとは何か: 馬具のたとえと3層の進化
英語の “harness” は 馬具 のことだ。馬そのものは強力でも、車を引かせるには胴体に巻く帯、手綱、車との連結金具がなければ仕事にならない。AI の文脈でも同じで、LLM を仕事させるための 周辺装置の総体 をハーネスと呼ぶ。中身はシステムプロンプト、ツール(MCP を含む)、コンテキスト管理、メモリ、エージェントループ、ガードレールやログといった部品だ。
つまりハーネスエンジニアリングとは、「LLM を中心に置いて、周りの装置をどう設計するかの工学」である。プロンプトの言い回しを磨くのとも、文脈に入れる情報を選ぶのとも、別の階層の話になる。AIエンジニアリングの主役は、この数年で次のように移ってきた。
- 2022-2024プロンプト工学何を聞くか。Few-shot、Chain-of-Thought、ロール設計など、1回の指示文を磨き上げる。
- 2025コンテキスト工学何を見せるか。RAG、履歴圧縮、CLAUDE.md / AGENTS.md など、文脈ウィンドウに何を詰めるかを設計する。
- 2026ハーネス工学どう動かすか。ツール、メモリ、ループ制御、承認ゲート、観測まで含めた周辺装置を設計する。
違いを一言でいえば、プロンプト工学が1ターン内の指示文を扱うのに対し、ハーネス工学はセッション全体から運用のライフサイクルまでを扱う。失敗の症状も違う。前者は「指示通りに動かない」として現れ、後者は「ループの暴走」「トークンの浪費」「無断の破壊操作」「何が起きたか追えない」として現れる。症状の取り違えも起きやすく、プロンプトのせいだと思って文言を磨いていたら実はメモリが古い情報を返していた、モデルのせいだと思って高いモデルに替えたら実はハーネスに再試行もログもなかった、というのが典型だ。
この言葉が広まった背景には、「賢いモデル + 貧弱なハーネス」は「ふつうのモデル + 良いハーネス」に負ける、という経験則がある。解説記事では、Vercel がツールを約8割削って成功率を80%から100%に上げた例や、LangChain が同じモデルのままハーネスの変更だけで Terminal Bench 2.0 の順位を下位から上位5位に上げた例が引かれている(いずれも各記事が引用する報告値で、本サイトでは再現していない)。
そして、普段使っている Claude Code / Codex CLI / Cursor も、それぞれ設計思想の違うハーネスだ。CLAUDE.md、ツールの許可、サブエージェント、hooks といった、普段「設定」と呼んでいるもののほとんどが、ハーネスの構成要素にあたる。
2. なぜ「書いて」理解するのか
上のような部品名は、解説を読めば覚えられる。だが「モデルがツールを使う」と言われたとき、実際にワイヤ上で何が起きているかをイメージできる人は少ない。
実は、モデルは何も実行していない。モデルがやるのは「bash ツールを ls という引数で呼びたい」と宣言するテキストを返すことだけで、実行するのはハーネス側のコードだ。この一点を体で理解すると、Claude Code の権限ダイアログがなぜあの位置に挟まるのか、hooks がなぜツール呼び出しに介入できるのか、すべて自明になる。だから書く。
3. 全体像: 4ステップのループ
先にこれから作るものの構造を見ておく。ハーネスの心臓部は、この4ステップを完了まで繰り返すループだ。
4. コード全文: mini_agent.py
以下が全文だ。pip install anthropic と ANTHROPIC_API_KEY の設定だけで動く。コメントの (1)〜(5) が次章の解剖と対応している。
# mini_agent.py — 最小構成のエージェントハーネス(約60行)# 使い方: ANTHROPIC_API_KEY を設定して `python mini_agent.py "タスク"`import jsonimport subprocessimport sys
from anthropic import Anthropic
client = Anthropic()
# (1) ツール棚: モデルに見せる「使ってよい道具」の宣言TOOLS = [ { "name": "bash", "description": "シェルコマンドを実行し、標準出力と標準エラーを返す。", "input_schema": { "type": "object", "properties": {"command": {"type": "string", "description": "実行するコマンド"}}, "required": ["command"], }, }, { "name": "read_file", "description": "テキストファイルを読んで内容を返す。", "input_schema": { "type": "object", "properties": {"path": {"type": "string", "description": "読むファイルのパス"}}, "required": ["path"], }, },]
# (2) システムプロンプト: ハーネスが毎回注入する「前提」SYSTEM = "あなたはターミナルで作業するコーディングエージェントです。ツールを使ってタスクを完了してください。"
DANGEROUS = ("rm ", "del ", "format", "mkfs", "> /dev/")
def run_tool(name: str, args: dict) -> str: # (3) ツール実行: モデルは「宣言」するだけで、実行するのはこちら側 if name == "bash": cmd = args["command"] if any(word in cmd for word in DANGEROUS): # (4) ガードレール if input(f"実行しますか? [{cmd}] y/N: ").lower() != "y": return "ユーザーが実行を拒否しました。" r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=60) return (r.stdout + r.stderr)[:5000] or "(出力なし)" if name == "read_file": with open(args["path"], encoding="utf-8") as f: return f.read()[:5000] return f"未知のツール: {name}"
def agent_loop(task: str) -> str: messages = [{"role": "user", "content": task}] while True: # (5) エージェントループ: ここがハーネスの心臓部 resp = client.messages.create( model="claude-opus-4-8", max_tokens=4096, system=SYSTEM, tools=TOOLS, messages=messages, ) messages.append({"role": "assistant", "content": resp.content}) if resp.stop_reason != "tool_use": # ツール要求がなければ完了 return next(b.text for b in resp.content if b.type == "text") results = [] for block in resp.content: if block.type == "tool_use": print(f"[tool] {block.name} {json.dumps(block.input, ensure_ascii=False)[:80]}") results.append({ "type": "tool_result", "tool_use_id": block.id, "content": run_tool(block.name, block.input), }) messages.append({"role": "user", "content": results}) # 結果を文脈に追記
if __name__ == "__main__": print(agent_loop(sys.argv[1] if len(sys.argv) > 1 else "このディレクトリのファイル一覧を教えて"))ループ部分の機構(3ターンにわたるツール実行と messages 配列の積み上げ)は、モックを使ったテストで検証済みだ。API呼び出しを差し替えて「bash要求 → read_file要求 → 完了」の3ターンを流すと、tool_result が正しい tool_use_id に紐付き、会話履歴が user → assistant → user(tool_result) → … の順で積まれていくことが確認できる。
5. 解剖: 5つの部品がハーネスの教科書と対応する
複数の解説記事を突き合わせると、ハーネスの部品リストはおおむね11個に収束する。システムプロンプト、ツール、メモリ、コンテキスト管理、ループ、出力パース、状態管理、エラー処理、ガードレール、検証、サブエージェントだ。60行ハーネスが備えているのは、このうちツール、システムプロンプト、ガードレール、ループという骨格の部分にあたる。コメント番号ごとに対応を見ていく。
(1) ツール棚 = Tool Registry。 TOOLS はモデルに渡す「メニュー」で、JSON Schema で引数の型まで宣言する。重要なのは、ここに実行コードが1行もないこと。モデルが受け取るのは道具の説明書だけだ。Claude Code の Read / Edit / Bash、MCP サーバーが追加するツールも、ワイヤ上ではこれと同じただの宣言リストにすぎない。
(2) システムプロンプト注入。 ユーザーが書いた覚えのない前提(役割、作業ディレクトリ、規約)を毎リクエスト先頭に積む。Claude Code が起動時に CLAUDE.md や git 状態を読み込んで注入しているのは、この SYSTEM 文字列の豪華版だ。
(3) ツール実行 = ハーネスの専権事項。 run_tool() が subprocess や open() を呼ぶ。モデルの宣言と実行の間にコードが挟まっているからこそ、次の(4)が可能になる。
(4) ガードレール。 危険そうなコマンドだけ input() で人間に確認を求める。たった4行だが、これが Claude Code の権限ダイアログ・permission mode・hooks の原型だ。宣言と実行が分離しているアーキテクチャだから、この位置に任意の検問を挟める。
(5) エージェントループ。 stop_reason が "tool_use" である限り、結果を見せてもう一度モデルに聞く。end_turn が返ったらタスク完了。エージェントの「自律性」の正体は、この繰り返しの中でモデルが毎回「次の一手」を選び直していることだ。
6. ここから Claude Code までの距離
60行ハーネスは動くが、実務で1日使えば破綻する。何が足りないかを並べると、製品ハーネスが積んでいる機構の意味が逆算できる。
見ての通り、骨格(ループ+ツール+注入)は60行で再現でき、差分はすべて運用機構だ。OpenAI Codex チームの「Agents aren’t hard; the harness is hard(エージェントは難しくない。ハーネスが難しい)」という言葉は、この差分の作り込みが難しいという意味であって、ループが難しいという意味ではない。
製品ハーネス同士でも、作り込みの方向は違う。同じモデルを使っていても、Claude Code で動かすか Cursor で動かすかで挙動も得意分野も変わる。解説記事での整理をまとめると、おおむね次のようになる。
| 観点 | Claude Code | Codex CLI | Cursor |
|---|---|---|---|
| 設計思想 | 透明性と制御性。破壊的な操作は明示承認 | 速度重視。ツール呼び出しがモデルAPIに統合 | IDE 密結合と対話性。差分レビューを承認ゲートにする |
| コンテキスト戦略 | 関連ファイルを選んで読む。CLAUDE.md を起点に階層展開 | AGENTS.md を読み、短く起動する | コードインデックスとセマンティック検索で必要な箇所を注入 |
| 向くタスク | 長い文脈、複雑なリファクタ、自律実行 | 中粒度の作業の即実行 | 日常のイテレーション、UI 構築 |
7. 理解が変わる3つのポイント
写経して動かすと、普段の Claude Code の見え方が変わる。効果が大きい順に3つ挙げる。
- 権限ダイアログの位置が必然になる。 宣言と実行の間にハーネスのコードが必ず挟まる以上、そこが唯一の検問所だ。「なぜモデルに直接ルールを言い聞かせるより hooks のほうが確実か」の答えもここにある — モデルは宣言しかできず、実行を握っているのはハーネスだから。
- コンテキスト管理の切実さがわかる。
messages配列は1タスクでも驚くほど伸びる。ツール結果を[:5000]で切り詰める1行の意味、そして Claude Code のコンパクションや部分読みが「節約術」ではなく生存条件だと体感できる。 - ツール設計がプロンプト設計より効く理由が見える。 モデルの挙動は
TOOLSの description に強く引っ張られる。ツールの説明文を1行変えるだけで呼び方が変わる様子は、自作ハーネスなら数分で実験できる。
8. 自分のハーネスを見直す5つの観点
構造がわかったら、次は普段使っている製品ハーネスを「設定」ではなく「自分が設計している周辺装置」として見直す。観点は5つある。
- システムプロンプト層を階層化したか。 グローバル(
~/.claude/CLAUDE.md)・プロジェクト(./CLAUDE.md)・サブパス(.claude/rules/*.md)に分け、@インポートで読ませる。60行版のSYSTEM文字列を、用途別に分けて育てるイメージだ。 - ツールを削ったか。 前述の Vercel の例のように、多すぎるツールはモデルを迷わせる。MCP サーバーや内蔵ツールを過剰に有効化していないか棚卸しする。
- メモリを育てているか。 auto memory や MEMORY.md を放置せず、失敗も成功も書き残して同じミスを潰す。
- サーキットブレーカーがあるか。 タイムアウト、再帰の深さ、コスト上限、破壊的な操作の承認ゲート。60行版に欠けていた、無限ループとトークンの浪費への保険だ。
- 検証ループが回っているか。
npm run check、テスト、LLM による判定、pre-commit hook など、「出力した」と「正しい」の間に橋を架ける。
ハーネスは一度作って終わりではなく、失敗をルールに変換し続けるラチェット(逆戻りしない歯車)として育てるものだ。このサイト自体も、プロジェクトルールを定義する CLAUDE.md、探索や設計を分担するサブエージェント、破壊的な操作を抑える hooks、外部データをつなぐ MCP という製品ハーネスの上で更新している。CLAUDE.md を一行足すたびに、ツールを一つ削るたびに、hooks を一つ書くたびに、自分のハーネスは少しずつ強くなる。
9. まとめ
- ハーネスは LLM を仕事させる周辺装置の総体で、ハーネスエンジニアリングはプロンプト工学・コンテキスト工学の上に積み上がる設計領域
- ハーネスの心臓部は
while stop_reason == "tool_use"のループで、約60行あれば動くものが書ける - モデルはツールを実行できない。宣言するだけで、実行・検問・記録はすべてハーネス側の仕事
- 製品ハーネスとの差分はコンテキスト管理・権限・メモリなどの運用機構で、そこがハーネスエンジニアリングの主戦場
自分のハーネス(Claude Code)を育てる具体策は、下の関連記事をどうぞ。
関連記事
- Claude Code導入ガイド: Windows/macOS/WSLと初期設定 — 製品ハーネスを導入し、初期設定を詰める
- Claude Code hooksレシピ7本 — (4)ガードレールを製品ハーネス側で組む
- MCP入門 — 本稿の「ツール棚」を外部に拡張する規格
- AGENTS.md完全ガイド — (2)システムプロンプト注入を育てる方法
- CLAUDE.md ジェネレーター + 採点 — ハーネスの起点になる CLAUDE.md を生成・採点できる無料ツール
関連して読む
ai-agent・claude-codeを続けて読む
· 参考リンク 7件国会会議録APIをMCPサーバー化する:TypeScript最小実装とClaude Code登録まで
国会会議録検索APIをMCP TypeScript SDK v2(@modelcontextprotocol/server 2.0.0)でstdioサーバーにする手順。検索・発言取得の2ツール、出典を返すoutputSchema、JSON-RPCを直接流した動作確認ログ、claude mcp addの構文まで(2026-09-14実測)。
ai-agent・claude-codeを続けて読む
· 参考リンク 3件AIエージェントに「原因調査だけ」を任せる依頼テンプレート
CodexやClaude Codeに修正を許可せず、再現条件・根拠・反証・影響範囲まで調査させるプロンプトと受け入れ基準を解説します。
この記事の情報・検証メモ
- harness-engineering
- ai-agent
- claude-code
- python
- tool-use
- claude-api
- context-engineering
- prompt-engineering
- 公開日
- 情報確認
- 参考リンク
- 7件
- 更新性
- 長く使える
仕様・料金・提供範囲が変わりやすいテーマは、公開日・更新日・情報確認日を分けて管理します。 導入前には必ず記事末尾の一次情報と公式ドキュメントで最新状況を確認してください。
一次情報・参考リンク
- Tool use overview — Claude Docs https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview
- Bash tool — Claude Docs https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool
- Agent Harness Engineering — Addy Osmani https://addyosmani.com/blog/agent-harness-engineering/
- The Anatomy of an Agent Harness — Avi Chawla https://blog.dailydoseofds.com/p/the-anatomy-of-an-agent-harness
- What Is an Agent Harness? The Architecture Behind Claude Code, Codex, and Cursor https://www.mindstudio.ai/blog/what-is-agent-harness-architecture-explained
- The Third Evolution: Why Harness Engineering Replaced Prompting in 2026 https://www.epsilla.com/blogs/harness-engineering-evolution-prompt-context-autonomous-agents
- What Is an Agent Harness? The Infrastructure That Makes AI Agents Actually Work https://www.firecrawl.dev/blog/what-is-an-agent-harness