sandbox.commands.run and sandbox.files. Every command the model runs, and every file it writes, then stays in an isolated VM instead of on your server. The model API key stays in your app; the sandbox never sees it.
Setup
pydantic-ai-slim[anthropic] installs Pydantic AI with only the Anthropic provider, which reads the key from the environment. To use another provider, install its extra and change the model string. See Models in the Pydantic AI docs, and API keys to create a Superserve API key.
Wire the sandbox into the agent
The highlighted lines declare the sandbox as the agent’s dependency and define abash tool backed by it. Everything else is standard Pydantic AI usage.
deps_type=Sandbox sets the dependency type, and deps=sandbox passes the sandbox to the run. Each tool gets it as ctx.deps, so one agent defined at module level can work with a different sandbox on every run. See Dependencies in the Pydantic AI docs.
The tool returns the SDK’s CommandResult. Pydantic AI sends it to the model as JSON with stdout, stderr, exit_code, and truncated, so the model sees failed commands as well as their output.
Run code with file tools
Pydantic AI is async-first. WithAsyncSandbox and async tools, sandbox calls do not block the event loop. Add file tools next to bash to let the model write a script, run it, and read the result.
ModelRetrysends the message back to the model as the tool result, so the model can correct the path and try again. Unhandled ordinary exceptions from a tool stop the run.UsageLimits(request_limit=10)caps the model requests in the run (the default is 50). Past the limit, the run raisesUsageLimitExceeded.
sandbox.files paths must be absolute. See Read & write files for binary content and directory downloads.
Stream tool calls
run_stream_events() yields the model’s text as it arrives, and an event for each tool call and each tool result.
model_response_str() returns the tool result as the model receives it. Output from a run (trimmed; the model’s wording varies):
One sandbox per conversation
To serve many conversations, store each conversation’s sandbox ID with its Pydantic AIConversation. On each turn, reconnect to the sandbox, continue the conversation, and pause the sandbox afterwards. Files and installed packages stay available, and you do not pay for an idle VM between turns.
result.conversation holds the messages, usage, and conversation ID that the next run needs. It is a Pydantic value, so Chat stores it next to the sandbox ID in one JSON document. The second turn understands “that file” because of the conversation, and finds the file because it lives in the sandbox. See Carrying a conversation whole in the Pydantic AI docs.
Sandbox.connect() resumes a paused sandbox, or one that is still pausing, before it returns. See Lifecycle and Connect.
Usage limits count the usage that a conversation carries, not only the current run. A conversation stops with
UsageLimitExceeded after 50 model requests in total unless you pass a higher usage_limits to each run. See Usage limits.Good to know
- The model can call only the tools you define. Keep
kill(),pause(), andupdate()in your application code, not in a tool. - To limit what model-written code can reach, create the sandbox with network rules.
- Pass
timeout_secondstocommands.run. Without it, the SDK request times out after 30 seconds, so the tool call fails on longer commands such as package installs. - The default sandbox has
python3but nopython. Tell the agent in itsinstructions, or use a template with the tools you need. - As a backstop for crashes, set
timeout_secondsandauto_delete_secondswhen you create the sandbox. See Auto-delete.
Resources
Pydantic AI docs
Agents, tools, dependencies, and message history in Pydantic AI.
Run commands
Options for
commands.run, including environment variables and working directory.