#AI Agent#TypeScript

Agent 浏览器自动化实战

让 Agent 自己开浏览器、点页面、抓数据、填表单——agentic browsing 是 AI 自动化的高价值场景,也是工程深水区。本文讲清架构(Playwright/Puppeteer + 视觉模型)、操作模式(坐标点击 vs DOM 选择器)、健壮性(等待/重试/反爬)、安全沙箱,以及它当前能做和做不好的事。

本文要点

  • 主题聚焦:AI Agent、TypeScript
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2025年11月28日·更新于 2026年7月23日

让 Agent 自己打开浏览器、看页面、点按钮、把数据抓回来——这件事听上去很简单(不就是个 RPA 吗),真做起来全是坑:页面改一版选择器就废、滚动加载时序难判、反爬一上就封号、坐标点击差几像素点错。agentic browsing 是当前 AI 自动化里价值最高、也最容易翻车的一类场景。这篇把它从架构到落地一次讲透。

一、为什么值得做,又为什么难

价值很直接:凡是"人坐在电脑前点点点"的重复活,理论上都能让 Agent 干。典型三类:

  • 数据抓取:竞品价格、招聘信息、公开报表——结构化站点的批量采集。
  • 表单填写:批量提交、多系统录入、把一份表单填到 N 个政府/内部系统里。
  • 流程自动化:登录 → 查询 → 导出 → 下载 → 整理,这种跨系统的"胶水流程",传统 RPA 写起来比写 Agent 还累。

难在哪?不是"打不开网页",而是世界是动的、且对你不友好:

  1. 页面多变:前端一发版,DOM 结构、class 名、按钮文案全变,你写死的选择器第二天就 404。
  2. 反爬:headless 检测、频控、行为指纹、验证码——人家专门防你。
  3. 复杂交互:懒加载、无限滚动、SPA 路由、Shadow DOM、canvas 渲染——"看到"和"能点"是两回事。

所以 agentic browsing 的核心命题不是"怎么驱动浏览器"(Playwright 已经解决了),而是 怎么让 Agent 在一个不断变化、还防着你的环境里,稳定地把事干完。

二、架构:浏览器控制 + LLM 决策

经典两段式架构:浏览器引擎负责"执行",LLM 负责"看与决策"。

text
LLM(决策)  ──动作──▶  Playwright/Puppeteer(执行)  ──页面──▶  浏览器
   ▲                                                                  │
   └─────────────── 感知(截图 / DOM / a11y tree) ◀──────────────┘
  • 执行层:Playwright(推荐,API 现代、自动等待强)或 Puppeteer。负责开页面、点击、填表、截图、跑 JS。
  • 决策层:LLM 接收"当前页面是什么 + 任务目标",输出下一步动作(点击哪、输入什么、滚动、还是 done)。
  • 感知层:这是分歧最大的地方——截图(视觉模型) vs DOM/无障碍树(选择器),两种路线,下面单独讲。

一个最小的 agent 循环长这样:

ts
import { chromium } from "playwright";
import { askLLM } from "./llm";

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://shop.example.com/orders");

for (let step = 0; step < 20; step++) {
  // 感知:截图喂给视觉模型
  const shot = await page.screenshot();
  const action = await askLLM({
    image: shot,
    task: "翻到订单最后一页,然后把每笔订单的金额记下来",
    tools: ["click(x,y)", "scroll(dy)", "extract()", "done()"],
  });

  if (action.name === "done") break;
  if (action.name === "click") await page.mouse.click(action.x, action.y);
  if (action.name === "scroll") await page.mouse.wheel(0, action.dy);

  await page.waitForLoadState("networkidle"); // 等动作落地
}

循环要设步数上限——否则模型卡在"找不到目标就一直试",token 烧穿。

三、操作模式:坐标点击 vs 选择器

感知层两条路线,各有死穴。

坐标点击(视觉模型,像人):截图给多模态模型,它返回"点 (x=340, y=512)"。

  • 优点:通用。任何页面、任何框架、甚至 canvas/图片都能点。不依赖 DOM 结构,前端改版照样跑。
  • 缺点:易偏。视口缩放、设备像素比(DPR)、滚动后坐标漂移,模型也可能幻觉一个不存在的按钮。而且每次截图上传,慢且贵。

