以前の記事「Loop Engineeringとは何か」では、概念として「失敗から改善するシステム」を扱いました。そして前回の実装編「Harness Engineeringを実装する」では、エージェントに何をさせて良いかを権限として明文化しました。
今回はその続きです。権限の中でエージェントが動いた後——「出てきた結果が基準を満たしているか」を判定し、満たしていなければ自動でやり直させる仕組み、Loop Engineeringを実際にコードとして組みます。
Loop Engineeringの核は単純です。
- 生成する
- 評価する
- 基準を満たさなければ、フィードバックを添えて再生成する
- 基準を満たすか、上限回数に達したら止める
このループを「頭の中で」ではなく、実際に止まる・回るコードとして書いていきます。
1. 評価関数を先に決める
ループを組む前に、最も重要な決定をします。「何をもって合格とするか」です。ここが曖昧だと、ループは無限に回るか、逆に一度で「合格」を出してしまいます。
評価は大きく2種類に分けられます。
- 機械的に判定できるもの:テストが通るか、Lintエラーがないか、JSONスキーマに一致するか
- 人間的な判断が要るもの:文章の質、デザインの良し悪し、事業判断
Loop Engineeringが強いのは前者です。後者を無理にループへ組み込もうとすると、評価そのものが不安定になり、ループが収束しません。まずは機械的に判定できる基準だけをループの対象にします。
def evaluate(result: str) -> tuple[bool, str]:
"""合格ならTrue、不合格ならFalseとフィードバック文字列を返す"""
if "TODO" in result:
return False, "TODOが残っています。実装を完了させてください。"
if len(result) < 100:
return False, "内容が短すぎます。具体例を追加してください。"
return True, ""
2. 再実行ループを組む
評価関数ができたら、それを使ってループ本体を書きます。ポイントは「上限回数を必ず設ける」ことです。上限がないループは、許可された権限の範囲内で、リソースを無限に消費し続けるリスクになります。
MAX_ATTEMPTS = 3
def run_with_loop(task_prompt: str, generate_fn) -> str:
feedback = ""
for attempt in range(1, MAX_ATTEMPTS + 1):
prompt = task_prompt if not feedback else f"{task_prompt}\n\n前回の指摘: {feedback}"
result = generate_fn(prompt)
ok, feedback = evaluate(result)
if ok:
print(f"[Loop] attempt {attempt}: 合格")
return result
print(f"[Loop] attempt {attempt}: 不合格 — {feedback}")
raise RuntimeError(f"{MAX_ATTEMPTS}回試行しても基準を満たしませんでした。人間の確認が必要です。")
3回試しても合格しない場合、ループを黙って諦めさせず、明示的に例外を投げて人間に引き渡します。これはHuman-in-the-loopとは何かで扱った原則と直結します——「エージェントが判断に迷ったら、そこで止めて人に渡す」という境界線です。
3. フィードバックの質がループの収束を決める
このループで一番失敗しやすいのは、フィードバックが曖昧なケースです。「もっと良くしてください」のような抽象的な指摘では、次の試行も同じ結果になりがちです。
評価関数が返すフィードバックは、具体的な修正指示の形にします。
def evaluate(result: str) -> tuple[bool, str]:
issues = []
if "TODO" in result:
issues.append("「TODO」という文字列を削除し、該当箇所を実装してください")
if result.count("```") % 2 != 0:
issues.append("コードブロックの開始・終了が対応していません")
if not issues:
return True, ""
return False, " / ".join(issues)
「何が悪いか」ではなく「何をすればいいか」を書くのがコツです。エージェントへの指示も、人への指示も、この点は変わりません。
4. ループの実行ログを監査ログに合流させる
前回の記事で作った実行ログの仕組みに、ループの試行回数も記録しておくと、後から「このタスクは何回で収束したか」を追跡できます。収束までの平均試行回数は、評価基準が厳しすぎる・緩すぎるを判断する材料になります。
import json, datetime
def log_loop_result(task_name: str, attempts: int, success: bool):
entry = {
"timestamp": datetime.datetime.utcnow().isoformat() + "Z",
"task": task_name,
"attempts": attempts,
"success": success,
}
with open(".claude/loop_audit.log", "a") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
まとめ
Loop Engineeringの実装は、次の4点に集約されます。
- 評価基準は、機械的に判定できるものに絞る
- 上限回数を必ず設け、超えたら人間に引き渡す
- フィードバックは「何が悪いか」ではなく「何をすべきか」で書く
- 試行回数をログに残し、評価基準そのものの妥当性を後から検証できるようにする
拙著『AIエージェント設計論 — Harness/Loop EngineeringからRAGまで』では、この考え方の全体像を扱っています。