あなたのエージェント設定は、いまやインフラです
エージェントが数体を超えると、その定義はあなたが編集する設定であることをやめ、インフラになります。インフラのやっかいなところは、頭の中に収めきれなくなった時点から、宣言された状態と動いている状態が放っておけば乖離していき、それを誰も報告しないことです。直し方は、サーバーが学んだ規律です。集合をファイルとして宣言し、現実と差分を取り、適用したものを固定し、そして背後で起きた変更の上から適用することを、乗り越えて収束させるのではなく拒否します。ただし、見落としやすい落とし穴が一つあります。リコンサイラーが扱えるのは、あなたが宣言し忘れずにいたものだけです。
動かしているエージェントの登録簿をつけていますが、いつもそれを生成していたわけではありません。最初は手で管理するリストでした。そして手で管理するリストは、存在するものから遅れていきます。エージェントが何週間も動いているのに、ファイルには一度も入らない。そこで、ディスク上に見つけたものから登録簿を組み立てるジェネレーターに置き換えました。加えて、スケジュールで走り、コミットされたファイルがもう一致しなくなると非ゼロで終了するチェックも用意しました。自分が何を作ったのかに気づくまで、時間がかかりすぎました。それは、サーバー上で誰かが作り直していたら笑い飛ばしていたはずのものの、下手な再発明でした。そのチェックが見張っている隙間には、一つ隣の分野で名前がついています。ドリフトです。
あなたのエージェント設定は、気づかないうちにインフラになった
しばらくの間、エージェントはただの設定です。一体、多くて二体、その定義は、ファイルの中にあるのと同じくらい、頭の中にもあります。base prompt を編集し、skill を足し、tool を与え、その全体の形を一度に把握できます。何もずれていません。ずれる相手が存在しないからです。ファイルがそのままエージェントなのです。
十分な数を動かせば、それは終わります。定義は集合になります。エージェントごとの base prompt あるいは CLAUDE.md、読み込む skill、その memory store、呼び出してよい tool、pin された model、動く environment。その集合はファイルで宣言され、動いている対応物を持ちます。それが一つではなく二つのものになった瞬間、あなたは設定を編集しているのではなく、インフラを運用しています。もちろん、設定も動いているものとずれることはあります。一度も再読み込みされなかった編集済みの nginx.conf が、まさにそれです。違いは、その可能性ではなく、既定の状態のほうにあります。ある規模より下では気づきます。両方の半分を手元に持っているからです。持てなくなる地点を越えると、乖離はあなたが捕まえる出来事であることをやめ、何も報告しない静止状態になります。
失敗はドリフトであって、個々の編集ではない
エージェントが仕事の途中でおかしな振る舞いをするので、動いているインスタンスに手を入れ、出血を止めるためにその指示を直し、その変更を二度と書き戻さない。memory store を手で編集して、悪いエントリを一つ落とす。長く動いているセッションは起動時に指示を読み込み、その後であなたがファイルを編集したので、宣言された版は先へ進み、動いているほうは元のまま取り残された。どれも間違った行動ではありません。生きた問題を直すために手を入れるのは、しばしば正しい判断です。失敗は、その後で、宣言したものと動いているものが食い違い、その食い違いが誰にも自らを知らせないことです。
私たちはサーバーをこのやり方で運用するのをやめることを学びました。一度手で立ち上げられ、誰にも作り直せなくなるまで何年も稼働中にパッチを当てられ続けたスノーフレーク・サーバーは、動いている状態の中にしか存在せず、一度も書き留められませんでした。私たちは、それはサーバーには受け入れられないと決めました。そして私たちは、まさにそのやり方でエージェントを動かしはじめ、それをイテレーションと呼びました。
これは、一つ隣の基層で解決済みの規律だ
サーバーの世界は、これをより賢い箱で解決したのではありません。規律で解決しました。それを実装するツールの間で、退屈なほど一貫した規律です。Terraform と Pulumi は state ファイルを保ち、何かに触れる前に plan を示すので、変更は驚きとして見つかるのではなく diff として確認されます。Kubernetes の reconcile ループは、動いている状態を宣言された状態へと寄せていきます。Ansible は冪等なモジュールを好むので、よくできた playbook は再実行され、収束します。その下にある一つの動き。望む集合をファイルとして宣言し、現実と突き合わせ、宣言したものへ収束させる。reconcile は賢くもなければ新しくもありません。新しいのは、エージェントの構成がこのカテゴリーに属するほど大きくなったこと、そしてエージェントを動かしている人のほとんどが、それをそこへ移していないことです。
ベンダーが書き下したもの
Anthropic の ant apply は、CLI バージョン 1.30.0 以降を必要とし、agents、skills、memory store、environments、deployments をリポジトリの中のファイルとして扱います。各リソースをファイルに記述し、コマンドを実行すると、端末で plan を表示して待ちます。それを承認すると、claude-lock.json、つまり lockfile を書き出し、各リソースの ID を二つの指紋の隣に記録します。最後に送った内容のハッシュと、API が返したハッシュです。次の実行では、あなたのファイルを両方と比較するので、あなたが行った編集と、向こう側で起きた変更とを見分けられます。
ドキュメントは、変更が向こう側で起きたときに何をするかについて、正確です。
If a resource was edited, archived, or deleted outside these files
(in the Claude Console, for example), the plan ends with
`This plan cannot be applied:` and the reason. The command then
exits with `refusing to apply`. Pass `--force` to overwrite the edit
or create a replacement.
これは Anthropic のプラットフォーム上でホストされたリソースを管理するのであって、ローカルなプロセスの群れを管理するのではありません。ですから、私が自分のマシンで動かしているエージェントを管理するわけではありません。ベンダーが私のセットアップのためにツールを作ったとか、それを出したこと自体が、エージェント設定が一般に何らかの一線を越えた証拠だ、と主張しているのではありません。ベンダーは、自分がホストするあらゆるものに向けて宣言的なツールを書きます。それがしているのは、セマンティクスを書き下すこと、plan と lock と拒否、そしてローカルな事例に、それまで持たなかった語彙を与えることです。カテゴリーの主張が拠って立つのは、当事者としての証拠、つまり古びつづけたあの登録簿であって、ベンダーの製品計画ではありません。
拒否こそが、その系譜からの離脱だ
拒否こそが、ant apply がその系譜に加わるのではなく、そこから離れる地点です。Terraform はあなたにドリフトを見せ、次の apply でそれを元に戻すことを提案します。Kubernetes のコントローラーは、帯域外の編集を絶え間なく上書きし、何も言いません。サーバーの既定は収束すること、変更を宣言された状態へと畳み戻すことです。ant apply は、そうする代わりに止まります。lockfile が新しい部分なのではありません。kubectl apply は同じ三方向の比較を行うために、last-applied の記録を何年も保ってきましたが、それはマージすることを選びました。新しいのは、衝突点での答えです。
収束ではなく拒否するのは、前例のないことではありません。npm ci は、lockfile と manifest が食い違っているとインストールしません。どちらかを黙って書き換えるのではなく、エラーで止まります。しかしそれは少数派の選択であり、エージェントはその選択をするだけの十分な理由になります。帯域外の編集は、たいていあなたが手を入れて行った生きた修正です。黙って収束すれば、あなたはその修正を元に戻し、それが失われたことを決して知りません。ローカルな群れは、どんなツールの助けもなく、すでにあなたにこれをしています。セッションを再起動すると、それはファイルを読み込み直し、あなたが生きているほうに打ち込んだ訂正を捨て、その喪失に何も印をつけません。
これはまた、拒否と plan の違いでもあり、見落としやすい違いです。plan はすでにドリフトを表に出すからです。違いは、既定の側にあります。plan はあなたに変更を見せ、それを通り越して承認させます。あなたが習慣でスクロールして了解する diff の中の一行です。拒否は、ファイルがその変更を記録するまで先へ進みません。それは書き込み経路の上で止めますが、私が作った検出器はそこでは止めません。私のスケジュールされたチェックは、古びたファイルを事後に捕まえ、自分自身のタイマーで、脇のほうで、非ゼロで終了します。それは本物で役に立ち、ブロックするゲートが何も見ない、apply と apply のあいだの窓を覆います。しかし検出器は無視されることがあり、それは乖離の後で発火するのであって、乖離のところでではありません。この二つは同じ仕事の二つの半分であって、同じ半分ではありません。
lockfile に見えないもの
拒否が守るのは lockfile の中にあるものだけで、ドキュメントはその縁について明快です。ant apply は、これらのファイルの外であなたが作ったリソースを取り込めません。すでに存在するエージェントを記述したファイルを適用すると、手に入るのは二体目のエージェントであって、一体目の所有権ではありません。import のステップはそもそも存在せず、これは受け継いだ元のサーバーよりも厳格です。Terraform にはまさにこのための terraform import がありますが、ant apply には何もないので、唯一うまくいく順序は、リソースを作る前に宣言することです。
つまり、宣言された状態 対 動いている状態、という枠組みは、いつも甘すぎたのです。リコンサイラーが実際に握っているのは、宣言された状態 対 動いている状態のうちあなたが宣言し忘れずにいた部分です。手で立ち上げて一度も書き留めなかったリソースは、ドリフトしません。ドリフトとは比較であり、その向こう側には何もないからです。それはプラットフォームの上に、管理されないまま、管理された集合の隣に座っていて、あなたの追跡下のリソースについてはあれほど大声で拒否する plan が、それについては何も言いません。
二つのことが、これをサーバーよりエージェントにとって鋭くします。memory store は宣言されるリソース型の一つですが、エージェントが実行時にそこへ書き込む記憶は、宣言されたファイルの一部ではありません。ですから lockfile は、そのリソースの、振る舞いを最も形づくる部分の指紋を、決して取りません。そして、管理されないスノーフレークに対するサーバーの治療法、つまり宣言から作り直すことは、自分自身の記憶を書いたエージェントには完全には効きません。作り直せば、それが学んだものを捨ててしまいます。エージェントの状態を持つ部分は、両方向できれいな reconcile に抗います。一方では捉えられず、もう一方では置き換えられないからです。
その記録が覆うもの、覆わないもの
モデルのバージョン、私の別のエッセイが一本まるごとかけてベンダーの変更を自分の変更と見分けられるように固定せよと説いているもの、それはエージェントの宣言の中の一つのフィールドであり、ほかのすべてと同じハッシュへ畳み込まれています。あのエッセイは、私が線の自分側を知っていることを前提にしていました。その方法全体は、私が自分側を変えていない、だから変わったのは依存先のほうだ、という前提の上に立っています。こちらは、その前提が偽になる日についてです。私が自分側を確かに変え、それを一度も記録しなかった日です。これはもう一方の、より広い版なのではなく、その前提条件です。
そして、それが行き着く、覆いきれない隙間は、規律を増やしても閉じません。すべてを書き留めよ、と自分に言い聞かせることは、いつも失敗する対処法です。あなたが忘れるものは、あなたが忘れるものを捕まえるために保っている、まさにその記録には見えないからです。機械的な答えは、逆の向きに走ります。宣言が先のリコンサイラーは、あなたが宣言したものしか見られません。発見が先の掃引は、存在するものを見ます。実際にそこにあるものを列挙し、それをあなたが宣言したものと突き合わせれば、隙間は、閉じたと願うものではなく、読めるものになります。それが、名前をつけないまま私がつまずいて入り込んだ部分です。登録簿の直し方は、もっと注意深く手入れすることではなく、存在するものからそれを生成することでした。拒否する lockfile と、発見する掃引とを組み合わせれば、管理された集合をようやく全体の集合と突き合わせられます。掃引を省けば、lockfile は、あなたが書き留めたものを覆い、それ以外は何も覆いません。