신뢰할 수 있는 컴퓨터 사용 에이전트 구축: 새벽 3시에도 견디는 아키텍처

발행: (2026년 3월 8일 PM 08:17 GMT+9)
8 분 소요
원문: Dev.to

Source: Dev.to

죄송하지만, 저는 외부 웹사이트의 내용을 직접 가져올 수 없습니다. 번역이 필요한 텍스트를 여기 채팅에 복사해 주시면, 해당 부분을 한국어로 번역해 드리겠습니다.

우리가 만들게 될 것

  1. Visual state verification loop – 매 행동 전후에 화면 상태를 분류합니다.
  2. Layered retry orchestrator – 인간에게 에스컬레이션하기 전에 결정론적 대체 방안을 적용합니다.
  3. Cost guardrails – 하드 제한을 통해 예산 초과를 방지합니다.
  4. Idempotent task design – 중간에 충돌이 발생해도 중복된 부작용 없이 작업을 지속합니다.

전제 조건

  • Python asyncio에 대한 친숙함
  • LLM 비전 API(Claude, GPT‑4V 등)에 대한 기본 이해
  • 브라우저 또는 데스크톱 자동화 도구(Playwright, Selenium, pyautogui) 사용 경험
  • 새벽 3시의 조용한 실패에 대한 건강한 두려움

시각적 상태 검증 루프

Gotcha: 단일 스크린샷을 절대 신뢰하지 마세요. 모델은 종종 무엇을 해야 할지 알고 있지만, 실제로 어디에 있는지는 확인할 수 없습니다.

핵심 인사이트는 개별 요소가 아니라 상태를 분류하는 것입니다. “제출 버튼이 보이나?” 라고 묻는 대신 “우리는 확인 페이지에 있나요?” 라고 물어보세요. 상태 분류는 레이아웃 변화와 비동기 렌더링에 훨씬 더 강인합니다. 이러한 요인들이 실제 운영 환경에서 발생하는 대부분의 실패를 일으키기 때문입니다.

async def verified_action(agent, action, expected_state, max_attempts: int = 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)

왜 상태 분류인가?

  • 레이아웃 변경 및 비동기 렌더링을 처리합니다.
  • 불안정한 실패율을 60‑70 %에서 관리 가능한 수준으로 낮춥니다.

계층형 재시도 오케스트레이터

동일한 LLM 접근 방식을 다섯 번 재시도하는 것은 비용이 많이 들고 효과가 떨어지는 경우가 많습니다. 세 가지 별도 레이어를 사용하세요:

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)
  • L2 없이 하면 인간 에스컬레이션 비율이 ~3 %에서 ~15 %로 급증합니다.
  • 일반적인 워크플로에 대해 결정론적 경로를 스크립트화하고, 에지 케이스는 LLM이 처리하도록 하세요.

비용 가드레일

혼란스러운 에이전트는 1분에 수십 개의 Vision API 호출을 발생시킬 수 있습니다. 데코레이터를 사용해 강제 제한을 적용하세요:

@cost_guardrail(
    max_cost_usd=0.50,
    max_actions=25,
    timeout_seconds=180,
    rate_limit_window_seconds=300,   # 5‑minute sliding window
    max_calls_per_window=50
)
async def fill_invoice_form(agent, invoice_data):
    # Your agent logic here
    # The decorator aborts execution if any limit is breached
    ...

강제 적용할 네 가지 제한:

  1. 작업당 예산 상한
  2. 액션 수 상한
  3. 강제 타임아웃
  4. 슬라이딩 윈도우 속도 제한

외부 소비자에게 제공하는 API 속도 제한과 동일한 엄격함으로 이들을 다루세요.

멱등 작업 설계

모든 작업을 안전하게 재‑실행 가능하도록 설계하십시오:

  • 사전 검사 작업이 이미 완료되었는지 시작하기 전에 확인합니다.
  • 제출물에 멱등성 토큰을 태그합니다.
  • 모든 작업을 타임스탬프와 함께 기록하여 복구 시 정확히 어디서 재개해야 하는지 알 수 있게 합니다.

에이전트가 동일한 양식을 두 번 제출하면, 아무리 많은 LLM 지능을 사용해도 데이터 무결성 문제를 해결할 수 없습니다.

일반적인 실패 함정 및 완화 방안

실패 유형일반적인 증상완화 방안
Async rendering페이지 로드 전에 스크린샷이 찍혀 → 불안정한 실패 발생캡처하기 전에 상태 준비 여부를 확인하는 체크를 추가합니다.
Modal / popup hijacks예상치 못한 대화창이 즉시 컨텍스트를 깨뜨림모든 동작 전에 실행되는 전역 모달 해제 핸들러를 사용합니다.
Auth expiry mid‑task세션이 조용히 종료되어 반복 재시도가 발생검증 레이어에서 로그인 화면을 감지하고 재인증을 트리거합니다.
Budget burns파이프라인이 멈춰 밤새 수백 달러를 소모비용 가드레일을 적용하고 한도를 지속적으로 모니터링합니다.

결론

먼저 state verification layer를 구축하세요—이는 가장 큰 범주의 실패를 제거합니다. 그 다음에는 더 깊은 재시도 대신 layered fallbacks를 추가하고, 마지막으로 모든 프로덕션 배포 전에 hard cost guardrails를 설정하세요.

컴퓨터‑사용 에이전트에서의 경쟁 우위는 더 이상 모델 자체가 아니라, 그것을 감싸는 신뢰성 엔지니어링입니다. 지루한 인프라를 먼저 구축하면, 새벽 3시의 당신이 고마워할 것입니다.

0 조회
Back to Blog

관련 글

더 보기 »

당신의 에이전트는 작고 저위험인 HAL

개요 나는 code를 검토하고, architecture를 설계하며, faults를 찾고, designs를 비평하는 멀티‑에이전트 시스템과 작업한다. 이러한 시스템은 조용하고 … 방식으로 실패한다.