에이전트 프레임워크를 사용하면 모델에 웹 도구를 쉽게 붙일 수 있습니다. 하지만 웹에 쉽게 접근할 수 있게 되는 것은 아닙니다. LangChain, CrewAI, AutoGen 에이전트가 어느 정도 규모로 페이지를 가져오기 시작하면 곧 403 오류, CAPTCHA 페이지, 다른 국가용 콘텐츠가 쌓이기 시작하고, 모델은 이 모든 것을 실제 정보인 양 태연하게 요약합니다. 에이전트의 웹 도구를 AI 에이전트용으로 설계된 프록시로 연결하면 접근 문제는 해결되지만, 올바른 위치에 연결했을 때만 그렇습니다.
이 가이드는 그 연결 방법을 다룹니다. 각 프레임워크의 웹 도구를 Residential Proxy로 보내는 방법, 모델 API는 프록시를 거치지 않게 하는 방법, 그리고 작업별로 스티키 세션과 로테이션 세션을 선택하는 방법을 보여 줍니다. 아직 프록시 유형을 고르는 중이라면 먼저 웹을 탐색하는 AI 에이전트에 가장 적합한 프록시 비교 글을 참고하세요. 이 글은 이미 유형을 선택했다고 가정합니다.
첫 번째 규칙: 프로세스가 아니라 도구에 프록시를 설정합니다
프록시를 추가하는 가장 빠른 방법은 HTTPS_PROXY를 export하고 재시작하는 것입니다. 에이전트 프로세스에서는 그렇게 하지 마세요. OpenAI, Anthropic을 비롯한 대부분의 모델 SDK는 같은 환경 변수를 읽기 때문에 모든 모델 호출도 레지덴셜 네트워크를 거치게 됩니다. 그러면 추론 단계마다 지연 시간이 늘고, 토큰을 위해 프록시 트래픽을 소모하면서도 얻는 것은 없습니다. 모델 API는 IP로 차단하지 않기 때문입니다.
각 도구가 사용하는 HTTP 클라이언트나 브라우저에 프록시를 명시적으로 설정하세요. 나머지는 모두 일반 연결을 그대로 사용합니다. 라이브러리가 환경 변수만 읽는다면 모델 호스트를 NO_PROXY에 추가하세요.
프록시 URL
Shifter 게이트웨이에서는 타기팅과 세션을 사용자 이름에 지정합니다. 하나의 엔드포인트 p.shifter.io:443로 모든 국가를 처리합니다.
import os, uuid
USER = os.environ["SHIFTER_USER"]
PASSWORD = os.environ["SHIFTER_PASS"]
def proxy_url(country: str = "us", session: str | None = None, ttl: int = 600) -> str:
"""Rotating by default; pass a session id to keep one exit IP for a task."""
username = f"{USER}-country-{country}"
if session:
username += f"-sid-{session}-ttl-{ttl}"
return f"http://{username}:{PASSWORD}@p.shifter.io:443"
def new_session() -> str:
return uuid.uuid4().hex[:12]
아래의 모든 예제에서 이 헬퍼를 재사용합니다.
LangChain
대부분의 LangChain 에이전트는 두 가지 방식 중 하나로 웹을 읽습니다. 검색용 문서 로더, 또는 클릭과 입력을 하는 에이전트를 위한 브라우저 툴킷입니다.
문서 로더. WebBaseLoader는 proxies 딕셔너리를 받아 가져오는 모든 페이지에 사용합니다.
from langchain_community.document_loaders import WebBaseLoader
proxy = proxy_url(country="de")
loader = WebBaseLoader(
["https://shop.example/de/product/123"],
proxies={"http": proxy, "https": proxy},
)
docs = loader.load()
로더에는 로테이션이 적합합니다. 각 페이지가 독립적이므로 요청마다 새 IP를 쓰면 부하가 분산됩니다.
브라우저 툴킷. Playwright 툴킷은 실제 브라우저를 구동합니다. 프록시를 설정해 브라우저를 직접 실행한 뒤 툴킷에 넘겨 주세요.
from langchain_community.agent_toolkits import PlayWrightBrowserToolkit
from playwright.sync_api import sync_playwright
session = new_session()
playwright = sync_playwright().start()
browser = playwright.chromium.launch(proxy={
"server": "http://p.shifter.io:443",
"username": f"{USER}-country-us-sid-{session}-ttl-1800",
"password": PASSWORD,
})
tools = PlayWrightBrowserToolkit.from_browser(sync_browser=browser).get_tools()
여기서는 세션 ID가 중요합니다. 탐색하는 에이전트는 이동하고, 읽고, 클릭하고, 다시 돌아옵니다. 단계 사이에 IP가 바뀌면 사이트에서는 방문자가 세션 도중에 도시를 옮겨 다니는 것으로 보이며, 이는 봇 방어 시스템이 찾는 바로 그 패턴입니다. 에이전트 실행마다 세션 ID를 하나씩 쓰면 작업 전체에서 하나의 출구가 유지됩니다.
CrewAI
CrewAI 에이전트는 도구를 사용하며, 가장 깔끔한 방법은 자체 HTTP 클라이언트를 가진 작은 커스텀 도구를 만드는 것입니다.
import requests
from crewai.tools import BaseTool
class FetchPage(BaseTool):
name: str = "fetch_page"
description: str = "Fetch a public web page as seen from a given country."
def _run(self, url: str, country: str = "us") -> str:
proxy = proxy_url(country=country)
r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
r.raise_for_status()
return r.text[:20000]
이 도구는 조사를 담당하는 에이전트에게 주고, 글쓰기나 검토만 하는 에이전트에게는 주지 마세요. 에이전트가 다섯인 크루에서 다섯 모두 프록시가 필요한 경우는 드뭅니다.
모델이 country를 선택하게 하면 시장 조사 크루에 유용합니다. 조사 에이전트는 추가 코드 없이 한 페이지가 미국, 독일, 일본에서 각각 어떻게 보이는지 비교할 수 있습니다. 모델이 존재하지 않는 코드를 지어내지 않도록 허용 국가 목록을 도구 설명에 적어 두세요.
AutoGen
AutoGen의 AgentChat에서 도구는 일반 Python 함수이므로, 프록시는 함수 내부의 클라이언트에 설정합니다. AutoGen의 이벤트 루프에는 비동기 클라이언트가 잘 맞습니다.
import httpx
from autogen_agentchat.agents import AssistantAgent
async def fetch_page(url: str, country: str = "us") -> str:
"""Fetch a public web page through a residential IP in the given country."""
async with httpx.AsyncClient(proxy=proxy_url(country=country), timeout=30) as client:
r = await client.get(url, follow_redirects=True)
r.raise_for_status()
return r.text[:20000]
researcher = AssistantAgent(
name="researcher",
model_client=model_client, # your model client, not proxied
tools=[fetch_page],
)
모델 클라이언트는 별도로 생성되며 프록시를 전혀 거치지 않습니다. 이것이 첫 번째 규칙의 핵심입니다.
작업별로 스티키와 로테이션 선택하기
이를 결정하는 것은 프레임워크가 아니라 작업입니다. 간단한 기준은 다음과 같습니다.
| 에이전트 작업 | 세션 | 이유 |
|---|---|---|
| 서로 독립적인 여러 페이지 가져오기 | 로테이션 | 요청을 분산하며 유지할 상태가 없음 |
| 여러 단계의 탐색, 폼, 장바구니 | 스티키, 실행당 ID 하나 | 사이트는 한 명의 방문자를 예상함 |
| 몇 시간 동안 로그인 상태로 진행하는 워크플로 | ISP Proxies | 청구 기간 내내 같은 도시와 ISP |
| 국가별 비교 | 로테이션, 호출당 국가 하나 | 각 호출이 각각 하나의 시장 |
장단점을 더 자세히 알아보려면 프록시 IP 로테이션 의사결정 가이드를 참고하세요. 로그인 상태를 오래 유지하는 세션에는 청구 기간 내내 같은 도시와 ISP를 유지하는 ISP Proxies가 계정을 열어 둔 채 작업하는 에이전트에 적합합니다.
실패를 모델이 알 수 있게 하기
에이전트는 자신이 차단당했다는 사실을 잘 알아차리지 못합니다. CAPTCHA 페이지도 요약할 텍스트일 뿐입니다. 두 가지 작은 습관으로 잘못된 답변을 막을 수 있습니다.
- 본문을 반환하지 말고 403과 429에서 예외를 발생시킵니다. 프레임워크가 오류를 모델에 전달하므로, 모델은 차단 페이지를 근거로 추론하는 대신 재시도하거나 오류를 보고할 수 있습니다.
- 같은 세션이 아니라 새 세션으로 재시도합니다. 스티키 세션이 차단되면 재시도용으로 새 세션 ID를 생성하세요. 같은 출구로 재시도하면 대개 같은 결과가 나옵니다.
재시도 횟수에는 상한을 두세요. URL당 세 번이면 충분합니다. 그 이상은 대상 사이트가 이미 거부하기로 한 트래픽에 비용을 지불하는 것입니다.
비용 관리
에이전트는 루프에 빠질 수 있습니다. 같은 페이지를 계속 다시 읽는 플래너는 토큰을 쓰는 속도만큼 빠르게 프록시 트래픽을 소모합니다. 위 예제처럼 페이지 본문은 모델에 전달하기 전에 잘라 내고, 가져온 페이지는 실행 기간 동안 캐시하며, 도구 호출별 바이트 수를 로그로 남기세요. 그러면 폭주하는 에이전트를 인보이스가 아니라 대시보드에서 먼저 발견할 수 있습니다.
자주 묻는 질문
LangChain은 프록시를 지원하나요?
네. WebBaseLoader 같은 문서 로더는 proxies 인수를 받으며, 브라우저 툴킷은 직접 실행한 브라우저를 그대로 사용하므로 프록시는 브라우저에 설정합니다.
LLM API 호출도 프록시를 거쳐야 하나요?
아니요. 모델 API는 IP로 차단하지 않습니다. 프록시를 거치게 하면 지연 시간이 늘고 트래픽이 낭비됩니다. 프록시는 웹 도구에만 설정하고, 라이브러리가 환경 변수를 읽는다면 모델 호스트를 NO_PROXY에 추가하세요.
AI 에이전트에는 로테이션과 스티키 세션 중 무엇이 적합한가요?
독립적인 페이지 가져오기에는 로테이션, 여러 단계의 탐색에는 실행당 세션 ID 하나를 쓰는 스티키, 몇 시간씩 이어지는 로그인 상태의 워크플로에는 ISP Proxies가 적합합니다.
에이전트가 직접 국가를 선택할 수 있나요?
네. country를 도구 인수로 노출하고 허용되는 코드를 도구 설명에 나열하세요. 그러면 게이트웨이가 각 호출을 해당 국가를 통해 라우팅합니다.
결론
프록시는 프로세스가 아니라 도구에 설정하세요. 페이지가 서로 독립적이면 로테이션을, 에이전트가 방문자처럼 행동하면 실행당 스티키 세션 하나를 사용하고, 모델이 차단을 콘텐츠로 착각하지 않도록 차단을 분명하게 드러내세요.