省 Token,我给 Agent 做了个网页快照生成器

0 阅读7分钟

最近,我在做一件挺有意思的事,今天来给大家分享了:

我为 Tapero 撸了一个网页快照生成器。

一开始的动机其实很简单 —— 网页实在太大了!

你如果直接把一整个页面的 HTML 丢给 AI,让它去理解、定位、操作,基本等于在烧钱。

一个稍微复杂一点的页面,动不动就几千上万行代码,里面还夹杂着各种无关的 div、style、script,对 AI 来说全是噪音,但对 Token 来说全是成本呀。

这就变成了一个很现实的问题:

AI Agent 想做网页自动化,但每走一步都要喂这么一坨东西进去,不仅贵,还不稳定(AI 也会误判)。

所以,网页快照这个东西就变得很关键了。你可以把它理解成 —— 给页面做一层压缩且结构化的描述,只保留对自动化真正有用的信息。AI 不再看原始 HTML,而是看这份精简版的快照。

业界是怎么做的?

其实这个方向已经有人在做了,比如 Playwright MCP(鼻祖),还有后来 Agent Browser 的实现。

它们大多是基于浏览器的 Accessibility Tree 来生成快照。

简单说,Accessibility Tree 是浏览器为了无障碍访问(比如屏幕阅读器)生成的一棵语义树。它会把页面里的元素转成“角色 + 文本 + 状态”这样的结构,比如按钮、输入框、标题之类的。

这种方式有个很大的优点:语义清晰,噪音很少。

它们生成的快照大概长这样(简化示意):

- heading "Welcome to Amazon"
- textbox "Search"
- button "Search"
- link "Today's Deals"

或者更结构化一点:

role=button name="Submit" focused=true
role=textbox name="Email" required=true

我这里有一张 Agent Browser 实际生成快照的截图,咱们来看看:

01-agent-browser-snapshot.jpeg

嗯,看起来很干净,对吧?对 AI 也很友好。

但它也有一个问题:信息被过度抽象了。很多原始 HTML 里的结构信息,比如层级关系、属性细节,有时候就丢了。这就可能造成误判,比如页面有两个一样的按钮,AI 不知道应该点哪个?

我的实现:基于 DOM 的快照

好,一番调研后,终于轮到我动手了,现实却给了我一拳。

我当前的执行环境,拿不到 Accessibility Tree —— 它需要更高的权限(我不想申请这个权限),或者直接在浏览器内核层做。

再加上,我其实也不太满意 Accessibility Tree 的信息损失,它太偏语义了。但在做自动化的时候,有些原始信息却很重要,比如:

  • 明确的层级结构、布局结构
  • 元素的属性(id、name、type 等)
  • 一些不在语义树里的元素(很多非标网页没有注明语义)

所以我走了另一条路:直接基于 DOM 来生成快照

思路很简单:

  1. 遍历 DOM 树
  2. 过滤掉无关节点(script、style、div 等)
  3. 提取元素的关键属性 + 文本
  4. 压缩成一个结构化的文本表示层

经过 3 次迭代后,我最后选择 Pug 子集来作为快照的格式,看起来长这样:

page(ref="n1" title="Demo" lang="en" url="https://example.com")
  form#feedback(ref="n2" inviewport)
    input(ref="n3" type="email" name="email" placeholder="Email" required)
    button(ref="n4" type="submit" inviewport) Continue

我也对同一个网页生成了快照,来和 Agent Browser 对比一下:

02-tapero-snapshot.jpg

可以看到,信息更加丰富了,但一点都不乱。当然了,我也提供了选项来输出极简快照。但我更愿意信息丰富一点,牺牲一点点 Token,让 AI 识别起来更准些!

这个格式有几个我自己挺喜欢的点:

  • 结构一眼能看懂(缩进就是层级)
  • 信息保留得比较完整(标签 + 属性 + 文本)
  • 扩展了状态属性(如 inviewport, disabled, hidden 等)
  • 对 AI 也很友好(比 HTML 干净太多)

