Прокси для AI-агентов: browser-use, Crawl4AI и MCP-сервер

Агенты на облачных виртуалках выходят в сеть с IP дата-центров. Даём актуальную рабочую настройку прокси для browser-use, Crawl4AI и MCP-сервера, таблицу выбора между sticky и ротацией и разбираем ловушки, на которых сгорает бюджет.

Proxy Setup for AI Agents: browser-use, Crawl4AI and an MCP Server
В этой статье

AI-агент на облачной виртуалке выходит в сеть с IP дата-центра. Сайты видят этот IP раньше всего остального, поэтому агент получает результаты не для той страны, упирается в лимиты, которые не он создал, или попадает в бесконечный цикл CAPTCHA. Правильная настройка прокси для AI-агентов решает проблему исходящего трафика в конфигурации браузера, а не в промпте. Многошаговые задачи держат один sticky IP от начала до конца, параллельные запросы ротируются. Ниже — рабочая конфигурация для browser-use, Crawl4AI и MCP-сервера, а также контроль расходов и самые частые сценарии отказов.

Почему настройка прокси для AI-агентов важнее, чем для скриптов

Агентам прокси нужен по тем же трём причинам, что и скраперам: география, лимиты запросов и репутация IP. Только нужен он острее, потому что агенты работают без присмотра и сами решают, когда повторять попытку. Заблокированный скрипт останавливается. Заблокированный агент пробует снова, перефразирует, открывает новую вкладку и пробует ещё раз — и каждая попытка стоит трафика и токенов LLM.

  • География. Цены, поисковая выдача, наличие товаров и юридические уведомления зависят от страны. Агент, сравнивающий цены для немецкого клиента с виртуалки в US-East, собирает не те данные. Таргетинг по стране в имени пользователя прокси решает это, не трогая логику агента. Как это устроено, объясняет статья о том, как работают геотаргетированные прокси.
  • Репутация облачных адресов. Диапазоны AWS, GCP, Azure и Hetzner публичны, и сервисы репутации IP относят их к хостингу, а не к потребительскому доступу. Многие сайты сразу показывают таким диапазонам челлендж или режут скорость, как бы ни выглядел запрос. Как устроен этот скоринг, разбирает наш материал о репутации IP.
  • Общие лимиты. Несколько агентов за одним NAT-шлюзом делят один публичный IP. Каждый по отдельности ведёт себя вежливо, но вместе они выглядят как один агрессивный клиент.

Чего прокси не исправляет: резидентный IP не делает автоматизированный браузер похожим на человека. Поведенческий детект и анализ отпечатков продолжают работать — как именно, показывает парная статья о том, как сайты обнаруживают AI-агентов в браузере. Используйте прокси, чтобы получать корректные данные с правильной географией и честно распределять нагрузку. Не используйте их, чтобы обойти сайт, который прямо запретил автоматизацию.

Sticky-сессии или ротация: решайте для каждой задачи, а не для проекта

Простое правило: если у задачи есть состояние, закрепите IP; если нет — ротируйте. Сценарий «вход → навигация → добавление в корзину → оформление заказа» должен от начала до конца идти через один выходной IP, потому что многие сайты привязывают сессионные cookies, CSRF-токены или оценки риска к IP, с которого началась сессия. Если IP меняется посреди сценария, вас часто разлогинивает или снова показывает челлендж. С независимыми запросами страниц всё наоборот: распределение по разным IP держит каждый из них в рамках лимитов на IP.

С backconnect-шлюзом режим переключается сменой имени пользователя. USERNAME-sid-abc123-ttl-10m сохраняет один выходной IP, пока вы используете этот ID сессии. Обычный USERNAME ротируется. USERNAME-country-US закрепляет страну. Подробнее о времени жизни сессий — в руководстве по sticky- и ротационным сессиям.

