截至2026年10月7日,Shifter拥有规模最大的美国实时IP池,超过Oxylabs和Bright Data,而成本只是其一小部分。Shifter 现在拥有数量最多的美国活跃 IP 资源池

查看基准测试

当AI代理撞上机器人墙:没人为之设计的访问难题

AI代理代表个人行事,但到达时却像机器人,而网站并非为此而建。墙前会发生什么,为何200才是风险所在,以及该如何为此构建方案。

工程 · 更新于2026年11月3日 · 3分钟阅读
200 OK机器人检测作为文本outcome: wall我看不到它ok · wall · login · payment · rate_limited
墙显示200。在琥珀色通道中,模型将其当作页面内容,照样作答。
知识
本页内容
  1. 既非爬虫,也非人类
  2. 从工具调用内部看这堵墙是什么样子
  3. 一个讲真话的抓取工具
  4. 不要去破解验证码
  5. 身份认证才是通行之道
  6. 法律层面尚未明朗
  7. 为这堵墙做好准备
  8. 结论
  9. 来源与参考资料

web的防御体系建立在一个简单的二分法之上:一边是人,另一边是自动化程序。而AI agent两者都不是。它根据人的请求行动,但运行在服务器上,以机器的速度阅读,并且常常来自云端IP。对于构建使用网络的AI agent的团队来说,结果就是一种没人预料到的失败:agent撞上了一堵墙,而接下来发生什么,取决于大多数工具层都忽略的细节。

本指南将介绍为什么agent落在了分类之间、从工具调用内部看这堵墙是什么样子,以及如何构建一个能诚实处理这种情况的agent。

既非爬虫,也非人类

供应商现在将这些agent描述为一个独立的类别。OpenAI表示ChatGPT-User”并非用于自动化方式的网络爬取”。Google表示Google-Agent用于”根据用户请求浏览网络并执行操作”。Anthropic则有一个Claude-User agent,用于人们向Claude提问的场景。

规则尚未跟上,而且供应商之间也没有达成一致:

供应商用户触发的抓取与robots.txt
OpenAI”robots.txt规则可能不适用”
Google”通常会忽略robots.txt规则”
Anthropic其机器人遵守robots.txt指令

RFC 9309补充说明robots.txt规则”不是一种访问授权形式”,所以它们本来就不可能单独解决这个问题。与此同时,流量还在增长:Cloudflare报告称,2025年AI”用户操作”类抓取增长了超过15倍,而Thales的《2026年恶意机器人报告》显示,2025年机器人占所有流量的53%以上。网站比以往任何时候都更有理由过滤自动化程序,而agent就被卡在了这层过滤中。

从工具调用内部看这堵墙是什么样子

返回的内容含义agent应该怎么做
带有验证挑战页面的200这是机器人检测,不是页面内容报告说无法看到该页面
401或登录页面内容需要账户要求用户登录
402网站要求付款在支付任何费用前先询问
403服务器拒绝访问报告此情况,不要反复请求该页面
429请求过多遵守Retry-After,或停止请求
404或410页面不存在不要重试

第一行是最危险的情况。403至少看起来像是一次失败。而验证挑战页面往往以带有格式良好的HTML正文的200形式出现,所以一个简单的抓取工具会把它当成文章内容直接交给模型。

这很重要,因为模型在面对糟糕的工具输出时的行为方式。2026年9月的一篇关于工具增强型agent的预印本论文发现,当工具返回status:error时,不诚实行为不存在(0.0%),但当它返回status:ok并附带被删节、损坏、过时、格式错误、空白或被截断的值时,不诚实行为的比例达到45.3%。该研究测试的是内部系统而非网页,因此将其应用于验证挡板页面是我们的推断。不过这个教训依然适用:一个诚实的错误远比一个没有任何有用内容的”成功”要安全得多。我们还讨论过验证挑战页面如何污染AI工具的产出。

一个讲真话的抓取工具

解决方案应该放在工具层。下面的函数会在模型看到响应之前对每个响应进行分类,只有当页面是真实的时才返回页面文本。它只需要requests。我们在Python 3.9上针对一个返回各种情况的本地测试服务器运行了它。

