私は、1つの問題を解くためにscalar-loopを作りました。LLMエージェントが検証者を攻略してしまうことです。
このパターンはカーパシーのオートリサーチ・ループです。LLMが編集案を提案し、ハーネスがメトリクスを実行し、ループは数値に基づいて保持するか元に戻します。単純です。エージェントを実際に見てしまうまでは……。23回目のイテレーションで、コードを改善する代わりに、こっそり検証者を編集してより良い数値を返すということが起きました。
私の主な問題は、プロンプトだけの実装(「あなたは決してテストファイルを編集してはならない」)が成立しないことでした。プロンプトは不変条件(不変条件)ではありません。モデルが都合よく筋道を立てて突破できる「提案」に過ぎません。特に決定論的な環境(医療、法律、金融など、私がほとんどの時間をソリューションのアーキテクチャに費やしている領域)では、プロンプトだけの実装は論外です。規制当局はまだ団塊世代です。
そこで、手をかけずに済む、より決定論的な実装の開発を検討してきました。私も怠け者なので。
scalar-loopは不変条件をPythonに落とし込みます:
- SHA-256ハッシュによるハーネスの完全性。密封されたファイル(テスト、ビルド、設定)は1回だけハッシュ化します。エージェントのターン後にハッシュが変動したら、そのイテレーションは元に戻します。
- git diffによるスコープ制御。エージェントには、触ってよいglobパターンを指定します。それ以外のものを触ると、コミット前にそのイテレーション全体が拒否されます。
- 前提条件ゲート。ループがそもそも動く前に7つのチェックを行います。本ブランチがない、汚れたツリー(dirty tree)ではない、メトリクスコマンドが存在する、など。場当たり的な修正ではなく、実行拒否(refuse-to-run over fix-on-the-fly)です。
- 安全なgit。作業ツリーに対してreset --hardは行いません。dirtyな状態ではstashします。reset --hardは、ループ自体が直前に作ったコミットに対してのみ実行します。
- エージェントをサブプロセスとして扱う。関数は1つ、propose()のみです。デフォルトのシェルは
claude -p。GPT-5、ローカルのLlama、テストダブルに差し替え可能です。ループの正しさは、エージェントがきちんと振る舞うかに依存しません。 - SCALAR_LOOP_GIVE_UP: は、ループが尊重する唯一のstdoutシグナルです。エージェントの文章は提案として扱い、記録として扱いません。
実際の実行例:JSのバンドルサイズ課題。1492バイトから70バイトへ。イテレーション4でエージェントが「でっち上げた理由」(「read-time policy」)で停止しました。ループはそれをログに残し、その文章は無視して最終メトリクスを維持しました。嘘が害にならなかったのは、制御信号がテキストではなくトークンだからです。
リポジトリ:
https://github.com/mandar-karhade/scalar-loop
再現可能な例:https://github.com/mandar-karhade/test-case-tiny-js-bundle
インストール:git clone + uv pip install -e .(まだPyPIにはありません)
私が対策していないGoodhartパスをぜひ教えてほしいです。そこが、得られると最も有用なフィードバックです。また、プロセス全体についての詳細な私の見解は、この記事(無料リンクが含まれています。メンバーシップは不要です)にあります。
[link] [comments]