Задача агентаРежим IPПочему
Вход и действия внутри аккаунтаSticky, один ID сессии на аккаунтСессионные cookies и оценки риска часто привязаны к IP, с которого выполнен вход. Смена IP посреди сессии выглядит как захват аккаунта.
Многостраничная форма или тест оформления заказаSticky на весь сценарийCSRF-токены и состояние корзины сохраняются. Неудачный шаг можно повторить с того же IP.
Исследовательский агент читает 50 не связанных между собой страницРотацияОбщего состояния нет. Распределение запросов позволяет избежать ограничений на IP со стороны любого отдельного домена.
Массовый краулинг для загрузки в RAGРотация или небольшой пул sticky-слотовРотация распределяет нагрузку. Sticky-слоты позволяют JS-тяжёлому сайту сохранять cookies на протяжении нескольких страниц.
Сравнение по странам (один URL, несколько стран)Таргетинг по стране, ротация внутри страныНужен один ответ на рынок, а не на IP.
Мониторинг одной страницы каждые N минутРотацияПовторные обращения с одного IP складываются в паттерн. Ротация держит нагрузку на IP низкой.

browser-use: настройка прокси в актуальном API

browser-use принимает аргумент proxy=ProxySettings(...) у Browser (псевдоним BrowserSession) с полями server, username, password и bypass. Учётные данные уходят в браузер и никогда не попадают в контекст LLM. Под капотом browser-use запускает Chromium с --proxy-server и отвечает на запрос авторизации прокси через CDP (Fetch.authRequired), так что вписывать учётные данные в URL не нужно. Обратите внимание на путь импорта: ProxySettings находится в browser_use.browser, а не в корне пакета (см. справочник параметров браузера и profile.py).

import asyncio
import os
import uuid

from browser_use import Agent, Browser, ChatOpenAI
from browser_use.browser import ProxySettings

PROXY_USER = os.environ["PROXYHAT_USER"]
PROXY_PASS = os.environ["PROXYHAT_PASS"]


async def run_checkout_test() -> None:
    # Один sticky ID сессии на весь сценарий: вход -> корзина -> оформление идут через один выходной IP.
    session_id = uuid.uuid4().hex[:12]

    browser = Browser(
        headless=True,
        proxy=ProxySettings(
            server="http://gate.proxyhat.com:8080",
            username=f"{PROXY_USER}-sid-{session_id}-ttl-10m",
            password=PROXY_PASS,
            bypass="localhost,127.0.0.1",
        ),
        # Держим агента на тестируемом сайте; заодно это ограничивает, куда можно вводить sensitive_data.
        allowed_domains=["https://staging.example-shop.com"],
        # Ключ Chromium: вообще не загружать изображения.
        args=["--blink-settings=imagesEnabled=false"],
    )

    agent = Agent(
        task="Log in with x_user / x_pass, add the cheapest USB-C cable to the cart, "
             "stop at the payment page and report the order total.",
        llm=ChatOpenAI(model=os.environ["OPENAI_MODEL"]),
        browser=browser,
        # Модель видит плейсхолдеры x_user / x_pass, но не сами значения.
        sensitive_data={
            "https://staging.example-shop.com": {
                "x_user": os.environ["SHOP_USER"],
                "x_pass": os.environ["SHOP_PASS"],
            }
        },
        use_vision=False,   # без скриншотов на каждом шаге: меньше токенов
        max_failures=2,     # по умолчанию 5; каждый сбой — ещё один круг загрузок страниц
    )
    history = await agent.run(max_steps=25)   # по умолчанию 500
    print(history.final_result())


asyncio.run(run_checkout_test())

