首页 / 文章

为何编程智能体不断重塑浏览器

为何长期运行的编码代理会趋向于浏览器形态的工具:辅助脚本、作为内存的Cookie、紧凑的页面视图、HTTP回退机制以及训练数据偏差。

发布者:REACTAPP.TOP阅读约 12 分钟2722 词

来源: Why Do AI Agents Love Building Web Browsers?

没人要求的浏览器

这些编程机器人有着一种重复出现的习惯:由于可以免费访问网络、拥有编写和执行代码的能力,再加上工作时长足够长,它们往往会构建出类似浏览器的工具。其实它们并非出于想要浏览器的动机,而是追求一种廉价且可重复使用的工具,能够打开URL、点击操作、填写表单、等待并提取数据、保存Cookie和会话信息,以及在出错时截取屏幕截图。从这种角度来看,这不过是人类开发者会做的日常工程工作罢了。

Chromium作为产品并非最终目标,这些机器人追求的其实是浏览器所具备的功能——将网络协议和规范转化为可执行的操作。

产品与抽象概念

所谓 Chrome 或 Firefox 那样的浏览器其实是一个庞大的应用程序:包含标签页、地址栏、渲染引擎以及进程模型。而这些都不是智能体所需要的。它需要的是将网页的运作方式转化为可直接执行的操作。

人们在使用时并未意识到这一点。登录页面显然会要求输入凭证,按钮显然可以点击,你点击后等待下一页出现,然后继续操作。而智能体无法依赖这种隐含的共识,因此必须将同样的工作转化为明确的操作步骤。如果让一个编程智能体在任意网站上工作,你就给了它一个其自然抽象形式早已明确的任务,于是它就会朝着那个边界方向发展。

现代网页也并非为作为结构清晰的 API 而设计的。通常它包含被 JavaScript 包裹的 HTML 文档,还有登录会话、Cookie、重定向、客户端渲染、无限滚动、弹窗、文件上传以及异步请求等功能。单纯的 HTTP GET 请求仅仅是个开始,在这样的环境中处理实际任务最终需要执行一系列类似的操作。

open(url)
click(selector)
fill(selector, text)
wait_for(selector)
extract(selector)
go_back()
screenshot()

那不是图形化浏览器,但无疑是一种通用的网络访问界面。

为何长时间任务需要辅助工具

简短的任务可以临时处理。一次性查询无需任何基础设施,因为不会重复进行且几秒钟就能完成,所以可以直接用暴力搜索的方式处理。

"Find the current exchange rate."
"Look up this company's homepage."
"Find the opening hours of this restaurant."

长时间运行的任务则有所不同。想象一下,有一个智能体每隔几小时就要监控数百个网站,反复执行复杂的流程,在保持登录状态的同时抓取页面内容,持续数天收集证据,或者不断轮询某个服务直到有变化出现。如果让模型处理每一次点击和每一次检查,成本将会急剧上升。一种简单的执行方式如下:

  1. 打开浏览器,这是获取页面内容的工具。
  2. 检查页面,以渲染后的DOM作为观察依据。
  3. 找到搜索框,输入查询内容,点击“搜索”并等待。
  4. 提取数据。
  5. 对数据进行分析处理。
  6. 重复上述步骤。

