
ループエンジニアリング — Claude Code で自律ループを作ってみる
Introduction
Claude Code や Codex のような AI Agent(コーディングエージェント)
を実際の開発で効率的に動かす技術は、リリース後に以下のように進化してきました。
- プロンプトエンジニアリング : 1回の指示文をどう書くか
- コンテキストエンジニアリング : モデルに渡す情報(文脈)を毎回どう整理するか
- ハーネスエンジニアリング : エージェントが効率的に働く環境(ツール・権限・検証)をどう作るか
数ヶ月ごとに「◯◯エンジニアリング」というワードが出てきて、
次は何エンジニアリングなのかという感じですが、
最近出てきたのはループエンジニアリング(Loop Engineering)です。
あるサイトでは「開発者は 18 ヶ月足らずで prompt engineering → context engineering → harness engineering → loop engineering へ移った」とまとめています。
また、Anthropic の Boris Cherny 氏(Claude Code の原型を作った人)の発言は以下。
I don't prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops.
(私はもう Claude にプロンプトを入力していない。Claude にプロンプトを打ち、何をすべきか判断するループが走っている。私の仕事はループを書くことだ)
本記事では、このループエンジニアリングの概要を確認したうえで、
Claude Code の Stop hook を使った最小構成で自律ループを実際に作ってみます。
また、そのループを bash・/goal・スキル・サブエージェント・Agent SDK など複数の実装パターンで比較してみます。
About Loop Engineering
用語の登場
「Loop Engineering」という言葉を体系化したのは、Google のエンジニア Addy Osmani 氏です。
2026年6月7日のブログ記事 Loop Engineering で、次のように定義しています。
Loop engineering is replacing yourself as the person who prompts the agent. You design the system that does it instead.
(ループエンジニアリングとは、「エージェントにプロンプトを打つ人間」を自分からシステムに置き換えること。あなたは代わりにそれをやるシステムを設計する)
背景には、前述の Boris Cherny 氏の発言と、
OpenClaw 作者 Peter Steinberger 氏の Xのポストがあります。
Here’s your monthly reminder that you shouldn’t be prompting coding agents anymore.You should be designing loops that prompt your agents.
(もうコーディングエージェントにプロンプトを出すべきではありません。エージェントにプロンプトを出させるようなループを設計すべきです。)
Osmani 氏はループエンジニアリングを「ハーネスの一つ上の階(one floor above the harness)」と表現しています。
ハーネスが「1つのエージェントが働く環境」だとすれば、
ループはその環境を「タイマーで起動し、ヘルパーを用意して、自分自身に次の仕事を与える」制御構造です。
各層の関係は以下。
| 階層 | 最適化の対象 | 典型的な問い |
|---|---|---|
| プロンプトエンジニアリング | 1回の指示 | どう書けば狙いどおり答えるか |
| コンテキストエンジニアリング | 毎ターンの文脈 | 何をいつコンテキストに入れるか |
| ハーネスエンジニアリング | 実行環境 | エージェントが仕事を遂行・検証できる環境か |
| ループエンジニアリング | 反復の制御構造 | 次の仕事をどう見つけ、どう検証し、いつ止めるか |
なお、下の層が不要になるわけではありません。
プロンプトもコンテキストもハーネスも残ったまま、
その上に自律的な反復を足すのがループエンジニアリングです。
Closed loop とは
まず、いままでのやり方とループエンジニアリングの違いを、Claude Code での具体例で見てみます。
「テストが失敗しているので直したい」というよくある場面です。
↓がいままで(人間がループを回す)の構造。
❯ テストが失敗してるので直して
(結果を確認する)
❯ まだ fizzbuzz(15) が失敗してる。直して
(結果を確認する)
❯ 直った。じゃあ lint も通して
(結果を確認する)…
結果の観測・検証・次の指示(ループの制御)を都度、人が担当しています。
これでは Claude が速くても、人間の作業時間は減りません。
↓がループエンジニアリング。
❯ /goal ./test.sh が exit 0 で終わる。5ターン試してだめなら停止
あとで解説しますが、↑の/goal は Claude Code 組み込みコマンドで、
指定した条件を満たすまで Claude を自動で作業に戻します。
このように、検証や継続判断を仕組み(フック、スクリプト等)に移し、
人間は停止条件を設計して、最後に結果をチェックする役に回ります。
この、「人間が担っていた反復の制御を仕組みに置き換える」
というのがループエンジニアリングです。
ちなみに Anthropic は、エージェントを以下のようにシンプルに説明しています。
Agents can handle sophisticated tasks, but their implementation is often straightforward. They are typically just LLMs using tools based on environmental feedback in a loop.
(エージェントは高度なタスクをこなせるが、その実装はたいていシンプルだ。典型的には、環境からのフィードバックに基づいてループの中でツールを使う LLM にすぎない)
ループエンジニアリングが扱う「ループ」は、
以下のサイクルを停止条件が満たされるまで繰り返す構造です。
Plan(計画) 何をするか決める
↓
Action(実行) コードを書く・コマンドを実行する
↓
Observe(観測) テスト結果・エラーなどの実行結果を読み取る
↓
Verify(検証) ゴールに照らして評価する
├─ OK … 停止条件を満たした → ループ終了
└─ NG … 結果をフィードバックして Plan に戻る(ループ継続)
1回のプロンプトで回答させる(one-shot)のとは違い、
closed loop ではエージェントが
実環境からのフィードバック(テスト結果、コマンド出力など)を観測し、
ゴールに照らして評価してから次の行動を決めます。
間違った方向性で延々と作業をするのではなく、
途中で自分のミスを捕まえて自律的に軌道修正します。
そして、ループの成否を分けるポイントは以下の点です。
- 検証(Verify)を実際のものにする
AIの自己申告ではなく、テストや実行結果など、
機械的に判定できる根拠で「done」を判定する。
Claude 公式ブログも「チェックが定量的であるほど、
Claude は自己検証しやすい」と述べています。
- 停止条件(termination)を明確に決める
「バグを直す」ではなく「test.sh が exit 0 で終わること」のように、
機械判定できる形で完了を定義する。
これが書けないタスクは、そもそも自律ループに向いていません。
また、検証の質を上げる手法としてmaker と checker の分離があります。
1つのモデルに「作らせて、自分で採点させる」と、
自分のバグを見逃しがちです(自己採点バイアス)。
Anthropic はこれを evaluator-optimizerというパターンとして紹介しています。
(1つの LLM が生成し、別の LLM が評価とフィードバックをループで返す)
Claude Code 公式ブログが分類するループ(4種類)
Anthropic の Claude Code チームは「Getting started with loops」で、
ループを「停止条件が満たされるまで、
エージェントが作業サイクルを繰り返すこと」と定義し、以下の4種類に分類しています。
| ループ | 引き金 | 停止条件 | 主な道具 |
|---|---|---|---|
| Turn-based | ユーザーのプロンプト | Claude が「完了」または「追加コンテキストが必要」と判断 | verification skill |
| Goal-based | 手動プロンプト | ゴール達成 or 最大ターン数到達 | /goal |
| Time-based | 指定した時間間隔(例: 5分ごと) | キャンセル or 作業完了 | /loop、/schedule |
| Proactive | イベント/スケジュール(人間は非同期) | 各タスクはゴール達成で終了。ルーチン自体は止めるまで継続 | 上記すべての組み合わせ |
※表は公式ブログの記述をもとに整理したものです
verification skill は、「完了と報告する前に必ず行う検証手順」を SKILL.md に書いておき、
Claude に自己検証させる仕組みです。
公式ブログの例は「編集が成功しただけで UI 変更を完了と報告せず、
dev サーバーで実際に動作確認し、テストも通してから報告する」という手順書で、
通常の turn-based loop に検証を足して強化する位置づけです。
/goalは完了条件を定義するコマンドです。
Claude が停止しようとするたびに evaluator モデルが条件をチェックし、
達成するか指定ターン数に達するまで Claude を作業に戻します。
例:
/goal get the homepage Lighthouse score to 90 or above, stop after 5 tries.
/loop はプロンプトを一定間隔で再実行するコマンドです。
例:/loop 5m check my PR, address review comments, and fix failing CI
※ローカルで動くため、PC を落とすと止まります
/schedule はループを、Anthropic 管理のクラウド上の
「ルーチン(routine)」として動かすコマンドです。
※クラウド上で動くため、PCを閉じていても実行可能
※2026年7月時点では research preview
ループを構成する部品
Osmani 氏は、ループに必要な部品を「5つ+メモリー(記憶)」として挙げています。
Claude Code で対応する物と合わせてみましょう。
| 部品 | 役割 | Claude Code での対応物 |
|---|---|---|
| Automations | スケジュールで起動。仕事を発見・トリアージ | scheduled tasks・cron、/loop、/goal、hooks、GitHub Actions |
| Worktrees | 並列エージェントの作業を隔離 | git worktree、--worktree、subagent の isolation: worktree |
| Skills | プロジェクト知識・手順の外部化 | Agent Skills(.claude/skills/ の SKILL.md) |
| Plugins / Connectors | 既存ツールとの接続 | plugins、MCP サーバー |
| Subagents | 「作る係」と「チェックする係」の分離 | subagents(.claude/agents/)、agent teams |
| + 外部メモリ | 会話の外に置く状態(何が済み、次は何か) | Markdown ファイル(CLAUDE.md・進捗ファイルなど)、git 履歴などの外部システム |
※対応物の列は Osmani 氏の記事がベース
ちなみに「ループ」の原始的な形は、Geoffrey Huntley 氏が2025年7月に紹介した
Ralph ループとのことです。
while :; do cat PROMPT.md | claude-code ; done
同じプロンプトを毎回新しいコンテキストで延々と流し込むだけの bash ループですが、
「状態を会話ではなくリポジトリ側(PROMPT.md・テスト)に持たせて反復する」
という発想は、現在のループエンジニアリングに通じる例です。
Environment
検証は以下の環境で実施しました。
| 項目 | バージョン |
|---|---|
| OS | macOS 26.5.1(Apple Silicon / arm64) |
| Claude Code | 2.1.201 |
| jq | 1.8.1(stop_hook_active ガードを使う場合のみ) |
| Node.js | 20.19.0(パターン6の Agent SDK 実行に使用) |
| @anthropic-ai/claude-agent-sdk | 0.3.206 |
Setup
必要なものは Claude Code のみです。
本記事のデモは bash だけで完結します。
Claude Code のセットアップはこちらを参照してください。
% claude --version
2.1.201 (Claude Code)
- jq(後述の
stop_hook_activeガードを使う場合)
% brew install jq
Try
ループをつくる
では実際にループをつくってClaude Codeで確認してみましょう。
デモの内容は以下です。
- 目的
「エージェントが終了と判断しても、検証をパスしないと終われない」ループを作成 - 作るもの
- わざとバグを入れた
fizzbuzz.sh(Claude に修正させる) - テスト
test.sh(「完了」の定義。全部通れば exit 0) - Stop hook の検証ゲート
verify.sh(テストが失敗している限り Claude の停止をブロックし、テスト結果をフィードバック)
- わざとバグを入れた
期待する動作としては以下です。
- Claude にテストと無関係な作業を頼む
- Claude が終了しようとした瞬間にゲートがテストを実行
- 失敗しているので終了できない
- Claude が(頼んでいない)バグ修正を行う
- テスト全通過ではじめて終了できる
Stop hook(Claude が応答を終えようとしたときのフック)が、
冒頭の「Plan → Action → Observe → Verify → 継続 or 停止」の closed loop のうち、
Verify と継続/停止の判断を担う構成です。
※公式ブログの分類でいえば turn-based loop に検証ゲートを足したもの
ファイル構成は以下。skills も subagents も使わず、
「実際の検証」と「停止条件」だけに絞っています。
loop-demo/
├── .claude/
│ ├── settings.json # Stop hook の設定
│ └── hooks/verify.sh # 検証ゲート
├── fizzbuzz.sh # バグあり実装
└── test.sh # 実行可能な仕様(テスト)
1. プロジェクトの作成
まず「Claude に修正させる対象」と「完了の定義」を用意します。
わざとバグを仕込んだ FizzBuzz と、その実行可能な仕様としてのテストです。
% mkdir -p loop-demo/.claude/hooks && cd loop-demo
fizzbuzz.sh(バグ: 15 の倍数のケースが抜けている):
#!/bin/bash
n=$1
if [ $((n % 3)) -eq 0 ]; then
echo "Fizz"
elif [ $((n % 5)) -eq 0 ]; then
echo "Buzz"
else
echo "$n"
fi
test.sh(全部パスすることが完了の定義):
#!/bin/bash
fail=0
check() {
actual=$(./fizzbuzz.sh "$1")
if [ "$actual" != "$2" ]; then
echo "NG: fizzbuzz($1) = $actual (expected: $2)"
fail=1
else
echo "OK: fizzbuzz($1) = $2"
fi
}
check 1 "1"
check 3 "Fizz"
check 5 "Buzz"
check 15 "FizzBuzz"
exit $fail
% chmod +x fizzbuzz.sh test.sh
% ./test.sh
OK: fizzbuzz(1) = 1
OK: fizzbuzz(3) = Fizz
OK: fizzbuzz(5) = Buzz
NG: fizzbuzz(15) = Fizz (expected: FizzBuzz)
% echo $?
1
今は 15 のケースだけ失敗する状態です。
2. 検証ゲート設定
次に「テストが通るまで終われない」仕組みを作ります。
中身はテストを実行して結果に応じた exit code を返すだけの
シェルスクリプト(.claude/hooks/verify.sh)です。
#!/bin/bash
# Stop hook: テストが通るまで Claude を止めない検証ゲート
cd "$CLAUDE_PROJECT_DIR" || exit 0
RESULT=$(./test.sh 2>&1) && exit 0 # テスト通過 → 停止を許可(ループ終了)
echo "テストが失敗しています。全テストが通るまで修正を続けてください:" >&2
echo "$RESULT" >&2
exit 2 # 停止をブロック → Claude は作業を継続
hooksの設定(.claude/settings.json)は以下。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/verify.sh"
}
]
}
]
}
}
% chmod +x .claude/hooks/verify.sh
仕組みは公式の hooks 仕様そのままです。
- Stop hookは Claude が応答を終えようとしたときに実行される
- フックが exit 2で終わると「Claude の停止を防ぎ、会話を継続させる」(公式より)
- そのとき stderr がフィードバックとして Claude に渡るので、テスト結果をそのまま渡せば、Claude は「何が失敗しているか」を見て修正に戻る
exit 0=停止条件を満たした、exit 2=検証 NG なのでループ継続
という対応です。
3. フック単体の動作を確認する
Claude Code に組み込む前に、ゲートが正しく判定するかを単体で確認しておきます。
※ CLAUDE_PROJECT_DIR はフック実行時に Claude Code が設定する環境変数
バグあり状態(ブロックされる):
% CLAUDE_PROJECT_DIR="$PWD" ./.claude/hooks/verify.sh
テストが失敗しています。全テストが通るまで修正を続けてください:
OK: fizzbuzz(1) = 1
OK: fizzbuzz(3) = Fizz
OK: fizzbuzz(5) = Buzz
NG: fizzbuzz(15) = Fizz (expected: FizzBuzz)
% echo $?
2
バグを直した状態(停止が許可される):
% CLAUDE_PROJECT_DIR="$PWD" ./.claude/hooks/verify.sh
% echo $?
0
exit 2(ブロック+フィードバック)と exit 0(停止許可)の両側が期待どおり動いています。
確認できたら、fizzbuzz.sh を元のバグあり状態に戻しておきましょう。
4. Claude Code で回す
準備ができたので、Claude Code 上でループを回します。
ここで Claude に頼むのは、バグ修正ではなく「README.md を書いて」というドキュメント作成です。
% claude
❯ この処理の概要をREADME.md に記載してください
普通に「バグを直して」と依頼するとClaude が普通に直して終わるので意味がありません。
無関係な依頼をして、README を書き終えて停止しようとした瞬間に検証ゲートが発動し、
頼んでいないバグ修正をループが強制する状態を確認してみます。
この「無関係な依頼でも修正を強制する」挙動は、ループの働きを見せるためのデモ用設計です。
実際の開発リポジトリで同じフックを入れると、
壊れたテストがある限りどんな依頼でも修正が始まり、本来のタスクが処理できません。
実運用では対象タスク・変更ファイル・テスト範囲を適切に限定してください。
実際に実行したときの流れは以下です。
- Claude が README.md を書き、応答を終えようとする
- Stop hook が
test.shを実行 → 失敗 → exit 2 で停止をブロック - stderr のテスト結果(
NG: fizzbuzz(15) = Fizz ...)が Claude にフィードバックされる - Claude は停止できず、
fizzbuzz.shの修正に取りかかる - 修正後、再び停止しようとする → 今度はテスト全通過 → exit 0 → セッション終了
実際に動作させたセッションログは以下。
❯ この処理の概要をREADME.md に記載してください
⏺ まず対象ディレクトリの中身を確認して、「この処理」が何を指すか把握します。
(中略: ファイルを読み、README.md を作成)
⏺ Write(README.md)
⎿ Wrote 62 lines to README.md
⏺ README.md を作成しました。
(…応答を終えようとする)
⏺ Ran 7 stop hooks
⎿ Stop hook error: [".claude/hooks/verify.sh"]:
テストが失敗しています。全テストが通るまで修正を続けてください:
OK: fizzbuzz(1) = 1
OK: fizzbuzz(3) = Fizz
OK: fizzbuzz(5) = Buzz
NG: fizzbuzz(15) = Fizz (expected: FizzBuzz)
⏺ 検証ゲート(Stop フック)が作動し、テストが通るまで停止がブロックされました。
ゲートの要求どおり fizzbuzz.sh のバグを修正します。
⏺ Update(fizzbuzz.sh)
⎿ Added 3 lines, removed 1 line
3 -if [ $((n % 3)) -eq 0 ]; then
3 +if [ $((n % 15)) -eq 0 ]; then
4 + echo "FizzBuzz"
5 +elif [ $((n % 3)) -eq 0 ]; then
⏺ Bash(./test.sh)
⎿ OK: fizzbuzz(1) = 1
OK: fizzbuzz(3) = Fizz
OK: fizzbuzz(5) = Buzz
OK: fizzbuzz(15) = FizzBuzz
⏺ 全テストがパスしました(exit 0)。
これでゲートの通過条件(テスト成功)を満たしたので、正常に停止できます。
「README を書いて」としか頼んでいないのに、エージェントが『終わった』と
判断しても検証ゲートを通らない限り終われません。
実際、Claude 自身が
「検証ゲートが作動し、テストが通るまで停止がブロックされました」
と認識して修正に向かっています。
プロンプトではなくループ(検証と停止条件)が、定義した完了条件を強制する、
これがループエンジニアリングの最小の形です。
5. 無限ループを防ぐ
「テストが通るまで止まれない」は、無限ループのリスクでもあります。
Claude Code には安全弁が用意されています。
Stop hook が8回連続でブロックすると、
Claude Code がフックを上書きしてターンを終了します(v2.1.143〜)。
※公式では "blocks eight times in a row without progress" と表現
正当に8回以上の反復が必要な場合は、
環境変数 CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で上限を引き上げられます。
もう1つの安全弁が stop_hook_active ガードです。
Stop hook には実行のたびに JSON が渡され、その中の stop_hook_active は
「いま Claude が動いているのは、さっき Stop hook が止めた結果か?」を表すフラグです。
これが true でもかまわずブロックし続けると、
「止める → 続けさせる → また止める…」の無限ループになりかねないので、
フックの冒頭に「true なら何もせず終了(exit 0)」といった早期 return を入れておきます。
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # すでにフック起因で継続中なら、それ以上ブロックしない
fi
※今回の検証ゲートはテストが通れば自然に止まるから↑のガードは入れない
逆に言うと、機械判定できる停止条件を書けないタスクをこの仕組みに入れてはいけません。
※本記事の verify.sh は、cd に失敗したとき exit 0 で通す fail-open な作りです(デモ簡略化のため)。厳密に運用する場合は exit 2 で止める設計にしてください
ループを6つの方法で作る
ここまで作った Stop hook は、複数あるループの実現方法の1つです。
これ以外にもループ実現の方法はあるので、
Claude Code でループを組む方法を、6つの分類で紹介します
(パターン1は a / b の2形態があるため、例としては計7つです)。
| パターン | 手法 | 誰が検証するか | 特徴 |
|---|---|---|---|
| 1a | プロンプト内セルフ検証 | モデル自身(自己申告) | 最も簡単&最も脆い |
| 1b | Ralph ループ(bash + claude -p) |
外側の bash(テストの exit code) | 拡張機能ゼロで作れる |
| 2 | /goal |
別の小型モデル(会話内容から判定) | 組み込み。1行で書ける |
| 2 | /loop・/schedule |
なし(起動のみ。検証は他パターンと併用) | 時間駆動・クラウド常駐 |
| 3 | Stop hook(本編のデモ) | フック(テストの exit code) | テストが通るまで止めない |
| 4 | 検証スキル | モデル自身(渡した手順に従う) | 「何をすべきか知る」層 |
| 5 | Maker-Checker サブエージェント | 別コンテキストの checker | 自己採点バイアスの緩和 |
| 6 | Agent SDK | 外側のコード | CI・プロダクト組み込み |
これらの手法については「どこからが強制か」が確認すべきポイントです。
1a・4 はモデル(確率的な指示)に依存し、1b・3・6 は外側から強制します。
2(/goal)と 5 はその中間で、別のモデル・別のコンテキストによる判定です。
では、さきほどと同じループを、それぞれのパターンで作ってみましょう。
パターン1a: プロンプトにセルフ検証を記述
拡張機能を一切使わず、1回の実行の中で「通るまで修正」と指示するだけの形です。
これもループエンジニアリングの一種です。
% claude -p "./test.sh が全部通るように fizzbuzz.sh を修正してください。
テストを実行し、失敗したら直して再実行。全部通ったら TESTS_GREEN と出力して終了。
test.sh 自体は変更しないこと" --allowedTools "Read,Edit,Bash(./test.sh)"
ループは1ターン内の agentic loop だけで回り、検証はモデルの自己申告です。
なお --allowedTools は許可プロンプトなしで使えるツールの指定なので、
ツールの利用を制限するものではありません。
上の例でも Edit を許可している以上、
test.sh の書き換え(テストを消して通すような不正)は機械的に防げません。
対策は「test.sh は変更しない」というプロンプト指示になるため、
確実に禁止したい場合は --disallowedTools や PreToolUse hook で
制限する必要があります。
簡単に記述できますが、お試し・使い捨ての用途になると思います。
パターン1b: Ralph ループ(bash while + claude -p)
外側の bash で回す最小の自律ループです。
Ralph ループの名で知られる形式で、
停止判定をモデルの自己申告ではなく、外側の ./test.sh(exit code)で
機械的に行うのがパターン1aとの違いです。
#!/bin/bash
# test.sh : テストが通るまで claude -p を回す(上限つき)
MAX=5; i=0
until ./test.sh > /dev/null 2>&1; do
i=$((i+1))
[ "$i" -gt "$MAX" ] && { echo "上限 $MAX 回に到達。停止します"; exit 1; }
echo "=== Iteration $i/$MAX ==="
claude -p "./test.sh を実行し、失敗しているテストが通るよう fizzbuzz.sh を修正してください。test.sh は変更しないこと" \
--allowedTools "Read,Edit,Bash(./test.sh)"
done
echo "all green"
毎回新たなコンテキストで claude -p が起動するため、
長時間セッションでの劣化を避けて実行する手法です。
反復をまたぐ状態はコンテキストではなくファイル(今回はテスト結果)などに持たせます。
バッチなどに向きますが、反復の上限を付けて
別ブランチ/ワークツリーで処理するなどの工夫が必要です。
パターン2: 組み込みコマンド(/goal・/loop・/schedule)
Claude Code には最初からループ用のコマンドが組み込まれています。
今回のデモを /goal でやるなら、フックもスクリプトも不要です。
❯ /goal ./test.sh が exit 0 で終わる。5ターン試してだめなら停止
Claude が停止しようとするたびに小型の evaluator モデルが
会話トランスクリプトから条件達成を判定し、未達なら理由付きで作業に戻します。
評価者は自分ではツールを実行できないため、「./test.sh の実行結果が全てOK」のように、
Claude の出力に証拠が現れる条件にするのがポイントです。
曖昧な条件を提示した場合、「達成した」と適当に報告するループになる可能性があります。
時間駆動にするなら /loop(ローカルで一定間隔で再実行)、
クラウドで動かすなら /schedule(research preview)にするのも選択肢としてありです。
❯ /loop 10m ./test.sh を実行し、失敗があれば fizzbuzz.sh を修正して
なお /loop・/schedule が担うのは「起動のタイミング」だけで、
/goal のような達成判定は付きません。
検証が必要なら、プロンプト内の指示や Stop hook・/goal と組み合わせます。
パターン3: Stop hook
さきほど試した検証ゲートがこれです。
フックは LLM の判断を経由しない強制手法であり、
「テスト/lint を必ず通す」のような確実に守らせたいルールは、
CLAUDE.md(確率的な指示)ではなくフックに置くのがよさそうです。
パターン4: 検証スキル(verification skill)
検証の手順を Skill として定義する方法です。
公式ブログで紹介されている verification skill と同じ発想で、
.claude/skills/verify-fizzbuzz/SKILL.md を置きます。
---
name: verify-fizzbuzz
description: fizzbuzz.sh への変更を「完了」と報告する前に必ず検証する
---
fizzbuzz.sh を変更したら、完了と報告する前に必ず次を行ってください。
1. `./test.sh` を実行する
2. `NG:` が1件でもあれば修正して 1. からやり直す
3. 全件 `OK:` の出力を確認してから完了を報告する
test.sh 自体は変更しないこと。
Skillは「モデルが何をすべきかを知る」ための仕組み、
hooksは「任意のタイミングで実行を強制する」仕組みです。
両方を組み合わせると「知っていて、かつ強制」の構成になります。
※スキルの frontmatter に hooks を書くこともできる
パターン5: Maker-Checker(検証専任サブエージェント)
作った本人に採点させると、自分のバグを見逃しがちです(前述の自己採点バイアス)。
そこで「Write/Edit 権限を持たない検証専任のサブエージェント」を用意し、
別のコンテキストから採点させます。
Anthropic の リポジトリ(cwc-long-running-agents) が示す
builder/evaluator パターンの最小版です。
.claude/agents/evaluator.md:
---
name: evaluator
description: 修正が仕様を満たすか懐疑的にレビューする検証専任エージェント。編集権限なし
tools: Read, Grep, Bash
---
あなたは、別の実装者が「完了」と主張した作業をレビューします。
実装者の自己評価は信用しないでください。
1. `./test.sh` を実行し、結果を自分の目で確認する
2. fizzbuzz.sh / test.sh を読み、テストへの小細工(test.sh の書き換えなど)がないか確認する
返答の1行目に PASS または NEEDS_WORK とだけ書き、
2行目以降に判断の根拠(NEEDS_WORK なら修正可能な指摘のリスト)を書いてください。
もっともらしさは正しさではありません。証拠がなければ NEEDS_WORK です。
Bash は ./test.sh の実行と ls / cat などの閲覧にのみ使い、ファイルの変更・作成は行わないこと。
使い方は、メインセッションで↓のように依頼するだけです。
❯ fizzbuzz.sh を修正してください。完了したと思ったら evaluator サブエージェントに
レビューさせ、PASS が返るまで修正とレビューを繰り返してください
tools に Write/Edit を持たせていないため、evaluator の役割は評価に絞られます。
ただし Bash を許可しているので、完全な read-only ではありません。
そのためプロンプト側で Bash の用途を制限しています。
(機械的に制限するなら PreToolUse hook を併用)
この「作る係」と「チェックする係」の分離により、
自己採点バイアスを緩和し、検証の独立性を高めます。
パターン6: Agent SDK でコードから制御する
ループを CI/CD やプロダクトに組み込むなら Agent SDK です。
Claude Code と同じ agentic loop をライブラリとして呼び出せます。
(Typescript/Python)
認証は、個人の自動化ならサブスクリプションの
OAuth トークン(claude setup-token で発行)も使えます。
外部ユーザー向けのプロダクトに組み込む場合は API キー等が必要です。
% npm install @anthropic-ai/claude-agent-sdk
% npm install -D tsx
比較表で「検証の主体=外側のコード」としたとおり、ポイントは
query()(内側の agentic loop)の外側で、自前のコードが
test.sh を実行して継続/停止を判定することです。
ralph.sh(パターン1b)の TypeScript 版です。
loop.ts:
import { query } from "@anthropic-ai/claude-agent-sdk";
import { execFileSync } from "node:child_process";
// 検証は外側のコードで行う(エージェントの自己申告に頼らない)
const testsPass = (): boolean => {
try { execFileSync("./test.sh"); return true; } catch { return false; }
};
const MAX = 3;
for (let i = 1; i <= MAX && !testsPass(); i++) {
console.log(`=== Iteration ${i}/${MAX} ===`);
for await (const msg of query({
prompt: "./test.sh が通るように fizzbuzz.sh を修正して。test.sh は変更しないこと",
options: {
settingSources: [], // user・project・local のファイル設定を読み込まない
allowedTools: ["Read", "Edit(./fizzbuzz.sh)", "Bash(./test.sh)"],
permissionMode: "dontAsk",
maxTurns: 10, // 1回の実行あたりの agentic turn 上限
},
})) {
if (msg.type === "result") console.log(msg.subtype); // success / error_max_turns など
}
}
if (testsPass()) {
console.log("all green");
} else {
console.error("上限到達。人間へのエスカレーションが必要です");
process.exitCode = 1; // CI でも失敗として扱えるようにする
}
権限は allowedTools で Edit は fizzbuzz.sh、Bash は test.sh の実行だけに限定し、
permissionMode: "dontAsk"(許可していない操作は確認なしで拒否)にしています。
また、SDK はデフォルトで作業ディレクトリの
.claude/ 設定(CLAUDE.md・hooks 等)を読み込みます。
loop-demo には本編の Stop hook が残っているので、
そのまま動かすと query() の中でもゲートが発動してしまいます。
「検証は外側のコードだけ」というこのパターンのデモとして独立させるため、
settingSources: [] で読み込みを無効化しています。
バグがある状態から実行した結果が以下です。
% npx tsx loop.ts
=== Iteration 1/3 ===
success
all green
1回の反復で fizzbuzz.sh が修正され、外側の testsPass() が
テスト全通過(exit 0)を確認して終了しました。
テストが通らないまま上限に達した場合は process.exitCode = 1 で終わるため、
CI でも失敗として検知できます。
停止判定・反復上限・エスカレーションの要否判断を外側のコードが持つので、
そのまま CI やジョブ基盤に載せられます。
実運用では PreToolUse hook やサンドボックスの併用も検討してください(権限の仕様)。
停止条件・安全弁・検証の考え方はここまでとまったく同じで、
置き場所がシェルや設定ファイルからコードに変わるだけです。
実際の開発で Goal / 停止条件をどう決めるか
ここまでのデモは「テスト4件が通る」というシンプルなものでしたが、
実際の開発でループを使うときもやることは同じで、
「曖昧な依頼を、機械判定できる停止条件として定義する」
ように設計します。
考え方は以下。
- コマンドの exit code・数値・件数に落とす: 「直った」「きれいになった」ではなく、コマンドの成否や数値の閾値で表現する
- 複合条件は「すべて満たす」のチェックリストにする : テスト・lint・型チェックなど、判定を1つずつ並べる(本編の
verify.shに検証コマンドを足していくイメージ) - 必ず上限とセットにする: 反復回数・予算の上限を添え、達成できなければ停止して人へ通知
例は以下。
| 曖昧 | 停止条件への翻訳例 |
|---|---|
| バグを直して | npm test が exit 0 で終わる |
| コード品質を上げて | npm run lint・npm run typecheck・npm test がすべて exit 0 |
| 表示を速くして | Lighthouse スコアが 90 以上(公式の /goal 例と同じ発想) |
| ライブラリ移行を進めて | 移行対象リストのファイルがすべて変換済みで、テストが通る(=キューが空) |
逆に「ドキュメントを読みやすくして」のように機械判定できない依頼は、
そのままでは自律ループに載せられません。
その場合は「見出し構成が仕様書と一致する」「リンク切れが 0 件」のように
判定できる部分だけを停止条件にし、
残りの質の評価は人(または別コンテキストの checker)に残すのが現実的です。
また、停止条件を満たすまでが長い大きなタスクでは、
1回のセッションで完走させようとせず、進捗を PROGRESS.md などのファイルに外部化して
反復をまたいで引き継ぐ設計が推奨されています。
参考:Effective harnesses for long-running agents
Cautions
ループエンジニアリングは強力ですが、「自動で反復する仕組み」なので注意点もあります。
実行前に以下を確認しておきましょう。
コスト(トークン消費量)
ループは反復のたびに読み直し・ツール実行・検証を繰り返すため、
トークン消費は確実に増えます。
Anthropic の実測でも
「エージェントはチャットの約4倍、マルチエージェントシステムは約15倍のトークンを使う」
と報告されています。
特に Maker-Checker(パターン5)のように別コンテキストを立ち上げる構成では、コストはさらに増えます。
なので、以下のような対策をして必要以上にトークンを消費しないように気をつけましょう。
- 反復上限を設定(
--max-turns、SDK のmax_turns・予算上限など) - 消費量を定期確認(
/usageなど。公式ドキュメント参照) - (Pro/Max プラン)
/usage-creditsでクレジットの上限設定など claude -p・Agent SDK・API利用時は別枠の消費になる場合があるので課金条件を確認
モデルに不正をさせない
「テストが通るまで」と制限すると、モデルがテストを消したり、条件を緩くしたり、
不正をしてループを抜けようとすることがあります。
以下のように対策を行い、正しく目的を達成できるようにしましょう。
- 「test.sh は変更しない」のような禁止事項をプロンプトやスキルに明記
- checker(evaluator)に diff を確認させ、テストへの不正を検出させる
- PreToolUse hook や
--disallowedToolsなどの制限で該当ファイルへの書き込みを拒否
cwc-long-running-agents の evaluator も、
「別コンテキストから、証拠を自分で確認して PASS / NEEDS_WORK を返す」
という設計をしています。
プロンプトインジェクション
無人ループは、Web ページ・Issue 本文・ログといった信頼できない入力を読み、
実際のアクションを実行します。
そこに悪意ある指示が紛れ込むと、ループがそれを実行してしまうリスクがあります。
以下はループエンジニアリングに限った話ではありませんが、
セキュリティは最大限とれる対策をしましょう。
- ツール権限は最小に絞る(危険操作は hook や 設定で拒否)
- push・デプロイ・外部送信のような不可逆アクションには人間の承認を必須とする
- 長時間の無人実行は専用ブランチ・worktree・サンドボックスに隔離する
Claude Code の公式セキュリティガイドにも prompt injection への組み込み保護の解説や
ベストプラクティスがまとまっているので参考にしてください。
停止条件を書けないタスクには使わない
繰り返しになりますが、機械判定できる停止条件を書けないタスクを自律ループに載せると、
「達成しました」を繰り返すだけの意味のないループになりがちです。
曖昧なタスクはまず人がループを回し、「done」を定義できるようになってから
自動化するのが安全です。
そして、ループが出力した成果物を確認し、検証する責任は人間にあります。
それを忘れないようにしてください。
Summary
本記事では、ループエンジニアリングの概要を整理し、
Claude Code の Stop hook で最小の自律ループを作って、
実際に動作することを確認しました。
ループエンジニアリングとは、「エージェントにプロンプトを打つ人」を
「エージェントにプロンプトを打つシステム」に置き換える設計手法です。
プロンプト → コンテキスト → ハーネスと積み上がってきた抽象化の、
いまのところ最上階にあたります。
ポイントはモデルの性能ではなく、次の2点でした。
- 検証を本物にする : テストのような機械判定できる根拠で「完了」を判定する
- 停止条件を設計する : 機械判定できる形で完了を定義し、上限とセットで運用する
今回試したとおり、この2点は Stop hook 1つでも実現できます。
そして後半で見たとおり、同じ原理は bash の Ralph ループから /goal、検証スキル、
Maker-Checker のサブエージェント分離、Agent SDK による CI 組み込みまで、
さまざまな手法にそのまま持ち上げられます。
最後に、Addy Osmani 氏の言葉をもう一度。
Build the loop. But build it like someone who intends to stay the engineer, not just the person who presses go.
(ループを構築しましょう。ただし、単に「go ボタンを押す人」ではなく、エンジニアであり続ける意志を持つ人として構築しましょう)
ループが出力した成果物を読み、検証し、判断する責任は、
これまでどおりエンジニアの側にあります。
その責任を手放さないかぎり、ループエンジニアリングは
「人間が張り付く時間」を「設計と検収に使う時間」
に置き換えてくれる強力なツールになります。
次にエージェントへ何か依頼するとき、
「このタスクはループにできないか?」
と考えてみてはいかがでしょうか。
References
- Loop Engineering — Addy Osmani(2026-06-07) — 用語を体系化した記事
- Peter Steinberger 氏の X 投稿(2026-06-07) — "You should be designing loops that prompt your agents."
- Getting started with loops — Claude 公式ブログ(2026-06-30) — 4種類のループ、
/goal・/loop・/schedule、verification skill - Hooks reference — Claude Code docs — Stop hook の exit code 挙動・入力スキーマの仕様
- Automate actions with hooks(hooks guide)— Claude Code docs — 8回上限と
stop_hook_activeガードの公式例 - Claude Code CHANGELOG(v2.1.143) — 8回連続ブロック時のターン終了と
CLAUDE_CODE_STOP_HOOK_BLOCK_CAPの導入 - Create custom subagents — Claude Code docs — サブエージェントの定義・独立コンテキスト・ツール制限
- Agent SDK overview — Claude Code docs — Claude Code と同じ agent loop をライブラリとして提供
- anthropics/cwc-long-running-agents(GitHub) — builder/evaluator ループの教材(turnkey ではなく example ingredients と明記)
- Building effective agents — Anthropic(2024-12-19) — エージェントの定義、evaluator-optimizer
- Effective harnesses for long-running agents — Anthropic(2025-11-26) — 長時間タスクの状態外部化
- How we built our multi-agent research system — Anthropic(2025-06-13) — エージェント/マルチエージェントのトークン消費(約4倍/約15倍)
- Security — Claude Code docs — prompt injection への組み込み保護と untrusted content のベストプラクティス
- Manage costs effectively — Claude Code docs —
/usage・/usage-credits・トークン削減策 - Harness engineering: leveraging Codex in an agent-first world — OpenAI(2026-02) — ハーネスエンジニアリングの定義
- ReAct: Synergizing Reasoning and Acting in Language Models(Yao et al.) — 推論+行動ループの理論的ルーツ
- The Anthropic leader who built Claude Code says he ditched prompting — now he just writes loops. — The New Stack(2026-06-10) — 系譜の整理と Cherny 氏の発言
- Ralph Wiggum as a "software engineer" — Geoffrey Huntley(2025-07-14) — 最も原始的な bash ループ
- Designing agentic loops — Simon Willison(2025-09-30) — エージェントループ設計論(「loop engineering」命名以前の先行考察)
- Key takeaways from Boris Cherny on Acquired Unplugged — WorkOS(2026-06-02) — Cherny 氏インタビューの要約