Замечания к этому шаблону:

  • Для прокси с авторизацией используйте HTTP-порт. Документация сетевого стека Chromium указывает, что для SOCKSv5 никакие методы аутентификации не поддерживаются, поэтому socks5://…:1080 с учётными данными не заработает ни в одном агенте на базе Chromium. Используйте http://gate.proxyhat.com:8080.
  • Учётные данные прокси — это не sensitive_data. sensitive_data предназначен для значений, которые агент должен вводить на страницах. Учётные данные прокси передаются в ProxySettings, который модель никогда не видит. Не вставляйте URL прокси в строку task, «чтобы агент знал».
  • allowed_domains важнее, чем кажется. browser-use пишет предупреждение, если вы передаёте sensitive_data без ограничения доменов, потому что страница с prompt injection может иначе уговорить агента ввести секреты где-то ещё.
  • Облачный режим — это другой исходящий канал. Browser(use_cloud=True) запускает браузер в Browser Use Cloud с его собственными прокси. Ваши ProxySettings применяются к локальному браузеру или браузеру по cdp_url, который вы запускаете сами.
  • Расширения по умолчанию экономят трафик. enable_default_extensions включён по умолчанию и загружает uBlock Origin Lite и обработчик cookie-баннеров, так что большая часть рекламы и трекеров вообще не расходует ваш трафик.

Crawl4AI: прокси на каждый краул, ротация и sticky-сессии

В Crawl4AI прокси задаётся для каждого краула в CrawlerRunConfig — либо как proxy_config, либо через proxy_rotation_strategy. Документация по прокси помечает старый аргумент proxy в BrowserConfig как устаревший и рекомендует настраивать прокси на уровне запроса. Для sticky-поведения proxy_session_id закрепляет один прокси из стратегии за всеми краулами с этим ID.

Backconnect-шлюз — это один хост, поэтому «список прокси» здесь означает список имён пользователей с сессиями. Каждый ID сессии — слот со своим выходным IP, а round-robin распределяет URL по слотам:

import asyncio
import os
import uuid

from crawl4ai import (AsyncWebCrawler, BrowserConfig, CacheMode,
                      CrawlerRunConfig, ProxyConfig, RoundRobinProxyStrategy)

USER = os.environ["PROXYHAT_USER"]
PASS = os.environ["PROXYHAT_PASS"]


def slot() -> ProxyConfig:
    # Одна sticky-сессия на слот, поэтому у каждого слота свой постоянный выходной IP.
    sid = uuid.uuid4().hex[:10]
    return ProxyConfig(
        server="http://gate.proxyhat.com:8080",
        username=f"{USER}-sid-{sid}-ttl-10m",
        password=PASS,
    )


strategy = RoundRobinProxyStrategy([slot() for _ in range(8)])

browser_cfg = BrowserConfig(
    headless=True,
    text_mode=True,   # пытается отключить изображения и другой тяжёлый контент
    avoid_ads=True,   # блокирует распространённые рекламные и трекинговые домены
)

bulk_cfg = CrawlerRunConfig(
    proxy_rotation_strategy=strategy,   # следующий слот для каждого URL
    cache_mode=CacheMode.BYPASS,
    check_robots_txt=True,
    max_retries=1,
)


async def main(urls: list[str]) -> None:
    async with AsyncWebCrawler(config=browser_cfg) as crawler:
        results = await crawler.arun_many(urls, config=bulk_cfg)
        for r in results:
            print(r.url, r.success, len(r.markdown or ""))

        # Сценарий с состоянием: один и тот же слот прокси И одна и та же страница браузера на каждом шаге.
        step_cfg = bulk_cfg.clone(proxy_session_id="acct-42", session_id="acct-42")
        await crawler.arun("https://example.com/login", config=step_cfg)
        await crawler.arun("https://example.com/account/orders", config=step_cfg)


asyncio.run(main(["https://example.com/a", "https://example.com/b"]))

Две настройки легко перепутать. session_id сохраняет одну и ту же страницу браузера между вызовами arun(), а proxy_session_id — один и тот же прокси. Сценарию с состоянием нужны обе. Если вместо sticky-слотов нужна ротация с привязкой к стране, используйте один ProxyConfig с именем пользователя USERNAME-country-US.

Ловушка с умножением повторов: proxy_config принимает и список прокси для перебора при сбоях, а цикл повторов в async_webcrawler.py проходит весь список на каждой попытке. Поэтому заблокированный URL может загружаться до (max_retries + 1) × len(list) раз. При 10 запасных прокси и max_retries=3 это 40 полных загрузок страницы ради одного URL, который всё равно не загрузится. Держите список коротким и задавайте max_retries осознанно.

