|
| 1 | +# AG2 agents with a Tenki sandbox |
| 2 | + |
| 3 | +[AG2](https://github.com/ag2ai/ag2) 1.0 is a ground-up rewrite of the framework (`from ag2 import Agent` — not the old AutoGen 0.x `ConversableAgent` API) with first-class sandbox tools: `SandboxShellTool` and `SandboxCodeTool` run whatever the model writes inside a pluggable sandbox backend. This example uses AG2's **native Tenki backend** — `ag2.extensions.tenki` (merged in [ag2ai/ag2#3096](https://github.com/ag2ai/ag2/pull/3096), maintained with Tenki) — so every shell command and code snippet executes in an isolated [Tenki](https://tenki.cloud) microVM instead of on your machine. |
| 4 | + |
| 5 | +## The agent |
| 6 | + |
| 7 | +One `TenkiEnvironment` powers both tools: |
| 8 | + |
| 9 | +```python |
| 10 | +import asyncio |
| 11 | + |
| 12 | +from ag2 import Agent |
| 13 | +from ag2.config import AnthropicConfig |
| 14 | +from ag2.extensions.tenki import TenkiEnvironment |
| 15 | +from ag2.tools import SandboxCodeTool, SandboxShellTool |
| 16 | + |
| 17 | +async def main() -> None: |
| 18 | + env = TenkiEnvironment() |
| 19 | + async with env: |
| 20 | + agent = Agent( |
| 21 | + "developer", |
| 22 | + config=AnthropicConfig(model="claude-sonnet-4-6"), |
| 23 | + tools=[SandboxShellTool(env), SandboxCodeTool(env.code_environment())], |
| 24 | + ) |
| 25 | + reply = await agent.ask("Write hello.txt, then read it with Python.") |
| 26 | + print(await reply.content()) |
| 27 | + |
| 28 | +if __name__ == "__main__": |
| 29 | + asyncio.run(main()) |
| 30 | +``` |
| 31 | + |
| 32 | +(Running this agent needs an LLM key — `verify.py` below doesn't.) |
| 33 | + |
| 34 | +`env.code_environment()` matters: Tenki's default image ships `python3` (not `python`), and the returned `CodeAdapter` maps the `python` runner accordingly. Pass the env itself to `SandboxShellTool`, the adapter to `SandboxCodeTool`. |
| 35 | + |
| 36 | +## Setup (Python 3.10+) |
| 37 | + |
| 38 | +```bash |
| 39 | +uv venv # or: python3.11 -m venv .venv |
| 40 | +uv pip install -r requirements.txt # ag2 (git pin, see note) + tenki SDK |
| 41 | +export TENKI_API_KEY=tk_... # from the Tenki dashboard |
| 42 | +``` |
| 43 | + |
| 44 | +> **Release note (2026-08):** the Tenki extension merged into ag2 `main` on 2026-08-13, after the latest PyPI release (1.0.1, 2026-07-29) — so `requirements.txt` pins the merge commit via git. Once the next ag2 release ships `ag2/extensions/tenki`, this becomes simply: |
| 45 | +> |
| 46 | +> ```bash |
| 47 | +> pip install "ag2[anthropic]" "tenki>=0.5.4,<1" |
| 48 | +> ``` |
| 49 | +
|
| 50 | +## Configuring `TenkiEnvironment` |
| 51 | +
|
| 52 | +```python |
| 53 | +from ag2.extensions.tenki import TenkiEnvironment, TenkiResources |
| 54 | +
|
| 55 | +env = TenkiEnvironment( |
| 56 | + workspace_id="your-workspace-id", # auto-detected when the key sees one workspace |
| 57 | + name="my-agent-sandbox", |
| 58 | + image="workspace/image:tag", |
| 59 | + env_vars={"APP_ENV": "sandbox"}, |
| 60 | + resources=TenkiResources(cpu_cores=2, memory_mb=4096, disk_size_gb=5), |
| 61 | + timeout=60, |
| 62 | + max_duration=900, |
| 63 | +) |
| 64 | +``` |
| 65 | +
|
| 66 | +| Parameter | Default | Purpose | |
| 67 | +| --- | --- | --- | |
| 68 | +| `api_key` | `TENKI_API_KEY` | API authentication | |
| 69 | +| `api_url` | `TENKI_API_URL` or production | API endpoint | |
| 70 | +| `workspace_id` | Auto-detected when unique | Tenki workspace scope | |
| 71 | +| `name` | `ag2` | Sandbox name in the Tenki dashboard | |
| 72 | +| `image` | Tenki default image | Registry image reference | |
| 73 | +| `env_vars` | `{}` | Environment variables baked into the sandbox | |
| 74 | +| `resources` | Tenki defaults | CPU / memory / disk overrides | |
| 75 | +| `timeout` | `60` s | Sandbox startup + per-command timeout | |
| 76 | +| `max_duration` | `900` s | Server-enforced sandbox lifetime backstop | |
| 77 | +| `workdir` | `/home/tenki` | Working directory for commands and files | |
| 78 | + |
| 79 | +## What the backend does |
| 80 | + |
| 81 | +- **One environment, both tools.** The same `TenkiEnvironment` serves `SandboxShellTool` directly and `SandboxCodeTool` via `code_environment()` — one sandbox, one cleanup path. |
| 82 | +- **Files persist across tool calls.** The factory caches sandboxes by their resolved parameters, so a file the agent writes in tool call 1 is still there in tool call 10. The sandbox lives until `aclose()` (the `async with env` exit), not per call. |
| 83 | +- **Cleanup has three layers.** Creation is failure-atomic (a sandbox that never becomes ready is terminated immediately), an `atexit` hook catches interpreter shutdown if `aclose()` never ran, and `max_duration` reclaims the VM server-side even if the client process dies. Sandboxes are created with inbound networking off, outbound on. |
| 84 | + |
| 85 | +## Verify |
| 86 | + |
| 87 | +```bash |
| 88 | +node verify.mjs # or: .venv/bin/python verify.py |
| 89 | +``` |
| 90 | + |
| 91 | +`verify.py` drives the real integration classes against live Tenki; no LLM key needed. It imports `ag2.extensions.tenki`, constructs `TenkiEnvironment` + `TenkiResources`, builds the `CodeAdapter`, then does what `SandboxShellTool` does per call — `env.open()` → live sandbox → `exec` (`42`) — plus a file round-trip across two `open()` calls to prove the caching, and asserts teardown on scope exit. The agent loop on top is covered by ag2's own CI. |
| 92 | + |
| 93 | +## Notes |
| 94 | + |
| 95 | +- The extension uses Tenki's **new [`tenki`](https://pypi.org/project/tenki/) Python SDK** (`tenki>=0.5.4,<1`) — the canonical successor namespace to the older `tenki-sandbox` package some earlier cookbook examples use. Don't install `tenki-sandbox` for this one. |
| 96 | +- Auth: a `tk_` API key works as-is. A `tenki login` browser session token must be prefixed `cookie:` for the Python SDK (`verify.py` handles this). |
| 97 | +- All credential/selection parameters also accept AG2 `Variable`s for per-request (multi-tenant) resolution — see the [AG2 Tenki docs](https://github.com/ag2ai/ag2/blob/main/website/docs/user-guide/extensions/tenki.mdx). |
0 commit comments