Documentation Index
Fetch the complete documentation index at: https://mintlify.com/opensandbox-group/OpenSandbox/llms.txt
Use this file to discover all available pages before exploring further.
Google Agent Development Kit (ADK) makes it easy to define Python functions as agent tools and route LLM tool-call requests to them automatically. This example wraps three OpenSandbox operations — write_file, read_file, and run_in_sandbox — as ADK tools and hands them to a Gemini agent. The agent receives natural-language prompts, decides which tools to call, and the results are routed back through ADK’s runner, all while the actual execution happens inside an isolated OpenSandbox container.
Prerequisites
Pull the sandbox image and start the server
docker pull sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/code-interpreter:v1.1.0
uv pip install opensandbox-server
opensandbox-server init-config ~/.sandbox.toml --example docker
opensandbox-server
Install Python dependencies
uv pip install opensandbox google-adk
Set required environment variables
export GOOGLE_API_KEY=your-gemini-api-key
export SANDBOX_DOMAIN=localhost:8080
Environment Variables
| Variable | Default | Description |
|---|
SANDBOX_DOMAIN | localhost:8080 | Sandbox service address |
SANDBOX_API_KEY | (optional) | API key if your server requires authentication |
SANDBOX_IMAGE | sandbox-registry…/code-interpreter:v1.1.0 | Sandbox image to use |
GOOGLE_API_KEY | (required) | Gemini API key |
GOOGLE_ADK_MODEL | gemini-2.5-flash | Gemini model name |
Full Example
The script creates a sandbox, registers three tool functions against a Gemini ADK agent, then sends three sequential prompts. Each prompt may trigger one or more tool calls; ADK prints tool events as they arrive and the sandbox is cleaned up in a finally block.
import os
from datetime import timedelta
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.runners import Runner
from google.adk.sessions.in_memory_session_service import InMemorySessionService
from google.adk.utils._debug_output import print_event
from google.adk.utils.context_utils import Aclosing
from google.genai import types
from opensandbox import Sandbox
from opensandbox.config import ConnectionConfig
def _required_env(name: str) -> str:
value = os.getenv(name)
if not value:
raise RuntimeError(f"{name} is required")
return value
async def main() -> None:
_required_env("GOOGLE_API_KEY")
domain = os.getenv("SANDBOX_DOMAIN", "localhost:8080")
api_key = os.getenv("SANDBOX_API_KEY")
image = os.getenv(
"SANDBOX_IMAGE",
"sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/code-interpreter:v1.1.0",
)
model_name = os.getenv("GOOGLE_ADK_MODEL", "gemini-2.5-flash")
config = ConnectionConfig(
domain=domain,
api_key=api_key,
request_timeout=timedelta(seconds=120),
)
sandbox = await Sandbox.create(image, connection_config=config)
async def run_in_sandbox(command: str) -> str:
"""Run a shell command in OpenSandbox and return the output."""
execution = await sandbox.commands.run(command)
stdout = "\n".join(msg.text for msg in execution.logs.stdout)
stderr = "\n".join(msg.text for msg in execution.logs.stderr)
if execution.error:
stderr = "\n".join(
[stderr, f"[error] {execution.error.name}: {execution.error.value}"]
).strip()
output = stdout.strip()
if stderr:
output = "\n".join([output, f"[stderr]\n{stderr}"]).strip()
return output or "(no output)"
async def write_file(path: str, content: str) -> str:
"""Write a file inside the sandbox."""
await sandbox.files.write_file(path, content)
return f"wrote {len(content)} bytes to {path}"
async def read_file(path: str) -> str:
"""Read a file from the sandbox."""
return await sandbox.files.read_file(path)
agent = Agent(
name="opensandbox_adk",
model=model_name,
instruction=(
"You have access to OpenSandbox tools. Use write_file to create or "
"update files, read_file to read files, and run_in_sandbox to run "
"commands."
),
tools=[run_in_sandbox, write_file, read_file],
)
app = App(name="opensandbox_adk", root_agent=agent)
session_service = InMemorySessionService()
runner = Runner(app=app, session_service=session_service)
session = await session_service.create_session(
app_name=app.name,
user_id="local-user",
)
prompts = [
"Use write_file to save /tmp/math.py that prints 137 * 42.",
"Run the script using run_in_sandbox and report the result.",
"Write /tmp/notes.txt with 'ADK + OpenSandbox', then read it back.",
]
try:
for prompt in prompts:
content = types.Content(
role="user",
parts=[types.Part(text=prompt)],
)
async with Aclosing(
runner.run_async(
user_id=session.user_id,
session_id=session.id,
new_message=content,
)
) as agen:
async for event in agen:
print_event(event, verbose=True)
finally:
await sandbox.kill()
await sandbox.close()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Run the example:
uv run python examples/google-adk/main.py
How It Works
Sandbox creation
A single Sandbox instance is created before the agent starts. All three tool functions close over this instance, so every tool call targets the same container.
Tool registration
run_in_sandbox, write_file, and read_file are plain async Python functions with docstrings. ADK uses those docstrings as tool descriptions when constructing the function-calling schema for Gemini.
Prompt loop
Each prompt is wrapped in a types.Content message and passed to runner.run_async(). ADK streams events (tool calls, tool results, model responses) back through the async generator; print_event prints each one.
Cleanup
The finally block calls sandbox.kill() and sandbox.close() regardless of whether an exception was raised during the prompt loop.
The three prompts are executed sequentially in the same session, so the agent retains memory of previous tool results across turns.
References