신뢰할 수 있는 컴퓨터 사용 에이전트 구축: 새벽 3시에도 견디는 아키텍처
Source: Dev.to
죄송하지만, 저는 외부 웹사이트의 내용을 직접 가져올 수 없습니다. 번역이 필요한 텍스트를 여기 채팅에 복사해 주시면, 해당 부분을 한국어로 번역해 드리겠습니다.
우리가 만들게 될 것
- Visual state verification loop – 매 행동 전후에 화면 상태를 분류합니다.
- Layered retry orchestrator – 인간에게 에스컬레이션하기 전에 결정론적 대체 방안을 적용합니다.
- Cost guardrails – 하드 제한을 통해 예산 초과를 방지합니다.
- 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
...
강제 적용할 네 가지 제한:
- 작업당 예산 상한
- 액션 수 상한
- 강제 타임아웃
- 슬라이딩 윈도우 속도 제한
외부 소비자에게 제공하는 API 속도 제한과 동일한 엄격함으로 이들을 다루세요.
멱등 작업 설계
모든 작업을 안전하게 재‑실행 가능하도록 설계하십시오:
- 사전 검사 작업이 이미 완료되었는지 시작하기 전에 확인합니다.
- 제출물에 멱등성 토큰을 태그합니다.
- 모든 작업을 타임스탬프와 함께 기록하여 복구 시 정확히 어디서 재개해야 하는지 알 수 있게 합니다.
에이전트가 동일한 양식을 두 번 제출하면, 아무리 많은 LLM 지능을 사용해도 데이터 무결성 문제를 해결할 수 없습니다.
일반적인 실패 함정 및 완화 방안
| 실패 유형 | 일반적인 증상 | 완화 방안 |
|---|---|---|
| Async rendering | 페이지 로드 전에 스크린샷이 찍혀 → 불안정한 실패 발생 | 캡처하기 전에 상태 준비 여부를 확인하는 체크를 추가합니다. |
| Modal / popup hijacks | 예상치 못한 대화창이 즉시 컨텍스트를 깨뜨림 | 모든 동작 전에 실행되는 전역 모달 해제 핸들러를 사용합니다. |
| Auth expiry mid‑task | 세션이 조용히 종료되어 반복 재시도가 발생 | 검증 레이어에서 로그인 화면을 감지하고 재인증을 트리거합니다. |
| Budget burns | 파이프라인이 멈춰 밤새 수백 달러를 소모 | 비용 가드레일을 적용하고 한도를 지속적으로 모니터링합니다. |
결론
먼저 state verification layer를 구축하세요—이는 가장 큰 범주의 실패를 제거합니다. 그 다음에는 더 깊은 재시도 대신 layered fallbacks를 추가하고, 마지막으로 모든 프로덕션 배포 전에 hard cost guardrails를 설정하세요.
컴퓨터‑사용 에이전트에서의 경쟁 우위는 더 이상 모델 자체가 아니라, 그것을 감싸는 신뢰성 엔지니어링입니다. 지루한 인프라를 먼저 구축하면, 새벽 3시의 당신이 고마워할 것입니다.