DEV Community

SoftwareDevs mvpfactory.io
SoftwareDevs mvpfactory.io

Posted on • Originally published at mvpfactory.io

Building Reliable Computer-Use Agents: Architecture That Survives 3 AM

What We Will Build

By the end of this tutorial, you will have a production-ready architecture for computer-use agents that handles the failures demos never show you. We will build four concrete patterns: a visual state verification loop, a layered retry orchestrator with deterministic fallbacks, cost guardrails that prevent budget blowouts, and idempotent task design that survives mid-run crashes.

Let me show you a pattern I use in every project that runs unattended automation overnight.

Prerequisites

  • Familiarity with Python asyncio
  • Basic understanding of LLM vision APIs (Claude, GPT-4V, or similar)
  • Experience with any browser or desktop automation tool (Playwright, Selenium, pyautogui)
  • A healthy fear of silent failures at 3 AM

Step 1: Visual State Verification Layer

Here is the gotcha that will save you hours: never trust a single screenshot. The model usually knows what to do — it just cannot confirm where it actually is. Build a verification loop that classifies screen states before and after every action.

async def verified_action(agent, action, expected_state, max_attempts=3):
    for attempt in range(max_attempts):
        screenshot = await agent.capture_screen()
        current_state = await agent.classify_state(screenshot)

        if current_state != expected_state.precondition:
            await agent.recover_to_state(expected_state.precondition)
            continue

        await agent.execute(action)
        post_screenshot = await agent.capture_screen()
        post_state = await agent.classify_state(post_screenshot)

        if post_state == expected_state.postcondition:
            return Success(post_state)

    return Failure(current_state, expected_state)
Enter fullscreen mode Exit fullscreen mode

The key insight: classify states, not elements. Instead of asking "is the submit button visible?", ask "are we on the confirmation page?" State classification is far more resilient to the layout shifts and async rendering that cause roughly 60-70% of production failures.

Step 2: Layered Retry with Deterministic Fallbacks

Here is what the docs do not mention, but experience teaches fast: retrying the same LLM approach five times is just expensive failure. You need three distinct layers.

class RetryOrchestrator:
    async def execute_with_fallback(self, task):
        # L1: LLM visual reasoning (~85% of runs resolve here)
        result = await self.llm_agent.attempt(task, retries=2)
        if result.success:
            return result

        # L2: Deterministic automation via DOM/a11y tree (~12% caught)
        if task.has_scripted_path:
            result = await self.scripted_agent.attempt(task)
            if result.success:
                return result

        # L3: Human escalation queue (~3% reach this)
        return await self.escalation.queue(task, context=result.debug_info)
Enter fullscreen mode Exit fullscreen mode

Without L2, your human escalation rate jumps from 3% to around 15%. Script deterministic paths for your most common workflows and let the LLM handle the edge cases it is actually good at.

Step 3: Cost Guardrails

A confused agent in a retry loop can fire dozens of vision API calls per minute. Here is the minimal setup to get this working — a decorator that enforces hard circuit breakers:

@cost_guardrail(max_cost_usd=0.50, max_actions=25, timeout_seconds=180)
async def fill_invoice_form(agent, invoice_data):
    # Your agent logic here
    # The decorator kills execution if any limit is breached
    ...
Enter fullscreen mode Exit fullscreen mode

Enforce four limits: per-task budget cap, action count ceiling, hard timeout, and a sliding-window rate limit over 5-minute windows. Treat these with the same rigor you treat rate limits on your own APIs.

Step 4: Idempotent Task Design

Design every task to be safely re-runnable. Check whether the task already completed before starting. Tag submissions with idempotency tokens. Log every action with timestamps so recovery knows exactly where to resume.

This is not optional. Tasks will be interrupted and retried. If your agent submits the same form twice, no amount of LLM intelligence fixes that data integrity problem.

Gotchas

  • Async rendering traps: Screenshots captured before the page finishes loading cause the majority of flaky failures. Always add a state-readiness check before capturing.
  • Modal and popup hijacks: Unexpected dialogs break agent context instantly. Build a global modal-dismissal handler that runs before every action.
  • Auth expiry mid-task: Sessions die silently. Your state verification layer should detect login screens and trigger re-authentication, not retry the failed action.
  • Budget burns are silent by default: Without explicit cost guardrails, a single stuck pipeline can burn through hundreds of dollars overnight. I have seen it happen — the fix took five minutes, the budget did not come back.

Wrapping Up

Build the state verification layer first — it eliminates the largest category of failures. Add layered fallbacks instead of deeper retries. Set hard cost guardrails before your first production deployment.

The competitive advantage in computer-use agents is no longer at the model layer. It is in the reliability engineering that wraps them. Build the boring infrastructure first. Your 3 AM self will thank you.

Top comments (0)