Use Agent Sandboxes with Pydantic AI
Let a Pydantic AI agent answer a question with one Python script instead of many tool calls.
You keep your tools as they are. Pydantic AI’s Code Mode (a capability that turns an agent’s tools into functions
the model can call from Python) gives the model one run_code tool, and Agent Sandboxes runs each script.
Code Mode writes the type stubs from your tools, checks each script against them, sends errors back to the model, and records every run in Logfire. You add one argument.
- Follow Run code from Python to set
LOGFIRE_API_KEYto a key with permission to run code in agent sandboxes. - Set
OPENAI_API_KEYto a key from the OpenAI API keys page. The example sends model requests to OpenAI. - Install Logfire with the
agent-sandboxesextra, and Pydantic AI Harness with thecodemodeextra:
pip install 'logfire[agent-sandboxes]>=5.6.0' 'pydantic-ai-harness[codemode]>=0.67.0'
Add CodeMode to the agent’s capabilities, and pass logfire.agent_sandboxes as the function that creates the
sandbox pool:
from pydantic_ai import Agent
from pydantic_ai_harness import CodeMode
from tools import get_request_stats, list_customers, list_open_tickets
import logfire
logfire.configure()
logfire.instrument_pydantic_ai()
agent = Agent(
'openai:gpt-6.1-sol',
tools=[list_customers, get_request_stats, list_open_tickets],
capabilities=[
CodeMode(
monty_sandbox=logfire.agent_sandboxes,
)
],
)
result = agent.run_sync(
'Which enterprise customers missed their availability target (successful requests / all requests) '
'over the last 7 days, and which of them have open support tickets?'
)
print(result.output)
tools.py is the file from Answer questions that need dozens of tool calls.
Pass the function itself, not logfire.agent_sandboxes().
Code Mode calls it on the first run_code call of each agent run, so concurrent runs each get their own pool.
No connection opens until the model first calls run_code.
Run python agent.py. For the sample data, a correct answer names Customer 9, Customer 42, Customer 87, and
Customer 111 as below their target, and Customer 42 and Customer 87 as the ones with open tickets.
The model sees one tool, run_code, with your three tools listed as Python functions. It does not call your tools
one at a time. It writes scripts like this one, and each script continues from the variables of the one before it:
import asyncio
customers = await list_customers(plan='enterprise')
stats_lists = await asyncio.gather(*[get_request_stats(customer_id=c['id'], days=7) for c in customers])
missed = []
for c, rows in zip(customers, stats_lists):
total = sum(r['requests'] for r in rows)
errors = sum(r['errors'] for r in rows)
if total and (total - errors) / total < c['target']:
missed.append(c)
[c['name'] for c in missed]
The exact code changes from run to run. This script makes 41 tool calls and reads 1,680 rows, and it returns four names to the model. Your tools run in your process, as they do without Code Mode. Only the script runs in the sandbox.
In three runs of this question, the agent was correct each time and used a median of 4,680 input tokens. With plain tool calls, the same agent was correct in one run of three and used 111,162. See the full comparison, which includes a smaller model and the limits of the test.
Each agent run starts a new session. To let a later turn use the variables an earlier turn defined, pass a repl_store
to CodeMode. Code Mode saves the session at the end of each run and loads it at the start of the next run in the same
conversation. See Keep the REPL (read-eval-print loop) across runs in
the Pydantic AI docs, and Keep an agent’s work across conversation turns for how this works.
With Agent Sandboxes, the state that Code Mode saves is the ID of a snapshot that Logfire stores. Logfire keeps it for 7 days, so a conversation that resumes later than that starts with a fresh session.
Run python agent.py, then open the Live view. You should see the agent run’s trace.
Inside it, find the run_code tool call. It contains a
run code span with the script the model wrote, and a span for each tool the script called.
UserError:monty_sandboxtakes a URL or a function that returns the pool, not the pool itself: you passedlogfire.agent_sandboxes(). Passlogfire.agent_sandboxeswithout the parentheses.UserErrorthat mentionsmonty_sandbox_url:monty_sandbox_urlis the deprecated form ofmonty_sandbox. Pass onlymonty_sandbox.LogfireConfigError: Agent sandboxes require a Logfire API key: the run started without an API key. SetLOGFIRE_API_KEY, or passapi_keytologfire.configure().- The model calls a tool directly instead of from
run_code: Code Mode keeps some tools outside the sandbox, such as tools that load lazily. See Selective tool sandboxing.
- Work with large tool results: tools that return more rows than the model should read.
- What sandbox code can do: the parts of Python that scripts can use.
- Code Mode: every Code Mode option, in the Pydantic AI docs.