アップデート一覧に戻る
New releaseAug 9, 2026

monty v0.0.20

AI向けの、Rustで書かれた最小限で安全なPythonインタプリタ

共有

Monty

AIが使用するための、Rustで書かれた最小限で安全なPythonインタープリター。

CI Codspeed Coverage PyPI versions license Join Slack

実験的 - このプロジェクトはまだ開発中であり、本番環境での使用には耐えられません。

AIが使用するための、Rustで書かれた最小限で安全なPythonインタープリター。

Montyは、LLMが生成したコードを実行するために完全なコンテナベースのサンドボックスを使う際のコスト、レイテンシ、複雑さ、そして一般的な手間を回避します。

その代わりに、エージェントに組み込まれたLLMが書いたPythonコードを安全に実行できます。起動時間は数百ミリ秒ではなく、一桁マイクロ秒単位です。

Montyができること:

  • 合理的なPythonコードのサブセットを実行する - エージェントが実行したいことを表現するのに十分な範囲
  • ホスト環境へのアクセスを完全にブロックする: ファイルシステム、環境変数、ネットワークアクセスはすべて、開発者が制御できる外部関数呼び出しを介して実装される
  • ホスト上の関数を呼び出す - アクセスを許可した関数のみ
  • 型チェックを実行する - Montyは完全なモダンPython型ヒントをサポートし、型チェックを実行するためのtyを単一バイナリに同梱しています
  • 外部関数呼び出し時にバイト列としてスナップショット化できる - インタープリターの状態をファイルやデータベースに保存し、後で再開できます
  • 極めて高速に起動する(コードから実行結果まで1μs未満)、またCPythonに匹敵する実行時パフォーマンスを持つ(一般的に5倍高速から5倍低速の間)
  • Rust、Python、JavaScriptから呼び出せる - MontyはCPythonに依存しないため、Rustを実行できる場所ならどこでも使用できます
  • リソース使用量を制御する - Montyはメモリ使用量、スタックの深さ、実行時間を追跡し、事前設定された制限を超えた場合に実行をキャンセルできます
  • 標準出力と標準エラー出力を収集し、呼び出し元に返す
  • ホスト上で非同期または同期コードを介して、非同期または同期コードを実行する
  • 標準ライブラリの小さなサブセットを使用する: sys、os、typing、asyncio、re、datetime、json、dataclasses(近日対応予定)

Montyができないこと:

  • 標準ライブラリの残りを使用する
  • サードパーティライブラリ(Pydanticなど)を使用する - 外部Pythonライブラリのサポートは目標ではありません
  • クラスを定義する(近日対応予定)
  • matchステートメントを使用する(これも近日対応予定)

要するに、Montyは非常に制限されており、1つのユースケースのために設計されています:

エージェントが書いたコードを実行すること。

なぜこれを行う必要があるのかについての動機は、以下を参照してください:

非常に簡単に言うと、上記すべての考え方は、従来のツール呼び出しに依存する代わりにPython(またはJavaScript)コードを書くように求められれば、LLMはより速く、より安く、より確実に動作できるというものです。Montyは、サンドボックスの複雑さやホスト上で直接コードを実行するリスクなしに、それを可能にします。

注記: Montyは(近い将来)Pydantic AIでcodemodeを実装するために使用される予定です。

使用方法

MontyはPython、JavaScript/TypeScript、またはRustから呼び出すことができます。

Python

インストールするには:```bash uv add pydantic-monty

(または、古い世代向けには `pip install pydantic-monty`)

`pydantic-monty` は、`pydantic-monty-client`(
`pydantic_monty` モジュール)と `pydantic-monty-runtime`(`monty` ワーカー
バイナリ)をペアにするメタパッケージです。バイナリが他の場所から既に
入手できる場合は、`pydantic-monty-client` だけをインストールしてください。

使用方法:```python
from typing import Any

import pydantic_monty

code = """
async def agent(prompt: str, messages: Messages):
    while True:
        print(f'messages so far: {messages}')
        output = await call_llm(prompt, messages)
        if isinstance(output, str):
            return output
        messages.extend(output)

await agent(prompt, [])
"""

type_definitions = """
from typing import Any

Messages = list[dict[str, Any]]

async def call_llm(prompt: str, messages: Messages) -> str | Messages:
    raise NotImplementedError()

prompt: str = ''
"""


Messages = list[dict[str, Any]]


