Skip to main content
Run a Claude Code browser agent on a managed Kernel browser pool with a single SDK call. Environment Pools handle provisioning, agent execution, and teardown — you provide a task prompt and a browser profile. Source script: cookbooks/computer-use-kernel.mdx (runnable snippet below)

Architecture

What happens under the hood:
  1. You call create_rollout() with an AgentSpec and browser config
  2. Environment Pools claims a browser from your Kernel pool
  3. Claude Code + agent-browser launches inside the browser session
  4. The agent executes your task, interacting with the live website
  5. Results (reward, screenshots, traces) are returned

Prerequisites

  • Python 3.11+
  • uv or pip
  • API keys:
    • SYNTH_API_KEY — your Synth platform key
    • KERNEL_API_KEY — your Kernel API key
    • ANTHROPIC_API_KEY — for Claude Code agent
  • Kernel CLI: npm install -g @onkernel/cli

1. Install the SDK

pip install synth-ai
# or
uv add synth-ai

2. Create a Kernel browser profile

Create a persistent browser profile with saved authentication state:
# Create a named profile
kernel profiles create --name my-site

# Launch a browser to log in manually
kernel browsers create --stealth --profile-name my-site --save-changes
Open the live-view URL that Kernel prints, log in to your target site, then close the browser:
kernel browsers delete <session-id>
Your login cookies are now saved in the my-site profile.

3. Create a browser pool

kernel browser-pools create \
  --name agent-pool \
  --profile-name my-site \
  --stealth \
  --size 5
This pre-warms 5 browser instances with your saved auth state. Rollouts claim one browser each.

4. Run a single agent rollout

import os
from synth_ai import SynthClient
import time

# --- Config ---
SYNTH_API_KEY = os.environ["SYNTH_API_KEY"]
BACKEND_URL = "https://api.usesynth.ai"
client = SynthClient(api_key=SYNTH_API_KEY, base_url=BACKEND_URL)

# --- Create the rollout ---
result = client.pools.agent_rollouts.create(
    {
        "task_ref": {
            "dataset": "my-computer-use-task",
            "task_id": "check-dashboard",
        },
        "agent": {
            "harness": "claude-code",
            "model_id": "claude-sonnet-4.5",
            "permission_mode": "bypassPermissions",
        },
        "environment": {"backend": "browser"},
        "browser": {
            "task_prompt": "Navigate to the analytics dashboard and export last week's revenue report as CSV.",
            "profile": "my-site",
            "timeout_sec": 120,
            "headless": True,
            "capture_screenshot": True,
        },
        "pool_tags": ["browser", "kernel"],
        "timeouts": {"agent_sec": 120},
    },
)

rollout_id = result.get("rollout_id")
print(f"Rollout started: {rollout_id}")

# --- Poll until complete ---
while True:
    status = client.pools.agent_rollouts.get(rollout_id)
    state = status.get("status")
    print(f"  Status: {state}")
    if state in ("succeeded", "failed", "cancelled", "error", "completed"):
        break
    time.sleep(5)

# --- Results ---
reward = status.get("reward_primary", 0.0)
print(f"Reward: {reward}")
print(f"Final status: {status.get('status')}")

5. Run multiple seeds in parallel

Environment Pools handles concurrency for you — just submit multiple rollouts:
import concurrent.futures

seeds = [0, 1, 2, 3, 4]

def run_seed(seed: int) -> dict:
    result = client.pools.agent_rollouts.create(
        {
            "task_ref": {"dataset": "my-task", "task_id": f"task-{seed}"},
            "agent": {
                "harness": "claude-code",
                "model_id": "claude-sonnet-4.5",
                "permission_mode": "bypassPermissions",
            },
            "environment": {"backend": "browser"},
            "browser": {
                "task_prompt": f"Complete task variant {seed}",
                "profile": "my-site",
                "timeout_sec": 120,
            },
            "pool_tags": ["browser", "kernel"],
        },
    )
    rollout_id = result.get("rollout_id")
    # poll until done...
    return {"seed": seed, "rollout_id": rollout_id}

with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool:
    futures = [pool.submit(run_seed, s) for s in seeds]
    for f in concurrent.futures.as_completed(futures):
        print(f.result())
Keep max_workers at or below your Kernel pool size. If you submit more rollouts than available browsers, they will queue until a browser is released.

6. Add LLM verification

Include a verifier model and expected output to get automated reward scoring:
"browser": {
    "task_prompt": "Find the total revenue for Q4 2025 on the dashboard.",
    "profile": "my-site",
    "timeout_sec": 120,
    "verifier_model": "claude-sonnet-4-20250514",
    "expected": "The agent should navigate to the revenue dashboard and report a numeric total.",
},
The verifier compares the agent’s output against the expected description and assigns a reward between 0.0 and 1.0.

Key Concepts

ConceptDescription
Environment PoolPre-provisioned set of browser instances with saved auth state
AgentSpecWhich agent harness to use (claude-code, codex, opencode)
Browser configTask prompt, profile name, timeouts, screenshot capture
Pool tagsRoute rollouts to specific pool types (browser, kernel)
RolloutA single agent execution against one browser instance

Troubleshooting

  • “Pool not found” — Ensure your Kernel pool has matching tags (browser, kernel) or pass pool_id directly in the request.
  • “Auth expired” — Re-login to the Kernel profile and recreate the browser pool.
  • Timeouts — Increase timeout_sec in the browser config and agent_sec in timeouts.
  • Rate limits — Reduce concurrency or increase pool size.

Next Steps

  • Optimize with GEPA: Use this same setup as a container backend for GEPA optimization to evolve agent skills automatically.
  • Add custom rewards: Implement verifiers that check specific page state or downloaded files.

Ready to get started?

Get Started

Sign up and start running browser agents today.

Schedule Demo

See Synth in action with a personalized walkthrough.