1000 万开发者、Agent 请求一年涨 1700%:Cloudflare 这组数字该怎么读
💡 工具推荐: User Agent 解析器、CSV 统计、JSON 格式化
2026 年 10 月 20 日,Cloudflare 发布了一组数字:平台上构建的开发者超过 1000 万,较上一季度的 740 万增长约 35%;过去一年里,其网络上来自 AI Agent 的日均请求增长超过 1700%;并且今年首次出现「超过一半的互联网流量不是人类产生的」。同一份公告里,产品管理副总裁 Rita Kozlov 给出的判断是:软件生命周期正在从「开发者为人类构建」扩展为「开发者和 Agent 一起为人类和 Agent 构建」。这三个数字很容易被当成标题党,但如果换一个视角,它们其实在描述一件很具体的事:你的网站可能已经在为机器服务,而你不知道。这篇文章做三件事:说清这几个数字是怎么算的、有哪些口径限制;给出一套在你自己的日志里复现它们的脚本;以及在这些数据真的成立时,该改什么、不该改什么。
先量自己的流量构成,再谈为 Agent 优化
一、公告里的三个数字,以及它们的口径
第一个数字是开发者规模:1000 万,上一季度是 740 万。这是平台侧统计,口径是「在 Cloudflare 上构建」——注册、用 Workers 或相关产品部署,具体怎么算只有他们知道,所以看到 35% 的季度增长时,合理的态度是「这是一个方向,不是一个精确的行业指标」。第二个数字是 Agent 流量:日均请求一年增长超过 1700%。这个数字在公告里有一处关键限定——它衡量的是「来自 AI Agent 的请求」,而 Agent 的定义取决于识别方法(User-Agent 匹配、行为特征、还是签名验证),不同方法差别很大。第三个数字最容易被误读:「今年首次,超过一半的互联网流量不是人类产生的」。注意主语是 Cloudflare 的网络,而且「非人类」包含爬虫、监控、API 调用、被劫持的物联网设备、以及 Agent——把所有非浏览器流量归为「Agent 驱动的新时代」是过度解读。正确的用法是:把这三个数字当成「需求侧变化确实在发生」的佐证,然后回到自己的日志里做自己的测量。
# 1) Classify your own traffic before you believe anyone's percentage.
import re
from collections import Counter
NAMED_AGENT = re.compile(
r"(GPTBot|OAI-SearchBot|ClaudeBot|Claude-User|PerplexityBot|"
r"Google-Extended|Amazonbot|Applebot-Extended|meta-externalagent|"
r"Bytespider|CCBot)", re.I)
BROWSER = re.compile(r"(Mozilla|Chrome|Safari|Firefox|Edg)", re.I)
def classify(ua: str) -> str:
if not ua:
return "unknown"
if NAMED_AGENT.search(ua):
return "named-agent"
if BROWSER.search(ua):
return "browser"
if re.search(r"(python-requests|httpx|aiohttp|curl|go-http-client|okhttp)", ua, re.I):
return "script"
return "other"
# Feed it your access log lines and look at the distribution.
print(Counter(classify(ua) for ua in user_agents).most_common())二、为什么「非人类过半」既值得警惕也值得利用
值得警惕的部分很实际:如果你按人类访客设计页面——大图、动效、瀑布式依赖、首屏要跑三次请求——那么一个按秒来问的 Agent 会把你的带宽和上游计算按倍数放大,而它永远不会点广告、不会注册、不会看你的推荐位。值得利用的部分同样实际:Agent 来你这里,通常是因为它需要某个具体事实——某个工具的参数怎么填、某个格式的规范是什么、某个 API 的字段含义。这类请求的答案往往只有几百字节,但你的页面可能为了展示它加载了四十万字节。也就是说,同一份信息,人类需要包装,机器只需要数据。把两者用同一个端点服务,是成本问题的根源。真正该做的判断不是「要不要欢迎 Agent」,而是「我的内容里哪一部分是事实型的、可以低成本地直接给出去」。
# 2) Aggregate hides cost. Break share down by agent AND by path.
from collections import defaultdict
stats = defaultdict(lambda: {"hits": 0, "bytes": 0})
for row in access_log: # ts, ua, path, bytes_sent
kind = classify(row["ua"])
if kind != "named-agent":
continue
agent = NAMED_AGENT.search(row["ua"]).group(1).lower()
stats[(agent, row["path"])]["hits"] += 1
stats[(agent, row["path"])]["bytes"] += row["bytes_sent"]
for (agent, path), s in sorted(stats.items(), key=lambda kv: -kv[1]["bytes"])[:15]:
mb = s["bytes"] / 1e6
print(agent.ljust(22), path.ljust(32), str(s["hits"]).rjust(8), round(mb, 1), "MB")
# The pattern that matters: are agents pulling your heaviest HTML pages
# thousands of times, or asking for a small number of specific facts?三、先量自己的:把日志按身份分类
第一段脚本做分类:命名 Agent(GPTBot、ClaudeBot、PerplexityBot 这类),浏览器,脚本客户端(python-requests、curl、httpx 这些),以及其他。第二段做细分:只保留命名 Agent,按 Agent 名字和访问路径做聚合,输出命中次数和传输字节数,按字节倒序取前 15。这两段的价值在于把一个抽象百分比变成你自己的三行结论:谁在来、来多少次、拿走多少字节。经验上常见的三种形态是:某些 Agent 只抓首页与 sitemap,说明它在做索引;某些 Agent 反复抓同一批工具页,说明它在为回答问题取数;还有一类只抓 API 端点,说明有人把你的站接进了自己的流水线。三种形态的应对完全不同,不先分开就谈不上优化。
// 3) Give agents a cheap, machine-readable surface instead of a 40KB page.
// One JSON endpoint can replace a thousand full HTML renders.
export default {
async fetch(req: Request): Promise<Response> {
const url = new URL(req.url);
const slug = url.searchParams.get("tool");
if (!slug) {
return Response.json({ error: "tool parameter required" }, { status: 400 });
}
const meta = await getToolMeta(slug); // your existing lookup
if (!meta) {
return Response.json({ error: "not found" }, { status: 404 });
}
return Response.json(
{ slug: meta.slug, title: meta.title, category: meta.category, updated: meta.updatedAt },
{ headers: { "Cache-Control": "public, max-age=3600" } }
);
},
};
// Same facts, a fraction of the bytes, and cacheable at the edge.
// Document it in llms.txt so well-behaved agents find it instead of scraping.四、给 Agent 一条便宜通道,而不是一道闸门
第三段脚本是一个极小的 JSON 端点:入参一个工具 slug,返回标题、分类和更新时间,带上 Cache-Control 让边缘直接命中。它不是取代页面,而是给「只需要事实」的调用方一条低成本路径。配套的动作有两个:一是把这个端点写进 llms.txt 或文档,让守规矩的 Agent 走正门而不是从 HTML 里刮;二是在响应里留一个「最后一次更新」的时间戳,让调用方可以判断要不要重取。第二段脚本已经告诉你哪些路径被反复取用,那些路径就是最该做机器可读版本的地方。注意一个反直觉的点:这个端点省下的是你的钱,不是对方的。Agent 的成本由它的调用方付,你的带宽和上游计算由你付,所以这是纯粹的降本动作,不需要对方配合。
# 4) Quotas belong to identities, not IPs. Agent fleets rotate addresses.
import time
from collections import OrderedDict
class Window:
def __init__(self, limit: int, seconds: int = 60):
self.limit, self.seconds = limit, seconds
self.seen = OrderedDict()
def allow(self, identity: str) -> bool:
now = time.time()
hits = [t for t in self.seen.get(identity, []) if now - t < self.seconds]
hits.append(now)
self.seen[identity] = hits
self.seen.move_to_end(identity)
return len(hits) <= self.limit
browser = Window(limit=120) # a human browsing a few tool pages
agent = Window(limit=30) # a crawler that respects the contract
# Identity = verified agent name (or API key), never the source IP.
# Return 429 with Retry-After so well-behaved agents back off instead of retrying hard.五、配额按身份算,不按 IP 算
第四段脚本是一个按身份计数的滑动窗口。为什么不用 IP?因为正规 Agent 跑在云上,出口地址池会轮换,你今天封掉的 IP 明天就是另一台无关的机器;而恶意爬虫也早就学会轮换。按身份算的含义是:优先信任带签名的 Agent 身份(如果对方提供),其次用 API key,最后才是 UA 加限速。限速本身要有礼貌:返回 429 并带上 Retry-After,让守规矩的 Agent 退避,而不是逼它重试到更凶。至于人类访客,阈值要明显高一些——一个正常人在工具站上连点十几个页面是常态。这套东西写起来不到二十行,但它把「Agent 来不来」从情绪问题变成了配置问题。
# 5) Price agent traffic as its own line item. Blended averages lie.
report = {
"human": {"requests": 0, "egress_gb": 0.0, "compute_ms": 0},
"browser": {"requests": 0, "egress_gb": 0.0, "compute_ms": 0},
"agent": {"requests": 0, "egress_gb": 0.0, "compute_ms": 0},
}
for row in access_log:
bucket = report[classify(row["ua"])] if classify(row["ua"]) in report else report["human"]
bucket["requests"] += 1
bucket["egress_gb"] += row["bytes_sent"] / 1e9
bucket["compute_ms"] += row.get("upstream_ms", 0)
for name, r in report.items():
avg = r["egress_gb"] / r["requests"] if r["requests"] else 0
print(name.ljust(8), str(r["requests"]).rjust(9), round(r["egress_gb"], 2), "GB",
round(avg * 1000, 1), "KB/req")
# If agent requests are cheap per call and high in volume, the fix is caching
# and a JSON surface. If they are expensive per call, the fix is the endpoint.六、把 Agent 流量单独计价,然后才谈边缘计算
第五段脚本把流量拆成人类、浏览器、Agent 三条线,各自算请求数、出网 GB 和上游计算毫秒,并打印每次请求的平均字节数。这一步看起来只是记账,实际上决定了架构:如果 Agent 请求单次很便宜但量很大,解法是缓存加 JSON 端点;如果单次很贵,说明它在触发重计算,解法是把那部分计算变成离线产物。公告里 Kozlov 强调 Workers 跑在 isolate 而不是虚拟机或容器上,这句话的成本含义正在这里——高频、短、无状态的 Agent 请求,在按请求计费、冷启动极低的模型下最便宜;而如果你的架构是每次请求都拉起一个容器,Agent 流量会直接把账单拉起来。所以「为 Agent 优化」的落点,往往不是买更多机器,而是把最贵的那个环节挪出请求路径。
边缘计算的价格结构,决定了 Agent 流量到底贵不贵
📌 常见问题 FAQ
Cloudflare 说超过一半流量不是人类,具体指什么?
按公告原文,这是指 Cloudflare 网络上「人类产生的流量已不足一半」,指的是他们自己的网络统计,而不是全网。非人类流量包括爬虫、监控探针、API 调用、自动化脚本与 AI Agent 等,不能全部等同于 Agent。
1700% 的增长是怎么算的?
公告的表述是一年内 Cloudflare 网络上 AI Agent 的日均请求增长超过 1700%。这个数字依赖他们对「AI Agent 请求」的识别方法,不同识别口径会得出不同结果,因此适合作为趋势参考,不适合当成行业基准值。
我的站很小,需要为 Agent 做优化吗?
看数据。先用第一、二段脚本量一遍:如果命名 Agent 的请求占比低于 1%、且不集中在少数重页面,那优化的回报很低。如果某个工具页被同一批 Agent 每天取用上万次,做一个 JSON 端点几乎立刻见效。
直接在 robots.txt 里全封掉 Agent 不行吗?
可以,但有代价。封掉的是「被引用」的机会——很多 Agent 的价值在于把你的内容作为答案来源。更细的做法是分层:允许取事实型端点、限制重页面的抓取频率、对不遵守约定的流量限速。这比一刀切更能保住曝光。
为什么按身份限流比按 IP 好?
云上 Agent 的出口地址会轮换,按 IP 限流会误伤无关机器,也拦不住刻意轮换的爬虫。按身份(签名 Agent 名或 API key)限流更稳定,也更容易对守规矩的一方放行、对不守规矩的一方收紧。