DOM / 无障碍树(选择器,精确):把页面结构序列化喂给模型,它返回"点第 3 个元素"。

ts
// 用 a11y snapshot 而非原始 DOM:更小、更稳、语义化
async function snapshot(page: Page) {
  const tree = await page.accessibility.snapshot();
  return flatten(tree).map((n, i) => ({ i, role: n.role, name: n.name }));
}

const items = await snapshot(page);
const pick = await askLLM({
  task: "点击'导出 CSV'",
  elements: items, // [{i:0,role:"button",name:"导出 CSV"}, ...]
});
if (pick.i != null) {
  // 真正执行时再走 Playwright 的语义定位,而不是裸 selector
  await page.getByRole(items[pick.i].role, { name: items[pick.i].name }).click();
}
  • 优点:精确、便宜、省 token。点击走 Playwright 的语义定位(角色 + 名称),前端小改也不挂。
  • 缺点:依赖页面结构稳定;遇到 canvas、复杂 Shadow DOM、纯视觉交互就抓瞎。

实战选型:优先 a11y tree(便宜、稳),视觉模型作为兜底——结构化站点 90% 的操作用选择器搞定,碰到 canvas/验证码这种"结构里没有的"再切截图。别一上来就纯视觉,既贵又抖。

四、健壮性:别 sleep,要等待,要能恢复

这是 agentic browsing 真正拉开水平的地方。90% 的"昨天还能跑今天就挂",都是时序问题。

铁律:不要 waitForTimeout。 固定 sleep 是定时炸弹——网络快了浪费、慢了没等到就崩。永远用显式等待条件:

ts
// 错:盲等
await page.waitForTimeout(3000);

// 对:等到某件事发生
await page.getByRole("button", { name: "提交" }).click();
await expect(page.getByText("提交成功")).toBeVisible({ timeout: 10_000 });
await page.waitForURL("**/dashboard");
await page.waitForLoadState("networkidle"); // 等 XHR 都消停

重试与恢复。Agent 的每一步都可能失败,要包一层"试几次、不行就换路":

ts
// 元素可能压根不在(比如"下一页"已禁用),别让 Agent 傻等
async function clickMaybe(locator: Locator, retries = 3) {
  for (let i = 0; i < retries; i++) {
    if ((await locator.count()) === 0) return false;
    try {
      await locator.first().click({ timeout: 2000 });
      return true;
    } catch {
      await locator.waitFor({ state: "visible", timeout: 2000 });
    }
  }
  return false;
}

滚动与分页。懒加载和无限滚动是 agent 的老朋友:每滚一段、等 networkidle、再让模型判断"还有没有更多"。没有终止条件的循环就是死循环——要么靠"按钮 disabled/消失",要么靠"连续两次内容没变化"作为结束信号。

可调试性。agent 跑挂了,你得能回看"它当时看到的是啥、点了啥"。开 Playwright 的 trace:tracing: { snapshots: true, screenshots: true },失败时导出 trace 用 viewer 逐帧回放,比对着日志瞎猜高效十倍。生产里建议把每次任务的 trace 和动作日志一起存,排障和回归测试都靠它。

五、反爬对抗:能装的尽量装,该认怂就认怂

反爬不是技术硬刚,是降低被识别概率 + 知道何时该交给人。

  • UA 与指纹:默认 headless UA 一眼假,换真实 UA;用 playwright-extra + stealth 插件补 navigator.webdriver 等指纹。但别迷信——专业风控照样能抓。
  • 节奏:人是慢的、随机的。请求之间加随机延时(几秒到几十秒),别 10 个并发一起上。频控是反爬里最朴素也最有效的。
  • headless 检测:能跑 headless: false + xvfb 就别跑纯 headless;实在要 headless,chromium 比 chrome 更易被识别。
  • 登录态:能复用 cookie/storageState 就别每次走登录(登录页往往是风控最重的)。
