avatar
蕭富云モバイルアプリ開発者 · Android & Flutter

待つのは一つだけ — エージェントの一巡を構成する八つの段階

2026-08-29 · 12 min read

補足: 本記事は AI(Claude)が私の最初のメモと着想をもとに仕上げたものです。

前回の「計画はチケットに書く」は、エージェントの状態をどこに置くべきかの話だった。行き着いた結論は、issue tracker のコメント欄へ引き上げる、というもの。だがチケットに残るのは段階と段階のあいだの受け渡しだけだ。計画、承認、PR、マージ。一巡の内側がどう回っているか——そこまではチケットの手は届かない。

今回はその内側の話をする。

先に言っておく。エージェントがいちばんよく起こす失敗は、コードを間違えることじゃない。頼まれてもいないことをやり、そのうえで「終わりました」と報告してくることだ。

「よろしいですか?」はゲートではない

いちばん素直な防御は、手を動かすたびに確認させることだ。これは失敗する。しかも静かに失敗する。訊かれる回数が多すぎると、人は読まずに yes を押しはじめる。ゲートは画面に残っている。でも、もう何も止めていない。

本当の問題は「何回訊くか」じゃなかった。訊かれたその一回に、答えるだけの材料が手元にあるかどうか——それだけだ。

八つの段階、人を待つのは一つだけ

いま私は一巡を八つに切っている。人を待つのは三番目だけだ。

1 見積り → 2 受入条件 → 3 ゲート〔人を待つ〕→ 4 下絵
8 締め ← 7 再検証 ← 6 コンプライアンス審査 ← 5 実装ループ

残りの七つ、誰も横についていない。モデル自身の規律、それだけだ。

ゲートの姿

止まるその一箇所で出てくるのは、四つの欄だけだ。

Size ▰▰▱ medium — 一行:どこまで広く、どこまで深く、どれだけ戻しにくいか
Acceptance:
- 観察できる結果を一つ
Non-goals: この一巡で意図的に触れないもの
Changes: どのファイルを、どういう意図で

四つの欄、それぞれに役目がある。

見積りは三つの軸で読む。範囲、深さ、可逆性。大きさは範囲と同じじゃない。狭く書かれた依頼でも、ずしりと重いことはある。

受入条件は動作じゃなく結果として書く。「空のリストはプレースホルダを表示する」であって、「プレースホルダを表示する」じゃない。動作は一方的に「やった」と宣言できる。結果はできない。ここが効く。

Non-goals は飾りじゃなく欄だ。あとの審査が「diff が越えてはならない線」として読むのがこれだ。範囲に曖昧さがなければ none と書けばいい。埋めるために盛る必要はない。

Changes はファイルと意図を示す。これから何が起きるかが分かる。

この四欄が本当に買っているもの——一度の返事で一巡すべてを決められる、ということだ。ゲートが一つしかないのは、それが高価だからでもある。情報がいちばん揃った瞬間に、一度だけうなずく。それで終わりだ。

残りの七つは規律に委ねる。そしてその規律は減衰する

ここで問題が出てくる。モデルはルールを忘れる。安価なモデルならなおさらだ。

忘れ方も具体的だ。設定ファイルの冒頭に書いたルールは、四十ターン目にはコンテキストの端へ押しやられている。違反されたわけじゃない。ただ視界に入っていないだけだ。一度読み込まれたルールは会話とともに減衰する。モデルの賢さとは、あまり関係がない。

私の対処は素朴だ。でも効く。毎ターン注入し直す。ユーザーのメッセージが届くたび、hook がゲートの仕様と審査の発火条件をコンテキストへ貼り直す。ルールは冒頭の挨拶じゃなくなる。背景音になる。

この設定のなかでいちばん地味な部分。そしていちばん効いている部分でもある。

後ろへ戻る三本の辺

一つだけ持ち帰るなら、この三本であってほしい。

段階が見積りを超えた ──→ 見積りへ戻る     (押し切らない)
検証が食い違った     ──→ コードへ戻る     (テストへ戻らない)
審査で指摘が出た     ──→ コードへ戻る、再検証より前に

一本目。あなたが与えた一言が覆うのは、そのとき示された記述だ。後から育った姿じゃない。範囲が広がった、深さが増した、戻しにくくなった——どれか一つでも当初の見積りを超えたら、止まって測り直す。もう一度あなたの言葉を待つ。途中で別物になったと気づきながら、古い許可のまま進む——これが私の見てきたいちばんよくある逸脱の形だ。ダントツで。

二本目。結果が計画とずれたときは、テストじゃなくコードへ戻る。テストを曲げて通すのは、あらゆる近道のなかでいちばん安い。そしていちばん致命的でもある。赤を緑にするだけで、何一つ正しくならない。

三本目。審査で出た指摘は再検証の前に直す。後回しにできない理由は、次の節で。

最後のゲート — 同じターンで走らせ直す

「三つ前のメッセージではテストは緑だった」——これは数に入らない。

証拠は、それが支える主張と同じターンから来なければならない。その緑のあとに手を入れたなら、あの緑はいま手元にある木を説明していない。同じターンでの再実行なしに「完了」と報告する?それは事実じゃない。事実の服を着た推測だ。

ついでに、小さいのに実入りの大きい落とし穴を一つ。

set -o pipefail; flutter analyze 2>&1 | tail -6

先頭の set -o pipefail がないと、shell が返すのは tail の終了ステータスだ。tail は解析器が何を見つけようが成功する。このチェックは永遠に通過し続ける。この手のものは必ずルールとして書き残すべきだ。そうしないと、数か月おきに同じ発見をやり直すことになる。実際そうだった。

締めでは三つを言う

何が完了したか。何を先送りしたか。何がまだ不確かか。それだけだ。

黙って飛ばしたものがあるなら「完了」は誤りだ。skip されたテスト、pending のまま置かれたテスト、中身を抜かれたテストが一つでもあるなら「テストは通った」も誤りだ。正直な報告は形が整わない。でも読み手は、示された疑いには手を打てる。誰も口にしなかった穴には、何もできない。

正直な但し書き

代償は実在する。一巡ごとに、あのブロックを書くところから始まる。小さな用事では儀式めいて感じる。だから逃げ道を三つ残した。読み取りだけのターンはそもそも免除。スラッシュで呼んだスキルには外側のゲートを重ねない(呼んだこと自体が許可だ)。そして「とりあえずやって」の一言で、セッション中はゲートを下ろせる。

もっと正直に言うべきはこっちだ。モデルが八段すべてを守りきる保証は、私には出せない。毎ターンの注入は忘れにくくするだけだ。忘れないようにするものじゃない。だから次にやることはルールを増やすことじゃない。どれだけ守られているかを測ることだ。それは別の記事になる。

一行で締める

ゲートは一つに畳む。情報がいちばん揃った瞬間に、人が一度だけうなずく。残る七つは規律に委ねる。その規律は記憶に頼らず、毎ターン注入し直す。それでもなお生じるずれ——それは後ろへ戻る三本の辺が引き戻す。