1000 万开发者、Agent 请求一年涨 1700%:Cloudflare 这组数字该怎么读

·阅读约12分钟·Evergreen Tools Team

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)限流更稳定,也更容易对守规矩的一方放行、对不守规矩的一方收紧。