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






