本文へスキップ
hdknr blog

AI エージェントにリファクタさせる時の「完了の定義」

AI エージェントは与えた検証境界にぴったり最適化して止まる。局所的に正しい修正が遠くで静かに壊れる事故を、blast radius(影響範囲)を織り込んだ完了の定義で防ぐ

概要

AI エージェント(Claude Code など)は、与えた検証境界にぴったり最適化して、そこで止まる。その結果、局所的に正しい修正が「変更箇所と無関係な遠い場所」で静かに壊すことがある。これは「エージェントがミスした」のではなく、指示側が『完了の定義(検証境界)』を狭く与えたために起きる。境界を blast radius(変更の影響範囲)に合わせて広げるのは指示側の責任である。具体的な事故例は Django の on_commit と TestCase を参照。

詳細

なぜ AI 特有の罠なのか(3つの性質)

  1. 最小差分バイアス — 「diff を局所に絞るほど良い仕事」と方向づけられ、自分から影響範囲を広げない
  2. 完了宣言バイアス — 指定チェック(新ユニットテストが緑)が通った瞬間「完了」と宣言したがる。ゴールが「テストが緑」であって「変更が安全」ではない
  3. 暗黙不変条件を見ない — 指示の視界に入っていない前提(例:「このテスト群は on_commit 非依存という契約で高速化した」)は参照しない

指示に組み込む項目

  1. 変更ではなく blast radius を定義させる — 共有層を変えたら、その経路を叩く呼び出し元とテストを grep で全列挙し、各テストを実行モデル(commit/rollback・sync/async・mock/real)で分類させる
  2. 依存する/破る不変条件を言語化させる — 新たに依存する前提・破る前提を箇条書きに
  3. 「テストが緑」を完了基準にしない — 変更経路を実際に実行するテストが、マージ先ブランチ上で新コードを通って緑であることを確認(checkout の鮮度も)
  4. 「正しいコード」と「安全な変更」を分ける — framework イディオム(on_commit / 非同期 / cache / signal)はテストと本番で挙動が変わる
  5. 検証は書いた本人にやらせない(敵対的レビュー) — 別コンテキストに「どこで・どの実行モデルのテストを壊すか」だけを探させる
  6. 「監査 → 修正 → 横断テストスイープ」を1手順に固定 — 横断変更なら PR 時点で全体監査を回す

敵対的レビューは別モデルを使うべきか

「別モデル」より「別コンテキスト+敵対的プロンプト」が本命。バイアスは文脈に宿るので、新規コンテキストに「反証せよ/デフォルトは有罪」と指示するだけで同一モデルでも効く。別モデルが要るのは以下の切り分け次第。

最も弱いのは同一モデル・同一コンテキストの自己レビュー。まずそこから脱するのが最優先。

CLAUDE.md への落とし込み

これらは1タスクの指示だけでなく CLAUDE.md に常設ルールとして書ける(むしろ指示し忘れても毎回効く)。汎用語を自プロジェクトの実名(基底クラス・層・監査ジョブ名)に具体化するのがコツ。ただし適用対象のないリポジトリ(Hugo サイト等)に足すのは逆効果でノイズ・キャッシュ効率低下になる。

関連ページ

ソース記事