Как дать MCP-ассистенту доступ в веб через прокси

Вариантов два: запустить MCP-сервер Playwright от Microsoft с прокси в конфиге или написать небольшой MCP-инструмент, который загружает страницы через прокси и возвращает текст. Первый подходит, когда ассистенту нужно кликать и вводить текст. Второй — когда нужно только читать страницы, потому что каждый вызов обходится гораздо дешевле.

Вариант 1: Playwright MCP с прокси с авторизацией

У @playwright/mcp есть флаги --proxy-server и --proxy-bypass (README). Для прокси с учётными данными используйте JSON-файл конфигурации. Его browser.launchOptions передаётся напрямую в Playwright, чья опция proxy принимает отдельные поля username и password:

{
  "browser": {
    "browserName": "chromium",
    "isolated": true,
    "launchOptions": {
      "headless": true,
      "proxy": {
        "server": "http://gate.proxyhat.com:8080",
        "username": "USERNAME-sid-mcp01-ttl-10m",
        "password": "PASSWORD"
      }
    }
  },
  "capabilities": ["core"]
}
npx @playwright/mcp@latest --config ./playwright-mcp.json --image-responses omit

Не храните этот файл в репозитории и сделайте его доступным для чтения только пользователю, от которого запущен сервер. ID сессии здесь означает, что вся сессия ассистента идёт через один IP, — это подходит для задач вида «войди и сделай X». Чтобы получить новый IP, перезапустите сервер с новым ID. --image-responses omit не даёт скриншотам уходить в модель. "isolated": true (флаг --isolated) держит профиль в памяти, чтобы cookies не перетекали между задачами. README также подчёркивает, что --allowed-origins и --blocked-origins — не граница безопасности. Считайте их гигиеной, а не защитой.

Вариант 2: минимальный инструмент загрузки через прокси на официальном MCP Python SDK

В v2 Python SDK класс FastMCP переименован в MCPServer (руководство по миграции). Коду, написанному под v1 с from mcp.server.fastmcp import FastMCP, нужен новый импорт. Ниже — инструмент только для чтения, который загружает страницы через шлюз, ограничивает размер ответа и никогда не выводит URL прокси:

import os
import re

import httpx
from mcp.server import MCPServer

mcp = MCPServer("proxied-fetch")

_USER = os.environ["PROXYHAT_USER"]
_PASS = os.environ["PROXYHAT_PASS"]
_MAX_BYTES = 1_500_000
_COUNTRY = re.compile(r"^[A-Za-z]{2}$")
_SESSION = re.compile(r"^[A-Za-z0-9]{1,24}$")


def _proxy(country: str | None, session: str | None) -> str:
    user = _USER
    # Проверяем значения от модели: они попадают в имя пользователя прокси.
    if session:
        if not _SESSION.match(session):
            raise ValueError("session must be 1-24 alphanumerics")
        user = f"{_USER}-sid-{session}-ttl-10m"
    elif country:
        if not _COUNTRY.match(country):
            raise ValueError("country must be a 2-letter code")
        user = f"{_USER}-country-{country.upper()}"
    return f"http://{user}:{_PASS}@gate.proxyhat.com:8080"


@mcp.tool()
async def fetch_page(url: str, country: str | None = None, session: str | None = None) -> str:
    """Fetch a public http(s) page through a residential proxy and return its text.

    country: optional 2-letter exit country. session: optional ID to reuse one exit IP.
    """
    if not url.startswith(("http://", "https://")):
        return "error: only http(s) URLs are allowed"
    try:
        async with httpx.AsyncClient(proxy=_proxy(country, session), timeout=30,
                                     follow_redirects=True) as client:
            async with client.stream("GET", url) as resp:
                body = b""
                async for chunk in resp.aiter_bytes():
                    body += chunk
                    if len(body) > _MAX_BYTES:
                        break
        text = body.decode(resp.encoding or "utf-8", errors="replace")
        return f"HTTP {resp.status_code} {resp.url}\n\n{text[:200_000]}"
    except ValueError as e:
        return f"error: {e}"
    except httpx.HTTPError as e:
        # Только тип исключения: некоторые сообщения могут содержать URL прокси.
        return f"error: fetch failed ({type(e).__name__})"


