ハーネスを60行で自作する: Claude Codeの中身を理解する最短ルート
Claude Codeの正体である「ハーネス」をPython約60行で自作し、エージェントループ・ツール実行・ガードレールの仕組みをコードで理解します。写経して動かせる最小実装付き。
- harness-engineering
- ai-agent
- claude-code
- python
- tool-use
- claude-api
- 情報確認
- 参考リンク
- 2件
- 更新性
- 長く使える
- 読了目安
- 約7分
仕様・料金・提供範囲が変わりやすいテーマは、公開日・更新日・情報確認日を分けて管理します。 導入前には必ず記事末尾の一次情報と公式ドキュメントで最新状況を確認してください。
ハーネスエンジニアリング入門では「Claude Code の正体はハーネスである」という話を概念図で整理した。本稿はその実践編だ。Python 約60行で動くハーネスを実際に書いて、Claude Code の中で毎日起きていることをコードのレベルで理解する。読み終わる頃には、while ループ1個とツール2個が「エージェント」と呼ばれるものの骨格だと腹落ちしているはずだ。
1. なぜ「書いて」理解するのか
ハーネスの解説記事を読むと、システムプロンプト・ツール・ループ・ガードレールと部品名は覚えられる。だが「モデルがツールを使う」と言われたとき、実際にワイヤ上で何が起きているかをイメージできる人は少ない。
実は、モデルは何も実行していない。モデルがやるのは「bash ツールを ls という引数で呼びたい」と宣言するテキストを返すことだけで、実行するのはハーネス側のコードだ。この一点を体で理解すると、Claude Code の権限ダイアログがなぜあの位置に挟まるのか、hooks がなぜツール呼び出しに介入できるのか、すべて自明になる。だから書く。
2. 全体像: 4ステップのループ
先にこれから作るものの構造を見ておく。ハーネスの心臓部は、この4ステップを完了まで繰り返すループだ。
3. コード全文: 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) → … の順で積まれていくことが確認できる。
4. 解剖: 5つの部品がハーネスの教科書と対応する
コメント番号ごとに、教科書的なハーネス構成要素との対応を見ていく。
(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 が返ったらタスク完了。エージェントの「自律性」の正体は、この繰り返しの中でモデルが毎回「次の一手」を選び直していることだ。
5. ここから Claude Code までの距離
60行ハーネスは動くが、実務で1日使えば破綻する。何が足りないかを並べると、製品ハーネスが積んでいる機構の意味が逆算できる。
見ての通り、骨格(ループ+ツール+注入)は60行で再現でき、差分はすべて運用機構だ。OpenAI Codex チームの「Agents aren’t hard; the harness is hard(エージェントは難しくない。ハーネスが難しい)」という言葉は、この差分の作り込みが難しいという意味であって、ループが難しいという意味ではない。
6. 理解が変わる3つのポイント
写経して動かすと、普段の Claude Code の見え方が変わる。効果が大きい順に3つ挙げる。
- 権限ダイアログの位置が必然になる。 宣言と実行の間にハーネスのコードが必ず挟まる以上、そこが唯一の検問所だ。「なぜモデルに直接ルールを言い聞かせるより hooks のほうが確実か」の答えもここにある — モデルは宣言しかできず、実行を握っているのはハーネスだから。
- コンテキスト管理の切実さがわかる。
messages配列は1タスクでも驚くほど伸びる。ツール結果を[:5000]で切り詰める1行の意味、そして Claude Code のコンパクションや部分読みが「節約術」ではなく生存条件だと体感できる。 - ツール設計がプロンプト設計より効く理由が見える。 モデルの挙動は
TOOLSの description に強く引っ張られる。ツールの説明文を1行変えるだけで呼び方が変わる様子は、自作ハーネスなら数分で実験できる。
7. まとめ
- ハーネスの心臓部は
while stop_reason == "tool_use"のループで、約60行あれば動くものが書ける - モデルはツールを実行できない。宣言するだけで、実行・検問・記録はすべてハーネス側の仕事
- 製品ハーネスとの差分はコンテキスト管理・権限・メモリなどの運用機構で、そこがハーネスエンジニアリングの主戦場
概念の全体地図はハーネスエンジニアリング入門を、自分のハーネス(Claude Code)を育てる具体策は下の関連記事をどうぞ。
関連記事
- ハーネスエンジニアリング入門 — 本稿の前提となる概念整理。3層進化と製品ハーネス比較
- Claude Code公式ベストプラクティス日本語ガイド — 製品ハーネス側の設定を実務で詰める
- MCP入門 — 本稿の「ツール棚」を外部に拡張する規格
- AGENTS.md / CLAUDE.md / メモリ運用プレイブック — (2)システムプロンプト注入を育てる方法
一次情報・参考リンク
関連して読む
- · 参考リンク 5件
ハーネスエンジニアリング入門: LLMの周りを"配線する"設計
2026年に第3の柱として浮上したハーネスエンジニアリングを、馬具メタファーから3層比較、エージェント・ループ、Claude Code/Cursor/Codexの設計差まで図解で整理します。
- · 参考リンク 5件
Claude Opus 5の実力:Fable級を半額で使えるのか
Claude Opus 5の実力、料金、Fable 5・Sonnet 5との違い、Claude CodeとAPIの移行注意点を、Anthropic公式情報から実務目線で解説します。
- · 参考リンク 5件
国会の情報を AI エージェントから引く——houan-mcp の7つの実用ユースケース
Claude Desktop / Claude Code / Cursor から国会会議録と議案情報を出典付きで引ける @codeagentjp/houan-mcp の使いどころを、記者・法務・研究者・市民目線で7つのユースケースに整理します。答弁検索、法案ウォッチ、大臣発言の時系列追跡まで。