ts
const context = await browser.newContext({
  storageState: "session.json",   // 复用登录态,别老去触发登录风控
  userAgent: "Mozilla/5.0 ...",   // 真实 UA
  viewport: { width: 1440, height: 900 },
});
await sleep(2000 + Math.random() * 6000); // 每步之间随机停顿 2~8s

验证码:该认怂就认怂。 滑块、点选、reCAPTCHA——绕验证码在多数场景是法律和 TOS 的红线,而且成功率感人。生产里正确做法是检测到验证码就暂停,把会话交回人工,而不是去搞打码平台。Agent 的价值是"批量处理 90% 的常规请求",剩下 10% 的硬骨头交给人,这才是合理分工。

六、安全:沙箱、凭证隔离、别让它乱逛

Agent 能开浏览器,意味着它能点任何链接、提交任何表单。不受控的 agent 浏览器 = 一只会乱跑的猫,可能点到钓鱼页、可能把凭证 POST 到第三方。

域名白名单是第一道闸:

ts
const ALLOWED = ["example.com", "api.example.com"];

await context.route("**/*", (route) => {
  const host = new URL(route.request().url()).hostname;
  return ALLOWED.some((h) => host.endsWith(h))
    ? route.continue()
    : route.abort(); // 任何非白名单请求直接拒
});

其他硬约束:

  • 浏览器沙箱:在容器/VM 里跑,别在开发机裸跑。容器内 --no-sandbox 是可以的,但前提是容器本身已经隔离。
  • 凭证隔离:每个任务用独立的 storageState,跑完即弃,不复用、不落盘明文。敏感凭证走密钥管理,别写进 prompt。
  • 限制能力面:禁用文件下载到任意路径、拦截 file://、关闭弹出窗口、设置下载目录白名单。
  • 审计日志:记录每个动作(访问了什么 URL、点了什么、提交了什么),出问题能回放。

七、能力边界:什么能做好,什么别硬上

心里要有数,别给老板画饼。

能做好:

  • 结构化站点的重复操作(批量抓取、批量录入、定期巡检)。
  • 流程相对固定、页面变化频率低的内部系统和后台。
  • 有 API 就别用浏览器的场景——API 永远优先于浏览器自动化。agentic browsing 是"没有 API 时的最后手段",不是首选。

做不好(别硬上):

  • 强反爬/验证码密集的站点(支付、社交、票务):ROI 极低,稳定性极差。
  • 复杂 SPA + canvas 重度交互(在线文档、设计工具):DOM 里啥都没有,视觉点击又抖。
  • 高风险不可逆操作(转账、下单、删除):必须有 dry-run + 人工确认,绝不让 agent 全自动点到底。

经验法则:如果一个任务你能用 API 解决,就别上浏览器;能稳定 selector 化,就别上视觉模型;能让人审一下最终动作,就别全自动。 agentic browsing 的工程价值在"把人工点击的 80% 自动化",剩下 20% 留给人,而不是追求 100% 无人值守——那通常是个无底洞。

小结

  • 架构是两段式:Playwright 执行 + LLM 决策,分歧在感知层(视觉 vs a11y tree)。
  • 优先选择器,视觉兜底:结构化场景用 a11y tree(便宜稳),canvas/视觉交互才切截图。
  • 健壮性靠显式等待,不是 sleep:条件等待 + 重试恢复 + 明确的终止条件,是稳定性根基。
  • 反爬靠节奏和指纹,验证码交人工:别硬刚风控,agent 只吃 80% 常规流量。
  • 安全靠白名单和沙箱:域名限制、凭证隔离、容器隔离,别让 agent 自由冲浪。

一句话:agentic browsing 的难点从来不是"打开网页",而是"在一个不断变化、还防着你的世界里稳定地完成任务"。 把架构做对(两段式 + 感知分层)、把时序做对(等条件别 sleep)、把边界画清(API 优先、验证码交人),它就是当下 AI 自动化里 ROI 最高的场景之一;反过来想靠它"全自动绕过一切风控",那大概率是给自己挖坑。

Related

相关推荐