更新を任せることは、判断を任せることではない

LayerXのuphy氏は、個人のタスク管理システムを2回作り直している。1回目はMarkdownを手で書き換える運用で、書き戻し漏れとビューのズレが絶えなかった。2回目はLLMに丸投げする運用にしたが、今度は判断そのものが人間の手を離れ、別の形で破綻した。3回目に氏がたどり着いたのが、「判断は人間・更新はエージェント・計算はスクリプト」という三分法だった(出典)。

タスクの優先順位づけや完了判定は人間に残す。タスクファイルの書き換えは「task-manager」という専用のサブエージェント一本に絞り、約240行の仕様書と10個の不変条件で縛る。期日の自動繰り上げやガントチャート生成といった決定論的な処理はPythonスクリプトに落とす。氏はこの切り分けの根拠を「計算と整合チェックは、LLMにやらせるより決定論のコードの方が確実」と説明している。

最初はPRレビューツールParryの姿とほぼ重なって見えた

Parryが掲げる「confirm, don't write」は、AIが書き、人間が確認するという構図だ。これをuphy氏の三分法に当てはめると、「更新はエージェント」がAIによるPR作成、「判断は人間」がレビュアーによる四軸への回答、という対応がきれいに付く。CIやlintのような決定論的な検査には手を出さないというParryの立場も、「計算はスクリプト」の領域に踏み込まないという線引きと一致する。三分法とParryは、最初は同じ設計思想の言い換えに見えた。

だが、この一致には見落としがある。uphy氏の「更新はエージェント」は、240行の仕様書と10個の不変条件という、書き換えの正しさを規定する具体的な縛りとセットになっている。エージェントが何をどう書き換えていいかは、判断ではなく手順として固定されている。Parryの側には、この「更新エージェントを縛る規約」に相当するものが存在しない。Parryが確認するのは出来上がったPRの中身、つまり四軸の回答であって、そのPRを書いたエージェントがどういう手順で動いたかではない。

実際に、更新の手順が縛られていないことで事故が起きた

このブログ記事群を1週間分まとめて作る作業を、複数の並列フォークに分担させたことがあった。各フォークに執筆からPR送信までを丸ごと任せたところ、同じブランチに対してparry_create_prが複数回呼ばれ、1ブランチにつき3件ずつPRが重複して作られた。さらに、並列フォークが同じ作業ディレクトリを共有していたため、あるフォークのcheckout操作が別フォークの作業に混入し、公開済みだった記事2件のステータスをpublishedからdraftへ巻き戻す差分が、無関係な記事のPRに紛れ込んだ。レビュー前に発覚したため実害はなかったが、四軸の回答内容を確認するという工程だけでは、この事故は防げなかった。四軸はPRの中身についての判断であり、複数のエージェントが同じブランチを同時に触ってよいかという、更新の手順そのものについての規約ではなかったからだ。

三分法の一角しか担っていない、という自覚

この事故のあと取られた対処は、「git操作は必ずメインスレッドが1記事ずつ順番に行う。フォークに直接gitさせない」という運用ルールを明文化することだった。これは、uphy氏が240行の仕様書でtask-managerエージェントを縛ったのと同じ種類の対処であり、Parryという仕組みそのものが持っていた機能ではない。Parryは三分法のうち「判断は人間」の一角だけを担っており、「更新はエージェント」をどう縛るかは、Parryの外側で人間が書いた運用ルールに委ねられていたことになる。

三分法として並べたとき、Parryの四軸が保証しているのは最後の一つの判断だけで、その判断の材料となるPRがどんな手順で生まれたかという前提部分は、範囲の外にあるということが、事故を通じて初めてはっきりした。

仕様書という縛り方が、この先も持つかは分からない

uphy氏のtask-managerエージェントも、10個の不変条件を実際に毎回守っているかどうかを機械的に検証しているわけではない。書かれた仕様をLLMが解釈して動く点では、まだ人間の注意力に近い部分が残る。今回のブランチ運用ルールも同じ性質を持つ。フォークが増え、同時に走るエージェントの数が増えていったとき、文章で書かれた不変条件だけで更新の手順を縛り続けられるのか、それともいずれ決定論的な仕組みに落とし込む必要が出てくるのかは、まだ検証していない。