永続・共有・可搬——stateless エージェントの「共有できる状態」を、こう設計した
OpenAB Connect は初日から、エージェントの実行環境を stateless に設計してきた。uid 1000、sudo なし、root ファイルシステムは読み取り専用、ワークスペースはセッションとともに破棄。これは意図的だ——エージェントは暴走するし、シェルは騙される。環境は中身を信頼できないものとして扱わなければならない。だが矛盾はすぐに現れた。実際の仕事には状態が要る。半日かけて育てたワークスペース、複数のエージェントで共用する API キー、新しい環境に持ち込んで再実行したいデータ——どれもセッションと生死をともにすべきものではない。今回のアップデートは、「実行環境は無状態」と「仕事には状態が要る」のあいだに、共有できる状態の層をどう設計したかという話だ。
まず切り分ける——何が stateless で、何がそうであってはならないか
エージェントの実行環境は stateless であるべきだ。各セッションは使い捨てのサンドボックスで、 壊れたら捨てて、きれいなものを立ち上げ直せばいい。だが /workspace の中のデータは別の話だ。 この二つを縛り付けてしまうのは、いちばん犯しやすく、いちばん痛い間違いでもある。接続を閉じて そのエージェントを片付けたかっただけなのに、三時間ぶんの成果が一緒に消える。
そこで最初の決定:ワークスペースのライフサイクルは、セッションのライフサイクルから独立 できなければならない。今回のリリースでは、デプロイ時に三つから選べる——使い捨て(emptyDir)、 新しい永続ディスクの作成(PVC)、既存ディスクのマウント。前の二つは以前からあった。要は三つ目 で、これによって「データ」は、複数のセッションが代わる代わる使える一級市民になる。
「借り物」は「持ち物」ではない——誤削除を防ぐ一線
既存ディスクのマウントを許した瞬間、危険が一つ現れる。接続を削除するとき、そのディスクを 消していいのか?
私たちの答えは一つの固い規則だ。この app が作ったものだけを、この app が消す。あなたが マウントするよう指定したものは、一切消さない。事前に作っておいたディスクや、ほかのエージェント が共用しているディスクは、この接続にとって「借り物」であって「持ち物」ではない——接続を削除 すれば pod は片付くが、ディスクは元のまま残る。
当たり前に聞こえるが、正しくやるには、所有権が証拠に基づかなければならない。推測では だめだ。私たちが踏んだ穴はこうだった。初期のクリーンアップロジックは、「記録にディスク名が ある」だけで削除していた。自分が作ったディスクしか記録されない時代には安全だった——だが既存 ディスクのマウントを開放した瞬間、同じロジックが、ユーザーが削除を許可した覚えのないディスク を消しに行く。修正後の原則はこうだ。所有権は、デプロイ時点の実際の証拠で決める(これは本当に、 今回、私が作ったものか?)。名前がたまたま一致することから推論しない。慣習から推測した名前は 証明されていない所有権であり、証明されていないなら、消さない。
共有——セッションだけでなく、エージェントも越える

永続化だけでは足りない。一つの OpenAI キー、複数のエージェントが読む設定——エージェントを 立ち上げるたびに打ち直し、それぞれに保存するようなものではない。そこで今回、Kubernetes の管理 パネルを追加した。ここで作った PVC と Secret は共有で、どの接続からも独立している——特定の 接続に属さず、どの接続を削除しても影響を受けず、消えるのはパネルで手動で削除したときだけだ。
Secret については、一つの原則にこだわった。平文はこの app を通らない。デプロイ時に、共有 Secret の特定の key をエージェントの環境変数として直接指定できる——使うのは Kubernetes の secretKeyRef で、値は最初から最後までクラスタの中に留まる。app が運ぶのは名前だけで、中身には 触れない。「キーを入力欄に貼り付ける」のとは、セキュリティの等級が違う。
可搬——汚染ではなく、複製
状態が共有できるようになると、次の要求は自然に出てくる。既存のディスクを起点に、複製を 一つ作って新しいエージェントに遊ばせたい。ただし元のディスクには触れずに。
ここで私たちは、Kubernetes の CSI clone をあえて使わなかった。下層のストレージドライバの 対応が必要で、多くの環境(開発でよく使う local-path を含む)にはそれがないからだ。使ったのは、 もっと素朴だが、どこでも動くやり方だ。ごく短命の job を立ち上げ、コピー元(読み取り専用)と コピー先を同時にマウントし、cp -a で中身を写して、自分で消える。この job は厳格に harden して ある(非 root、全 capability の破棄、読み取り専用の root fs、service-account トークンなし、 コピー元は読み取り専用)。あなたのデータに触れるコードなのだから、信頼できないコードを扱う やり方で扱うべきだ。
正直な境界を一つ。この方法で複製できるのは、遊んでいるディスクだけだ。いまどこかの pod に 使われているディスク(RWO——マウントできるのは一度に一つ)については、選べないようにして 「使用中」と明示する。job をタイムアウトまで待たせたりはしない。「使用中」のディスクを邪魔 せずに複製するには、本当に CSI スナップショットが要る——それは未来に残した一歩だ。
あの矛盾に戻る
「stateless なエージェント」と「共有できる状態」は、衝突しているように聞こえて、実は していない。stateless なのは実行環境だ——その層は使い捨てで、信頼せず、いつでも捨てられて ほしい。状態を持つのは仕事そのものだ——その層は永続し、共有でき、持ち運べてほしい。今回の アップデートがやったのは、この二つの層をきれいに分けることだ。サンドボックスは使い捨ての ままに、データには、どのセッションよりも長く生き、明示的に所有されるか明示的に借りられ、複製 して運べる住処を与える。
app を閉じても、エージェントは走り続ける。接続を削除しても、データは残る。同じ信念の 両面だ。短命であるべきものは短命に。永続すべきものには、吟味に耐える所有権のモデルを。