n4nAI

Building a shopping agent with computer-use APIs

Step-by-step tutorial to build a computer use shopping agent with Anthropic's computer-use API and Playwright, from env setup to automated checkout.

n4n Team3 min read635 words

Audio narration

Coming soon — every post will get a voice note here.

A computer use shopping agent turns a vision-language model into a remote worker that clicks, types, and screenshots its way through an e-commerce site. In this tutorial we build one against Anthropic’s computer-use API, wiring it to a real browser so you can watch it log in, add items to a cart, and stop at checkout. The same pattern applies to any site where you’d rather not write brittle selectors.

Prerequisites

  • Python 3.11 or newer
  • An Anthropic API key exported as ANTHROPIC_API_KEY
  • The anthropic and playwright Python packages
  • Chromium binary installed for Playwright
pip install anthropic playwright
playwright install chromium

You should be comfortable reading base64 screenshots and mapping pixel coordinates to mouse events. No frontend framework knowledge is required—the agent avoids DOM selectors entirely.

Architecture

We run a headless Chromium instance via Playwright. Each turn we capture a screenshot, send it to Claude with the computer_20241022 tool, and execute the returned action (click, type, scroll, key, wait, or finish) against the live page. The loop terminates when the model emits finish or we hit a step cap.

A computer use shopping agent needs a deterministic viewport. We use 1024×768 and strip animations so layout shifts don’t desync the model’s coordinate memory. As a test bed we use https://www.saucedemo.com, a public Sandbox with fixed credentials and stable markup.

Step 1: Browser harness

The harness wraps Playwright and exposes primitive actions. It never interprets the page; it only forwards pixels and mouse commands.

from playwright.sync_api import sync_playwright
import base64

class BrowserEnv:
    def __init__(self, width=1024, height=768):
        self.width = width
        self.height = height
        self.playwright = sync_playwright().start()
        self.browser = self.playwright.chromium.launch(headless=True)
        self.page = self.browser.new_page(viewport={"width": width, "height": height})
        # Kill animations that break coordinate stability
        self.page.add_style_tag(
            content="* { transition: none !important; animation: none !important; }"
        )

    def goto(self, url):
        self.page.goto(url, wait_until="networkidle")

    def screenshot(self) -> str:
        png = self.page.screenshot()
        return base64.b64encode(png).decode("utf-8")

    def click(self, x, y):
        self.page.mouse.click(x, y)

    def type(self, text):
        self.page.keyboard.type(text, delay=10)

    def key(self, k):
        self.page.keyboard.press(k)

    def scroll(self, x, y, dx, dy):
        self.page.mouse.move(x, y)
        self.page.mouse.wheel(dx, dy)

    def close(self):
        self.browser.close()
        self.playwright.stop()

Step 2: The computer-use loop

Anthropic’s tool schema is versioned. We pin computer_20241022 and pass the current screenshot as an image block in the user turn. The model returns a tool_use block; we execute it and acknowledge with a tool_result.

from anthropic import Anthropic

client = Anthropic()

TOOL = {
    "type": "computer_20241022",
    "name": "computer",
    "display_width_px": 1024,
    "display_height_px": 768,
}

SYSTEM = """You control a browser at 1024x768.
Complete the user's shopping task on the current site only.
When the cart is populated and you reach the checkout overview page, emit action 'finish'.
Never use real payment data; if a card field appears, type 4242424242424242.
"""

def run_agent(env: BrowserEnv, task: str, max_steps=25):
    messages = [{"role": "user", "content": task}]
    for step in range(max_steps):
        shot = env.screenshot()
        messages.append({
            "role": "user",
            "content": [{
                "type": "image",
                "source": {"type": "base64", "media_type": "image/png", "data": shot}
            }]
        })
        resp = client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1024,
            system=SYSTEM,
            tools=[TOOL],
            messages=messages,
        )
        tool_use = next(b for b in resp.content if b.type == "tool_use")
        action = tool_use.input
        print(f"step {step}: {action.get('action')} {action.get('coordinate', '')}")
        if action["action"] == "finish":
            break
        execute(env, action)
        messages.append({"role": "assistant", "content": resp.content})
        messages.append({
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": "executed"
            }]
        })

