1 回で終わらないタスクを「完了まで」自走させる ─ Claude Code Ralph Wiggum ループ入門

テストが通るまで「もう一回やって」「やっぱりここ直して」を、私は手で何度も打ち込んでいました。1 回の出力では終わらないタスクに付きっきりで、再実行のトリガーを毎回自分で引いています。出力を見て、直すべき箇所を指摘して、また実行して、また見て。気づけば「Claude に任せたはずの作業」を、私が監督として張りついて回していました。

シリーズ #14 毎朝同じチェックを Claude に投げ直す生活 で扱ったのは「定刻に投げ直す」時間軸の自動化でした。本記事はその縦の繰り返し、つまり 「完了するまで止まらない」ループ です。鍵になるのが Ralph Wiggum(ラルフ・ウィッグム)という手法と、Anthropic の公式プラグイン ralph-wiggum です。

TL;DR

  • Ralph Wiggum は「同じプロンプトを、完了するまで AI に投げ続ける」手法です。ジェフリー・ハントレーの「Ralph はただの Bash ループだ」が原典で、転んでも立ち上がるシンプソンズのキャラクターから名前を取っています
  • Anthropic の公式プラグイン ralph-wiggum があります。/ralph-loop "タスクの説明" --completion-promise "DONE" --max-iterations 20 の 1 行で始まります
  • 仕組みは Stop hook(Claude がターンを終えて終了しようとした瞬間に発火する hook。詳細は #11 Hooks 入門)です。終了を差し止めて、同じプロンプトをそのまま投げ返し、<promise>DONE</promise> が出るまで回します
  • 「完了まで働かせる」公式の手段は 3 つあります。時計が止め時を決める /loop(#14)、モデルが条件成立を判定する /goal、自分のスクリプトが判定する Stop hook(= Ralph)です
  • 罠は 4 つ。完了条件が曖昧だと止まらない/--max-iterations を付け忘れる/嘘の合言葉で抜けさせる/既存の大規模コードには向かない
  • 今週やる 1 つ: テストが通るまで自走させる小さな新規タスクで、--max-iterations 10 を付けて 1 回試す

「もう一回」を手で何度も打ち込んでいませんか

白状します。テスト駆動開発(TDD、先にテストを書いてから実装を進める方法)で小さな API を書かせるとき、私の手元はこう動いていました。

  • 「テストを先に書いて、それから実装して」と投げる
  • テストが 3 件落ちる。「この 3 件を直して」と打つ
  • 2 件は通った。1 件はまだ落ちている。「残りを直して」と打つ
  • 通った。「ついでに型注釈も付けて」と打つ

1 発で完璧を求めず、失敗を見てから直す進め方そのものは正しいはずです。問題は、その「失敗を見て、もう一度投げる」のトリガーを毎回私が手で引いていることでした。Claude は失敗を直す能力を持っているのに、直すきっかけを与える係として私が張りついています。

似た構造に心当たりはないでしょうか。リファクタリングで「まだ重複が残ってる、もう一回」を 5 回打つ。ドキュメントの整形で「ここの表記が揺れてる、直して」を繰り返す。どれも、AI の能力ではなく、人間の手数で回しているループです。

前回の #14 で扱ったのは「定刻になったら投げ直す」でした。今回ほしいのは「終わっていなければ投げ直す」です。

問題は「何回やり直すか」ではなく「いつ止めるか」を誰が決めるか

繰り返し実行の話には、軸が 2 つあります。「いつ再実行するか」と「いつ止めるか」 です。

シリーズ #14 の /loop が答えていたのは前者でした。5 分おき、あるいは Claude が動的に選んだ間隔で、時計が再実行のタイミングを決めます。止め時は人間がセッションを閉じるか、7 日の失効を待つか、でした。

完了するまで自走させたいなら、答えるべきは後者です。「いつ止めるか」を機械に委ねる。そして、その止め時を判定する者には 3 通りの選択肢があります。

  • 時計に決めさせる(/loop、#14 で扱った)
  • モデルに条件成立を判定させる(/goal
  • 自分で書いたスクリプトに判定させる(Stop hook)

このうち 3 番目、「自分のスクリプトが、決めた合言葉が出るまでループを止めない」 を最小の仕掛けで実現するのが Ralph Wiggum です。

Ralph Wiggum とは「ただの Bash ループ」

Ralph Wiggum という名前は、シンプソンズに登場する少年ラルフ・ウィッグムから来ています。何度つまずいても気にせず立ち上がる、その粘り強さが手法の精神を表しています。

提唱者のジェフリー・ハントレーは、この手法を一言で「Ralph はただの Bash ループだ」と説明しています(ghuntley.com/ralph、非公式の原典)。実体は、次の 1 行がすべてです。

while :; do cat PROMPT.md | claude-code ; done

同じプロンプトファイルを、AI エージェントに延々と食わせ続けるだけのループです。たとえば shanraisshan/ralph-wiggum-self-evolving-loopralph.sh は、回数を区切った for ループの中で claude -p(プロンプトを 1 回だけ渡して結果を返す非対話モード)を呼び、出力に <promise>COMPLETE</promise> が現れたら抜ける、という構造でした。

for ((i=1; i<=$1; i++)); do result=$(claude -p "$(cat "$PROJECT_ROOT/prompt.md")" --output-format text 2>&1) || true echo "$result" if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then echo "見つかった、$i 回目で完了" exit 0 fi
done

ハントレーはこの手法を「不確実な世界で、決定論的にダメ(deterministically bad)」と表現しています。1 回ごとの出力は荒いですし、外すこともあります。でもその失敗は予測可能なデータで、プロンプトをギターの弦のように少しずつ調律していけば、いずれ収束する、という考え方です。失敗をエラーとして恐れるのではなく、次の改善のための材料として扱います。

実際の成果として、公式プラグインの説明には次の例が並んでいます。

  • Y Combinator のハッカソンで、一晩に 6 つのリポジトリを生成した
  • ある 5 万ドルの案件を、API 利用料 297 ドルで仕上げた
  • 3 か月かけて独自のプログラミング言語(cursed)を作っている

そして、この素朴なループを Anthropic が公式プラグインとして仕立て直しました。

公式 ralph-wiggum プラグインの正体は Stop hook

ralph-wiggum は、Anthropic の公式リポジトリ anthropics/claude-code に含まれるプラグインです。外部の Bash ループと決定的に違うのは、今開いているセッションの中だけで回る ことです。while true を別ターミナルで走らせる必要はありません。その仕掛けが Stop hook です。

まず入れ方です。公式マーケットプレイスを追加して、プラグインをインストールします。

/plugin marketplace add anthropics/claude-code
/plugin install ralph-wiggum@claude-code-plugins

使うときは 1 行です。タスクの説明、完了の合言葉、反復回数の上限を渡します。

/ralph-loop "ToDo の REST API を作る。要件: CRUD 4 操作・入力バリデーション・テスト。すべて満たしたら <promise>COMPLETE</promise> と出力する。" --completion-promise "COMPLETE" --max-iterations 50

これを 1 回叩くと、あとは Claude が自分で回します。API を実装し、テストを走らせ、落ちたら直し、要件を満たすまで反復し、満たしたら合言葉を出して止まります。

中で何が起きているかを順に追います。

  1. /ralph-loop が状態ファイル .claude/ralph-loop.local.md を作ります。中身は YAML frontmatter(ファイル先頭の --- で囲んだメタ情報欄)に反復回数・上限・完了の合言葉を持ち、その下に渡したプロンプト本文が入ります
  2. Claude がタスクに取り組み、ターンを終えて終了しようとします
  3. その瞬間に Stop hook(hooks/stop-hook.sh)が発火します。状態ファイルがあれば、終了を差し止めます
  4. 直前の出力に <promise>...</promise> があり、中身が完了の合言葉と完全一致すれば、状態ファイルを消してループを終えます。一致しなければ、同じプロンプトをそのまま投げ返します
  5. 反復回数を 1 つ増やし、上限に達していれば停止します

差し止めと投げ返しの実体は、Stop hook が返す JSON です。公式の stop-hook.sh は、完了でも上限到達でもないとき、こう出力します。

jq -n \ --arg prompt "$PROMPT_TEXT" \ --arg msg "$SYSTEM_MSG" \ '{ "decision": "block", "reason": $prompt, "systemMessage": $msg }'

decision: block で終了をブロックし、reason に最初のプロンプトをそのまま入れて返します。これが「同じプロンプトを完了まで投げ続ける」の正体です。hook が exit code と stdout で decision を返す仕組み自体は #11 Hooks 入門 で扱った通りで、Ralph はその Stop イベントを完了条件ループに使っているだけです。

図にすると 1 枚に収まります。

flowchart TD Start(["/ralph-loop でタスクを渡す"]) --> State["状態ファイル<br/>.claude/ralph-loop.local.md<br/>(反復回数・上限・完了の合言葉)"] State --> Work["Claude がタスクに取り組む<br/>ファイルを編集・テストを実行"] Work --> Exit["ターンを終えて<br/>終了しようとする"] Exit --> Hook{"Stop hook が判定"} Hook -- "完了の合言葉が出た" --> Done(["ループ終了<br/>状態ファイルを削除"]) Hook -- "上限回数に到達" --> Stop2(["強制終了<br/>状態ファイルを削除"]) Hook -- "どちらでもない" --> Feed["同じプロンプトを<br/>そのまま投げ返す<br/>(decision: block)"] Feed --> Work

この自己参照ループの肝は、プロンプトが毎回まったく同じ ことです。変わるのは Claude が触ったファイルと git の履歴だけ。Claude は次の反復で、自分が前回書いたコードやテスト結果をファイルから読み直し、そこを起点に改善します。指示を変えていないのに前進するのは、過去の自分の仕事が成果物として残っているからです。

生 Ralph と公式プラグインの違い

「生の Ralph」(外部の Bash ループ)と公式プラグイン(セッション内の Stop hook)は、同じ思想ですが文脈の扱いが違います。

生 Ralph(外部ループ)公式プラグイン(Stop hook)
回し方別ターミナルで while / for ループ今のセッション内で /ralph-loop 1 行
1 回ごとの実行claude -p をまっさらな状態で起動同じセッションでターンを継続
会話の文脈毎回リセット。記憶はファイルと git だけセッションに蓄積する
前回作業の引き継ぎファイル・git 履歴を読み直すファイル・git 履歴 + 会話文脈
止め方ループ条件 or プロセス停止/cancel-ralph(状態ファイルを消す)

どちらも「作業がファイルに残る」ことが前提です。違いは会話文脈を持ち越すかどうか。生 Ralph は毎回まっさらなので、文脈の汚れが溜まらず長く回せます。一方、公式プラグインは会話文脈を引き継ぐので、直前のやり取りを覚えたまま反復できますが、回し続けると文脈が膨らみます。

止めたいときは /cancel-ralph を実行します。状態ファイル .claude/ralph-loop.local.md を削除するだけのコマンドで、ファイルが消えれば次の Stop hook は素通りし、ループが終わります。

「完了まで働かせる」公式 3 手段の使い分け

ここまで Ralph(Stop hook)を見てきましたが、「完了まで Claude を働かせる」公式の手段はほかにも 2 つあります。/goal の公式ドキュメントには、この 3 つを並べた比較が載っています。日本語に整理するとこうなります。

手段いつ発火するかいつ止まるか
/goal前のターンが終わるたびモデルが条件成立を確認したら
/loop時間間隔が経過したとき自分で止めるか、Claude が完了と判断したら
Stop hook(= Ralph)前のターンが終わるたび自分のスクリプト or プロンプトが判定したら

止め時を判定する者で言い換えると、モデル(/goal)・時計(/loop)・自分のスクリプト(Stop hook) の 3 択です。/goal と Stop hook はどちらもターンが終わるたびに発火しますが、/goal はセッション限定の手軽な近道(条件を打つとそのセッションだけ有効)で、Stop hook は設定ファイルに住み続ける常設の仕掛け、という違いがあります。

使い分けの目安はこうなります。

  • 完了条件を自然な日本語で書けて、その判定をモデルに任せたい → /goal
  • CI やデプロイのような外部の状態を、一定間隔で見張りたい → /loop(#14)
  • 「この合言葉が出たら完了」を厳密に決めて、テストが緑になるまで自走させたい → Ralph(Stop hook)

/goal は #14 でも「条件を満たすまで会話を続けたい場合」として 1 行だけ触れました。Ralph との距離が近い手段なので、別途あらためて扱います。本記事は「合言葉で止める Ralph」に絞ります。

良いプロンプトの書き方 4 原則

Ralph の成否は、モデルの賢さより プロンプトの書き方 で決まります。公式プラグインの説明にある 4 つの原則を、実例とともに見ます。

  1. 完了条件を明確にする

「ToDo API を作って、いい感じにして」では、いつ終わりか機械に判定できません。チェックリストと合言葉で締めます。

ToDo の REST API を作る。
完了条件:
- CRUD 4 エンドポイントが動作する
- 入力バリデーションが入っている
- テストが通る(カバレッジ 80% 超)
- README に API ドキュメントがある
- 出力: <promise>COMPLETE</promise>

  1. ゴールを段階に割る

「完全な EC サイトを作って」のような巨大な一塊は、ループが収束しません。フェーズに分けます。

フェーズ 1: ユーザー認証(JWT、テスト)
フェーズ 2: 商品カタログ(一覧・検索、テスト)
フェーズ 3: カート(追加・削除、テスト)
すべてのフェーズが終わったら <promise>COMPLETE</promise> と出力する。

なお JWT(ログイン後にサーバが発行する署名付きトークン)のような略語は、プロンプト内でも初出時に補っておくと、反復のたびに解釈がぶれません。

  1. 自己修正を仕込む

「機能 X のコードを書いて」だけだと、書いて終わりになります。直す手順まで指示します。

機能 X を TDD で実装する:
1. 失敗するテストを書く
2. 機能を実装する
3. テストを走らせる
4. 落ちたらデバッグして直す
5. 必要ならリファクタする
6. すべて緑になるまで繰り返す
7. 出力: <promise>COMPLETE</promise>

  1. 脱出口を用意する

不可能なタスクで無限に回り続けないよう、--max-iterations を必ず付けます。公式も第一の安全装置として必須だと書いています。

# 推奨: 必ず妥当な反復上限を設定する
/ralph-loop "機能 X を実装する" --max-iterations 20

プロンプト側にも、詰まったときの振る舞いを書いておくと安全です。たとえば「15 回反復しても完了しないなら、何が進行を妨げているか・何を試したか・別案を文書化して止まる」のように指示します。

罠 4 つ

罠 1: 完了条件が曖昧で、いつまでも止まらない

--completion-promise完全一致 で判定されます。stop-hook.sh の中身を見ると、ワイルドカード照合(* などで曖昧に一致させる glob 方式)ではなく文字列リテラルの比較(=)で、<promise> タグの中身が合言葉と一字一句同じかを見ています。だからこそ合言葉は短く一意な文字列(DONEALL GREEN など)にします。逆に「いい感じになったら終わり」のような曖昧な完了条件をプロンプト本文に書いても、機械は判定できず、合言葉が出ないまま回り続けます。

罠 2: --max-iterations を付け忘れて止まらなくなる

合言葉も上限も設定しないと、Ralph は文字どおり無限に走ります。setup-ralph-loop.sh も起動時に「このループは手動では止められません。--max-iterations--completion-promise を設定しない限り無限に走ります」と警告を出します。最初は必ず小さめの上限(--max-iterations 10 など)から始めて、挙動を見てから伸ばします。

罠 3: 嘘の合言葉でループを抜けさせてしまう

長く回ると、Claude が「もう十分だろう」と判断して、条件を満たしていないのに合言葉を出してしまう誘惑があります。公式はこれを明確に禁じています。/ralph-loop のコマンド定義にも「完了の合言葉は、その記述が完全かつ疑いなく真であるときにのみ出力してよい。詰まったと思っても、抜けるために嘘をついてはならない」と書かれています。状態ファイルを更新するたびに hook が出す画面メッセージにも、同じ警告(嘘で抜けるな)が添えられます。プロンプトを書く側も、合言葉を出す条件を検証可能な形(テストが緑・全要件を満たす)にしておくことが、この誘惑を断つ近道です。

罠 4: 既存の大規模コードベースに使う

Ralph が輝くのは、更地から作る新規プロジェクト(greenfield、既存コードのない状態から作るもの)で、テストや lint のような自動検証が効く場面です。ハントレー自身が「既存のコードベースで Ralph を使うことは絶対にしない」と言い切っています。完成度はおよそ 9 割を狙う前提で、最後の仕上げと設計判断は人間が担う、という温度感です。本番障害の調査のような、人間の判断が要る作業には向きません。

今週やる 1 つ

今週、私が最初に試すなら、小さな新規タスクで自走を 1 回体験します。単機能の CLI でも、エンドポイントが 2 つだけの小さな API でも構いません。テストが緑になるまで自走させるプロンプトを書いて、上限を低めに設定します。

/ralph-loop "Python で気温の単位変換 CLI を作る。摂氏・華氏・ケルビンを相互変換する。要件: 引数で変換元と変換先と数値を受け取る・不正入力はエラーメッセージを出す・pytest のテストを書き、全て通す。完了条件をすべて満たしたら <promise>ALL GREEN</promise> と出力する。" --completion-promise "ALL GREEN" --max-iterations 10

走らせている間、別ターミナルで今が何反復目かを覗けます。

grep '^iteration:' .claude/ralph-loop.local.md

10 回以内に ALL GREEN が出れば、テストが緑になるまで Claude が一人で直し切ったということです。出なければ、どこで詰まっているかが反復のログに残ります。そこを見て、完了条件かプロンプトの指示を調律してから、もう一度回します。失敗はエラーではなく、次の調律のためのデータです。

まとめ

1 回で終わらないタスクは、「完了まで止まらないループ」に乗せれば、付きっきりの再投入から抜けられます。要は、止め時の判定を誰に委ねるかです。時計に委ねるなら /loop(#14)、モデルに委ねるなら /goal、自分のスクリプトに委ねるなら Stop hook、つまり Ralph Wiggum です。

Ralph は Anthropic の公式プラグインになっていて、Stop hook が同じプロンプトを完了まで投げ直します。仕掛けは素朴な Bash ループと同じ思想で、過去の自分の仕事がファイルと git に残るから、指示を変えなくても前へ進みます。

シリーズ #13 Agent Teams は「同じ瞬間に複数の Claude を並べる」横軸、#14 Scheduled Tasks と Routines は「同じ Claude を別の時刻に起こす」縦軸でした。本記事はその先の「完了条件まで深掘りする」第 3 の軸です。新規プロジェクト・自動検証・反復上限。この 3 点さえ守れば、Claude は「呼ぶたびに監督が要る道具」から「合言葉が出るまで一人で粘るエージェント」に近づきます。