0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Loop Engineeringを実装する — 評価と再実行をコードに落とす

0
Posted at

以前の記事「Loop Engineeringとは何か」では、概念として「失敗から改善するシステム」を扱いました。そして前回の実装編「Harness Engineeringを実装する」では、エージェントに何をさせて良いかを権限として明文化しました。

今回はその続きです。権限の中でエージェントが動いた後——「出てきた結果が基準を満たしているか」を判定し、満たしていなければ自動でやり直させる仕組み、Loop Engineeringを実際にコードとして組みます。

Loop Engineeringの核は単純です。

  1. 生成する
  2. 評価する
  3. 基準を満たさなければ、フィードバックを添えて再生成する
  4. 基準を満たすか、上限回数に達したら止める

このループを「頭の中で」ではなく、実際に止まる・回るコードとして書いていきます。

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点に集約されます。

  1. 評価基準は、機械的に判定できるものに絞る
  2. 上限回数を必ず設け、超えたら人間に引き渡す
  3. フィードバックは「何が悪いか」ではなく「何をすべきか」で書く
  4. 試行回数をログに残し、評価基準そのものの妥当性を後から検証できるようにする

拙著『AIエージェント設計論 — Harness/Loop EngineeringからRAGまで』では、この考え方の全体像を扱っています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?