アルアカ - Arcadia Academia

Arcadia Academiaは「エンジニアリングを楽しむ」を合言葉に日本のデジタル競争力を高めることをミッションとするテックコミュニティです。

Claude Codeを自律ループで回す|worker⇄reviewerの実装と、暴走させない5つの安全装置

Featured image of the post

「Claude Codeに実装を任せたが、一度では仕上がらないので結局何往復もしている」「レビューを頑張っても、指摘→修正→再レビューを人間が仲介している」「自動で回したいが、暴走してレート枠を食いつくされるのが怖い」——AI駆動開発を実務で回し始めると、必ずここに行き着きます。

答えはシンプルで、人間がやっている「往復の仲介」をコードに移すことです。ただし、先に安全装置を仕込むのが大前提です。本記事では、弊社で実際に運用しているループ機構の設計をもとに、最小実装と 5つの安全装置を解説します。

💡
30秒で要点

・自律ループの最小形は worker(実装)⇄ reviewer(差分レビュー) を指摘が尽きるまで回すだけ
・実装ではなく停止条件が難しい。「指摘がなければ __REVIEW_OK__ だけ出力」とセンチネルを強制する
--session-id で worker と reviewer の文脈を別々に保つ。混ぜると自分の実装を甘く見る
最大ラウンド数レート上限検知の2段構えで空回りを止める
観測役と実行役を分離する。ループが死んでも監視側は生き残る設計にする
必ずgit管理下で回し、全ターンの進行ログを残す。あとで「なぜこうなったか」を追える

💡
AI駆動開発の仕組み化・社内導入のご相談はお気軽にどうぞ!

チームの開発フローに合わせた設計をご提案します。

お問合せはこちら


[目次を開く]

1. 自律ループとは:1往復で終わらせない

通常のAI活用は「指示→回答」の1往復です。しかし実装タスクでは、1回で完成することの方が稀です。現実にはこういう往復を人間が手で回しています。

  1. 実装させる
  2. 差分を見て指摘する
  3. 指摘を伝えて直させる
  4. また見る……

この2→3の仲介をコードに移すのが自律ループです。考え方の背景はLoop Engineeringとは?で解説しています。本記事は実装・運用編です。


2. 最小構成:worker ⇄ reviewer

一番小さく始められるのは、実装役とレビュー役を分けて回す形です。

flowchart LR
    A["worker<br>実装する"] --> B["git diff を取る"]
    B --> C["reviewer<br>差分をレビュー"]
    C -->|"指摘あり"| D["指摘を worker に戻す"]
    D --> A
    C -->|"指摘なし"| E["終了"]

Claude Codeはヘッドレスで呼べるので、このループは普通のスクリプトで組めます。

cmd = ["claude", "-p", prompt, "--output-format", "json"]
if session_id:
    cmd += ["--session-id", session_id]   # 初回:UUIDでセッションを固定
if resume:
    cmd += ["--resume", resume]           # 2回目以降:文脈を継続
if accept_edits:
    cmd += ["--permission-mode", "acceptEdits"]
⚠️
worker と reviewer は必ず別セッションにしてください。同じセッションで実装とレビューをやらせると、自分が書いたコードを甘く見ます。別の --session-id(有効なUUIDが必要)を割り当て、それぞれ --resume で回すのが基本形です。

ここからが本題です。ループ自体は10行で書けますが、安全に回すには仕掛けが要ります


3. 安全装置①:停止条件をセンチネルで持たせる

自律ループで最も難しいのは実装ではなく、「いつ終わるのか」の判定です。

自然文で「問題ありません」と返させるのは危険です。「大きな問題はありませんが、あえて挙げるなら……」のように書かれると、終了なのが継続なのが判別できません。

解決策は、終了時に出力する文字列を固定することです。

SENTINEL = "__REVIEW_OK__"

reviewer_prompt = f"""以下の差分をレビューしてください。
指摘がある場合は番号付きで具体的に指摘してください。
指摘が一つもない場合は、**{SENTINEL} だけ**を出力し、
前置き・講評・感想は一切書かないでください。

{diff}"""

text, meta = run_claude(reviewer_prompt, resume=reviewer_id, cwd=repo)
if SENTINEL in text:
    break   # ループ終了
💡
実運用でつまずいたのはここです。「センチネルだけを出力し、他は何も書かない」と強く指定しないと、「素晴らしい実装です!__REVIEW_OK__」のように装飾が付いて、判定は通るもののログが読みにくくなります。逆に指摘の本文中にセンチネルを例示されて誤終了するケースもあるので、センチネルは通常の文章に百%現れない文字列にしましょう。