if __name__ == "__main__":
    mcp.run(transport="stdio")

Зарегистрируйте его в своём MCP-клиенте как stdio-сервер. Передавайте PROXYHAT_USER и PROXYHAT_PASS через блок env клиента, а не через аргументы инструмента. В Claude Code, например, это claude mcp add --env PROXYHAT_USER=… --env PROXYHAT_PASS=… --transport stdio proxied-fetch -- python server.py. Ставьте --transport между последним --env и именем сервера, иначе CLI примет имя за ещё одну пару env. Проверки регулярными выражениями здесь не лишние: страница с prompt injection может заставить модель вызвать инструмент со странными аргументами, и без проверки она смогла бы записать произвольный текст в имя пользователя прокси. Для страниц, которым нужен JavaScript, используйте вариант 1 или наше руководство по ротации прокси в Playwright, если строите браузерный слой сами.

Контроль расходов: агенты тратят трафик так, как скриптам и не снилось

Резидентный трафик оплачивается за гигабайт, а агент загружает страницы целиком, часто по нескольку раз за шаг. Больше всего дают такие меры, примерно в порядке значимости:

  1. Не загружайте то, что модель не будет читать. Изображения, видео, шрифты и рекламные скрипты составляют большую часть веса современной страницы. Используйте text_mode=True и avoid_ads=True в Crawl4AI, ключ imagesEnabled=false или uBlock в browser-use и инструмент загрузки вместо браузера, если страница рендерится на сервере.
  2. Ограничивайте повторы в двух местах. Повторы фреймворка (max_failures, max_retries) и перепланирование на уровне агента перемножаются. Агент, который после 403 «пробует другой подход», часто просто перезагружает ту же заблокированную страницу. Ставьте max_steps достаточно низким, чтобы застрявший прогон умирал дёшево.
  3. Переиспользуйте контексты в сценариях с состоянием. Новый контекст браузера — это свежие cookies, новый челлендж и повторная загрузка всех закешированных ресурсов. В рамках одной задачи держите контекст (и sticky IP) живым.
  4. Останавливайтесь на сигналах блокировки. Считайте страницу CAPTCHA, 403 от вендора бот-менеджмента или 429 конечным состоянием, о котором нужно сообщить человеку. Не давайте агенту пробиваться сквозь них повторами. Почему стоит отступить, объясняет наше руководство по работе с CAPTCHA.
  5. Измеряйте по задачам, а не по месяцам. Логируйте байты на каждый прогон агента. Резкий скачок обычно означает цикл, а не более крупную задачу.

Сценарии отказов, которые стоит заложить в архитектуру

Большинство инцидентов с прокси у агентов сводятся к одной из четырёх причин: цикл CAPTCHA, умножение повторов, сессия, незаметно сменившая IP, или утечка учётных данных в контекст модели.

  • Циклы CAPTCHA. Агенты с включённым зрением пытаются «решить» челлендж, не справляются, перезагружают страницу и пробуют снова. Распознавайте страницы челленджей (известные хосты iframe, заголовки вроде «Just a moment…») и завершайте прогон. Массово отвечать на челленджи легитимный пайплайн не должен, а повторные неудачи с резидентного IP портят его репутацию для всех, кто им пользуется.
  • Умножение повторов. Описано выше: повторы фреймворка × списки запасных прокси × перепланирование агента. Следите за общим числом попыток на URL, а не только за сбоями.
  • Незаметный обрыв сессии. Sticky-сессия заканчивается по истечении TTL и может закончиться раньше, если вышестоящий узел ушёл в офлайн; следующий запрос тогда идёт с нового IP. Если запрос посреди сценария вдруг перенаправляет на страницу входа, считайте, что IP сменился. Перезапустите сценарий с новым ID сессии, а не давайте агенту импровизировать.
  • Учётные данные в контексте LLM. Всё, что попадает в модель, может оказаться в логах, трейсах, датасетах для оценки или в запросе на эксфильтрацию после prompt injection. Храните учётные данные прокси в переменных окружения, которые читает браузер или HTTP-клиент. Не кладите их в строки задач, системные промпты или вывод инструментов и очищайте сообщения исключений, прежде чем их возвращать. Опция secrets в Playwright MCP умеет вырезать известные строки из ответов инструментов, но её собственный README называет это удобством, а не функцией безопасности.