from __future__ import annotations

from dataclasses import asdict, dataclass

import requests

# Text that marks a page as a wall, not content. Extend it from your own logs.
WALL_MARKERS = ("verify you are human", "unusual traffic", "enable javascript and cookies",
                "attention required", "access denied", "captcha")
LOGIN_MARKERS = ("sign in to continue", "log in to continue")


@dataclass
class FetchResult:
    outcome: str         # ok, wall, login, payment, rate_limited, not_found, error
    status: int | None
    text: str            # only ever page content, empty unless outcome is ok
    retry_after: str | None
    note: str            # what the model should be told


NOTES = {
    "wall": "The site showed a bot check. You did not see this page. Say so, do not guess its content.",
    "login": "This page needs a login. Ask the user to sign in or to provide the content.",
    "payment": "The site asks for payment (HTTP 402). Ask the user before paying anything.",
    "rate_limited": "The site asked you to slow down. Wait, or tell the user it is unavailable now.",
    "not_found": "The page does not exist. Do not retry it.",
    "error": "The fetch failed. You did not see this page. Say so, do not guess its content.",
    "ok": "",
}


def classify(status, body):
    low = body.lower()
    if status == 402:
        return "payment"
    if status == 429:
        return "rate_limited"
    if status in (404, 410):
        return "not_found"
    if any(m in low for m in WALL_MARKERS):  # walls often arrive with a 200
        return "wall"
    if status in (401, 403):
        return "login" if status == 401 or any(m in low for m in LOGIN_MARKERS) else "wall"
    if status == 200 and any(m in low for m in LOGIN_MARKERS):
        return "login"
    return "ok" if status == 200 else "error"


def agent_fetch(url, agent_name="example-agent/1.0 (+https://example.com/agent)"):
    """A fetch tool that never hands the model a block page as if it were content."""
    try:
        r = requests.get(url, headers={"User-Agent": agent_name}, timeout=(10, 30))
    except requests.RequestException as exc:
        return FetchResult("error", None, "", None, f"{NOTES['error']} ({type(exc).__name__})")
    outcome = classify(r.status_code, r.text)
    return FetchResult(outcome, r.status_code, r.text if outcome == "ok" else "",
                       r.headers.get("Retry-After"), NOTES[outcome])

输出结果,按状态码、分类结果、模型是否收到页面文本、以及Retry-After排列:

/article   200  ok            content  
/challenge 200  wall          -        
/blocked   403  wall          -        
/members   200  login         -        
/priced    402  payment       -        
/busy      429  rate_limited  -        120
/gone      404  not_found     -        
/broken    503  error         -        
(no server)None error         -

九种情况中只有一种会给模型提供页面文本。/challenge这一行正是普通抓取工具会出错的地方,因为它返回了200。把这些标记列表当作一个起点,并根据你自己的日志不断扩充它,因为没有任何列表能捕获所有的验证挡板。如果你通过MCP server提供工具服务,该规范中有专门处理这种情况的地方:执行错误通过isError: true报告,客户端”应该(SHOULD)将工具执行错误提供给语言模型”。

不要去破解验证码

在这一点上,供应商们出奇地一致。OpenAI曾描述Operator被训练为在登录、支付信息和验证码(CAPTCHA)场景下请求用户接管。Anthropic的Claude in Chrome文档称,在登录页面或验证码处它”会暂停并要求你手动处理”。Google推荐的计算机使用指令中写道:“你绝不能(MUST NEVER)尝试解决或绕过”的列表中,就包括”任何类型的验证码(CAPTCHAs)”。

验证码是网站在要求一个真人出现,所以正确的做法就是把控制权交还给一个真人。这样做也能造就更好的产品:用户能清楚知道发生了什么,并自行决定如何处理。

身份认证才是通行之道

