Python コード実行 (Monty)

ADK-Rust は、モデルが記述した Python を Pydantic Monty インタープリタを介してプロセス内で実行します。コンテナも サブプロセスも不要で、起動はマイクロ秒単位です。この機能は 2 つのレイヤーで提供されます。

  • adk-codeembedded-python 機能)— MontyExecutorBuilder と 2 つの実行器プロダクト、MontyOneShotExecutor および MontyReplExecutor。いずれも CodeExecutor を実装しています。
  • adk-toolcode-embedded-python 機能)— MontyPythonCodeToolmonty_python_code)。これらの実行器を利用するエージェント向けツールです。

これは、Docker で完全な CPython を実行するコンテナバックの PythonCodeToolpython_code)を補完するものです。スクリプトで実際の Python エコシステム(pip パッケージ、C 拡張、完全な標準ライブラリ)が必要な場合は、そちらを使用してください。Monty は Python のサブセットを実装しており、その代わりにプロセス内での高速性、シリアライズ可能な インタープリター状態、および構造上保証されるネットワークなし・サブプロセスなしの保証を提供します。

[dependencies]
adk-tool = { version = "2.1.0", features = ["code-embedded-python"] }

またはアンブレラクレートを介して:

[dependencies]
adk-rust = { version = "2.1.0", features = ["minimal", "code-embedded-python"] }

ワンショットと REPL

1 つのビルダーが両方の成果物を生成します。モードはフラグではなく型にエンコードされています:

モードビルド状態並行性
単発実行build_one_shot()呼び出しごとに新しいインタープリター並行実行に対応
REPLbuild_repl()変数、関数、インポートは呼び出し間で保持される呼び出しはセッションごとに直列化される
use adk_code::{MontyExecutorBuilder, PathAccess};

let builder = MontyExecutorBuilder::new()
    .allow_path("/data", "/srv/agent/data", PathAccess::ReadOnly)
    .allow_path("/out", "/srv/agent/out", PathAccess::ReadWrite)
    .environ_var("PROJECT", "acme")
    .system_clock();

let one_shot = builder.clone().build_one_shot()?;
let repl = builder.build_repl()?;

REPL 実行器は、呼び出し間でシリアル化されたインタープリターを保存します。Monty は Python レベルの例外を通じてセッションを保持するため、失敗したスニペット によって蓄積された状態が破棄されることはありません。CodeExecutor のライフサイクルメソッドが セッションを管理します。start() はそれを初期化し、stop() はそれを破棄し、restart() は それをリセットし、start() の前の execute() は遅延初期化します。

セキュリティモデル

分離は、明示的なポリシーと省略による強制を組み合わせています。

  • ファイルシステム。 allow_path で許可されたディレクトリのみが到達可能であり、 それぞれ読み取り専用または読み書き可能として、仮想 マウントパスに対する pathlib.Path を通じてアクセスされます。Monty のマウントテーブルが境界を強制します(正規化 + シンボリックリンク脱出の検出)。その他のパスでは捕捉可能な OSError が発生します (存在確認は False を返します)。
  • 環境。 os.getenv / os.environ は、構築時に 許可された明示的なマップのみを読み取ります — ホストプロセスの環境が公開されることはありません。
  • クロック。 date.today() / datetime.now() は、場合にのみ動作します .system_clock() が許可された場合。それ以外の場合は OSError を送出します。
  • ネットワークとサブプロセス。 Monty にはどちらのための機能もありません。したがって、設定にかかわらず不可能です。
  • タイムアウト。 SandboxPolicy::timeout は Monty の ResourceLimits::max_duration(VM 内での実際のプリエンプション、呼び出しごと)に対応します。メモリ 上限(デフォルトは 256 MiB)によりヒープが制限されます。REPL モードでは、これは 累積 セッションヒープを制限します。

許可とリクエストポリシー。 ビルダーの許可は、あらゆる スクリプトが持てる最大アクセス権です。リクエストごとの SandboxPolicy はそれらの範囲内で 狭めることしかできません — 許可を超えるリクエストは、コードが実行される前に、超過分を示す ExecutionError::UnsupportedPolicy によりフェイルクローズで拒否されます。 許可はディレクトリ配下のサブツリー全体を対象とします。許可されたマウント または その任意のサブディレクトリ をリクエストすると成功し、有効なマウントは、対応するホストサブディレクトリによって裏付けられたリクエストされた パスになります。実行者が提供するものを正確にリクエストするには granted_policy() を 使用してください。

REPL セッションの有効なポリシーは呼び出し間で変化してはなりません。ポリシーがセッションで確立されたポリシーと異なる呼び出しは拒否され、restart() への案内が表示されます。

ホスト関数

登録された Rust 関数(同期または非同期)は呼び出し可能な Python 関数になり、スクリプトからベア名で参照できます。

use adk_code::MontyExecutorBuilder;
use serde_json::json;

let executor = MontyExecutorBuilder::new()
    .function_fn("row_count", "Count rows in the loaded dataset.", |args, _kwargs| async move {
        Ok(json!(args.len()))
    })
    .build_one_shot()?;

完全なトレイト形式では、HostFunctionnamedescription、 LLMプロンプト用の任意のsignature、およびJSON変換された 位置引数とキーワード引数を持つ非同期call)を実装します。レジストリの検証はbuild_*()で行われます: 名前は有効な Python 識別子であり、一意で、Python の組み込みと競合してはなりません。

