今回はループエンジニアリングを試してみたので、その概要をまとめます。
ループエンジニアリングとは
AIを開発に使うときの工夫のしどころは、モデルの性能が上がるにつれて移り変わってきました。
呼び方は人によって揺れますが、自分はおおむね次のように整理しています。
プロンプトエンジニアリング
単発の指示の書き方を工夫するやり方です。
役割を与える、出力形式を指定する、手順を分解して書くといったものになります。
出力の質は指示の書き方でほぼ決まっていました。
コンテキストエンジニアリング
指示の書き方よりも「何を読ませるか」を設計するやり方です。
モデルが賢くなるほど、うまい言い回しよりも必要な情報が渡っているかどうかが効いてきます。
CLAUDE.mdにプロジェクトの前提を書いたり、関連ファイルを絞って渡したりするのがこれにあたります。
ハーネスエンジニアリング
AIを取り囲む実行環境そのものを設計するやり方です。
どのツールを使わせるか、どこに書き込ませないか、どういう検証を通過させるか。
指示ではなく仕組みで振る舞いを縛ります。
ループエンジニアリング
ここからさらに進んで、人間が指示を出さない時間帯を作るやり方です。
あらかじめ入力(今回はissue)と1周の手順を決めておき、AIがそれを自分で繰り返します。
人間は回っている最中ではなく、回った結果をレビューします。
前の3つが1回のやり取りの質を上げるものだったのに対して、ループエンジニアリングはやり取りの回数そのものを減らすという点が違います。
やったこと
対象は個人開発の「Shelfie」のバックエンド(Rails)です。
issueを起票したらPRができあがる状態を目指しました。
用意したもの
.claude/配下に以下のファイルを新規に配置しました。
loop.md(23行)… 1周の手順。上から順に最初に該当したものだけ実行するrules/loop-policy.md(122行)… 担当範囲と停止条件skills/loop-triage(152行)… 着手するissueの選定とissueの事前検査skills/implement-issue(301行)… 実装から検証、PR作成までagents/gate-verifierほか(91行)… 機械判定ゲートの合否だけを出すstate/loop.md(263行)… セッションの記憶より優先する外部ステート
1周1 issueで、PRを作ったら終了する
連続で処理させると、レビュー待ちのPRが積み上がって人間の理解が追いつかなくなります。
1周を1件に区切っておけば、暴走したときの被害もその1件に収まります。
「迷ったら止まる」を機械的な条件に書き換える
「迷ったら」は主観なので、無人では判定できません。
停止条件は機械的な5つだけにして、どれにも当たらなければ自分で決めて進ませました。
検証役を実装役から分離する
合否を判定するgate-verifierにはBashとReadしか渡さず、フォーマッタの自動修正も禁止しました。
直すのが仕事ではなく、CIが通る状態かを事実で示すのが仕事だからです。
実行したコマンドと出力の列挙がない判定は無効、というルールにしました。
以上の準備をしたうえで、ループを複数回回してみましたがそれでも何回かループが止まることがありました。
わかったこと
止まった原因は、ほぼ全部issueの書き方でした。
止まった原因は該当のissueにコメントを残すようにしておいたのですが、そのコメントを見るに止まった原因に実装上の問題は特にありませんでした。
とくに繰り返し出た3つを挙げます。
grepを0件にする完了条件は、その範囲全体の編集許可と等しい
「git grep -i clerk -- app lib config specが0件になること」という完了条件を書きました。
一見すると明確な条件です。
ところが、これを満たすためには既存の実装で追加していたファイルの削除が必要でした。
しかしissueの「触ってよいファイル」には当然そのファイルは記載してません。
条件を満たすことと許可された範囲を守ることが両立しません。
git grep -- <dir>を空にする条件は、実質的に<dir>配下すべての編集許可と等価です。
これに気づかず、6周連続で同じ形の欠陥を出しました。
「触ってよい」としたファイルを、強制層が触らせなかった
.env.exampleに環境変数を追加する完了条件があり、「触ってよいファイル」にも入れていました。
にもかかわらず作業ができません。
原因はsettings.jsonのdenyにあるRead(/.env*)でした。
秘密情報を守るためのルールが、公開テンプレートである.env.exampleまで巻き込んでいたのです。
しかもEditツールは事前のReadを要求するので、このdenyは回避できません。
issueの記述は正しく、実装も正しい。
衝突していたのはissueと強制層のほうでした。
完了条件と指示が正面から衝突する
「max-buffer-sizeを指定しない根拠をコメントに残すこと」という指示と、「max-buffer-sizeでgrepして0件になること」という完了条件を、同じissueに書いていました。
指示どおりコメントを書くと、完了条件が落ちます。
同じ形が3箇所ありました。
3件に共通していたこと
人間が読めば意味が通るが、機械的に検証すると矛盾している、という点です。
人間相手のissueなら「まあそこは察してよ」で済んでいた曖昧さが、無人ループでは全部そのまま止まる原因になります。
まとめ
正直な感想として、今の開発規模ではオーバースペックでした。
ループを回すコストの正体は、AIを賢く動かすことではなく、issueを契約として書き下すコストです。
完了条件を機械判定できる形にして、触ってよいファイルを列挙して、条件どうしが矛盾していないか事前に検査する。
個人開発の規模だと、この起票コストが自分で実装するコストを上回ります。
現状としては、一旦shelfieのバックエンド開発は追加機能などの実装はissueドリブンでループで回して開発しています。
ループ自体がオーバースペックであることは紛れもない事実ですが、実際実装自体に自分が介入することはほぼないです。
レビューなども目は通しますが指摘することはほぼありません。
なので、昨今のAIのモデルの性能を信用して、任せられる部分は積極的に任せてしまおうという気持ちで開発を進めています。
規模が大きくなったときに効いてくるやり方だと思うので、いまは資産を貯める期間として付き合っていきます。
最後までご愛読ありがとうございました。

