Skip to content

Commit ba47882

Browse files
justinsbjanetkuo
andauthored
examples/sandboxed-tools: refactor tools into their own package (#886)
This lets us add more tools in a reasonable way, as most claw-style systems use a handful of tools. Co-authored-by: Janet Kuo <chiachenk@google.com>
1 parent d4b10bf commit ba47882

9 files changed

Lines changed: 676 additions & 183 deletions

File tree

examples/sandboxed-tools/README.md

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,54 +7,91 @@ By keeping the sandbox lifetime scoped strictly to the duration of a tool call,
77
## Architecture & Key Concepts
88

99
1. **Minimal OpenAI-Compatible Client (`pkg/llm`)**: A lightweight Go client built on `net/http` without a third-party OpenAI SDK that interacts with OpenAI-compatible API endpoints (such as the Gemini API via its OpenAI compatibility layer). It supports function calling (tools) and tool call responses.
10-
2. **Ephemeral Sandbox Execution**: When the LLM requests a tool call (e.g., `run_command`), the application provisions a temporary sandbox directly using the low-level `agentsclientset`, executes the requested command via the Pod "exec" API, and immediately deletes the `Sandbox` resource.
10+
2. **Ephemeral Sandbox Execution**: When the LLM requests a tool call, the application provisions a temporary sandbox directly using the low-level `agentsclientset`, executes the requested tool, and immediately deletes the `Sandbox` resource once execution completes.
11+
3. **Session Persistence via Snapshots**: To maintain continuity across different turns of a conversation, the application automatically snapshots the sandbox's home directory (`/home/clawtainer` by default) before deleting the sandbox.
12+
- These snapshots are saved as local tarball files on the host machine under `~/.local/sandboxed-tools/<session>/fs/backup-*.tar.gz`.
13+
- When a new tool call is made, the application creates a fresh sandbox and automatically restores the latest snapshot into `/home/clawtainer` before executing the tool.
14+
- Only the last 5 backups are retained per session; older backups are automatically pruned.
15+
16+
## Command-Line Arguments (CLI Options)
17+
18+
The application accepts the following command-line flags:
19+
20+
| Flag | Description | Default / Fallback |
21+
| :--- | :--- | :--- |
22+
| `-session` | **Required**. A unique alphanumeric name (max 40 characters) to identify this agent session and store/restore its filesystem snapshots. | None |
23+
| `-namespace`| The Kubernetes namespace where sandbox pods are created. | `default` (overrides `SANDBOX_NAMESPACE` env var) |
24+
| `-image` | The container image used for the temporary sandbox pod. | `debian:bookworm-slim` (overrides `SANDBOX_IMAGE` env var) |
25+
| `-homedir` | The directory inside the sandbox that is persisted via snapshot/restore. | `/home/clawtainer` (overrides `SANDBOX_HOME_DIR` env var) |
1126

1227
## Configuration
1328

14-
The application is configured via environment variables:
29+
The application is configured via environment variables (usually for API keys and endpoint configuration):
1530

16-
| Variable | Description | Default |
31+
| Variable | Description | Default / Fallback |
1732
| :--- | :--- | :--- |
1833
| `GEMINI_API_KEY` | Your Gemini API key (or `OPENAI_API_KEY`). | **Required** |
1934
| `OPENAI_BASE_URL` | The base URL for the OpenAI-compatible API. | `https://generativelanguage.googleapis.com/v1beta/openai` |
2035
| `OPENAI_MODEL` | The model name to use for chat completions (or `MODEL`). | `gemini-3.5-flash` |
21-
| `SANDBOX_IMAGE` | The container image used for the temporary sandbox pod. | `debian:bookworm-slim` |
22-
| `SANDBOX_NAMESPACE`| The Kubernetes namespace where sandboxes are created. | `default` |
36+
| `SANDBOX_IMAGE` | Fallback container image if `-image` flag is not set. | `debian:bookworm-slim` |
37+
| `SANDBOX_NAMESPACE`| Fallback Kubernetes namespace if `-namespace` flag is not set. | `default` |
38+
| `SANDBOX_HOME_DIR` | Fallback persisted directory if `-homedir` flag is not set. | `/home/clawtainer` |
39+
40+
## Available Tools
41+
42+
The LLM has access to a powerful suite of tools configured in the registry (`pkg/tools`):
43+
44+
* **`run_command`**: Executes an arbitrary shell command inside the sandbox container, returning `stdout`, `stderr`, and the `exit_code`.
45+
* **`ls`**: Lists the files and directories inside a specific folder (defaults to the current directory).
46+
* **`read`**: Reads the full contents of a file from the sandbox.
47+
* **`write`**: Writes specified content to a file, automatically creating parent directories if they do not exist and overwriting the file if it does.
2348

2449
## Running the Example
2550

51+
Make sure your Kubernetes cluster is running and accessible via your active `kubeconfig` context.
52+
2653
```bash
2754
# Set your API key
2855
export GEMINI_API_KEY="your-api-key-here"
2956

30-
# Run the chat interface
31-
go run ./examples/sandboxed-tools/main.go
57+
# Run the chat interface, specifying a session name
58+
go run ./examples/sandboxed-tools/main.go -session myfirstsession
3259
```
3360

3461
## Example Session
3562

3663
```
3764
================================================================================
3865
Welcome to the Sandboxed Tools example!
66+
Session Name: myfirstsession (Namespace: default)
67+
Sandbox Image: debian:bookworm-slim
3968
Using LLM Base URL: https://generativelanguage.googleapis.com/v1beta/openai (Model: gemini-3.5-flash)
40-
Sandbox Image: debian:bookworm-slim (Namespace: default)
4169
Key Concept: An Agent Sandbox is launched ONLY when a tool needs to be executed,
4270
and is immediately deleted afterward.
4371
Type your message (or '/exit' or '/quit' to quit):
4472
================================================================================
4573
46-
User> What is the current kernel version and uptime of the sandbox?
47-
48-
[Tool Execution] LLM requested tool "run_command" with command: "uname -r && uptime"
49-
I0522 12:00:00.123456 12345 main.go:448] launching sandbox for tool execution...
50-
I0522 12:00:05.123456 12345 main.go:462] executing command in sandbox sandbox.name="sandbox-tool-abcde" command="uname -r && uptime"
51-
I0522 12:00:06.123456 12345 main.go:465] deleting sandbox sandbox.name="sandbox-tool-abcde"
52-
[Tool Result] stdout:
53-
6.1.0
54-
12:00:05 up 1:23, 0 users, load average: 0.00, 0.00, 0.00
55-
stderr:
56-
57-
exit_code: 0
58-
59-
Agent> The sandbox is running kernel version 6.1.0 and has been up for 1 hour and 23 minutes.
74+
User> Create a greeting file with 'Hello from Sandbox' under my home directory, then list the files there.
75+
76+
I0530 12:00:00.123456 12345 main.go:744] launching sandbox for tool execution...
77+
I0530 12:00:05.123456 12345 main.go:759] restoring filesystem to sandbox... sandbox.name="sandbox-tool-abcde"
78+
I0530 12:00:05.124000 12345 registry.go:72] llm invoking tool tool.name="write" tool.arguments="{\"content\":\"Hello from Sandbox\",\"path\":\"/home/clawtainer/greeting.txt\"}"
79+
I0530 12:00:05.125000 12345 write_file.go:67] creating directory in sandbox dir="/home/clawtainer"
80+
I0530 12:00:05.500000 12345 write_file.go:75] writing file in sandbox path="/home/clawtainer/greeting.txt"
81+
I0530 12:00:06.123456 12345 main.go:790] snapshotting filesystem from sandbox... sandbox.name="sandbox-tool-abcde"
82+
I0530 12:00:06.200000 12345 main.go:445] saved filesystem state to new backup backup="/home/user/.local/sandboxed-tools/myfirstsession/fs/backup-20260530T120006.tar.gz"
83+
I0530 12:00:06.201000 12345 main.go:798] deleting sandbox sandbox.name="sandbox-tool-abcde"
84+
85+
I0530 12:00:06.300000 12345 main.go:744] launching sandbox for tool execution...
86+
I0530 12:00:11.300000 12345 main.go:759] restoring filesystem to sandbox... sandbox.name="sandbox-tool-fghij"
87+
I0530 12:00:11.301000 12345 main.go:378] restoring filesystem from latest backup backup="/home/user/.local/sandboxed-tools/myfirstsession/fs/backup-20260530T120006.tar.gz"
88+
I0530 12:00:11.305000 12345 registry.go:72] llm invoking tool tool.name="ls" tool.arguments="{\"path\":\"/home/clawtainer\"}"
89+
I0530 12:00:11.306000 12345 list_files.go:56] listing files in sandbox path="/home/clawtainer"
90+
I0530 12:00:12.100000 12345 main.go:790] snapshotting filesystem from sandbox... sandbox.name="sandbox-tool-fghij"
91+
I0530 12:00:12.150000 12345 main.go:445] saved filesystem state to new backup backup="/home/user/.local/sandboxed-tools/myfirstsession/fs/backup-20260530T120012.tar.gz"
92+
I0530 12:00:12.151000 12345 main.go:798] deleting sandbox sandbox.name="sandbox-tool-fghij"
93+
94+
Agent> I have created the file `greeting.txt` inside `/home/clawtainer` containing the message 'Hello from Sandbox'.
95+
When I listed the files inside `/home/clawtainer`, I found:
96+
- greeting.txt
6097
```

0 commit comments

Comments
 (0)