スクリプト内では、ホスト関数は常に同期的に呼び出されます。await を使用することはありません。返された Err はメッセージを含む捕捉可能な Python 例外になり、未登録の名前を呼び出すと、登録済みの名前を一覧表示する修正用例外が発生します。ホスト関数の実行には独自の実時間上限(host_function_timeout、デフォルトは 30 秒)があるため、停止した関数によって execute() がブロックされることはありません。

注記: ホスト関数はホストコードとして実行されます。これらは Monty の信頼境界ではなく、ユーザー自身の信頼境界です。インタープリターのサンドボックスは、それらの副作用を封じ込めません。

自己記述型エグゼキューター

両方の実行器はCodeExecutor::prompt_snippet()を実装し、構築済みの機能をレンダリングします。これには、モードのセマンティクス、アクセスレベルを伴うファイルシステムルート、環境変数名(値は決してレンダリングされません)、クロックの可用性、ネットワークなし/サブプロセスなしの保証、出力契約、および登録済みホスト関数用の Python スタブブロックが含まれます。MontyPythonCodeToolはスニペットをそのLLM向け説明に追加するため、プロンプトとインタープリター内の動作は同じ設定から導出され、乖離することはありません。

MontyPythonCodeTool

エージェント向けツール(monty_python_code、スコープ code:execute)は、 JavaScriptCodeToolを反映しています:エラーを情報として扱う JSON、camelCase の出力キー、および機能が無効な場合の 構造化された "rejected" フォールバックです。

use adk_code::PathAccess;
use adk_tool::MontyPythonCodeTool;
use serde_json::json;
use std::sync::Arc;

let tool = MontyPythonCodeTool::builder()
    .allow_path("/out", "/srv/agent/out", PathAccess::ReadWrite)
    .environ_var("PROJECT", "acme")
    .system_clock()
    .function_fn("get_weather", "Current weather for a city.", |args, _kwargs| async move {
        Ok(json!({ "temp_c": 21 }))
    })
    .build_repl()?;

let agent = LlmAgentBuilder::new("data_agent")
    .instruction("Use monty_python_code for calculations and data work.")
    .model(model)
    .tool(Arc::new(tool))
    .build()?;

MontyPythonCodeTool::new() は完全にサンドボックス化されたワンショットツールを構築し、 MontyPythonCodeTool::repl() は完全にサンドボックス化された REPL ツールを構築します。

セッションのスコープ設定

REPL モードでは、インタープリターセッションは完全な ADK セッション識別子 — アプリ名、ユーザー ID、セッション ID — によってキー付けされるため、ユーザー間で セッション ID 文字列が重複していても状態が漏洩することはありません。すべてのセッションは同じ 権限とホスト関数レジストリを共有し、セッションごとに異なるのはインタープリターの状態のみです。 セッションマップは LRU 上限(max_sessions、デフォルトは 100、0 は 1 として扱われます)によって制限されます。追い出されたセッションでは、次回の呼び出し時に透過的に新しい インタープリターが開始されます。

ツール引数

引数説明
code文字列(必須)実行するPythonソース
input任意input変数にバインドされるオプションのJSON値
timeout_secsintegerインタープリターの時間予算(デフォルトは30、1~300に制限)
resetbooleanREPLモードのみ:実行前に永続セッションを破棄

出力エンベロープ

{ "status": "success", "stdout": "", "stderr": "", "output": {"n": 42},
  "stdoutTruncated": false, "stderrTruncated": false, "durationMs": 3 }

exitCode はありません — 実行はプロセス内で行われ、プロセスは起動されません。 status は成功/失敗のシグナルです。stdoutTruncated / stderrTruncated は、キャプチャされた出力がサンドボックスポリシーのバイト上限(デフォルトではそれぞれ 1 MB) で切り詰められた場合に報告します。

スクリプトの最終式の値は output として返され、print() 出力は stdout としてキャプチャされます。失敗ステータス: "failed"(Python 例外 — ホスト関数によって発生した例外を含むトレースバックは stderr 内)、"timeout"(時間予算の超過)、"rejected"(不正な引数または機能が無効)。ToolError にはなりません。

エンベロープは両方のモードで固定されているため、ツールは Tool::response_schema() を通じてこれを宣言します — レスポンススキーマを公開するプロバイダーは、ツール宣言内で parameters とともにこれを受け取ります。

CodeAct との関係

CodeActAgent + adk-codeact-monty の経路も Monty 経由で Python を実行しますが、スクリプト内(call_tool(...))からの ADK Tool ディスパッチと、エージェントターンをまたぐ中断/再開を備えています。MontyPythonCodeTool は意図的にその両方を除外しています。これは自己完結型のコード実行ツールであり、その拡張性の接点はホスト関数レジストリです。CodeAct については コーディングエージェント を参照してください。

examples/monty_python_code_tool は、読み書き可能なマウント、環境変数、および登録済みホスト関数で構成された REPL モードの MontyPythonCodeTool を使用して LlmAgent を実行します。これは、複数ターンにわたる変数の永続化と、モデルが記述した Python からのホスト関数呼び出しを実演します。