# How to run smolagents code in a secure sandbox Wrap Runtime's four sandbox functions with smolagents' `@tool`. Every command the agent runs then executes in a disposable microVM. **On Runtime, the sandbox is a whole Linux machine, not a Python snippet runner.** smolagents' remote executors send the agent's Python snippets away and return their output. A Runtime sandbox is a Firecracker microVM with its own kernel, Ubuntu 24.04, Python 3.12, `pip` and `sudo`. The agent can install packages, run test suites and keep files between steps. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while the model is thinking. smolagents 1.26.0 was the current PyPI release on 25 October 2026. ## Where smolagents runs code today A `CodeAgent` writes Python and hands it to an executor. The executor is chosen with `executor_type`. smolagents 1.26.0 accepts five values: | `executor_type` | Where the snippet runs | What you set up | | ----------------- | ------------------------------------------------------------------ | -------------------------------------------- | | `local` (default) | Your own process, in smolagents' AST-walking `LocalPythonExecutor` | Nothing; imports need an allow-list | | `docker` | A Docker container on your machine | Docker installed, `smolagents[docker]` | | `e2b` | An E2B sandbox | An E2B account, `smolagents[e2b]` | | `modal` | A Modal sandbox | A Modal account, `smolagents[modal]` | | `blaxel` | A Blaxel sandbox | A Blaxel account, `smolagents[blaxel]` | | Runtime tools | A Runtime microVM, through tool calls | `withruntime`; one browser approval to start | The smolagents guide is direct about the default: "no local python sandbox can ever be completely secure", and "the only way to run LLM-generated code with truly robust security isolation is to use remote execution options". It also notes that the remote executors do not support managed agents (multi-agent setups) yet. ## Give a ToolCallingAgent the sandbox `sandbox_tools(sbx)` returns `runtime_exec`, `runtime_read_file`, `runtime_write_file` and `runtime_list_files` as typed functions. Their docstrings carry an `Args:` section. smolagents' `tool` decorator needs exactly that to build a tool's name, inputs and output type. So wrapping each one takes one line. ```bash no-run pip install smolagents withruntime ``` ```python check from smolagents import ToolCallingAgent, tool from withruntime import Sandbox from withruntime.tools import sandbox_tools def solve(model, task: str) -> str: with Sandbox.create( network={"internet": True, "allow": ["pypi.org", "*.pythonhosted.org"]}, timeout_seconds=900, on_lease_end="stop", ) as sbx: tools = [tool(f) for f in sandbox_tools(sbx)] agent = ToolCallingAgent(tools=tools, model=model, max_steps=15) return str(agent.run(task)) ``` `model` is any smolagents model, such as `InferenceClientModel()` or `OpenAIModel(...)`. The sandbox exists for the length of the `with` block. A package installed in step two is still there in step nine. The sandbox stops when the block ends, whether it ends normally or with an error. Prefer `ToolCallingAgent` here. A `CodeAgent` accepts the same tools and can call them from its code. But the snippets it writes still run in whichever executor `executor_type` names, which is your own process by default. ## Check the tools before paying for a model Each wrapped tool can be called on its own, so a smoke test needs no model: ```python check from smolagents import tool from withruntime import Sandbox from withruntime.tools import sandbox_tools with Sandbox.create() as sbx: run, read, write, ls = (tool(f) for f in sandbox_tools(sbx)) print(run.inputs) # command, cwd, timeout_seconds print(write(path="fib.py", content="a, b = 0, 1\nfor _ in range(90): a, b = b, a + b\nprint(a)\n")) print(run(command="python3 fib.py")) # {'exit_code': 0, 'stdout': '...', ...} ``` `runtime_exec` returns the exit code, stdout, stderr and a `timed_out` flag. The model then sees a failure as a failure. Relative paths land in `/workspace`. ## Run the whole agent in the sandbox smolagents' guide describes a second pattern for multi-agent systems. Put the agent, its model calls and its tools all inside the sandbox. The guide's drawback is that this "may require transferring sensitive API keys to the sandbox environment". Runtime removes that step. Store the model key once as a secret, bound to the provider's host: ```bash no-run printf %s "$OPENAI_API_KEY" | npx withruntime secrets set OPENAI_API_KEY --host api.openai.com ``` Every sandbox of the account then has `OPENAI_API_KEY` set to a placeholder. The host's proxy swaps in the real key only on HTTPS requests to `api.openai.com`. Managed agents inside the sandbox can therefore call the model, while nothing in the machine can read the key ([secrets sandboxes never see](/docs/security#secrets-sandboxes-never-see)). ```python check import sys from withruntime import Sandbox task = "What's the 20th Fibonacci number?" with Sandbox.create( network={"internet": True, "allow": ["api.openai.com", "pypi.org", "*.pythonhosted.org"]}, timeout_seconds=1800, on_lease_end="stop", ) as sbx: sbx.files.upload("./my_agents", "/workspace/my_agents") sbx.exec("pip install 'smolagents[openai]'", check=True, timeout_ms=300_000) run = sbx.exec( ["python3", "manager.py", task], cwd="/workspace/my_agents", timeout_ms=1_200_000, on_stdout=sys.stdout.write, ) print(run.exit_code) ``` `manager.py` is your own smolagents program. It uses `OpenAIModel` as it would on your laptop. The allow-list limits the sandbox to the model's API and the package index. ## Keep keys and spending in check - **The model never holds your Runtime key.** Your code creates the sandbox and binds the tools. The model only chooses commands and paths. - **Cap each agent run.** `timeout_seconds` with `on_lease_end="stop"` ends a forgotten sandbox. `max_total_cost_micros` stops a sandbox once its total cost reaches the value you set. - **Cap the key.** Give the application a key with a daily spending limit. Past that limit, creates fail with `spending_limit_reached`, and nothing is charged ([read-only keys and daily limits](/docs/security#read-only-keys-and-daily-limits)). ## Which executor fits Choose `local` only for trusted demos. Choose Docker when everything stays on one machine you control. Choose Runtime when the agent needs a real shell, when many agents run at the same time, or when managed agents need a model key without holding it. A paid account runs 100 sandboxes at the same time. Billing is by the CPU they use: $0.08 an hour for a 2 vCPU, 4 GiB sandbox with both CPUs busy ([pricing](/docs/pricing)). For the other Python frameworks, see [any other framework](/docs/frameworks#any-other-framework). For the risks this contains, see [run untrusted LLM code](/use-cases/run-untrusted-llm-code). For container isolation compared with a microVM, see [microVM vs container](/compare/microvm-vs-container). A new account gets 100 free hours of a 1 GB machine in its first 30 days, no card: ```bash no-run printf '%s\n' 'print(sum([125, 250, 375]))' > invoice.py npx withruntime sandboxes run -- python3 - < invoice.py ``` ## Related - [LLM tool use in a sandbox](/use-cases/llm-tool-use-sandbox) - [What is a code interpreter?](/glossary/code-interpreter) ## Sources - [smolagents: Secure code execution](https://huggingface.co/docs/smolagents/tutorials/secure_code_execution), read 25 October 2026 - [smolagents on PyPI](https://pypi.org/project/smolagents/), version 1.26.0, read 25 October 2026; `CodeAgent`'s `executor_type` values and the `@tool` schema were read from that release's source Facts on this page were checked on 25 October 2026.