
monty v0.0.20
Un intérprete de Python minimalista y seguro escrito en Rust para uso por IA
Monty
Un intérprete de Python mínimo y seguro escrito en Rust para uso por parte de la IA.
Experimental - Este proyecto aún está en desarrollo y no está listo para producción.
Un intérprete de Python mínimo y seguro escrito en Rust para uso por parte de la IA.
Monty evita el costo, la latencia, la complejidad y la molestia general de usar un sandbox basado en contenedores completos para ejecutar código generado por LLM.
En cambio, te permite ejecutar de forma segura código Python escrito por un LLM integrado en tu agente, con tiempos de inicio medidos en microsegundos de un solo dígito, no en cientos de milisegundos.
Lo que Monty puede hacer:
- Ejecutar un subconjunto razonable de código Python, suficiente para que tu agente exprese lo que quiere hacer
- Bloquear por completo el acceso al entorno host: el sistema de archivos, las variables de entorno y el acceso a la red se implementan mediante llamadas a funciones externas que el desarrollador puede controlar
- Llamar funciones en el host, solo las funciones a las que le des acceso
- Ejecutar verificación de tipos: Monty admite los type hints modernos de Python y viene con ty incluido en un solo binario para ejecutar la verificación de tipos
- Ser capturado como una instantánea de bytes en las llamadas a funciones externas, lo que significa que puedes almacenar el estado del intérprete en un archivo o base de datos, y reanudarlo más tarde
- Iniciar extremadamente rápido (<1μs desde el código hasta el resultado de la ejecución), y tiene un rendimiento en tiempo de ejecución similar a CPython (generalmente entre 5 veces más rápido y 5 veces más lento)
- Poder ser llamado desde Rust, Python o Javascript: como Monty no tiene dependencias de cpython, puedes usarlo en cualquier lugar donde puedas ejecutar Rust
- Controlar el uso de recursos: Monty puede rastrear el uso de memoria, la profundidad de la pila y el tiempo de ejecución, y cancelar la ejecución si supera los límites preestablecidos
- Recopilar stdout y stderr y devolverlos al llamador
- Ejecutar código asíncrono o síncrono en el host mediante código asíncrono o síncrono en el host
- Usar un pequeño subconjunto de la biblioteca estándar:
sys,os,typing,asyncio,re,datetime,json,dataclasses(pronto)
Lo que Monty no puede hacer:
- Usar el resto de la biblioteca estándar
- Usar bibliotecas de terceros (como Pydantic); el soporte para bibliotecas externas de Python no es un objetivo
- definir clases (el soporte debería llegar pronto)
- usar sentencias match (de nuevo, el soporte debería llegar pronto)
En resumen, Monty es extremadamente limitado y está diseñado para un caso de uso:
Ejecutar código escrito por agentes.
Para motivación sobre por qué podrías querer hacer esto, consulta:
- Codemode de Cloudflare
- Programmatic Tool Calling de Anthropic
- Code Execution with MCP de Anthropic
- Smol Agents de Hugging Face
En términos muy simples, la idea de todo lo anterior es que los LLM pueden trabajar más rápido, de forma más económica y más fiable si se les pide que escriban código Python (o Javascript), en lugar de depender de las llamadas a herramientas tradicionales. Monty hace eso posible sin la complejidad de un sandbox ni el riesgo de ejecutar código directamente en el host.
Nota: Monty se usará (pronto) para implementar codemode en Pydantic AI
Uso
Se puede llamar a Monty desde Python, JavaScript/TypeScript o Rust.
Python
Para instalarlo:```bash uv add pydantic-monty
(O `pip install pydantic-monty` para los boomers)
`pydantic-monty` es un metapaquete que combina `pydantic-monty-client` (el módulo `pydantic_monty`) con `pydantic-monty-runtime` (el binario del worker `monty`). Instala `pydantic-monty-client` por sí solo si el binario ya viene de otro lugar.
Uso:```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())
La ejecución se realiza en un grupo de subprocesos worker monty, por lo que incluso un error
de memoria provocado por código adversarial (desbordamiento de pila, aborto del asignador)
nunca puede hacer fallar tu proceso — el worker muere, lanza MontyCrashedError, y
es reemplazado. También hay una API totalmente síncrona:```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
Para instalar:```bash
npm install @pydantic/monty
El paquete JS es un binding nativo (napi) sobre el mismo pool de workers Rust que
usa el paquete Python — el binding y el binario worker monty se distribuyen mediante
paquetes npm específicos de cada plataforma:```ts
import { Monty } from '@pydantic/monty'
await using pool = await Monty.create() await using session = await pool.checkout()