Прежде чем винить агента, проверьте прокси отдельно: прогоните настроенный вариант имени пользователя через проверку прокси и убедитесь, что страна выхода и тип IP соответствуют ожиданиям. Более широкое сравнение типов IP для нагрузок агентов — в статье «Лучшие прокси для AI-агентов и сбора веб-данных для LLM».

Чего эта настройка не решает

Прокси меняет то, откуда идёт трафик, но не то, как он себя ведёт. Он не поможет агенту пройти поведенческий детект ботов и не даёт разрешения заходить на сайт, который запрещает автоматизацию в robots.txt или условиях использования. Законным сбор персональных данных он тоже не сделает. Если сайт предлагает API, фид или программу для подписанных агентов, эти пути обычно дешевле и надёжнее любой конфигурации прокси. Используйте резидентные IP для того, в чём они сильны: правильная география, честное распределение нагрузки и отсутствие унаследованной репутации облачного диапазона.

Часто задаваемые вопросы

Как добавить прокси в browser-use?

Передайте proxy=ProxySettings(server=..., username=..., password=...) в Browser, импортировав ProxySettings из browser_use.browser. browser-use запускает Chromium с --proxy-server и отвечает на запрос авторизации прокси через CDP, так что учётные данные никогда не попадают в LLM. Используйте HTTP-эндпоинт прокси: Chromium не поддерживает аутентификацию для SOCKS5.

Какие прокси нужны AI-агенту: ротационные или sticky?

Для всего, что имеет состояние, — входа, многостраничных форм, оформления заказа — используйте sticky-сессию: многие сайты привязывают cookies и оценки риска к IP, с которого открыта сессия. Ротация подходит для независимых запросов, например чтения материалов для исследования или массового краулинга, где распределение запросов держит каждый IP в рамках лимитов сайта.

Как настроить ротацию прокси в Crawl4AI?

Создайте список объектов ProxyConfig, оберните его в RoundRobinProxyStrategy и передайте как proxy_rotation_strategy в CrawlerRunConfig. С одним backconnect-шлюзом задайте каждому ProxyConfig своё имя пользователя с сессией, чтобы у каждого был свой выходной IP. Добавьте proxy_session_id, чтобы закрепить один прокси за многошаговым сценарием.

Можно ли дать Claude или другому MCP-ассистенту доступ в веб через прокси?

Да. Запустите Playwright MCP с файлом конфигурации, где browser.launchOptions.proxy содержит server, username и password, или напишите небольшой MCP-инструмент на классе MCPServer из Python SDK, который загружает страницы через прокси с помощью httpx. В обоих случаях храните учётные данные прокси в переменных окружения или файлах конфигурации, а не в промптах или выводе инструментов.

Почему AI-агенты расходуют так много трафика прокси?

Агенты загружают страницы целиком, часто по нескольку раз за шаг, а их повторы накладываются друг на друга: повторы фреймворка, списки запасных прокси и собственное перепланирование агента перемножаются. Блокировка изображений, шрифтов и рекламы, ограничение max_steps и повторов, переиспользование контекстов браузера и остановка на страницах CAPTCHA убирают большую часть потерь.

Готовы начать?

Резидентные, ISP и мобильные прокси в 195+ странах. Создайте бесплатный аккаунт.

Создать бесплатный аккаунт
← Вернуться в Блог