Google ADK
Give Google ADK agents a secure E2B sandbox to run code, commands, and files in.
Google ADK (Agent Development Kit) is Google’s framework for building agents, with Gemini as the default model. The E2B integration gives ADK workloads an isolated remote workspace for running code and commands instead of executing them on your host.
Choose an integration
Choose the native environment when your application directly controls the sandbox. Choose the advanced plugin when the agent itself should decide when to run code, manage files, or start a service.
Native: Google ADK environment
Google ADK includes the experimental
E2BEnvironment
for direct command execution and file access through ADK’s environment API. No
additional integration package is required.
pip install "google-adk[e2b]>=2.3.0"Create and close the environment explicitly in your application:
import asyncio
from google.adk.integrations.e2b import E2BEnvironment
async def main() -> None:
env = E2BEnvironment()
await env.initialize()
try:
await env.write_file("hello.py", 'print("Hello from E2B")\n')
result = await env.execute("python hello.py")
print(result.stdout)
finally:
await env.close()
asyncio.run(main())This path exposes the sandbox as a low-level workspace: your application calls
execute, read_file, and write_file itself.
Advanced: E2B plugin
The e2b-adk plugin exposes sandbox
operations as tools the model can call. It adds stateful code execution, shell
commands, file management, and background processes backed by the
E2B Code Interpreter. The plugin creates one sandbox lazily, shares it
across tool calls, and closes it with the ADK runner.
To use E2B with ADK:
- Create an
E2BPlugin. - Hand
plugin.get_tools()to yourAgentand register the plugin on theApp. - Run the agent — every tool call executes inside the sandbox, and the plugin creates and tears the sandbox down for you.
Install the dependencies
Install the plugin. It pulls in Google ADK and the E2B Code Interpreter SDK.
pip install e2b-adkYou need an E2B API key for the sandbox and a Gemini key for the model.
export E2B_API_KEY="..."
export GOOGLE_API_KEY="..."Example: Data analysis agent
A good fit for a sandbox is analysis the model shouldn’t do in its head. Here the agent is given a raw dataset and a question. Rather than eyeballing the numbers, it writes the data to a file, runs real pandas against it, and reports figures it actually computed — the sandbox is a calculator it can’t fool.
Create the plugin and agent
The plugin owns the sandbox. Pass its tools to the Agent and register it on the
App; the instruction is what makes the agent run code rather than guess.
from google.adk.agents import Agent
from google.adk.apps import App
from e2b_adk import E2BPlugin
INSTRUCTION = """You are a data analyst. You never guess numbers — you compute
them. Save any data you are given to a file with write_file, analyse it by
running real pandas with run_code, fix and re-run if the code errors, and report
the figures you computed. Never report a number you have not verified by running
code."""
plugin = E2BPlugin()
agent = Agent(
model="gemini-2.5-flash",
name="data_analyst",
instruction=INSTRUCTION,
tools=plugin.get_tools(),
)
app = App(name="data_analysis", root_agent=agent, plugins=[plugin])Run the analysis
Run the agent inside an InMemoryRunner. The sandbox is created lazily on the
first tool call, kept alive while the agent works, and killed when the
async with block exits — you never manage it yourself.
from google.adk.runners import InMemoryRunner
DATASET = """month,region,marketing_spend,revenue
2024-01,North,12000,48000
2024-02,North,15000,61000
2024-03,North,9000,37000
2024-04,North,18000,72000
2024-01,South,8000,26000
2024-02,South,11000,30000
2024-03,South,14000,33000
2024-04,South,17000,38000"""
async with InMemoryRunner(app=app) as runner:
# run_debug prints the conversation as it runs.
await runner.run_debug(
f"Here is marketing spend and revenue as CSV:\n\n{DATASET}\n\n"
"Save it to sales.csv, then tell me which region converts spend into "
"revenue more efficiently and which month performed best."
)The agent writes sales.csv with write_file, then uses run_code to load it
with pandas, compute revenue-per-dollar and per-month totals, and answer in
prose — every number backed by an execution in the sandbox:
data_analyst > North converts marketing spend into revenue more efficiently —
about $4.0 of revenue per $1 of spend versus roughly $2.5 for South. The best
single month was 2024-04 in North: $72,000 revenue from $18,000 spend.Pass verbose=True to run_debug to also print each tool call and its result
as the agent works.
Full example
import asyncio
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner
from e2b_adk import E2BPlugin
INSTRUCTION = """You are a data analyst. You never guess numbers — you compute
them. Save any data you are given to a file with write_file, analyse it by
running real pandas with run_code, fix and re-run if the code errors, and report
the figures you computed. Never report a number you have not verified by running
code."""
DATASET = """month,region,marketing_spend,revenue
2024-01,North,12000,48000
2024-02,North,15000,61000
2024-03,North,9000,37000
2024-04,North,18000,72000
2024-01,South,8000,26000
2024-02,South,11000,30000
2024-03,South,14000,33000
2024-04,South,17000,38000"""
async def main() -> None:
plugin = E2BPlugin(metadata={"example": "data-analysis"})
agent = Agent(
model="gemini-2.5-flash",
name="data_analyst",
instruction=INSTRUCTION,
tools=plugin.get_tools(),
)
app = App(name="data_analysis", root_agent=agent, plugins=[plugin])
async with InMemoryRunner(app=app) as runner:
# run_debug prints the conversation as it runs.
await runner.run_debug(
f"Here is marketing spend and revenue as CSV:\n\n{DATASET}\n\n"
"Save it to sales.csv, then tell me which region converts spend "
"into revenue more efficiently and which month performed best."
)
asyncio.run(main())Example: Code generation agent
The same tools support a code generator that verifies its own work. The instruction tells the agent to execute every snippet in the sandbox before returning it, so what you get back has already run and passed its tests — no untested code reaches the user.
import asyncio
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner
from e2b_adk import E2BPlugin
INSTRUCTION = """You are a code generator that returns verified, working code.
For every request: (1) write the function, (2) write tests, (3) EXECUTE in the
sandbox with run_code, (4) if it fails, fix and re-run until tests pass,
(5) return ONLY the final function. Never return code you haven't executed."""
async def main() -> None:
plugin = E2BPlugin(metadata={"example": "code-generator"})
agent = Agent(
model="gemini-2.5-flash",
name="codegen",
instruction=INSTRUCTION,
tools=plugin.get_tools(),
)
app = App(name="codegen", root_agent=agent, plugins=[plugin])
async with InMemoryRunner(app=app) as runner:
# run_debug prints the conversation as it runs.
await runner.run_debug(
"Write a Python function group_by(items, key) that groups a list "
"by a key function."
)
asyncio.run(main())Tools
plugin.get_tools() returns six tools, all sharing the plugin’s single sandbox
so state persists across calls. Each returns a dict with a success flag and
reports failures in the result rather than raising, so a bad call never aborts
the agent run. success means the tool ran: code that raised or a command
that exited non-zero still returns success: True with the failure captured in
error / exit_code — only a call that could not run at all returns
success: False.
| Tool | Does |
|---|---|
run_code | Run code in a stateful kernel (variables persist across calls) |
run_command | Run a shell command |
write_file / read_file | Write and read files in the sandbox |
list_files | List a directory |
start_background_command | Start a long-running process, with an optional preview URL for a port |
Configuration
E2BPlugin accepts keyword-only options, all optional. Anything you don’t set
falls back to the E2B SDK’s own default — the plugin overrides none of them.
plugin = E2BPlugin(
api_key=None, # defaults to the E2B_API_KEY env var
template=None, # E2B sandbox template
metadata=None, # dict[str, str] attached to the sandbox
envs=None, # environment variables inside the sandbox
timeout=None, # sandbox timeout in seconds (re-applied on every tool call)
lifecycle=None, # what happens on timeout — see below
# ...every other AsyncSandbox.create() option is forwarded verbatim
)The plugin keeps the sandbox alive while the agent is working: every tool call
pushes the expiry window forward by timeout (E2B’s default is 300s). An idle
gap longer than timeout still expires the sandbox under E2B’s default
lifecycle — pass
lifecycle={"on_timeout": {"action": "pause"}, "auto_resume": True} to pause
and auto-resume across idle gaps instead. See the
repository README
for the full option list.
Reference examples
Complete, runnable scripts live in the plugin repository.