从长远来看,答案在于让agent能以网站可验证的方式表明自己的身份。Web Bot Auth允许客户端使用HTTP Message Signatures对其请求进行加密签名。OpenAI已经对其云端浏览器发出的请求进行签名,Signature-Agent头被设置为https://chatgpt.com,而Google表示正在试验该协议。Cloudflare在2025年8月引入了已签名agent的分类,自2026年7月起,会将直接访问与中介访问区分开来,以此与已验证的机器人作区分。IETF的相关工作仍处于草案阶段,因此预计还会有变化。

如果你今天正在构建一个agent,请发送一个带有联系方式的描述性User-Agent,公开说明你的agent的用途,并为未来的签名机制做好规划。

法律层面尚未明朗

第一起重大案件展示了这个问题的开放程度。Amazon于2025年11月起诉Perplexity,针对其agentic浏览器,指控其违反了美国《计算机欺诈与滥用法》(Computer Fraud and Abuse Act)及加州的对应法律,而判决意见指出,这场争议的核心在于Perplexity选择不使用用户代理(user-agent)字符串。2026年8月,第九巡回上诉法院撤销了一项初步禁令,认定Amazon不太可能证明Perplexity根据该法案”访问”了其计算机系统。

法院对这一判决的适用范围非常谨慎:“我们并未建立一套管理agentic AI的新法律体系。“法院还表示,该结果并不会削弱Amazon通过其服务条款来管理访问权限的能力。所以这并不是在许可agent无视网站的意愿。这里提供的只是一般性信息,并非法律建议,而且该案件已发回地区法院继续审理。

为这堵墙做好准备

  • **假定遇到墙是常态。**围绕agent可能无法触及的页面来设计其计划,配上诸如使用其他来源或询问用户之类的后备方案。
  • **在模型看到任何内容之前先进行分类。**使用类似上文的工具,并记录分类结果,以便观察哪些网站会出现故障。
  • **优先提供官方渠道。**如果网站有API或数据产品,就使用它。这比浏览器会话更可靠。
  • **保持会话的一致性。**多步骤任务会在IP中途变化时中断。我们关于用于浏览网络的AI agent的代理的指南涵盖了轮换和粘性会话。
  • 明确代理的用途。住宅代理能帮助agent像本地访问者一样,在正确的国家、以一致的地址浏览公开页面。它们不会改变网站允许的内容,也不是绕过网站明确拒绝的手段。如果网站要求登录、付款或真人验证,请予以尊重。

结论

在网络为agent准备好相应分类之前,agent就已经抵达了网络世界,而眼下,这堵墙就是工作的一部分。把它当作一种正常的结果来对待:在工具层对其分类,向模型和用户如实说明情况,把验证码交还给真人处理,并为你的agent做好身份标识。一个会说”我无法看到这个页面”的agent,远比一个会胡乱猜测的agent更有价值。

来源与参考资料

常见问题

为什么AI代理会被网站拦截?

大多数反机器人防御会把流量分为人和自动化两类。代理代表人行事,但运行在服务器上,往往使用云端IP并以机器的速度运作,因此即使是真实用户请求的页面,它也容易被判定为自动化流量。

AI代理应该去解CAPTCHA吗?

不应该。OpenAI、Anthropic和Google都有文档说明,代理会把CAPTCHA交还给用户,或者干脆不去尝试。CAPTCHA意味着网站希望由人来做判断,所以代理应该停下来询问用户。

当页面被拦截时,代理应该怎么做?

报告情况。抓取工具应返回一个明确的结果,比如“遇到验证墙”“需要登录”或“被限速”,而不附带页面内容,这样模型就能告诉用户它无法查看该页面,而不是去猜测页面上有什么。

robots.txt适用于AI代理吗?

各家做法不同。OpenAI表示robots.txt规则可能不适用于用户发起的操作,Google表示其用户触发的抓取器通常会忽略这些规则,而Anthropic则表示其机器人会遵守这些规则。RFC 9309本身也指出,这些规则并不构成访问授权的一种形式,因此需要检查你自己的政策以及各网站的条款。

准备好开始了吗?

试用 Shifter 住宅代理,205M+ 个 IP,195+ 个国家,低至 $0.10/GB。

立即开始