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规则可能不适用” |
| ”通常会忽略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更有价值。
来源与参考资料
- OpenAI,机器人文档: developers.openai.com
- OpenAI,ChatGPT Work云端浏览器白名单机制: help.openai.com
- OpenAI,推出Operator(2025年1月23日): openai.com
- Google,用户触发的抓取器: developers.google.com
- Google,Gemini计算机使用: ai.google.dev
- Anthropic,关于网络爬取与网站所有者: support.claude.com
- Anthropic,Claude in Chrome: code.claude.com
- Cloudflare,已签名agent(2025年8月28日,文档于2026年11月更新): blog.cloudflare.com, developers.cloudflare.com
- Cloudflare Radar 2025年度回顾: blog.cloudflare.com
- Thales,《2026年恶意机器人报告》: cpl.thalesgroup.com
- IETF,Web Bot Auth协议草案: datatracker.ietf.org
- RFC 9309,机器人排除协议: rfc-editor.org
- RFC 9110与RFC 6585,HTTP状态码: rfc-editor.org, rfc-editor.org
- Model Context Protocol,工具规范: modelcontextprotocol.io
- Sethi等,《工具失败后的编造行为》(预印本,2026年9月): arxiv.org/abs/2609.14758
- 第九巡回上诉法院,Amazon.com Services诉Perplexity AI案,案号26-1444(2026年8月4日): uscourts.gov