4. 安全装置②:最大ラウンド数で殖める

センチネルがあっても、reviewer が指摘を出し続ければループは止まりません。細かい好みの問題を指摘し続けることは実際に起きます。

だから回数で物理的に止めます

for rnd in range(1, args.max + 1):     # 既定 6 回
    # worker → reviewer …
    if SENTINEL in review_text:
        break
else:
    log(f"最大{args.max}ラウンドに達したため中断します")

経験上、既定6回が実用的です。これを超えても収束しないタスクは、そもそもタスクの切り方が大きすぎることがほとんどでした。回数を増やすよりタスクを分ける方が早く終わります。


5. 安全装置③:レート上限を検知して自動停止する

これが無人運用で最も重要です。レート上限に当たったのにループが回り続けると、エラーを入力にして空回りしながらラウンドを消費します

出力を見て検知し、例外で抜けます。

_RATE_HINTS = ("rate limit", "usage limit", "rate_limit", "usage_limit",
               "too many requests", "リミット", "上限に達")

class RateLimited(Exception):
    """5h/7d のレート上限に当たった。ループは中断する。"""

low = (stdout + "\n" + stderr).lower()
if any(h in low for h in _RATE_HINTS):
    raise RateLimited("レート上限(5h/7d)に達したようです")

重要なのは、止めるだけでなく再開できるようにすることです。中断時にセッションIDを表示しておけば、枠が回復したあとに同じ文脈で続きから再開できます。

⚠️
サブスク認証で回す場合、追加課金は発生しませんが5h/7dの枠を消費します。ループは1タスクで数十回の呼び出しをするので、体感より早く枠を食います。課金がサブスク枠かAPI課金かは認証方式で決まります。仕組みはclaude -p(ヘッドレスモード)の課金はサブスク枠?API課金?で解説しています。

6. 安全装置④:観測役と実行役を分離する

これはコードではなく構成の話ですが、実際に一番効きました。

ループを回すプロセスの中に監視機能を持たせると、ループが死んだ瞬間に何が起きていたかも見えなくなります。役割を分けます。

flowchart LR
    A["実行役<br>能動・使い捨て"] -->|"状態をファイルに書く"| B["状態ファイル"]
    B -->|"読む"| C["観測役<br>受動・常駐"]
    C --> D["ブラウザで進捗を見る"]
  • 実行役(ループ):能動的、使い捨て。暴走してもここだけが死ぬ
  • 観測役(モニタ):受動的、常駐。ループが死んでも生き残り、何が起きたかを見せる

連携は状態ファイル1枚で十分です。ただしアトミックに書くのがコツで、直接書くと読み取り側が壊れたJSONを踏みます。

def write_status(d):
    """状態をアトミックに書く。失敗は無視してループ本体を止めない。"""
    try:
        d = dict(d)
        d["updated_at"] = int(time.time())
        tmp = STATUS_FILE + f".tmp.{os.getpid()}"
        with open(tmp, "w", encoding="utf-8") as f:
            json.dump(d, f, ensure_ascii=False)
        os.replace(tmp, STATUS_FILE)   # ← アトミックに差し替える
    except OSError:
        pass

もう一つのポイントは、状態書き出しの失敗でループを止めないことです。観測はあくまでおまけなので、ここで例外を上げると本末転倒です。


7. 安全装置⑤:git管理下+全ターンの進行ログ

自動編集を有効にして回す以上、いつでも捧てられる状態でなければ安心して任せられません。最低限この2つです。

① 対象は必ずgit管理下にする

いつでも git diff で差分を見れ、気に入らなければ捧てられます。そもそもreviewerに渡す入力が git diff なので、ループの前提でもあります。

② 全ターンの全文をログに残す

画面の流れる出力だけだと、あとで「なぜこうなったか」を追えません。弊社では1回の実行ごとにMarkdownの進行ログを出し、各ターンの全文・時刻・トークン数を残しています。

## worker (round 2) `14:23:07`  (out 1842tok, $0.0421)

(実装の応答全文)

## reviewer (round 2) `14:24:11`  (out 512tok, $0.0133)

(レビューの全文)

---
## 結果: __REVIEW_OK__ で正常終了

- 所要: 187秒 ・ 呼び出し 4- トークン合計: in 48,201 / out 3,104

このログはチューニングの材料になります。どのラウンドで無駄な往復をしているかが見えるので、プロンプトを直す判断がつきます。


8. 運用してわかったこと

