This puts the whole loop on one machine: the agent runs in a microVM on the B3OS node, calls the model the node serves, and calls a B3OS tool, all through http://127.0.0.1:7700 inside the sandbox. You need a key in B3OS_API_KEY (get one), curl and jq.

1

Create a sandbox with a grant

The agent template has Python 3.12 and Node 24. The api grant decides what code in the sandbox may call; here it may list and call models and scrape pages:

bash
SBX=$(curl -s https://beta-api.b3os.org/v1/sandboxes \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"template": "agent", "timeout_s": 600, "api": {"scopes": ["infer:read", "infer:run", "vision:run"]}}' | jq -r .id)echo $SBX

The sandbox runs until timeout_s passes or you delete it.

2

Write the agent

Save this as agent.py on your machine. It uses only the Python standard library. Inside the sandbox B3OS_API_URL is http://127.0.0.1:7700 and no key is needed: the node knows which sandbox is calling.

python
import json, os, urllib.requestAPI = os.environ["B3OS_API_URL"]def call(method, path, body=None): data = None if body is None else json.dumps(body).encode() req = urllib.request.Request(API + path, data=data, method=method, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=60) as resp: return json.load(resp)# Use the chat model this node serves.models = call("GET", "/v1/infer/models")["data"]model = next(m["id"] for m in models if m["b3os"]["kind"] == "chat")tools = [{"type": "function", "function": { "name": "scrape", "description": "Fetch a web page and return it as Markdown.", "parameters": {"type": "object", "properties": {"url": {"type": "string"}}, "required": ["url"]},}}]messages = [{"role": "user", "content": "What is https://example.com for? Answer in one sentence."}]for turn in range(4): reply = call("POST", "/v1/infer/chat/completions", {"model": model, "messages": messages, "tools": tools}) message = reply["choices"][0]["message"] messages.append(message) if not message.get("tool_calls"): print(message["content"]) break for tool_call in message["tool_calls"]: url = json.loads(tool_call["function"]["arguments"])["url"] page = call("POST", "/v1/vision/scrape", {"url": url, "formats": ["markdown"]}) print(f"scraped {url} in {page['timing']['total_ms']} ms ({page['tier']})") messages.append({"role": "tool", "tool_call_id": tool_call["id"], "content": page["markdown"][:8000]})
3

Copy it into the sandbox

bash
curl -s -X PUT "https://beta-api.b3os.org/v1/sandboxes/$SBX/files?path=/workspace/agent.py&mkdir=1" \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/octet-stream" \ --data-binary @agent.py
4

Run it and watch the output

?stream=true sends stdout and stderr as server-sent events while the command runs:

bash
curl -N -s "https://beta-api.b3os.org/v1/sandboxes/$SBX/exec?stream=true" \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"cmd": "python3 -u /workspace/agent.py", "timeout_ms": 300000}'

You see exec.stdout events with the scrape line and the answer, then exec.exit with the exit code.

5

See the calls the sandbox made

The request log records calls from inside sandboxes with connection: sandbox:

bash
curl -s "https://beta-api.b3os.org/v1/requests?sandbox_id=$SBX" \ -H "Authorization: Bearer $B3OS_API_KEY" | jq '.data[] | {path, duration_ms, connection}'

The model calls and the scrape reached the node over vsock, with no network hop. The scrape's own fetch of example.com still crosses the internet to that site.

6

Clean up

bash
curl -s -X DELETE "https://beta-api.b3os.org/v1/sandboxes/$SBX" \ -H "Authorization: Bearer $B3OS_API_KEY" | jq '{status, usage}'

Deleting returns the stopped sandbox with its final usage.

What the grant allowed

The sandbox had infer:read, infer:run and vision:run and nothing else. A call to any other product from inside it fails with forbidden. Scopes in a grant must be a subset of your key's, and sandboxes:write, the vault scopes and the platform scopes are granted only when named exactly. Agents in sandboxes covers grants, spend caps and vault connections.

Next

  • Same-host latency: what staying on the node saves per call.
  • Sandboxes: templates, snapshots, ports and terminals.
  • Infer: the models on the node and the OpenAI-compatible API.
Ask a question... ⌘I