現場実装の クムビーバー です。Loopは、AIに同じ作業を何度もさせることではありません。結果を観察し、次の方法を変え、外側の合格条件で止めるための実行構造です。
前回は、AIエンジニアリングを「判断品質・実行構造・運用品質」の3層に整理しました。今回は2層目のLoopを、失敗した単体テストを直す仕事に落として理解します。
3層の地図ではどこにいるか
| 層 | この回で使うもの |
|---|---|
| 判断品質 | エラー、対象コード、直前のテスト結果をContextにする |
| 実行構造 | Goal → Action → Observe → Verify → Retry |
| 運用品質 | pytest、回数上限、禁止操作、人間への引き継ぎ |
Loopだけを置いても、安全な反復にはなりません。判断材料と、止めるための検証器を3層から組み合わせます。
Loopをひとことで言うと
Loop Engineeringは、1体のエージェントが Goal → Action → Observe → Verify を回し、未達なら観察結果を使って次の方法を変える設計です。ここでは製品名ではなく、実行構造を説明するための言葉として使います。
Anthropic は、LLM がツールを使い環境フィードバックを得ながら回る形を agent と呼び、**「LLM using tools based on environmental feedback in a loop」**と説明しています。この回の Loop は、その層に近いです。
| 部品 | 意味 |
|---|---|
| Goal | 何をもって完了か(一文で指差せる) |
| Action | ツール呼び出し・編集・検索など |
| Observe | 結果・ログ・エラー |
| Verify | 外側の合否(テスト、スキーマ、人手) |
| Retry | 未達なら計画を直し、予算内で戻る |
経路の決め方:
- 人が決める: Goal と Verify(合格バー)
- エージェントが決める: Goal に届くまでの 途中ルート
Loopでは、Promptと同じくらい検証器が重要です。自己採点だけを完了条件にすると、説明は上手いのにテストは失敗したまま、という偽完了が起きます。
具体例:失敗した単体テストを直す
概念を、次の仕事に落とします。
失敗した単体テストを、1体のコーディングエージェントが直す
AIを動かす前に決めるカード
| 項目 | この回の設定 |
|---|---|
| 仕事 | 税込価格計算のバグを直す |
| Goal | tests/test_price.py::test_tax_inclusive が緑 |
| 入力 | 失敗ログ、対象ファイル、変更してよい範囲 |
| 禁止 | テスト期待値の改変、無関係ファイルへの拡散 |
| Verify | pytest -q …::test_tax_inclusive の exit code が 0 |
| Retry budget | 最大 5 回(超えたら人間へ) |
| 完了物 | パッチ差分 + run ログ |
失敗ログ例(設計検証用の合成例):
FAILED tests/test_price.py::test_tax_inclusive
AssertionError: assert 1108 == 1088
# 税込 = 本体 1007 × 1.08 を四捨五入して1088のはずが、税率1.10で計算している
Goal が「コードを良くする」ではなく テスト名で指差せることが重要です。
5つの部品を仕事に対応させる
| 部品 | この具体例 |
|---|---|
| Goal | tax_inclusive を通す |
| Action | 読取・編集・パッチ適用 |
| Observe | pytest 出力と差分 |
| Verify | exit code(「直ったと思う」は使わない) |
| Retry | Observe を次の仮説の材料にする |
- 悪い Verify: モデルに
{ "pass": true }を書かせる - 良い Verify: 人が既に信頼している pytest を外側に置く
2回の試行を追ってみる
会話履歴だけに閉じず、state を外に置きます。
goal: test_tax_inclusive green
attempt: 0
max_attempts: 5
last_observation: <pytest 出力>
patch_summary: <変更の要約>
status: running | done | escalated
| attempt | Action | Observe | Verify |
|---|---|---|---|
| 1 | 税率を 0.08 に変更 | assert 1087 == 1088(端数の floor が残る) | NG |
| 2 | 端数を round に | 当該テスト pass | OK |
while attempt < max_attempts:
attempt += 1
action = plan(goal, last_observation)
apply(action)
last_observation = run("pytest -q tests/test_price.py::test_tax_inclusive")
if last_observation.exit_code == 0:
status = done; break
なぜGraphにせず、1体のLoopでよいのか
| 合図(Graph 候補) | このテスト修正 |
|---|---|
| 専門役割が分かれる | いいえ |
| 並列が要る | いいえ |
| 専用レビューアが要る | Verify は pytest で足りる |
| 途中の人間承認が要る | 予算超過時のエスカレーションで足りる |
「グラフにできる」と「グラフにすべき」は別です。1体に畳んでも失うものがなければ、ループのままにします。
初心者がつまずきやすいところ
- 自己採点 Done(pytest は赤のまま)
- テスト改変で Goal を楽にする
- Retry 予算なし
- Observe を無視した大きな差分
- 役割が1つなのに早くGraphへ分けすぎる
設計レビューで確認すること
導入やレビューの場で、口頭で答えられるかを見る短いリストです(会議専用の儀式ではありません)。
- [ ] Goal はテスト名(または受け入れ条件)で指差せるか
- [ ] Verify は外部コマンド/スキーマ/人手か
- [ ] Retry budget と超過時のエスカレーションがあるか
- [ ] 変更してよい範囲が書いてあるか
- [ ] attempt ごとの Observe を残すか
- [ ] 「直った気がする」を Done にしていないか
- [ ] 1体ループに畳んでも失うものがないか(あるなら Graph 候補)
測るなら(型だけ)
| 観測 | なぜ |
|---|---|
| attempt 数 | 予算超過の検知 |
| 失敗クラス | 次の改善軸 |
| 禁止操作の有無 | 安全性 |
| Done / Escalated | 自動化の効き目 |
つまずいたこと
Verify を「モデルに pass と書かせる」にしていた時期があります。説明は上手いのに pytest は赤、という事故が続きました。検証器を外側に移した瞬間、議論が「感じ」から「exit code」に変わりました。
この回の要点
- Loop = 1体の循環。人が Goal/Verify、エージェントが途中ルート
- 検証器が本体。自己採点 Done は避ける
- 具体例は失敗テスト修正(pytest)
- 多くはループで足りる。専門・並列・別役検証・途中承認が出たら Graph
次回は、1体のLoopでは責任や差し戻し先が分かりにくい仕事を扱います。Graphを使い、観光FAQを並列調査から人間承認まで通す経路を図にします。
よくある質問
普通の繰り返し処理とLoop Engineeringは何が違いますか?
同じ命令を繰り返すのではなく、直前の観察結果を使って次の方法を変え、外部の合格条件と停止条件で制御します。
Loop Engineering とは何ですか?
設計ラベルとして、1体のエージェントが目標・行動・観察・検証を繰り返す循環を指します。Anthropic が言う「環境フィードバック付きのツール利用ループ」に近い層です(製品名ではありません)。
Verify にLLMの自己採点だけで足りますか?
足りません。この事例では外部のpytestを検証器にします。自己採点は補助に留めます。
いつループをやめてグラフにしますか?
役割分担・並列調査・専用レビュー・途中承認が必要になり、1体にまとめると責任や差し戻し先が分からなくなったときです。