Agentic OS 技術スタックを下から読む 第11回:手を動かさせる前に ―― サンドボックスという「縛られた実行環境」
ここまでの回では、主にモデルそのものを見てきた。
モデルから運行时へ
ここまでの回では、主にモデルそのものを見てきた。
モデルは、入力を読み、次に来るものを予測し、文章を組み立てる。効率を上げる工夫もあれば、能力を広げる工夫もある。だが、その中心にあるのは、あくまで「考える」ことだった。
もちろん、考えるだけでも十分に強力である。要約する。分類する。説明する。計画を立てる。文章を書く。これだけでも、多くの仕事は変わる。
しかし、それだけではまだエージェントではない。
エージェントは、手を動かす。検索する。ファイルを読む。ファイルを書き換える。コードを実行する。外部の道具を呼ぶ。何かを予約する。何かを送信する。つまり、世界の状態を変える。
ここで層が変わる。
前回まで見てきたのは、モデルの中で何が起きるかだった。今回から入るのは、そのモデルを実際に走らせ、行動させ、状態を抱えさせる運行时の層である。
その最初の関門は、安全である。
エージェントに手を動かさせるなら、その手はどこまで届いてよいのか。何を触ってよいのか。どれだけ計算してよいのか。失敗したとき、どこで止まるのか。
この問いを避けたまま、エージェントを外の世界につなぐことはできない。
考えるだけなら安全、動かすと危ない
文章を返すだけのモデルは、間違えることがある。
間違った説明をする。古い情報を言う。もっともらしいが存在しない話を作る。これは危険ではあるが、危険の形はまだ限られている。出てくるものは、基本的には文章である。
ところが、エージェントが自分でコードを書き、それを実行し始めると、話は変わる。
コードは文章ではない。動く。
動けば、ファイルを読むかもしれない。ファイルを書き換えるかもしれない。ネットワークにつながるかもしれない。大量のメモリを使うかもしれない。終わらない処理に入るかもしれない。
そこに悪意があるとは限らない。
むしろ、多くの場合はただの失敗である。生成されたコードにバグがある。入力の形を勘違いしている。終了条件を書き忘れている。大きすぎるデータを一度に読もうとする。保存先を間違える。
人間が書くコードにも、こうした失敗はある。エージェントがその場で書いたコードなら、なおさら起きる。
問題は、その失敗がどこまで広がるかである。
もし生成されたコードを、利用者の環境で、そのまま全権限で動かしたらどうなるか。作業中のファイルを読める。設定ファイルを読める。秘密情報に触れるかもしれない。重要なファイルを上書きするかもしれない。外部に通信できるなら、読んだものを送ることもできる。
これは、見知らぬ相手に家の鍵を渡すのに近い。
相手が悪人かどうか以前に、鍵を渡す設計そのものが危うい。間違えて台所を水浸しにするかもしれないし、玄関を開けっぱなしにするかもしれない。だから、最初から家全体には入れないほうがよい。
エージェントにコードを実行させるときも同じである。
考えさせるだけなら、出力を読んで判断できる。だが、動かさせるなら、失敗の影響範囲を先に決めておかなければならない。
手を縛った実行環境を用意する
そこで必要になるのが、サンドボックスである。
サンドボックスとは、動くことはできるが、手は縛られている実行環境である。中では処理を実行できる。計算もできる。必要なら一時的なファイルも作れる。だが、その影響は外へ勝手には漏れない。
砂場を思い浮かべるとわかりやすい。
子どもは砂場の中で自由に遊べる。穴を掘ってもよい。山を作ってもよい。崩してもよい。だが、その遊びは砂場の中に閉じている。家の床を掘るわけではない。道路に砂をまき散らすわけでもない。
サンドボックスの核心も同じである。
信用していないコードを、信用しなくてよい場所で動かす。
ここで大事なのは、コードを信用しようとしないことだ。
「このコードはモデルが書いたから大丈夫だろう」と考えない。「このタスクは簡単だから問題ないだろう」とも考えない。そうではなく、間違う前提で囲う。暴走する前提で止める。余計なものに触る前提で、触れないようにする。
これは、エージェントを疑うというより、実行という行為を正しく扱うということに近い。
どれほど賢いモデルでも、実行されたコードの副作用は現実に残る。だから、現実に残ってよい範囲を、先に区切る。
何を縛るのか
では、具体的に何を縛るのか。
大きく四つある。ファイル、ネットワーク、メモリ、計算量である。
まず、ファイルである。
実行中のコードに、利用者の環境全体を見せてはいけない。原則として、何も触らせない。必要がある場合だけ、読んでよい場所、書いてよい場所を明示する。
たとえば、変換したい表があるなら、その表だけを渡す。出力先が必要なら、一時的な保存場所だけを与える。作業に関係のない文書、設定、鍵、履歴には届かないようにする。
次に、ネットワークである。
ネットワークは便利だが、危険も大きい。外部の情報を取りに行けるということは、外部へ情報を送れるということでもある。どこへつながったのかを後から追うのも、簡単ではない。
だから、勝手に外へ出られる状態にしない。通信が必要なら、管理された窓口だけを通す。どの宛先に、どの方法で、何を送ってよいのかを、実行環境の外側で決める。
三つ目は、メモリである。
コードは、メモリを使いすぎることがある。大きなファイルを一度に読み込む。配列を無限に増やす。不要なものを捨てない。こうした処理がそのまま走ると、実行している箱だけでなく、周囲のアプリケーションや本体まで巻き込んで不安定にする。
そこで、使えるメモリに上限を置く。上限を超えたら止める。失敗はサンドボックスの中で終わらせる。
四つ目は、計算量である。
終わらない処理は珍しくない。条件が少し間違っているだけで、ループは止まらなくなる。組み合わせをすべて試す処理は、入力が少し大きくなっただけで急に重くなる。
だから、実行時間や処理量に上限を置く。一定の時間を超えたら打ち切る。外から止められるようにしておく。
この四つを縛ることで、目指す状態が見えてくる。
中で失敗しても、外は無事である。
これが、サンドボックスの基本である。
禁止するだけでは仕事にならない
ただし、すべてを閉じればよいわけではない。
何も読めない。何も書けない。どこにもつながらない。すぐ止まる。これでは安全かもしれないが、仕事にならない。
エージェントには、何かをさせたい。表を整形させたい。文章から情報を抜き出させたい。決まった場所からデータを取ってこさせたい。コードを試しに走らせ、結果を見て修正させたい。
そのためには、能力を渡す必要がある。
ただし、渡し方が重要である。
基本は、まず禁止することだ。何でもできる状態から少しずつ制限するのではない。何もできない状態から、必要なものだけを一つずつ開ける。
このファイルは読んでよい。この一時ディレクトリには書いてよい。この外部サービスには、この決められた窓口を通してだけ問い合わせてよい。実行時間はここまで。メモリはここまで。
こうして、許可を積み上げる。
これは、普通のオペレーティングシステムが長く扱ってきた問題と似ている。
プログラムにいきなり機械全体を渡すのではない。メモリの境界を作る。ファイルへの権限を分ける。外界に触るときは、決められた入口を通らせる。あるプログラムの失敗が、他のプログラムや本体に広がらないようにする。
エージェントの運行时でも、同じ発想が必要になる。
違うのは、動かすものが、人間が事前に十分確認したプログラムだけではないことだ。エージェントは、その場でコードを書き、その場で試す。何を実行するかが、実行直前まで確定しない。
だからこそ、最小限の権限から始める設計が重要になる。
エージェントに自由を与えるには、まず境界を作らなければならない。境界があるから、その中で動かせる。
隔離は程度の問題である
ここまで書くと、サンドボックスを置けば安全になるように見えるかもしれない。
だが、そこまで単純ではない。
隔離は難しい。
どこまで縛れば十分なのかは、仕事の内容によって変わる。短い計算だけなら厳しくできる。外部の情報を取りに行く仕事なら、何らかの通信が要る。大量のデータを扱うなら、メモリや時間の上限も広げざるを得ない。
締めすぎれば、エージェントは役に立たない。緩めすぎれば、危険が残る。
しかも、上限の値はいつも明快に決められるわけではない。メモリをどれだけ許すか。何秒で打ち切るか。どの通信を許すか。どのファイルを渡すか。これらは、実際の仕事を見ながら調整する部分が多い。
だから、隔離は「あるかないか」ではなく、「どれだけ縛れているか」として見るほうがよい。
低い危険の作業なら、簡単な囲いで足りるかもしれない。重要な情報を扱う作業なら、より強い囲いが必要になる。外部への副作用がある作業なら、実行環境だけでなく、承認や記録や取り消しの仕組みも考えなければならない。
また、一つの壁だけを信じきるのも危うい。
ファイルを縛る。ネットワークを縛る。資源の上限を置く。実行後に環境を捨てる。外部に出る操作は別の窓口で検査する。こうした複数の壁を重ねることで、一つが破れても全体がすぐには崩れないようにする。
完璧な隔離を前提にしない。
失敗するかもしれないものとして設計する。
この態度が、エージェントの実行環境では重要になる。
Agentic OS への含意
Agentic OS という見方で考えると、サンドボックスは単なる安全機能ではない。
それは、エージェントに行動を許すための基礎である。
モデルは考える。だが、エージェントは動く。動くなら、実行の場所が要る。実行の場所には、境界が要る。境界がなければ、行動の失敗がそのまま利用者の環境へ広がる。
オペレーティングシステムは、長いあいだ、似た問題に向き合ってきた。複数のプログラムを同じ機械の上で動かすために、メモリを分け、権限を分け、入口を決め、暴走を止める仕組みを作ってきた。
エージェントの運行时でも、同じ種類の問題が立ち上がる。
自分でコードを書いて実行する。道具を呼ぶ。外部のサービスに触れる。長く走る。こうしたエージェントほど、手を縛った実行環境が必要になる。
ここでの目的は、エージェントを小さく閉じ込めることではない。
むしろ逆である。安全に動ける範囲を作ることで、エージェントに実際の仕事を任せられるようにする。どこまでなら失敗してよいかを決めることで、試行錯誤を許せるようにする。
実行を隔離する。触れてよい範囲を決める。資源の上限を置く。暴走を止める。必要な能力だけを、管理された窓口から渡す。
これが、運行时における最初の関門である。
安全というテーマは、この先でも何度も出てくる。エージェントが外の世界に強く触れるほど、安全は一つの部品ではなく、全体を横断する層になる。
ただ、その入口にあるのは、とても素朴な考え方だ。
信用していないコードを、信用しなくてよい場所で動かす。
次回は、運行时のもう一つの柱に進む。エージェントは手を動かすだけではない。途中の状態を持ち、過去の結果を参照し、次の行動につなげる。
つまり、記憶の話に入る。
← 一覧へ