这套实现的优势

  • 保留了更多原始信息,AI 做定位更稳
  • 不依赖浏览器底层能力,通用性极强
  • 可控性高,想加什么字段都可以自己扩展

面临的挑战

  • 伪交互元素需要自己推断

    比如一个用普通 div 实现的按钮,语义上它不是 button,但行为上是,这类判断需要额外逻辑去补。

  • 执行效率需要控制在 150ms 内

    页面越大、机器性能越差,遍历和裁剪的成本就越高,这里其实挺考验工程优化的。

我踩过的一些坑

  • 隐藏元素要不要保留?

    有些下拉菜单默认是 hidden,但点击后会出现,保留则多增加体积,不保留则会影响 AI 的下一步推断。

  • 超大数据列表要不要修剪?

    比如无限滚动的列表,如果全保留,体积直接爆炸,但裁剪又可能丢关键数据。这就需要做个平衡,增加额外的逻辑来推断,是全保留,还是抽样保留。

  • 嵌套 iframe 要不要递归处理?

    不处理会丢信息,处理又可能带来性能问题和跨域限制。还有网页中有大量广告(基本都是通过 iframe 注入的)的情况,会严重拖慢快照的生成。

  • In Viewport 判定策略

    只保留可视区域可以大幅压缩体积,但有时候关键元素在首屏外。所以,我把它做成状态,让 AI 知道哪些元素在视口中?这样可以做出更好的推断。

  • Shadow DOM 的处理策略

    很多现代组件都藏在里面,不展开就“看不见”,展开又会让结构变复杂。还有一些节点是 closed 的,不让看。好在最终都找到了合适的处理方案。

  • 节点修剪策略

    怎么从 1000+ 节点里筛到 200~300 个,是个很微妙的平衡。需要先对所有节点分类,再通过模式分层控制,决定输出哪些节点。

实测效果

说了这么多,来点实际的。我拿几个典型页面跑了一下,下面我们来看看测结果。

1. X 的个人主页

  • 页面结构高度嵌套
  • 随着往下滚动,节点数量会越来越多

以下是跑出来的结果:

  • 耗时:18ms
  • 遍历节点:989
  • 保留节点:207
  • 压缩率:≈ 2%

对于这个结果,我其实挺满意的,基本把网页信息压到了一个 AI 可以接受的范围。

03-testing-x.jpg

2. Amazon 商品详情页

  • 页面结构非常复杂,节点爆炸
  • 需要重点保留:标题、价格、图片、购买按钮等信息

以下是跑出来的结果:

  • 耗时:36ms
  • 遍历节点:3509
  • 保留节点:649
  • 压缩率:≈ 4%

这个效果已经远远超出我的期望了,极大的压缩了网页的体积。

04-testing-amazon.jpg

3. Google 搜索结果页

  • 结构相对规整,但信息密度高
  • 重点是结果列表 + 标题 + 链接

以下是跑出来的结果:

  • 耗时:24ms
  • 遍历节点:1633
  • 保留节点:280
  • 压缩率:≈ 2%

快照出来之后,基本可以直接用于“抓取搜索结果”的自动化任务。

05-testing-google.jpg

最后

这一轮做下来,我对网页快照的感受是:

它是 AI Web Agent 的地图,有了它 AI 才能规划出执行路线。

它不能太复杂,体积不能太大,AI 的上下文有限,且 Token 也不便宜。

它也不能太简单,遗漏关键信息,相当于给 AI 一版错误的地图,它再聪明也很难做出正确的事。

好了,今天就到这!感谢有 Codex 和我结对编程,才能把这个快照生成引擎做出来!

我现正在公开做一个 AI Web Agent,我会把它从 0 到 1 的整个过程都记录下来。包括技术选型、踩坑、各种取舍,都会慢慢写出来。

如果你也在做类似的事,或者对这个方向感兴趣,欢迎来聊聊。