업데이트로 돌아가기
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는 메모리 사용량, 스택 깊이, 실행 시간을 추적하고 설정된 한도를 초과하면 실행을 취소할 수 있습니다
  • stdout과 stderr를 수집하여 호출자에게 반환합니다
  • 호스트에서 비동기 또는 동기 코드를 호스트의 비동기 또는 동기 코드를 통해 실행합니다
  • 표준 라이브러리의 작은 하위 집합을 사용합니다: sys, os, typing, asyncio, re, datetime, json, dataclasses (곧 지원 예정)

Monty가 할 수 없는 것:

  • 표준 라이브러리의 나머지를 사용
  • 서드파티 라이브러리(예: Pydantic) 사용 - 외부 Python 라이브러리 지원은 목표가 아닙니다
  • 클래스 정의 (곧 지원 예정)
  • match 문 사용 (이 역시 곧 지원 예정)

요컨대 Monty는 매우 제한적이며 한 가지 사용 사례를 위해 설계되었습니다:

에이전트가 작성한 코드를 실행하는 것.

이러한 방식을 원하는 이유에 대한 동기부여는 다음을 참조하세요:

아주 간단히 말하면, 위의 모든 접근 방식의 핵심 아이디어는 LLM이 전통적인 도구 호출에 의존하는 대신 Python(또는 Javascript) 코드를 작성하도록 요청받을 때 더 빠르고, 저렴하고, 더 안정적으로 작동할 수 있다는 것입니다. 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};

카테고리