実際に回してみて得た知見を、良いことも悪いことも書いておきます。

  • 回数を増やすより、タスクを小さく切る方が効く。6ラウンドで収束しないのは大抵タスクが大きすぎる合図
  • reviewerの観点を指定すると質が安定する。「正しさ・セキュリティ・テスト」のように軸を与えないと、毎回違うところを見て収束が遅くなる
  • 完全無人(--dangerously-skip-permissions)は隔離環境だけにした方がいい。通常は --permission-mode acceptEdits で十分
  • テストを回せるならレビューより先にテストループ。合否が機械的に決まる分、停止判定が圧倒的に楽
  • 生成物は最後に人が見てからコミットする。自動コミットは便利だが、レビューを飛ばす言い訳になりやすい
💡
複数のループやセッションを同時に回すなら、作業ディレクトリの衝突対策が別途必要になります。tmux起動やgit worktree分離の実践はClaude Codeを並行セッションで安定運用するにまとめています。

まとめ

  • 自律ループの最小形は worker(実装)⇄ reviewer(差分レビュー)。ループ自体は10行で書ける
  • 難しいのは停止条件。センチネル文字列を強制し、装飾を禁止する
  • --session-id で worker と reviewer の文脈を分ける。混ぜると自分の実装を甘く見る
  • 最大ラウンド数(既定6)とレート上限検知の2段構えで空回りを防ぐ。中断時は再開できるようにIDを残す
  • 観測役と実行役を分離し、連携は状態ファイル1枚でアトミックに。観測の失敗でループを止めない
  • git管理下+全ターンの進行ログ。あとで追えることがチューニングの前提になる

自律ループは「放っておけば完成する魔法」ではありません。人間がやっていた往復の仲介を、安全装置付きでコードに移すというだけの話です。その分、安全装置を先に入れてしまえば、安心して回せるようになります。

💡
AI駆動開発の仕組み化・社内導入のご相談はお気軽にどうぞ!

お問合せはこちら

よくある質問(FAQ)

Q. ループはどれくらいレート枠を消費しますか?

タスクの大きさによりますが、1タスクでworkerとreviewerを合わせて数回〜十数回呼び出すので、体感より早く枠を食います。初めは --max を小さくし、実行ログのトークン合計を見ながらペースをつかむのが安全です。

Q. センチネルが出ていないのに終了してしまいます

指摘の本文中にセンチネルを例示されている可能性が高いです(「問題がなければ__REVIEW_OK__と返すべきですが……」のような文)。判定を「出力全体をtrimした結果がセンチネルと完全一致」に変えると堅くなります。

Q. reviewer を複数にするべきですか?

観点が広いタスクでは有効です。「正しさ」「セキュリティ」「テスト」を別セッションのレビュアーに分けると、同じモデルでも見つけるものが変わります。ただしその分レート枠を使うので、まずは1人から始めてください。

Q. claude -p は対話モードと何が違いますか?

-p--print)は対話UIを出さずに応答を返して終了します。スクリプトから呼ぶならこちらです。--output-format json を付けると応答本文に加えてトークン数やコストが取れるので、ログに残しやすくなります。

関連記事

📄Arrow icon of a page linkLoop Engineeringとは?プロンプトエンジニアリングの次に来るAIエージェント設計|仕組み・5要素・実践を図解【2026年最新】

📄Arrow icon of a page linkclaude -p(ヘッドレスモード)の課金はサブスク枠?API課金?— 認証方式で決まる仕組みとcron無人運用の設計

📄Arrow icon of a page linkClaude Codeを並行セッションで安定運用する — tmux起動・git worktree分離・孤児プロセス対策の実践

📄Arrow icon of a page linkハーネスエンジニアリングとは?AIエージェント時代に「モデル選び」より重要な設計思想

📄Arrow icon of a page linkClaude Codeで疑似経営会議を開く — CFO・CHRO・COOを独立エージェントで並列に走らせて経営判断を壁打ちする

出典


関連記事

業務効率化・DX推進でお悩みですか?

オンラインセッションで課題を可視化し、最適な解決策をご提案します。

  • DX推進を何から始めればいいかわからない
  • ツール導入を検討している
  • 社内でデジタル人材を育成したい
まずは無料で課題整理

相談は完全無料・オンラインで気軽に

あなたを爆速で成長させるメンタリングプログラムはこちら

メンタープログラムバナー

業務効率化・DX推進のご相談はこちら

伴走支援プログラムの詳細を見る

エンジニアの基礎学習ゲーム

プログラミング学習支援

無料相談はこちら