ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Cloudflare Kitesurf:为AI Agent设计的远程浏览器方案

Cloudflare Kitesurf:为AI Agent设计的远程浏览器方案 Cloudflare 发布 Kitesurf 的消息出来后做 AI Agent 的团队都在关注同一个问题Agent 要读取网页、点击按钮、填写表单、判断页面状态到底用哪种浏览器方案最合适。Kitesurf 的定位非常直接——它是一个为 AI agents 构建的浏览器。和无头浏览器脚本不同Kitesurf 把浏览器当作一个可以被模型调用的远程工具同时把登录态、权限、内容提取和会话管理放在统一控制面里。这篇文章会拆解它解决什么问题、核心结构大概长什么样、如何用一个最小任务跑通接入流程以及从开发环境到生产环境有哪些配置和坑。如果说传统浏览器是给人用的图形界面无头浏览器是给自动化测试用的 DOM 执行器那么 Kitesurf 试图解决的是“AI 模型如何安全、可控、可审计地操作真实网页”。要判断它是否适合你的项目需要先理解 Agent 访问网页时真正会遇到的障碍。1. AI Agent 为什么需要专用浏览器而不是继续复用无头浏览器1.1 传统浏览器的操作方式并不适合 Agent 直接使用人在浏览器里可以靠视觉判断页面看到按钮的位置、颜色、文案再决定点哪里。AI Agent 没有这种直觉它需要更结构化的输入。一些团队用 Playwright 或 Puppeteer 编写脚本把“点击登录按钮”“填写用户名输入框”写成固定步骤。这套方案在页面不变时能用但真实网页经常变化按钮文案调整、DOM 结构重写、登录后出现动态验证码、页面内容通过异步接口加载。只要有一个元素没找到整个任务就失败。更麻烦的是安全边界。Agent 拿到的不仅是固定脚本它可能根据页面内容动态决定下一步。如果页面里出现一个恶意链接或者某个表单要求提交隐私数据脚本不会思考它只会执行。普通无头浏览器没有内置的域名白名单、动作白名单和审计机制一旦 Agent 被诱导访问内网地址或者把 Token 拼接进一个不该访问的 URL问题会非常难排查。Kitesurf 的切入点就在这里它把浏览器从“页面执行器”升级为“Agent 的受控工具”。浏览器本身运行在远端Agent 通过统一接口操作页面页面内容、用户动作、请求流量都在同一套策略体系里管理。1.2 从 Selenium、Playwright 到 Agent Browser 的演进Selenium 时代解决的核心问题是“浏览器自动化”它把浏览器操作封装成 WebDriver 协议适合回归测试。Playwright 和 Puppeteer 把自动化能力提升了一截可以拦截请求、模拟移动端、快速截图、生成可靠等待。但这些工具生来不是给大模型用的。Agent 场景下的浏览器需求更接近任务编排Agent 需要一次性完成“打开页面 - 登录 - 查询数据 - 提取结果 - 退出”这样的多步动作。Agent 需要在动作失败后自动重试并且能判断重试是否有效。Agent 需要在多个会话之间安全切换不同的身份和权限。平台方需要记录 Agent 每一步做了什么方便审计和回滚。Agent 能访问的范围必须被限制不能让它随心所欲地打开任何 URL。把这几条放到一起不能只靠一个浏览器的 API。它需要一套“控制面”管理身份、权限、会话、配额、日志再把控制能力暴露给 Agent。Kitesurf 可以理解为 Cloudflare 在这条路径上的产品化尝试。1.3 Kitesurf 在 Cloudflare 体系中的位置Cloudflare 过去几年已经有 Remote Browser Isolation远程浏览器隔离和 Browser Rendering API。前者主要用于安全访问场景用户点击链接时网页在远端沙箱中执行本地只收到渲染结果。后者允许开发者在 Workers 里调用 Playwright 启动浏览器完成渲染任务。这两者解决的是“人访问网页”和“脚本渲染网页”但都没有专门为“AI Agent 自己决策如何访问网页”设计。Kitesurf 补上的正是这一层。它把浏览器运行在 Cloudflare 边缘节点上由 Agent 控制面统一调度。Agent 不需要维护本地的 Chromium 进程也不需要担心系统依赖缺失。开发者在代码里通过 SDK 或 HTTP 接口创建会话、执行动作、提取信息然后把结果交给模型做下一步决策。好处是明显的本地环境不需要安装浏览器内核页面执行不会拖垮本地 CPU安全策略集中在云端可以统一更新所有动作都有日志方便定位问题。当然这也意味着你的 Agent 工作流对 Kitesurf 服务的 API 和网络稳定性有更强依赖后面会专门聊生产环境的注意点。2. Kitesurf 的核心设计思路与关键组件2.1 浏览器运行时远程浏览器隔离架构Kitesurf 浏览器运行在远端而不是在本地进程里。这和 Remote Browser Isolation 的架构很像页面请求、JavaScript 执行、DOM 渲染都发生在沙箱环境里客户端只接收结构化的提取结果、截图和关键事件。这种架构对 Agent 有几个具体好处。第一Agent 不需要下载完整页面资源。传统做法要打开一个页面可能要加载几十个 JS 文件、图片、字体这些资源对 Agent 判断页面没有任何价值却消耗时间和流量。远程执行可以在解析完成后直接返回“页面标题、主要文本、可点击元素列表”等结构化数据。第二本地环境的浏览器版本不一致问题消失了。在本地用 Playwright 跑开发机是 macOS生产是 Linux可能遇到字体缺失、沙箱权限、Chromium 依赖库缺失。远程浏览器只需要服务端维护一套完整环境。第三安全边界更清晰。页面里的恶意脚本作用在远端沙箱不会接触本地网络和文件系统。但远程执行也意味着不能用本地 Chrome DevTools Protocol 直接调试。遇到页面渲染错误时需要依赖 Kitesurf 提供的日志、截图和 DOM 快照来还原现场。2.2 Agent 控制面Session、Task、Tool 交互模型从使用角度看Kitesurf 对外暴露的核心抽象可以总结为三层抽象层作用面向对象Session一个独立浏览器会话包含登录态、Cookie、浏览历史Agent 任务实例Task一个需要完成的目标动作链比如“登录后下载报表”应用开发者Tool浏览器可执行的基础动作比如打开页面、点击、填写、截图模型或脚本调用Session 是最重要的边界。每次创建会话Cloudflare 会分配一个独立的浏览器实例或隔离上下文。这个实例有自己的 Cookie、本地存储和缓存多个会话之间互相隔离。这样可以让不同用户或不同租户使用不同登录态避免数据串号。Task 负责把目标拆成可执行动作序列。比如任务“查询订单号 ORD-10086 的状态”实际执行时包含打开订单查询页面、输入订单号、点击查询、等待结果、提取状态文本、返回 JSON。Kitesurf 可以把这些动作暴露成一个一个 Tool 给模型调用而不是让模型直接操作底层 CDP 协议。模型不需要知道 CSS 选择器怎么写它只需要说“查找输入框填入这个值”控制面负责把语义映射到具体元素。Tool 层还应该包含重试和异常处理。页面元素加载慢时工具可以等待一段时间再重试动态弹窗遮挡按钮时可以尝试滚动到元素再点击。这些细节对 Agent 的稳定性影响很大最好由平台层统一处理而不是让每个模型的 prompt 去描述规则。2.3 安全边界权限组、域名策略与内容策略AI Agent 访问网页最大的风险是“越权”。一个可以自由操作浏览器的 Agent可能会访问到云服务器的元数据接口、内网管理后台、或者是企业内部系统。Kitesurf 这类产品必须有权限策略来阻止这些行为。权限组可以按项目维度设置每个策略包含几个核心维度允许访问的域名列表可以精确到example.com也可以带通配符*.example.com。禁止访问的地址列表常见内网网段、169.254.169.254云元数据地址、localhost。允许执行的动作比如只允许读取不允许点击提交表单。内容返回限制是否返回原始 HTML还是只返回清洗后的文本。域名白名单应该默认开启而不是默认为“允许所有域名”。Agent 场景下把允许范围收得越紧被恶意页面诱导的可能性就越低。对于需要访问外部页面的场景可以单独为一个任务临时放开域名并设置会话失效时间。内容策略也很关键。很多 Agent 项目只需要页面中的一段文本或一个数值不需要完整 HTML。把返回内容限制为结构化文本能降低模型被网页里隐藏文案干扰的概率也能减少无效 Token 消耗。2.4 与本地无头浏览器的对比对比维度本地 Playwright / PuppeteerKitesurfAgent Browser浏览器运行位置本地或容器内云端边缘节点环境依赖需要安装 Chromium 和系统依赖无本地浏览器依赖会话隔离自己管理 Context平台层隔离负责 Cookie 和缓存权限控制需要自己实现内置域名、动作、内容策略日志审计需要自己接入平台统一记录动作日志模型交互需要封装工具提供 Task/Tool 抽象适合模型调用成本主要消耗本地资源按会话/请求计费需要控制配额可调试性本地调试方便可直接看 DevTools依赖日志和截图调试链路更长这张表不是说要完全替代 Playwright 本地方案。本地方案在调试阶段、离线环境、需要自定义浏览器内核参数时仍然有优势。Kitesurf 更适合已经跑通原型、需要上生产并做权限和审计控制的团队。3. 最小接入从零跑通一个 Agent 浏览器任务3.1 准备账号、项目与环境开始之前先确认下面几项已经准备好一个可用的 Cloudflare 账号且可以开通 Kitesurf。不同区域的套餐和开通方式以官方控制台为准。API Token权限范围至少包含 Kitesurf 的读写权限。建议不要使用全局 API Key使用细粒度 Token。Node.js 18或者 Python 3.9。下面示例用 Node.js 语法说明思路实际语言客户端请以官方 SDK 为准。一个可以访问外网且能连接 Cloudflare API 的开发环境。环境要求表项要求说明账号Cloudflare 账号开通 Kitesurf控制台按提示申请API Token最小读写权限不要使用全局 Token运行时Node.js 18 或 Python 3.9取决于 SDK 版本网络能访问 Cloudflare API生产环境考虑在边缘计算节点调用减少延迟3.2 安装 SDK 并初始化客户端下面代码只是示例用于说明接入思路不是官方库的实际包名和 API。具体依赖名和初始化方式要以 Kitesurf 当前文档为准。npm install cloudflare/kitesurf初始化客户端import { Kitesurf } from cloudflare/kitesurf; const client new Kitesurf({ apiToken: process.env.CLOUDFLARE_API_TOKEN, accountId: process.env.CLOUDFLARE_ACCOUNT_ID, });这里有几个容易踩的坑。第一API Token 不要写死在代码里应该通过环境变量注入。第二accountId 不要搞错一个账号下可能绑定多个 Cloudflare 产品给错了会报权限错误。第三SDK 版本要和你开通的 Kitesurf 服务版本一致老版本 SDK 可能不包含新接口。3.3 创建会话并执行导航与提取一个最简单的任务流是打开一个网址读取页面标题和正文摘要然后关闭会话。const session await client.sessions.create({ name: demo-session, browser: chromium, viewport: { width: 1280, height: 720 }, timeoutSeconds: 60, }); try { await session.navigate(https://example.com); const title await session.extract({ type: text, selector: h1, }); const summary await session.extract({ type: structured, fields: { description: meta[namedescription], mainText: main p:first-of-type, }, }); console.log(title:, title); console.log(summary:, summary); } finally { await session.close(); }关键点session.navigate不是简单的打开页面它是在远程浏览器里执行完整导航并等待页面达到可交互状态。session.extract是结构化提取返回的是字段和值的映射不是原始 HTML。这比直接让模型读整页源码要省很多 Token。这里要特别提示selector的写法和 Playwright 的locator类似但并不是所有 CSS 选择器都适配所有页面。遇到动态渲染的页面建议在extract前增加等待条件或者把等待逻辑放进工具的navigate参数中。3.4 验证输出结果和清理会话正常执行时控制台会输出类似下面的结果title: Example Domain summary: { description: Example Domain, mainText: This domain is for use in illustrative examples in documents. }如果输出为空先检查页面是否使用了 iframe 或者 Shadow DOM这两类内容通常需要特殊的提取方法。如果页面加载很慢检查timeoutSeconds是否太短以及目标站点是否对远端浏览器 IP 做了限制。无论任务成功还是失败session.close()都要执行。Kitesurf 的会话是云资源不关闭会话会持续占用配额。实际项目中建议用try/finally或异步任务框架的清理钩子来确保释放。4. 关键参数和安全配置详解4.1 会话参数超时、视口与导航策略创建会话时的参数直接影响 Agent 的执行表现。常用参数如下参数名默认值示例说明timeoutSeconds60单次导航或动作的最长等待时间viewport1280x720浏览器视口大小影响响应式页面和截图waitUntilload何时认为页面加载完成可设为networkidlerejectResourceTypes空需要拦截的资源类型如image,font,mediauserAgent默认 Chromium UA自定义 UA不建议伪造真实浏览器 UAwaitUntil参数非常影响任务时长。如果设置为networkidle页面在加载完所有请求后才返回适合需要完整数据的任务但可能等待很久。如果设置为load则更快但某些异步接口可能还没返回数据提取时容易拿到空值。建议在开发阶段用networkidle排查问题上生产后再根据场景调整为更快的策略。rejectResourceTypes可以显著提升加载速度。对于只需要提取文本的任务可以把image、font、media全部拦截。注意有些站点会在图片加载完成后才渲染某些元素拦截图片可能导致元素缺失。本地多测试不要直接套到生产。4.2 权限控制域名白名单与动作限制权限配置是生产环境的第一道防线。建议把策略文件放在项目里用代码审查的方式来管理变更而不是在控制台手工点。{ name: project-demo-policy, allowlist: [ example.com, *.cloudflare.com ], denylist: [ localhost, 127.0.0.1, 169.254.169.254, 10.0.0.0/8, 192.168.0.0/16 ], actions: { navigate: true, click: true, fill: true, screenshot: true, download: false }, content: { returnRawHtml: false, maxTextLength: 5000 } }这个配置说明了几个关键点allowlist决定 Agent 能去哪些域名denylist是兜底防止配置错误导致内网泄露。actions控制动作可用性比如下载功能在很多 Agent 场景里没必要开放。content.returnRawHtml建议保持false让平台返回清洗后的文本减少模型被隐藏代码干扰的概率。配置权限时最容易犯的错误是为了方便调试把allowlist设为*调试完忘记收紧。这个权限一旦放出去Agent 就可能被页面诱导访问任意地址。建议把所有任务先放到一个默认拒绝的策略里遇到新域名再单独审批添加。安全原则永远是“默认禁止按需放行”。4.3 验证码、登录态和 Cookie 处理Agent 访问真实业务系统时登录态和验证码是绕不开的问题。Kitesurf 这类产品通常会把 Cookie 作为会话的一部分保存下来但不同策略下 Cookie 的持久化方式不同。登录态处理思路如果任务需要登录第一步先导航到登录页用 Agent 填写账号密码。登录成功后把会话标记为“可信”后续任务复用同一个会话避免重复登录。如果需要长期复用可以把会话 ID 保存到自己的数据库里下次通过sessionId重新打开。不要让多个任务共享一个含敏感权限的会话除非确认该会话只访问白名单域名。验证码是正常自动化最容易卡住的地方。Kitesurf 本身不一定提供打码服务你需要在自己的任务里做判断如果页面出现验证码是暂停等待人工处理还是调用第三方验证码服务还是直接标记任务失败并通知人工。生产环境建议设置“验证码失败重试次数”最多 2 到 3 次避免 Agent 在同一个验证码页面上反复空转消耗会话额度。4.4 限流与配额配置Agent 浏览器会话是云资源每个会话都会有时间成本和配额成本。如果不设置配额可能出现一个异常任务长期占用资源或者某个调用方把额度耗尽。需要关注的配额指标指标建议做法原因并发会话数按项目或租户设置上限防止某个任务异常创建大量会话单会话空闲时间设置idleTimeoutSeconds长时间无操作自动释放资源单任务总时长设置任务级超时防止死循环或等待超长页面每月调用次数按项目和账号统计用于成本和预算控制数据传输量控制返回内容大小大量返回 HTML 会显著增加成本建议在实际配置时先按本地脚本估算每个任务的资源用量再乘以业务流量设定配额。配额过小会导致 Agent 任务频繁失败配额过大会产生预期外成本。先小后大观察一段时间再放宽。5. 从开发调试到生产环境的落地建议5.1 本地调试时的模拟方案Kitesurf 的云会话在开发调试时不如本地浏览器直接。为了不影响开发效率可以在代码里做一个驱动抽象本地模式使用 Playwright云端模式使用 Kitesurf切换时只改一个环境变量。// driver.js export async function createDriver() { const useCloud process.env.AGENT_BROWSER_DRIVER cloud; if (useCloud) { const { Kitesurf } await import(cloudflare/kitesurf); const client new Kitesurf({ apiToken: process.env.CLOUDFLARE_API_TOKEN }); return client.sessions.create({ name: task- Date.now() }); } const { chromium } await import(playwright); const browser await chromium.launch({ headless: true }); const context await browser.newContext(); return new PlaywrightDriver(context); }这样本地跑单元测试时不调用云 API直接用 Playwright 模拟。部署到生产环境时设置AGENT_BROWSER_DRIVERcloud切到 Kitesurf。抽象层的关键是保证接口一致比如navigate、extract、click、close。这套抽象不仅能方便本地调试也能在 Kitesurf 服务故障时临时切回本地方案。5.2 生产环境的日志、监控与告警上了生产之后最怕的是 Agent 浏览器任务失败但日志里没有关键信息。每个任务至少应该记录会话 ID。任务开始和结束时间。每一步动作打开了哪个 URL点击了哪个元素提取了什么字段。动作耗时时长。返回状态的错误码。截图或 DOM 快照路径方便失败后追溯。日志格式建议用 JSON方便接入日志平台。不要只在 console 打印生产环境至少要接入云日志服务或自建 ELK 套件。告警规则可以关注三个核心指标任务失败率、平均任务耗时、超时任务数量。如果某个任务连续失败 5 次应该触发告警通知到负责人而不是让 Agent 一直重试。5.3 成本控制与资源复用云浏览器成本通常和会话时长、页面资源加载量、返回内容大小有关。为了省钱可以做好几件事对不需要图片的页面开启资源拦截减少带宽和加载时间。把常见页面文本提前缓存到自己的数据存储中Agent 直接从缓存读取不重复打开页面。长会话复用登录态但不要长期占用空闲会话空闲 5 分钟就关闭。任务失败时要有指数退避重试不能高频重试。成本控制不是一上来就做而是等模型跑通之后逐步优化。关键是记录每个任务的成本才好在优化前后做对比。5.4 发布前检查清单上线一个小型 Agent 浏览器服务之前建议逐个确认下面这些项检查项状态域名白名单已配置且不包含内网网段必选禁止访问元数据地址已在策略中声明必选API Token 已限制权限不混用全局 Key必选本地无浏览器路径硬编码全部走驱动抽象必选所有动作日志已输出且不包含敏感字段必选设置了会话空闲超时和任务总时长超时必选验证码失败有明确的重试上限建议成本配额有监控告警建议失败任务有截图或 DOM 快照留存建议有方案可以临时切换回本地驱动建议这个清单适合每次发布前跑一遍。尤其是在策略调整之后必须确认白名单没有因为临时调试被错误放宽。6. 常见问题排查与最佳实践6.1 常见报错原因和处理方法问题现象常见原因检查方式处理建议创建会话超时API Token 权限不足或服务未开通检查控制台授权和服务状态重新生成 Token确认开启 Kitesurf导航后页面空白页面被资源拦截影响渲染查看截图和 DOM 快照关闭相关资源拦截延迟提取提取字段为空元素在 iframe 或 Shadow DOM 内在本地用 Playwright 检查 DOM调整 selector或改用完整文本提取访问内网地址被拒绝权限策略生效查看策略日志确认该域名是否需要加入白名单会话一直占用配额忘记调用 close查看会话列表用 try/finally 确保关闭页面提示可疑流量远端浏览器行为被站点识别查看请求日志和验证码降低访问频率设置合理等待出现问题时按顺序排查先看输入参数是否正确再看权限策略是否拦截然后看网络错误之后看 DOM 是否按预期渲染最后看服务端是否有错误码。不要一上来就怀疑 Kitesurf 不稳定大部分问题出在页面本身或权限配置。6.2 从现象到根因的排查链路一个实用排查链路可以整理成五步确认会话创建是否成功拿到 sessionId。用同一 sessionId 查询访问日志看 navigate 请求是否返回 2xx。拉取截图确认页面渲染状态。截图一片白说明资源加载或脚本执行有问题。执行一次最小提取比如读取document.title判断浏览器上下文是否正常工作。如果最小提取正常再逐步加入复杂选择器和结构化字段缩小问题范围。这个链路能把“页面空白”这类模糊问题拆解为“导航失败”“资源被拦”“DOM 未渲染”“选择器错误”中的一个。排查时保留失败现场尤其是截图和 DOM 快照比多次复现更高效。6.3 最佳实践不要把 API Token 放进页面参数或日志。若 Token 意外出现在日志中立即吊销并重新生成。不要使用裸try/catch吞掉异常。至少记录错误码、消息和会话 ID方便回溯。不要默认返回整页 HTML。优先使用结构化提取减少 Token 消耗也降低模型被无关信息干扰的概率。不要把 Cookie 持久化和权限放行混在一起。一个需要登录的会话即使 Cookie 有效也只能访问白名单内的域名。不要在一个任务里同时启动多个无状态会话。复用已有会话但必须设置空闲超时。对 Agent 的每个动作都要有审计记录。出了问题能回答“这个动作是谁触发的对应哪个模型请求”。6.4 下一步扩展方向Kitesurf 这类 Agent Browser 很适合和 Cloudflare 的其他产品组合使用。比如把 AI Gateway 接入 Agent 请求记录 prompt 和响应的完整链路用 Workers 做请求转发统一处理限流和鉴权把提取结果输出到 R2 存储做后续分析和建模。对于已经使用 Playwright 的团队不要急着全部迁移。先用一个低频任务接入 Kitesurf对比稳定性、成本和调试体验再决定是否推广。底层驱动抽象能让你在两种方案之间平滑切换这是控制风险最好的方式。对新手来说最有价值的练习不是去网上找复杂示例而是把“打开页面、提取标题、关闭会话”这个最小闭环反复跑通。然后逐步加入登录态、权限策略、异常重试。等你真正处理过一个“验证码挡住 Agent 任务”的问题之后就会理解为什么云浏览器看起来简单生产环境里仍然需要把权限、日志和配额设计好。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进