Module 05 · Tool use fundamentals · Lesson 4 of 4
Code execution and sandboxes
Give the model a sandbox instead of a tool per operation: why code generalises, what arbitrary code risks, and how process, container and microVM isolation with resource limits and no network keep it contained.
Why it matters
Twenty tools cover twenty operations. The twenty-first request, "sum the refunds by month and plot them", needs a tool nobody wrote. A sandbox that runs code the model writes covers it, and the next thousand like it, with one tool. That is why every coding agent and most data agents have code execution at their base. It is also the tool with the widest blast radius, because "run arbitrary code" is the thing security teams spend their careers preventing. Interviewers ask about sandboxes to see whether you can take the capability without taking the risk.
The idea
Why code generalises
A tool call is one operation with fixed arguments. Code composes: loops, conditions, parsing and arithmetic in one execution with no model round trips between them. A model cleaning a CSV with tools must call read_rows, look, filter, look, aggregate, each a lap of the loop and each result flowing through the context window. Given an interpreter it writes twelve lines, runs them once and reads a short answer. The intermediate data never enters the context, which is cheaper and more accurate: models are far better at writing a group-by than at summing a table pasted into the prompt. They have also seen vastly more Python and shell in training than they will ever see of your tool schemas.
What can go wrong
The code is untrusted by construction. It was written by a model that may have been reading a prompt injection a moment earlier. Four risks follow. Arbitrary code: it can fork-bomb the host, fill the disk, read anything the process can read. Network: it can post whatever it saw to a URL of its choosing, or fetch more code. Secrets: an API key in an environment variable is one line away. Cost: an endless loop on billed CPU, or a script calling a paid API ten thousand times. Each is addressed by containment, never by asking the model to behave.
Isolation levels
Containment comes in layers that trade start-up time against strength. A process sandbox, a subprocess with dropped privileges, a syscall filter and a restricted interpreter, starts in milliseconds and stops honest mistakes, but shares the host kernel. A container adds its own filesystem, namespaces and cgroups, starts in well under a second, and is the common default; it still shares the kernel, so a kernel exploit escapes it. A microVM boots its own minimal kernel in roughly 100 to 200 milliseconds and gives hardware-enforced isolation; it is what hosted code-execution products and serverless platforms use for multi-tenant untrusted code. Whichever level you choose, the surrounding controls are the same: CPU and memory limits, a wall-clock timeout, a disk quota, no network by default with an allow-list of hosts when something is genuinely needed, an ephemeral filesystem wiped per run or per session, no secrets in the environment (the runtime proxies calls that need them), and an allow-list of packages.
sandbox microvm cpu=1 mem=512MB disk=1GB timeout=60s
network deny allow=[pkg-mirror.internal]
fs ephemeral mounts=[/data:ro]
secrets none tool calls proxied by the runtime
The base of coding and data agents
A coding agent is a sandbox with a repository mounted and three tools on top: read, edit, run. A data agent is a sandbox with a dataframe library and the dataset mounted read-only. In both, the interesting work happens inside executed code, and the remaining tools mostly read results back. Programmatic tool calling closes the loop: code the model writes can call your other tools as ordinary functions, through stubs the runtime injects, and the runtime executes those calls from inside the sandbox. A hundred lookups then cost one model turn and the intermediate rows never enter the context.
| No execution | Restricted interpreter | Container sandbox | MicroVM sandbox | |
|---|---|---|---|---|
| Isolation | None needed; tools only | Process level; shares the kernel and address space | Namespaces and cgroups; shares the kernel | Own kernel; hardware-enforced |
| Start-up time | n/a | Milliseconds | Under a second, warm pools faster | About 100 to 200 ms |
| Cost | Only tokens | Lowest | Low; dense packing per host | Higher per instance; what hosted products charge for |
| Fit | Fixed action sets, strict permissions | Trusted users, small scripts, quick maths | Internal agents, single tenant, known workloads | Untrusted multi-tenant code, public products |
Trade-offs
Code execution trades a bounded tool surface for an unbounded one, so review and policy move from "which tools exist" to "what can the sandbox reach". Cold starts add latency, and warm pools add cost and state to manage. No network by default breaks the first thing most users ask for, installing a package, so the allow-list becomes a product decision. Ephemeral filesystems lose work between sessions unless you add explicit persistence, which reopens the data-exfiltration question. And the model's code can be subtly wrong in ways a schema would have caught: a tool call either validates or not, while a script can run clean and compute the wrong number.
In an interview
The line: "I give the model a sandbox rather than a tool per operation, because code generalises; then I contain it with a microVM or container, CPU and memory limits, a timeout, no network by default, an ephemeral filesystem and no secrets in the environment." At junior depth, know why code beats a tool per operation and that the code is untrusted. At mid depth, name the four risks and the controls that answer each. At senior depth, compare process, container and microVM isolation on start-up time and kernel sharing, explain programmatic tool calling, and say which level fits single-tenant internal agents versus a public multi-tenant product. Expect the follow-ups: "Why no network by default?" (exfiltration after injection) and "Where do the API keys live?" (outside the sandbox, behind a proxy the runtime controls).
Check yourself
A data agent runs model-written Python in a container with the production API key in an environment variable and open outbound network. A user uploads a document carrying a prompt injection. Which risk is unmitigated?