async def call_llm(prompt: str, messages: Messages) -> str | Messages:
    if len(messages) < 2:
        return [{'role': 'system', 'content': 'example response'}]
    else:
        return f'example output, message count {len(messages)}'


async def main():
    async with pydantic_monty.AsyncMonty() as pool:
        async with pool.checkout(
            script_name='agent.py',
            type_check=True,
            type_check_stubs=type_definitions,
        ) as session:
            output = await session.feed_run(
                code,
                inputs={'prompt': 'testing'},
                external_lookup={'call_llm': call_llm},
            )
    print(output)
    #> example output, message count 2


if __name__ == '__main__':
    import asyncio

    asyncio.run(main())

実行は monty ワーカーサブプロセスのプールで行われるため、敵対的なコードによって引き起こされるメモリエラー(スタックオーバーフロー、アロケータのアボート)でも、あなたのプロセスをクラッシュさせることは決してありません。ワーカーが終了し、MontyCrashedError を発生させ、そして置き換えられます。完全に同期するAPIもあります。```python import pydantic_monty

with pydantic_monty.Monty() as pool: with pool.checkout() as session: # session state persists between feed_run calls session.feed_run('x = 21') print(session.feed_run('x * 2')) #> 42

### JavaScript / TypeScript

インストールするには:```bash
npm install @pydantic/monty

JS パッケージは、Python パッケージが使用するのと同じ Rust ワーカープールに対するネイティブ (napi) バインディングであり、 バインディングと monty ワーカーバイナリは、プラットフォーム固有の npm パッケージとして 配布されます:```ts import { Monty } from '@pydantic/monty'

await using pool = await Monty.create() await using session = await pool.checkout()

// session state persists between feedRun calls await session.feedRun('x = 21') console.log(await session.feedRun('x * 2')) // 42

// external functions may be async const result = await session.feedRun('await fetch_data()', { externalLookup: { fetch_data: async () => 'data' }, })

ブラウザ(またはサブプロセスが不可能な任意の場所)では、同じパッケージは
`@pydantic/monty/wasm` サブパス配下にインプロセスの WebAssembly ビルドを公開します
(クラッシュ分離なし: サンドボックスのクラッシュはそこではホストのクラッシュです)。

### Rust

Rust から信頼できないコードを実行する場合、以下に示すインプロセス API ではなく、
[`monty-pool`](https://crates.io/crates/monty-pool) クレートを推奨します。
`monty-pool` は `monty` ワーカーのサブプロセスでのみコードを実行するため、追加の保護が得られます:
敵対的なコードによって引き起こされるクラッシュ(スタックオーバーフロー、アロケータのアボート)はワーカーのみを終了させます —
プールはその終了を検出してワーカーを置き換えます — また、親側のウォッチドッグは、ハードタイムアウトを超えた
ワーカーを強制終了できます。これは上記の Python パッケージと JavaScript パッケージが
基づいているのと同じエンジンです。使用法については
[monty-pool README](https://github.com/pydantic/monty/tree/main/crates/monty-pool) を参照してください。

`monty` クレート自体はインプロセスのインタプリタを提供します:```rust
use monty::MontyRun;
use monty_types::{CompileOptions, ResourceTracker, MontyObject, PrintWriter, ResourceLimits};

let code = r#"
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

fib(x)
"#;

let runner = MontyRun::new(code.to_owned(), "fib.py", vec!["x".to_owned()], CompileOptions::default()).unwrap();
let result = runner.run(vec![MontyObject::Int(10)], ResourceTracker::default(), PrintWriter::Stdout).unwrap();
assert_eq!(result, MontyObject::Int(55));

シリアライゼーション

REPL セッションは dump() でシリアライズでき、Dump::load() で復元できます。ダンプには、読み込み側のビルドが確認するバージョンが付与され、インタープリタの状態とともにセッションのメタデータ(スクリプト名、型チェック用スタブ)が格納されます。```rust use monty::{Dump, MontyRepl, Session, SessionRef, dump}; use monty_types::{CompileOptions, MontyObject, PrintWriter, ResourceTracker};

// Snapshot a session between snippets let mut repl = MontyRepl::new("main.py", ResourceTracker::default(), CompileOptions::default()); repl.feed_run("x = 41", vec![], PrintWriter::Stdout).unwrap(); let bytes = dump("main.py", None, SessionRef::Idle(&repl)).unwrap();

カテゴリ