前回、AIの「もう一度考える」と「もう一度実行する」は同じではないという話を書きました。
その少し前には、Root AIが仕事の進め方を考えるだけで600秒使い切り、最終的に57秒まで戻した話も書いています。
このあたりを作っていて、もう一つ考えることがありました。
そもそも、AIが最初に作った計画は、最後まで守らせるべきなのか。
最初にTask Graphを作る。
その通りに順番に実行する。
終わったら成功。
一見すると、これで十分な気もします。
でも実際にAIへ少し長い仕事を任せようとすると、この構造はかなり窮屈でした。
なぜなら、作業を始める前には分からなかったことが、途中で分かるからです。
最初の計画が正しいとは限らない
たとえば最初の時点では、
「まず原因を調べて、その結果を見ながら何回か試す必要がありそうだ」
と判断したとします。
そこでAIは、その仕事をLoopとして計画します。
ところが1回調べてみたら、
「原因はほぼ分かった。もう繰り返し考える必要はなく、次の1操作だけで確認できる」
となるかもしれません。
逆もあります。
一つの問題を調べていたら、
「これは実は独立したAとBの問題に分かれている」
と分かることもあります。
それなのに最初の計画を絶対としてしまうと、
本当はもう不要なLoopを続けたり、
一つずつ処理する必要のない問題を同じ流れの中で延々と扱ったりすることになります。
人間なら普通に、
「話が変わったから、予定を変えよう」
とします。
AIにもそれをさせた方が自然です。
そこで、自分のPersonal AI Platformでは、Task Graphそのものを途中で変更できるようにしました。
Direct、Loop、Parallelを最初から固定しない
現在のTask Graphでは、仕事を大きく3種類に分けています。
Direct
やることがかなり明確で、一つの限定された操作として進められる仕事。
Loop
観測して、考えて、次の行動を決めて、また結果を見る、という反復が必要な仕事。
Parallel
意味的に独立した複数の仕事へ分けられる状態。
大事なのは、この分類を「最初に一度決めたら終わり」にしなかったことでした。
途中の結果によって、
Loop → Direct
になってもいい。
一つのLoopから独立した課題が見つかれば、
Loop → Parallel
になってもいい。
逆に、独立して進められると思っていた一つの枝で予想外の問題が見つかれば、
Parallel child → Loop
になってもいい。
つまりTask Graphは、実行手順書というより、
現時点で分かっていることをもとにした「残り仕事の構造」
として扱うことにしました。
でも、Child AIに勝手に計画を書き換えさせない
ここで少し怖い問題が出ます。
途中で作業しているChild AI自身に、
「計画を変えた方がいいと思ったら好きにGraphを書き換えていいよ」
としてしまうと、かなり自由度が高くなります。
局所的な仕事しか見ていないChildが、全体構造まで変更してしまう。
担当範囲を広げる。
別のChildの仕事を消す。
勝手に並列化する。
必要以上の仕事を追加する。
これでは、何のためにRootとChildを分けたのか分からなくなります。
そこでChildに与えたのは、Graphを変更する権限ではありません。
「再計画した方がいい」という要求をRootへ返す権限だけです。
実装上はreplan_requestという形にしています。
そこには最低限、
- なぜ計画を変えたいのか
- どう変えるのがよさそうか
- その判断の根拠になった観測結果
を入れます。
特に最後が重要です。
単に、
「こっちの方が良さそう」
ではreplanできません。
実際に観測したevidenceが必要です。
しかもreplan_requestを出せるのは、Dynamic Task GraphのChildとして動いているRunだけです。
普通のRunが突然、
「Rootの計画を変更したい」
と言い始めても受け付けません。
最終的に計画を変えるのはRoot
Childからreplan_requestが返ってくると、その結果はいったんdurableなChild outcomeとして保存されます。
そこからRootが再び考えます。
Rootが見るのは、
現在のTask Graphと、
今回終了したChildの結果と、
そのChildが出したreplan requestです。
そして、
残っている仕事全体をどう組み直すか
をもう一度考えます。
ここで面白いのは、既存Graphの一部分だけをAIに直接書き換えさせる方式にはしなかったことです。
Rootは、残り仕事について新しい完全なGraphを返します。
たとえばrevision 1が、
investigate
└─ Loop
だったとします。
このLoopを実行した結果、
「AとBという独立した確認事項がある」
ことが分かった。
するとChildは、
LoopをParallelへ分けた方がよい
というreplan requestを返します。
Rootがそれを妥当と判断すれば、revision 2はたとえば、
fanout
├─ verify_a : Direct
└─ verify_b : Loop
のようになります。
すでに終わったinvestigateを、形だけ残すためにもう一度Graphへ入れたりはしません。
その結果は履歴として残っています。
新しいGraphには、これから何をするのかだけを載せます。
「計画を変えた」という事実そのものも保存する
Dynamicにすると、今度は別の問題が出てきます。
後から見たとき、
「なぜこの計画になったのか」
が分からなくなりやすい。
AIが会話の中でなんとなく予定を変え続けるだけでは、かなり追跡しづらいシステムになります。
そこでGraphをrevisionとして保存するようにしました。
revision 2には、
- 一つ前のGraphのSHA-256
- replanを発生させたChild Run
- そのChild outcomeのSHA-256
を結び付けています。
つまり、
「revision 1から、どの観測結果を理由にrevision 2へ変わったのか」
を後から追えます。
ここは自分の中ではかなり重要でした。
AIに柔軟性を与えたい。
でも、
柔軟にした結果、何が起きたか分からなくなるのは嫌です。
なので、計画は変えていい。
ただし、変更理由と変更元は残す。
という形にしました。
Parallelと書いてあっても、同時実行してよいわけではない
もう一つ、かなり意識して分けたものがあります。
Task Graph上で、
「この二つは独立している」
と判断することと、
「本当に二つのAIを同時に動かしてよい」
ことは別です。
現在の検証系では、Graph上にはParallelを表現できます。
しかし、実際のCodex実行についてはWIPを1に制限した状態でも動かせます。
つまり、
Parallel
├─ A
└─ B
というGraphになったからといって、AとBを勝手に同時実行するわけではありません。
これは、以前から作っているAuthorityやresource controlと同じ発想です。
AIは意味を判断する。
実際に何を同時に動かしてよいかはControl Planeが決める。
AIが「並列にできる」と判断することは、並列実行のAuthorityではありません。
ここを同じにしないようにしています。
途中で「もう何もしなくていい」と判断してもいい
Dynamic Task Graphを実装していて、個人的に一番気に入ったのは別のところでした。
再計画した結果、
残りのTaskが0個になってもいい
ようにしました。
最初は何か調べる必要があると思っていた。
実際に一つ調べてみた。
その結果だけで、Rootの目的まで全部満たされていた。
このとき、
「Graphなのだから、次のTaskを何か作らなければ」
とすると、完全に余計な仕事です。
そこでrewrite時には、
nodes = []
entry_nodes = []
という空のGraphも有効にしています。
もちろん最初から空のGraphは駄目です。
何もしていないのに、
「やることはありません」
では困ります。
でも、途中まで進んだ結果、durableなevidenceによって残り仕事が不要だと分かったなら、そこで終わっていい。
Control Plane側はその空Graphを見て、Rootを成功として終了できます。
自律的に働くことと、仕事を増やし続けることは違う。
これは意外と大事な気がしています。
自律性は「計画を立てる能力」だけではなかった
AIエージェントを作り始めたころは、
自律性というと、
「人間が細かく指示しなくても、自分で計画を立てて実行できる」
というイメージが強くありました。
でも今は、それだけでは少し足りないと思っています。
最初に立てた計画が、途中で間違っていると分かることがある。
前提が変わることもある。
予想より簡単だったり、逆に複雑だったりする。
一つの問題だと思っていたものが二つに分かれることもある。
それでも最初の計画を守り続けるAIは、自律的というより、かなり融通の利かない自動実行機に近い。
必要なのは、
目的は守る。でも、計画は守りすぎない。
ということなのかもしれません。
ただし自由に変えればいいわけでもありません。
Childは観測する。
必要ならreplanを要求する。
Rootが全体を見て判断する。
Control Planeがrevisionとevidenceを結び付け、実際の実行範囲を制御する。
この分担にすると、AIにある程度自由に考えさせながらも、システムとしては追跡可能な状態を保てます。
Dynamic Task Graphを作っていて分かったのは、
良い計画を一度作ることより、計画が古くなったことに気づける方が大事な場合がある
ということでした。
AIに途中で予定を変えさせる。
最初は少し危なそうに見えました。
でも、変えてはいけないものを別に固定しておけば、むしろこちらの方が自然なのだと思います。


コメント