Expected checkpoint output after env.goto and first loop iteration:

step 0: screenshot
step 1: type
step 2: click
step 3: screenshot

The model typically requests a screenshot first to orient, then starts interacting.

Step 3: Action execution

The API emits coordinates as [x, y] in CSS pixels relative to the viewport. Map them directly to Playwright’s mouse.

def execute(env: BrowserEnv, action: dict):
    a = action["action"]
    if a == "click":
        env.click(*action["coordinate"])
    elif a == "double_click":
        env.page.mouse.dblclick(*action["coordinate"])
    elif a == "type":
        env.type(action["text"])
    elif a == "key":
        env.key(action["key"])
    elif a == "scroll":
        env.scroll(action["coordinate"][0], action["coordinate"][1],
                   action["delta"][0], action["delta"][1])
    elif a == "wait":
        env.page.wait_for_timeout(800)

A sample tool_use payload looks like this:

{
  "type": "tool_use",
  "id": "cu_01ABC",
  "name": "computer",
  "input": {
    "action": "click",
    "coordinate": [488, 322]
  }
}

If you route through a gateway such as n4n.ai, the same OpenAI-compatible chat endpoint forwards provider cache-control hints and falls back automatically when Anthropic is degraded, but the tool schema and execution code stay identical.

Step 4: Prompting for shopping tasks

Vague instructions produce wandering agents. Embed credentials and a clear stop condition in the task string.

TASK = (
    "Log in with username 'standard_user' and password 'secret_sauce'. "
    "Add the 'Sauce Labs Backpack' to the cart. "
    "Open the cart, proceed to checkout, fill first name 'Test', last name 'User', "
    "zip '12345', continue, then finish at the overview page."
)

The system prompt already forbids real card entry. We rely on the demo site’s lack of a real payment step.

Step 5: End-to-end run

if __name__ == "__main__":
    env = BrowserEnv()
    env.goto("https://www.saucedemo.com")
    try:
        run_agent(env, TASK, max_steps=30)
    finally:
        env.close()

Successful run prints a trajectory ending in finish:

step 0: screenshot
step 1: type
step 2: click
step 3: screenshot
step 4: click
step 5: type
step 6: click
step 7: screenshot
step 8: click
step 9: finish

At that point the browser is at the checkout overview URL. No network call to a payment processor occurred.

Hardening the agent

Computer-use APIs are sensitive to layout drift. Concrete fixes we ship:

  • Disable CSS transitions on load (done in BrowserEnv).
  • Cap steps and restart from the cart URL if no finish within budget.
  • Run inside a container with no mounted secrets; the agent only sees pixels.
  • Log every (action, coordinate) tuple to replay failures offline.
import json, time
def logged_execute(env, action):
    with open("trace.jsonl", "a") as f:
        f.write(json.dumps({"t": time.time(), "a": action}) + "\n")
    execute(env, action)

Swap execute for logged_execute in the loop to get a forensic trace.

Safety boundaries

A computer use shopping agent can click anything rendered. Sandbox it strictly: no real auth cookies, no saved cards, network egress limited to the target domain. Use test card numbers only. If the model ever types outside a known input region, abort the loop.

The pattern here is model-agnostic. If you later swap Claude for another vision model that exposes a compatible tool interface, the BrowserEnv and execute layers require zero changes.

Where to take it next

You now have a minimal but real computer use shopping agent: a Playwright harness, a versioned computer-tool loop, and a constrained prompt. Production extensions include a DOM-diff fallback when the model stalls, a confirmation gate before finish, and per-token usage metering if you front the calls with an inference gateway. Build the trace log first—debugging pixel agents without it is guessing.

Tagscomputer-useshopping-agenttutorialbrowser-agent

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All computer-use & browser agents posts →