AIエージェントにPythonを書かせると、ツールコールより速く・安く動く。だが、そのコードをどう安全に実行するかは、多くの開発者が頭を悩ませている問題です。

この記事でわかること:

  • Montyがなぜ「Dockerでもサンドボックスサービスでもない」第三の選択肢なのか
  • Rust製インタープリタが実現するマイクロ秒起動の仕組みと主な機能
  • Pydantic AIへの統合方法と実際のパフォーマンス比較

ツールコールの限界

LLMがツールコールで外部関数を呼ぶ方式は安全だが逐次処理になりやすい。ロンドンとパリの天気を比較する程度のタスクでも、LLMへの往復が3〜4回必要になります。

この非効率さを解消する方法として、LLMにPythonコードそのものを生成させて実行させるアプローチがあります。CloudflareはこれをCodeModeと命名し、AnthropicやHuggingFaceも類似の手法を採用しています。問題は実行環境の安全性です。

  • exec()に直接渡すとホスト環境に無制限にアクセスされる
  • Dockerは195msのコールドスタートが発生し、運用も重い
  • Modal、E2Bなどのサンドボックスサービスは1,000ms以上かかり、外部依存が増える

Montyとは

Pydanticが開発した、Rust製の軽量Pythonインタープリタです。LLMが生成したPythonコードを安全に実行するために設計されており、GitHubスター数は7,000超。最新バージョンはv0.0.17(2026年4月22日リリース)です。

CPythonに制限を加えたものではなく、ゼロから書かれたバイトコードVMです。RustのパーサーライブラリRuffを使ってPythonソースを独自のバイトコードに変換・実行します。pip install pydantic-monty一行でインストールでき(パッケージサイズ約4.5MB)、DockerもクラウドアカウントもAPIキーも不要です。

速度とセキュリティ

起動時間の差は桁違いです。

方式 起動時間
Monty(型チェックなし) 4.5μs
Monty(型チェックあり) 4.8ms
Docker 195ms
サンドボックスサービス 1,000ms〜
Pyodide 2,800ms

Montyが高速な理由は、親プロセスに直接組み込まれて実行されるためです。ネットワーク呼び出しもコンテナの起動もありません。

セキュリティ面では、「全部許可して制限する」ではなく「全部禁止してから許可する」設計を採っています。open()__import__()eval()exec()はMonty内部に存在せず、外の世界とやりとりできるのは開発者が明示的に登録した外部関数だけです。

import pydantic_monty

code = "print(f'{get_greeting(tone=\'friendly\')} {place}')"

m = pydantic_monty.Monty(
    code,
    inputs=["place"],
    external_functions=["get_greeting"],
)

def get_greeting(tone: str) -> str:
    return "Hello" if tone == "friendly" else "Greetings"

m.run(inputs={"place": "World"}, external_functions={"get_greeting": get_greeting})

LLMが書くコードはcode文字列の中だけに閉じています。get_greetingを外部関数として渡さない限り、Montyのコード内からは呼び出せません。

主な機能

型チェック: AstralのRust製型チェッカーtyがバンドルされています。型スタブを渡してLLMが書いたコードを実行前に検証できます。

スナップショット: 実行状態をバイト列にシリアライズして中断・再開できます。CPythonプロセスはシリアライズ自体が不可能で、マイクロVMのスナップショットはギガバイト単位になりますが、Montyのスナップショットは数キロバイトです。人間の承認待ちが発生するエージェントでは大きな差になります。

REPL対応: LLMが以前に定義した関数や変数を次の実行でも参照できます。LLMがREPLを前提にコードを書く傾向に合わせた設計です。

非同期対応: asyncioを使った並列処理をサポートします。複数のAPIを同時呼び出しするコードも問題なく動きます。

リソース制限: メモリ・再帰深度・実行時間をインタープリタ内部で制限できます。

現時点の制約

クラス定義、match文、コンテキストマネージャは現時点で非対応(近日対応予定)です。標準ライブラリはsystypingasynciopathlibが対応済みで、redatetimejsonは追加予定。サードパーティパッケージは今後も対応予定なしです。

LLMが典型的なコード生成で使う機能はほぼカバーされていますが、フル実装のPythonランタイムとしては使えません。

Pydantic AIとの統合

Pydantic AIのCodeExecutionToolsetと組み合わせると、既存のツールセットをそのままMontyで実行できます。

# ツールコール版
agent = Agent('claude-sonnet-4-5', toolsets=[weather_toolset])

# CodeMode版(1行変更)
agent = Agent('claude-sonnet-4-5', toolsets=[CodeExecutionToolset(toolset=weather_toolset)])

Pydantic公式ブログの検証によると、ツールコール方式が12.2秒・4,100入力トークン・$0.019だったのに対し、CodeMode版は9.1秒・3,300入力トークン・$0.017に改善しています(参考)。タスクが単純なほど差は小さく、複雑になるほど広がります。

Montyが向く場面

用途として特に合っているのは、複数の外部APIを並列で呼ぶエージェント、実行途中で人間の承認ステップがあるワークフロー、エッジデバイスや組み込み環境でLLMコードを実行したい場合です。逆に、NumPy・pandasなどのサードパーティライブラリが必須なタスクや、複雑なクラス階層を使うコードにはまだ向きません。

Dockerほどの汎用性は持たせず、AIエージェントが必要とする範囲だけを軽量・安全に提供する。その割り切りがMontyの設計思想です。v0.0.17と発展途上ではありますが、GitHubスター数7,000超と活発な開発が続いており、AIエージェントのコード実行基盤として注目の選択肢になっています。