홈으로 돌아가기

OpenClaw용 Claude API 프록시: 코드와 도구 사용

Anthropic 차단 후 openclaw에서 Claude 액세스 복원하는 프록시 서버. curl_cffi로 브라우저 에뮬레이션, XML 프롬프트와 JSON 자동 수정으로 도구 사용 지원. 개발자를 위한 전체 코드, 설정 및 통합.

OpenClaw에서 Claude 복원: 도구 사용 프록시
Advertisement 728x90

# 웹 세션으로 Claude API 프록시: OpenClaw의 도구 사용 통합

Anthropic이 OpenClaw 같은 타사 앱에서 Claude 사용을 차단했습니다. Pro 구독자($100/월)는 $100 상당의 추가 사용 크레딧을 받았지만, 에이전트 통합 문제는 해결되지 않았죠. 해결책? Claude.ai 브라우저 세션을 모방하는 프록시 서버로 OpenAI 호환 API를 제공합니다. 이를 통해 OpenClaw에서 Claude Sonnet/Opus를 완전한 도구 사용 지원과 함께 실행할 수 있습니다.

Python으로 구축되었으며, Chrome TLS 지문을 위해 curl_cffi를 사용합니다. 쿠키와 디바이스 ID는 활성 세션에서 가져옵니다. 각 요청마다 새로운 대화를 시작하며, SSE를 통해 응답을 스트리밍합니다.

기본 세션 및 헤더 설정

세션은 실제 브라우저 쿠키로 Chrome을 완벽히 모방합니다. Camoufox 같은 반탐지 브라우저로 테스트 완료.

Google AdInline article slot
def make_session() -> cf_requests.Session:
    """Chrome TLS 지문으로 새로운 curl_cffi 세션 생성."""
    cookies = get_cookies()
    session = cf_requests.Session(impersonate=IMPERSONATE)
    session.cookies.update(cookies)
    return session

def get_headers(cookies: dict) -> dict:
    return {
        "User-Agent": (
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
            "(KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
        ),
        "Origin": "https://claude.ai",
        "Referer": "https://claude.ai/new",
        "anthropic-device-id": cookies.get("anthropic-device-id", ""),
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-origin",
    }

완성 요청 로직

  • /api/organizations/{org_id}/chat_conversations에 POST로 대화 생성.
  • text/event-stream으로 /chat_conversations/{conv_id}/completion에 POST.
def claude_complete(
    prompt: str,
    internal_model: str,
) -> Generator[str, None, None]:
    """claude.ai 완성에서 SSE 라인을 생성하는 동기 제너레이터."""
    cookies = get_cookies()
    org_id = cookies["lastActiveOrg"]
    headers = get_headers(cookies)

    session = make_session()

    # 1. 대화 생성
    r = session.post(
        f"https://claude.ai/api/organizations/{org_id}/chat_conversations",
        json={"uuid": None, "name": ""},
        headers=headers,
        timeout=15,
    )
    r.raise_for_status()
    conv_id = r.json()["uuid"]
    log.info(f"Conv 생성됨: {conv_id} model={internal_model}")

    # 2. 완성 스트리밍
    payload = {
        "prompt": prompt,
        "model": internal_model,
        "timezone": "Europe/Moscow",
        "attachments": [],
        "files": [],
    }
    r2 = session.post(
        f"https://claude.ai/api/organizations/{org_id}/chat_conversations/{conv_id}/completion",
        json=payload,
        headers={**headers, "Accept": "text/event-stream"},
        stream=True,
        timeout=60,
    )
    r2.raise_for_status()
    for line in r2.iter_lines():
        yield line

API 서버는 /v1/models, /v1/chat/completions, /health 경로를 가진 aiohttp로 실행됩니다. 127.0.0.1:8787에서 동작.

def make_app() -> web.Application:
    app = web.Application()
    app.router.add_get("/v1/models", handle_models)
    app.router.add_get("/health", handle_health)
    app.router.add_post("/v1/chat/completions", handle_chat_completions)
    return app

OpenClaw 통합

프로바이더 설정:

"claude-web": {
    "baseUrl": "http://127.0.0.1:8787/v1",
    "apiKey": "dummy",
    "api": "openai-completions",
    "models": [
      {
        "id": "claude-sonnet-4-6-web",
        "name": "Claude Sonnet 4.6 (웹)",
        "reasoning": false,
        "input": ["text"],
        "cost": {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
        "contextWindow": 200000,
        "maxTokens": 32000
      }
    ]
}

텍스트 생성은 즉시 작동합니다. 도구 사용은 약간의 수정이 필요합니다.

Google AdInline article slot

도구 사용 지원: 프롬프트 주입

시스템 프롬프트로 Claude가 XML 형식으로 도구 호출을 출력하도록 강제:

TOOL_SYSTEM_PROMPT = """도구에 접근할 수 있습니다. 도구를 호출할 때는 도구 호출 블록만 출력하세요 — 설명 없음, 앞뒤 텍스트 없음:

<tool_call>
{{
"name": "tool_name_here", "arguments": {{"param1": "value1", "param2": "value2"}}
}}
</tool_call>

규칙:
- <tool_call> 안 JSON은 유효해야 함 — 후행 쉼표, 주석 금지
- 여러 <tool_call> 블록을 순차 출력 가능
- <tool_result> 블록 수신 후 정상 응답 계속
- 도구 호출 안 할 때는 <tool_call> 태그 없이 정상 응답

사용 가능 도구:
{tools_json}"""

파싱은 JSON 오류(후행 쉼표, 주석)를 자동 수정하는 정규식을 사용합니다.

최종 도구 호출 및 결과 처리

  • 파서가 <tool_call> 블록 추출, JSON 수정, OpenAI 형식 변환.
  • 도구 결과는 사용자 메시지에 XML <tool_result id="...">내용</tool_result>로 주입.
def parse_tool_calls(text: str) -> tuple[list[dict], str]:
    tool_calls = []
    for match in TOOL_CALL_RE.finditer(text):
        raw_json = match.group(1).strip()
        obj = None
        try:
            obj = json.loads(raw_json)
        except json.JSONDecodeError:
            fixed = _try_fix_json(raw_json)
            obj = json.loads(fixed)
        if obj:
            tool_calls.append({
                "id": f"call_{uuid.uuid4().hex[:12]}",
                "type": "function",
                "function": {
                    "name": obj.get("name", ""),
                    "arguments": json.dumps(obj.get("arguments", {})),
                },
            })
    remaining = TOOL_CALL_RE.sub("", text).strip()
    return tool_calls, remaining

def inject_tool_results(messages: list[dict], tools: list[dict]) -> list[dict]:
    # OpenAI tool_calls/results를 Claude용 XML로 변환
    # ... (배치 및 XML 생성 로직)

주요 기능

  • 완벽 호환성: OpenClaw/Claude Sonnet 4.6(20만 컨텍스트)용 OpenAI API + 도구 사용.
  • 스텔스 모드: curl_cffi + 실제 쿠키/디바이스 ID, Chrome TLS.
  • 자동 수정: Claude의 흔한 JSON 오류(후행 쉼표, 주석) 처리.
  • 스트리밍: 웹 API SSE, 요청당 신규 대화.
  • 제한 준수: Pro 구독 범위 내, 추가 사용 없음.

제한사항 및 개선

Claude.ai 웹 API 안정성에 의존. 잠재적 문제:

Google AdInline article slot
  • 사이트 캡차.
  • 세션 타임아웃.
  • 엔드포인트 변경.

대처: 자동 쿠키 갱신, 디바이스 ID 로테이션, 오류 처리.

— Editorial Team

Advertisement 728x90

다음 읽기