而一个功能完备的智能体则会像人类开发者一样,通过编写可重用的辅助程序来应对这类任务。如此一来,后续的执行流程就会变为:

  1. 运行脚本。
  2. 检查输出结果是否仍然正确。
  • 仅当模型无法完成任务时才再次调用它,例如选择器返回空结果、页面结构发生变化,或提取的数据不再符合任务要求时。
  • 这属于重大的架构变革。模型将不再执行任何操作,而是负责决定应运行何种流程,而普通软件则使用选定的工具并收集相关数据。换言之,智能体会将自身的经验编译成脚本。

    在浏览器中探索,用代码重放

    浏览器是进行探索的绝佳工具。智能体可以在其中四处查看、导航、观察结果,并找出实现目标的操作序列。之后再逐一分析每个操作则是浪费时间。

    假设该智能体得知,在购物网站上查找产品总是需要进入首页,搜索“USB-C数据线”,设置四星及以上、快速配送以及价格上限等筛选条件,浏览搜索结果,然后提取产品名称、价格和产品编号。语言模型没有理由每次运行时都重新执行这五步操作,它可以将这些操作封装成流程并重复用于类似问题。不过这需要通过Cookie保持会话状态,因此该智能体选择Puppeteer、Playwright或类似工具作为页面获取工具。

    async function getNewProducts(query) {
      const browser = await playwright.chromium.connectOverCDP(
        `wss://${AUTH}@brd.superproxy.io:9222`,
      );
      const page = await browser.newPage();
      await page.goto("https://example.com/products");
      await page.fill("input[name=q]", query);
      await page.click("button[type=submit]");
      await page.waitForSelector(".results");
      return extractResults(page);
    }
    

    该代码片段通过 Chrome DevTools 协议将 Playwright 与远程托管的浏览器连接起来。当需要一个长期运行的代理每隔几小时就保持会话活跃且无需模型参与时,使用托管浏览器是合理的选择。它需要将账户凭证格式化为连接字符串,${AUTH} 占位符即代表这一内容。

    对于没有预置抓取工具的网站,若要进行可重复的结构化数据提取,甚至无需本地 Playwright 文件。同样的工作流程可以被整合到托管式抓取工具中:用通俗语言描述所需数据,即可获得自动生成的抓取器,然后在点击循环中运行它而无需模型。它的交互与解析功能实际上与传统的打开、点击、填写、等待、提取和截图操作相同,只是被托管在远程服务器上而已。

    一旦CSS选择器发生变化,这样的脚本就会失效,迫使智能体重新探索并编写新的脚本。如果该流程位于托管工具中,人工智能或许能够在无需任何人重新编写选择器的情况下修复它。

    人类开发人员的行为也是如此。首次访问一个网站时就会了解其运作方式,之后就无需再考虑每一次点击。当网站发生变化时,虽然仍会重新学习,但这些知识会以隐性形式存在。而大型语言模型则拥有成本更高的“记忆”。如果它每次都必须根据上下文重新构建流程,就会反复付出同样的“探索成本”。因此它会将学到的工作流程保存在脚本中并持续运行,直到页面不再匹配为止。

    作为模型外部记忆的Cookie

    至少在目前,没有任何语言模型能够在其上下文中维持长时间的网页会话。编写 .py 文件仍会调用模型,而存储 Cookie 则不会。随着会话的进行,浏览器会自行积累内存,因此这些内存已不在智能体的存储范围内。它会涉及三个相关部分:

    • Cookie 存储区,包括 HttpOnly Cookie 以及登录或机器人检测后由 JavaScript 设置的 Cookie。通常模型根本无法读取这些 Cookie,但浏览器会将其保留并附在后续请求中。
    • 源站点存储区,即 localStorage、sessionStorage 和 IndexedDB,其中的内容几乎全部由网站自行写入。
    • 会话历史记录,也就是实现 back() 或 tabs.goBack() 功能的后退/前进栈。

    有些状态存在于浏览器存储之外,比如服务器端的会话,或是只有在 JavaScript 执行后才会出现的 URL。代理无需自行读取这些内容,只需操作已包含该会话的浏览器层即可。

    交由浏览器处理的安全风险

    当代理能够读取任意页面和 Cookie 并操控实时会话时,提示注入就不再只是提示设计的问题,而变成了架构层面的隐患。任何运行实时浏览器的系统都会面临这一问题,但编程型代理的情况更为糟糕,因为它们通常可以随意编写并执行遇到的任何代码。应限制代理在无人监控时的操作行为,可从沙箱环境开始。

    一个不错的模板是Anthropic提供的计算机使用检查清单:使用专用虚拟机或容器,除非任务需要否则无需登录,维护域名允许列表,并要求人工审批任何涉及金钱或同意的内容。

    为何原始HTML不适合作为输入

    原始页面对语言模型而言是极差的输入,因为其中包含大量冗余信息。模型所需的内容可能埋藏在成千上万的标记、样式及应用代码之中。因此,一个好的浏览器工具会返回经过简化后的操作与观察结果,而非完整的HTML代码。

    该图展示了HTML页面的文档对象模型,也就是紧凑视图或无障碍视图需要压缩的树结构。实用的工具可以将它转换为规模小得多的形式。它不会提供整个文档,而是给模型类似这样的简短概要。

    Title: Search results
    Links:
      [1] Home
      [2] Next page
    Forms:
      search: input[name=q]
    Buttons:
      submit: button[type=submit]
    Main content:
      ...
    

    或者它可以展示无障碍访问树结构。

    - navigation
      - link "Home"
      - link "Search"
    - main
      - heading "Results"
      - list
        - listitem "Item 1"
        - listitem "Item 2"
    

    那种形式更接近推理模型所能使用的结构。因此,浏览器形态的层不仅能够实现交互,还将环境简化为一系列简洁的动作与观察结果。智能体只能看到与其任务相关的网页内容,而看不到页面渲染背后的机制。上下文得以保留,推理成本降低,错误虽然并非总是出现,但也有所减少。

    不过有一个需要注意的问题:包含所有引用信息的完整无障碍快照其大小可能会超过可见文本的容量。只有当封装层真正返回一份简短且易于管理的控件列表时,压缩才有意义。列表的简短程度取决于模型本身——小型模型无法使用原始DOM,而强大模型虽可使用,但仍需支付相应的token成本。两者都不需要完整的HTML格式。

    何时普通HTTP就足够了

    在代理程序了解某个网站的工作原理后,最廉价且可靠的实现方式可能根本不需要使用浏览器自动化技术。有时候,浏览器在背后所做的其实只是非常简单的事情。一个“下一页”按钮最终可能只是执行这么简单的操作而已。

    GET /api/products?page=2
    

    在这种情况下,渲染页面毫无意义,代理可以直接调用该端点。这样就形成了一系列可选方案。

    human interface
          ↓
    browser automation
          ↓
    discovered workflow
          ↓
    direct HTTP/API calls   ← only if the site still allows it
    

    它的价值在于展示应用程序的运行方式。一旦理解了这一点,代理就可以去除中间层,有时甚至直接使用纯HTTP。对于那些无需处理大量JavaScript代码的页面,代理完全可以跳过浏览器自动化操作,直接使用HTTP,最好再准备一个解封服务以应对被拒绝的请求。无论采用何种方式,自主代理都需要有备用方案,因为在开放网络环境中几乎必然会遇到Cloudflare防火墙或反机器人检测。如果没有低成本的重试机制,代理就会迷失方向,消耗更多资源,从而增加费用。

    存在两个复杂因素。

    首先,很多网站无法简化为仅使用HTTP。大多数网站都不允许被逆向工程:它们会限制非浏览器流量、更改选择器、识别客户端特征,还会添加反机器人检测或验证码。对于这类网站,无论是在本地还是通过托管服务,都需采用完整的浏览器处理方式。

    其次,备用方案并不总是有效。有文档支持的公共 JSON API 结构清晰、范围明确,且日志记录较为简单。而那些没有文档、由代理自行摸索出的内部接口则是另一回事:虽然调用成本更低,但相比点击可见按钮的脚本,人类更难对其进行审计,且日志信息也更少。充满冗余数据的 200 状态响应很容易被忽视,而出问题的页面则十分显眼。

    归根结底,代理并不忠于任何浏览器。它所追求的只是能够正常工作的、成本最低的与外部环境交互的接口:

    • 如果该接口是 JSON 端点且网站支持它,就使用 JSON。
    • 如果需要 JavaScript 执行和会话状态,就通过 Puppeteer 或 Playwright 使用真实浏览器,或使用托管浏览器。
    • 如果只需要下载 HTML,就使用 HTTP,若 GET 请求被阻止,则可使用代理或解封工具。
  • 对于没有现成抓取工具的网站,若需进行可重复的结构化数据提取,可将任务整合到托管的抓取工作流中,由代码工作线程处理HTTP请求,由浏览器工作线程处理实时会话。
  • 当重点在于可访问性树时,可直接将其交付。
  • 长期运行的网络代理通常会同时使用多种此类方法。关键技能在于根据实际情况在不同层级之间灵活切换。

    训练数据能解释这一点吗?

    答案并不明确。训练数据可以解释模型为何选择Playwright或Puppeteer,但却无法解释为何要最初开发类似浏览器的工具。

    2026年牛津大学与微软共同开展的名为BiasBusters的研究测试了与该问题密切相关的另一个问题,即训练数据中的相关内容是否会影响模型所选择的工具。研究团队继续使用一个包含大量元数据的语料库来训练一个小型模型,该语料库来自提供相同服务的多个天气API之一。

    • 在此之前,该模型几乎从未选择过那个API,概率仅为0.6%,与纯随机选择的概率大致相当。
    • 在经过带有偏见的语料库训练后,该模型选择该API的概率上升到了约12%,是之前的20倍。

    简而言之,无论任务是什么,仅在训练过程中进行相关操作就能显著影响模型更倾向于使用哪种工具或产品。不过这仅涉及工具的选择而已。具体的抽象操作(如打开、点击、填充、等待、以特定形状提取数据,具体由所用库决定)是由环境所规定的。即便模型从未接触过 Playwright,它仍然需要某种方式来打开页面、等待内容加载并输出结果,因为交互式页面本身就要求如此。

    这里存在两个独立的作用因素:

    • 打开、点击、填充、等待和提取这些操作的抽象形式是由环境决定的。无论模型的权重中是否包含 Playwright,交互式页面都包含导航、时间控制、操作界面以及渲染后的输出内容。
  • 根据模型所观察到的情况,这种工具——Playwright与自研客户端——的差异正如BiasBusters在可互换API的研究中所展示的那样。
  • 训练数据涵盖了后者,却未涉及前者。它无法解释为何问题会不断趋向于类似浏览器的抽象结构。更根本的原因是,网络本身就已经是按照这种结构组织的。

    回报最高的适配器

    像Claude Code这样的智能体并非在构建浏览器,而是在构建推理系统与恰好呈浏览器形态的环境之间的适配器。

    进化生物学中有一个概念叫“癌变”:即无关的甲壳类动物会逐渐演化出类似螃蟹的体型。

    那些物种并未认为成为螃蟹是个明智的选择。它们的生存环境总是对类似问题给出大致相同的答案。编程智能体可能也在走类似的路径:它们发现当前的方法效率低下,于是编写脚本,在失败时增加重试机制,认证成功后实现会话持久化,而当页面过于复杂难以处理时,则添加DOM或无障碍功能提取功能。

    The task requires the web.
            ↓
    The web exposes a browser-shaped interface.
            ↓
    The agent needs a way to operate that interface.
            ↓
    Repeated operations make ad-hoc reasoning expensive.
            ↓
    The agent writes reusable tools.
            ↓
    Those tools accumulate state, retries, and abstractions.
            ↓
    The tools start looking like a browser.
            ↓
    Known workflows get compiled into deterministic code.
            ↓
    The page changes so the agent re-explores.
            ↓
    If an API exists, use it to get the data.
    If not, and the site allows it, fall back to regular HTTP.
    If not, stay on the browser.
    

    经过足够多的轮次后,其结果会极其类似于一个小型浏览器。相比想象那些对浏览器有奇怪兴趣的数字人,这种认知模型更适用于描述自主智能体。它们的行为方式就像优秀的工程师一样:研究复杂的环境,找出其真正提供的接口,然后针对那些重复性高、具有状态变化且成本高昂的部分构建抽象层。网络早已为此目的提供了一种古老且极为成功的抽象工具,那就是浏览器。

    相关阅读

  • 通过技能提升智能体:重用、筛选、修复与验证 —— 一种让智能体持续提升自身技能的四步设计方法:保存可用代码、优化描述、提出经过审核的修复方案,并在交付前验证输出结果。
  • 利用 Agents CLI 从原型到生产环境:一个费用审批智能体示例 —— 通过 Agents CLI 构建、测试、评估并将 ADK 智能体部署到 Cloud Run,过程中还会涉及追踪功能、BigQuery 日志记录、CI/CD 流程以及 RAG 扩展功能。