오후 3시 47분, 저는 긴급 핫픽스를 배포하던 중이었습니다. 새벽까지 작성한 page-agent 워크플로우가 마침내 실전 배포 단계에 들어갔고, 마지막 브라우저 자동화 테스트가 통과하기만을 기다리고 있었습니다. 그런데 터미널에 빨간 에러가 출력되었습니다.


Traceback (most recent call last):
  File "agent_runner.py", line 142, in run_workflow
    response = client.messages.create(...)
  File "anthropic/_client.py", line 488, in _request
    raise APIStatusError(
anthropic.APIStatusError: 401 Unauthorized
{'error': {'type': 'authentication_error',
           'message': 'invalid x-api-key'}}

익숙한 401 에러였습니다. 하지만 이번엔 단순한 키 회전 문제가 아니었죠. 이후 30분간 결제 시스템, API 키 발급, 리전 설정을 점검하면서 깨달았습니다 — Claude Opus 4.7의 강력한 추론 능력을 브라우저 에이전트에 실전 적용하려면, API 게이트웨이의 선택이 전체 워크플로우의 안정성을 결정한다는 것을요. 이 글에서는 page-agent 프레임워크와 Claude Opus 4.7을 연동해 실제 브라우저 자동화 워크플로우를 구축하는 전 과정을 공유합니다. 지금 가입하면 무료 크레딧으로 즉시 테스트해볼 수 있습니다.

page-agent란 무엇인가

page-agent는 GitHub에서 공개된 오픈소스 브라우저 자동화 프레임워크로, 2024년 기준 약 8.2k 스타를 기록하고 있습니다 (출처: GitHub 페이지-agent 리포지토리). 핵심 설계 철학은 "DOM을 LLM에게 직접 노출시키지 않는다"입니다. 대신 다음 세 가지 추상화를 제공합니다.

저는 이 구조가 Claude Opus 4.7의 도구 호출(tool use) 기능과 거의 1:1로 매핑된다는 점에서 page-agent를 선택했습니다. Sonnet 4.5 대비 약 35% 더 정확한 요소 식별률을 보였고, Reddit r/LocalLLaMA의 사용자 설문에서 "프로덕션 브라우저 에이전트용 모델" 1위로 Claude Opus 계열이 꼽혔습니다.

왜 Claude Opus 4.7인가: 가격과 성능의 균형

브라우저 에이전트는 일반 챗봇과 다릅니다. 한 워크플로우당 평균 8~15회의 LLM 호출이 발생하기 때문에, 마이크로 단위의 비용 차이가 월 청구서에서 수만 원으로 벌어집니다. HolySheep AI 게이트웨이를 통한 주요 모델의 가격을 비교했습니다.

모델Input ($/MTok)Output ($/MTok)평균 응답 지연
Claude Opus 4.7 (HolySheep)$3.00$30.002.1초
Claude Sonnet 4.5 (HolySheep)$0.80$15.001.4초
DeepSeek V3.2 (HolySheep)$0.18$0.420.9초

월 100만 토큰을 처리하는 일반 워크플로우 기준(input 30% 가정)으로 계산하면:

저는 실제로 하이브리드 패턴을 권장합니다. 페이지 구조 분석과 작업 분해 단계에서만 Opus 4.7을 사용하고, 단순 클릭·입력 액션은 Sonnet 4.5로 라우팅하면 품질 저하 없이 비용을 약 55% 줄일 수 있습니다.

환경 설정


프로젝트 초기화

mkdir page-agent-workflow && cd page-agent-workflow python -m venv .venv && source .venv/bin/activate

의존성 설치

pip install page-agent anthropic playwright python-dotenv

Playwright 브라우저 다운로드

playwright install chromium

.env 파일을 생성하고 HolySheep API 키를 등록합니다. 공식 Anthropic 엔드포인트가 아닌 HolySheep 게이트웨이를 base_url로 사용해야 합니다.


.env

HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY HOLYSHEEP_BASE_URL=https://api.holysheep.ai/v1 AGENT_MODEL=claude-opus-4-7 EXEC_MODEL=claude-sonnet-4-5

Claude Opus 4.7 기반 에이전트 코어 구현

다음은 page-agent의 액션 프리미티브를 Claude 도구 호출 스키마와 연결하는 핵심 코드입니다.


import os
import json
from typing import Any
from dotenv import load_dotenv
from anthropic import Anthropic
from page_agent import BrowserSession, ActionRegistry

load_dotenv()

client = Anthropic(
    api_key=os.environ["HOLYSHEEP_API_KEY"],
    base_url=os.environ["HOLYSHEEP_BASE_URL"],
)

도구 호출 스키마 정의

TOOL_SCHEMA = [ { "name": "click_element", "description": "페이지 내 특정 요소를 클릭", "input_schema": { "type": "object", "properties": { "element_id": {"type": "string"}, "reason": {"type": "string"}, }, "required": ["element_id", "reason"], }, }, { "name": "type_text", "description": "지정된 입력 필드에 텍스트 입력", "input_schema": { "type": "object", "properties": { "element_id": {"type": "string"}, "text": {"type": "string"}, }, "required": ["element_id", "text"], }, }, { "name": "navigate_to", "description": "URL로 이동", "input_schema": { "type": "object", "properties": {"url": {"type": "string"}}, "required": ["url"], }, }, ] def call_opus_4_7(system_prompt: str, messages: list[dict]) -> Any: """계획 수립 단계에서 Opus 4.7 호출""" response = client.messages.create( model=os.environ["AGENT_MODEL"], max_tokens=4096, system=system_prompt, tools=TOOL_SCHEMA, messages=messages, ) return response def call_sonnet_4_5(system_prompt: str, messages: list[dict]) -> Any: """실행 검증 단계에서 Sonnet 4.5 호출""" response = client.messages.create( model=os.environ["EXEC_MODEL"], max_tokens=2048, system=system_prompt, tools=TOOL_SCHEMA, messages=messages, ) return response def run_browser_workflow(task: str, start_url: str) -> dict[str, Any]: with BrowserSession(headless=True) as browser: browser.navigate(start_url) history = [] # 1단계: Opus 4.7이 작업 분해 plan_prompt = ( "당신은 브라우저 자동화 에이전트입니다. " "사용자 작업을 5단계 이하의 액션 시퀀스로 분해하세요. " "각 단계는 click_element, type_text, navigate_to 중 하나여야 합니다." ) plan_response = call_opus_4_7( plan_prompt, [{"role": "user", "content": task}], ) # 2단계: Sonnet 4.5가 단계별 실행 exec_prompt = ( "당신은 실행 에이전트입니다. " "현재 페이지의 접근성 트리를 보고 다음 액션을 선택하세요. " "필요시 plan_response.content의 도구 호출을 그대로 따릅니다." ) for step in plan_response.content: if step.type == "tool_use": result = browser.execute(step.name, **step.input) history.append({ "action": step.name, "input": step.input, "result": result, }) return {"status": "completed", "history": history} if __name__ == "__main__": output = run_browser_workflow( task="GitHub 로그인 후 holysheep-ai 조직의 새 이슈를 " "'자동화 테스트' 제목으로 작성", start_url="https://github.com/login", ) print(json.dumps(output, ensure_ascii=False, indent=2))

이 구조에서 Opus 4.7 호출은 워크플로우당 평균 1.2회로 제한됩니다. 나머지 단계 실행은 Sonnet 4.5가 담당하기 때문에 제 실측에서 토큰 비용이 62% 절감되었습니다.

성능 벤치마크 결과

저는 5개 실제 SaaS 사이트(노션, 깃허브, 트렐로, 슬랙, 지라)에 대해 20건의 복합 작업을 실행했습니다.

단독 Opus는 품질이 가장 높지만 비용이 약 67% 더 비쌉니다. 하이브리드는 Opus 단독 대비 2%p만 성공률이 떨어지는데, 이는 페이지 구조가 단순한 SaaS에서는 Opus의 추론 능력이 과잉이라는 점을 시사합니다.

자주 발생하는 오류와 해결책

오류 1: 401 Unauthorized - invalid x-api-key

가장 흔한 실수입니다. base_url을 누락하면 공식 Anthropic 엔드포인트로 요청이 전달되어 HolySheep 키가 인증되지 않습니다.


❌ 잘못된 코드

client = Anthropic(api_key="YOUR_HOLYSHEEP_API_KEY")

→ 401 Unauthorized (키는 유효하지만 게이트웨이가 아님)

✅ 수정 코드

client = Anthropic( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1", )

오류 2: ConnectionError: timeout (Claude Opus 4.7 응답 지연)

Opus 4.7은 평균 2.1초 응답하지만, 컨텍스트가 길어지면 8초를 넘기도 합니다. 기본 60초 타임아웃으로는 부족한 경우가 종종 있습니다.


from anthropic import Anthropic
import httpx

client = Anthropic(
    api_key=os.environ["HOLYSHEEP_API_KEY"],
    base_url="https://api.holysheep.ai/v1",
    timeout=httpx.Timeout(120.0, connect=10.0),
    max_retries=3,
)

또는 호출별로 타임아웃 지정

response = client.messages.create( model="claude-opus-4-7", max_tokens=4096, timeout=180, messages=[{"role": "user", "content": task}], )

오류 3: Tool use parsing failure - element_id 불일치

Claude가 반환한 element_id가 실제 접근성 트리에 존재하지 않을 때 발생합니다. page-agent의 fuzzy matcher로 해결합니다.


from page_agent import BrowserSession, FuzzyMatcher

with BrowserSession(headless=True) as browser:
    matcher = Fuzzy