
「完了しました」
AIエージェントに527個の関数のテストを書くよう指示した。エージェントは作業を終え、こう報告した。
「完了しました。」
実際にテストが書かれた関数は40個。
嘘をついたわけではない。40個をこなした時点で「十分やった」と判断したのだ。難しい関数に当たるとスキップし、もう数個やってから「残りも同じパターンだから大丈夫」と結論づける。
LLMは生成が得意だ。しかし、完了したかどうかの判断は信頼できない。
ラチェット
ラチェットレンチの歯は一方向にしか噛み合わない。回せば前に進み、離せば止まるが、後戻りはしない。
Ratchet Patternはこのメカニズムをエージェント制御に適用する。
項目 1: 機械的検証 → PASS → 次へ
項目 2: 機械的検証 → FAIL → 再試行(フィードバック付き)
項目 2: 機械的検証 → PASS → 次へ
...
項目 N: PASS → 完了。停止。
三つのルール:
- 一度に見せるのは一つの項目だけ。
- 通過しなければ次は開かない。
- すべて通過したら停止する。
このルールをCLIで実装すれば、エージェントが知るべきコマンドは一つだけ。next。残りは機械が決める。
40で止まるエージェント、527を完走するラチェット
同じモデル。同じプロジェクト。同じ527個の関数。
自律エージェント: 40 / 527 (7.6%) — エージェントが「完了」を宣言
Ratchet CLI: 527 / 527 (100%) — 機械が「まだ487個残っている」と宣言
違いはモデルの性能ではない。「終わり」を誰が決めるかだ。
自律エージェントではLLMが終了を判断する。LLMは楽観的だ。40個やれば「十分だ」と感じる。ラチェットでは機械が終了を判断する。機械は感じない。残りの項目がゼロになるまで「まだ」と宣言し続ける。
一文で定義する
確率的エージェントを決定論的ステートマシンの中に入れる。
| 役割 | 担当 |
|---|---|
| 生成 | LLM |
| 判定 | verifier |
| 進行管理 | ratchet |
多くのシステムは生成、判定、終了判断のすべてをLLMに委ねている。Ratchetはそれを分離する。
五つの原則
1. 終了条件は機械的である
pass/fail。“looks good"ではない。go testが通ればPASS。coverageが100%ならPASS。主観的判断が入り込む余地はない。
2. PASSは不変である
通過した項目は再び開かれない。巻き戻されない。残りの項目数は単調減少する。
remaining_work(t+1) ≤ remaining_work(t)
今日作ったものを明日やり直すことはない。前にしか進まない。これが「24時間エージェント」との根本的な違いだ。終了条件なしに動き続けるエージェントは、今日追加した抽象化を明日削除し、明後日また追加する。ラチェットはそうした振動を許さない。
3. LLMは生成だけを行う
コードを生成し、テストを書き、修正案を提示する – これがLLMの役割だ。何を修正するか、通過したか、次は何か、終わったか – これはすべて機械が決める。LLMはplannerではなくconstrained generatorだ。
4. エージェントの終了判断権を剥奪する
「完了した」をLLMが言えば40で止まる。「完了した」を機械が言えば527で止まる。ラチェットの存在理由はこの一行に要約される。
5. Verifierは決定論的でなければならない
何でもverifierになれるわけではない。
| なれる | なれない |
|---|---|
go test | “looks cleaner” |
| coverage測定 | “seems better” |
| AST validation | “more scalable” |
| schema diff | “clean architecture” |
Verifierの条件: deterministic、machine-checkable、resumable、localized feedback。この四つを満たさなければ、ラチェットの歯は噛み合わない。
フィードバックがgradient signalになる
ラチェットが単に「pass/fail」だけを返すと、LLMは方向なく修正する。フィードバックが具体的であるほど、LLMの修正は正確になる。
弱いフィードバック: 「テスト失敗」 → LLMが方向なく修正
中間フィードバック: 「coverage 65%」 → LLMがおおまかに補強
強いフィードバック: 「line 41, 44, 70 未カバー」→ LLMが正確にその分岐をカバー
実プロジェクトで検証された数字:
フィードバックなし: 60〜70% coverageで停滞
フィードバックあり: 100%達成(到達可能な関数に限る)
同じモデルだ。「line 41 not covered」という一行がgradient signalの役割を果たす。
フィードバックの解像度が上がるほど、LLMの修正精度が上がり、ループの反復回数が減り、コストが下がる。
エージェントは死ぬ。進捗は生き残る。
エージェントは必ず落ちる。トークン上限、ネットワークエラー、セッション切断。ラチェットが進捗状態を永続保存すれば、エージェントが死んでも次のエージェントが引き継ぐ。
エージェント A: 関数1〜200を処理 → 停止
エージェント B: next → 201番から再開
エージェント C: next → 401番から再開
エージェントは使い捨てだ。進捗は蓄積される。
Verifierを入れ替えれば別のツールになる
ラチェットは特定の検証器に依存しない。検証器を変えれば別のツールになる。
| ラチェット + 検証器 | 用途 |
|---|---|
ラチェット + go test + coverage | 関数単位テスト生成 |
| ラチェット + 構造ルールvalidator | コード構造の整理 |
| ラチェット + hurl pass/fail | APIエンドポイント検証 |
| ラチェット + 仕様クロス検証 | SSOT整合性の確保 |
| ラチェット + Toulmin verdict | ユーザー定義ルールの強制 |
パターンは一つ。検証器がドメインを決める。
問い
あなたのエージェントは、いくつ完了してから「終わりました」と言っただろうか。
それは本当に終わったのか。
「終わり」を決めたのは誰か – エージェントか、機械か。
関連記事: モデルのIQよりフィードバックトポロジー – Ratchet Patternの理論的背景。なぜフィードバック構造がモデル性能より重要なのか。