给自家网页加一个「会操作的 AI 助手」,过去只有两条重路:要么做浏览器扩展 + 无头浏览器(用户要装东西,你还要维护一套截图驱动的多模态管线),要么后端重写交互接口。阿里巴巴开源的 Page Agent(npm 包 page-agent,MIT 协议)走了第三条路:一段页内 JavaScript,让网页自己长出一个 Agent——它直接读文本化的 DOM 而不是截图,所以普通文本模型就能驱动,不需要浏览器扩展、不需要 Python、不需要无头浏览器;模型自带(BYO LLM),主流厂商与本地模型都接。适用场景官方列得很实:给 SaaS 产品嵌一个 AI Copilot、把 ERP/CRM 里二十步的表单流程变成一句话、无障碍访问。本篇从一行 CDN 试用到接自己的模型、再到生产化的密钥代理,十分钟跑通全链路。
先理解:读 DOM 摘要,而不是看截图
Page Agent 的关键技术选择是纯文本 DOM 操作:它把页面结构整理成文本摘要交给模型,模型输出「点哪个元素、往哪个框填什么」的结构化指令,Agent 在页内执行。对照主流方案看差异——浏览器自动化派(browser-use、Computer Use 类)靠截图 + 多模态模型,通用但重;Page Agent 靠 DOM 文本 + 普通文本模型,轻、快、便宜,代价是它定位为客户端页内增强而不是服务端批量自动化(官方明确说明,且其 DOM 处理组件源自 browser-use 的致谢声明里写得清楚)。这个定位决定了它的甜区:你自己的产品、你能改 HTML 的页面——把「AI 助手」当成产品功能的一部分交付,而不是让用户额外安装什么。
Demo CDN 的免费测试模型只用于评估。官方 demo 脚本走的是他们的免费测试 LLM,有独立的服务条款约束,别接到真实业务流量上;正式使用换自己的模型端点(Step 2)。
Step 1:一行 CDN,三十秒看到效果
<!-- 加到任意网页的 body 尾部(demo 模型,仅供技术评估) -->
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/iife/page-agent.demo.js"
crossorigin="anonymous"
></script>
<!-- 国内访问 jsDelivr 慢可换 npmmirror 镜像:
https://registry.npmmirror.com/page-agent/1.12.4/files/dist/iife/page-agent.demo.js -->
刷新页面,右下角出现 Agent 悬浮聊天框,直接用自然语言下指令(比如「把这个输入框填上某某」),看它逐步操作页面元素。这一步零安装零配置,用来快速判断「文本化 DOM」方案对你的页面复杂度够不够用:表单、列表、常规后台页面效果通常很好;重度 Canvas/虚拟滚动的自定义渲染要降低预期。确认方案可行后,马上进 Step 2 换成自己的模型——别让业务数据流经 demo 端点。
?autoInit=false 参数加载脚本,就不会自动创建 demo agent——随后可以用 new window.PageAgent(...) 以自己的模型初始化,同一段脚本从试用无缝切到正式。Step 2:npm 集成,接自己的模型
npm create vite@latest my-page-agent-demo -- --template vanilla
cd my-page-agent-demo
npm install page-agent
npm run dev
// main.js
import { PageAgent } from 'page-agent'
const agent = new PageAgent({
model: 'qwen3.5-plus', // 模型名以你的服务商文档为准
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
apiKey: import.meta.env.VITE_LLM_API_KEY, // 从环境变量读,别硬编码
language: 'zh-CN',
})
await agent.initialize()
window.agent = agent // 挂到 window,方便浏览器控制台调试
初始化参数就是一套标准的 OpenAI 兼容配置:model、baseURL、apiKey 加界面语言。官方支持主流模型供应商与本地部署模型(本地模型同样走 OpenAI 兼容端点),所以已有大模型接入的团队基本零迁移成本。控制台里 window.agent.execute('...') 随手下指令验证效果,预期效果:指令下达后能看到 Agent 高亮当前操作的元素并逐步执行,全程没有一次截图调用。
apiKey 绝不能硬编码进前端源码并提交仓库。本节的 import.meta.env 写法仍会把 Key 打进前端产物,仅限本地开发自用;对外发布前必须切到 Step 6 的后端代理方案。
Step 3:真活儿来了——把 20 步表单变成一句话
// 场景一:智能填表(ERP/CRM/后台系统的典型痛点)
await agent.execute('收货人填张三,电话 13800000000,地址选「上海市浦东新区」,备注写「工作日送达」')
// 场景二:跨字段联动操作
await agent.execute('在「所属行业」下拉框选中「智能制造」,把不相关的高级选项全部收起')
// 场景三:让 Agent 先读页面再干活
await agent.execute('看一下这个表格里状态为「异常」的行,把单号汇总到搜索框里')
拿一个真实业务页面验收,指令遵循官方给的场景设计:「20 次点击变一句话」。观察三件事:准确性——字段定位是否精准(这是文本 DOM 方案的核心优势,元素名和占位符都是现成语义);边界——遇到下拉框、日期选择器这类非原生控件时表现如何;反馈——操作过程与结果是否有清晰的 UI 呈现。哪类控件老出错,就把对应控件的 label/aria 属性补规范,Page Agent 读的是语义化 DOM,页面可访问性做得越好,它就越准——这也是它被列为无障碍场景用途的原因。
Step 4:产品化嵌入——关掉悬浮框,绑你自己的入口
const agent = new PageAgent({
// ...模型配置同 Step 2
showUI: false, // 不渲染悬浮聊天框,纯编程控制
})
// 绑到你自己的产品按钮/快捷指令上
document.querySelector('#btn-autofill')
.addEventListener('click', () =>
agent.execute('用已登录用户的档案信息填完整个报名表单'))
// 也可以按页面状态动态组装指令
const mode = new URLSearchParams(location.search).get('mode')
if (mode === 'renew') {
await agent.execute('进入续费流程,保留原套餐档位,只改支付方式') // 示例
}
做产品嵌入时,悬浮聊天框通常不是你想要的形态——showUI: false 把 Agent 降级成纯能力层,入口、触发时机、指令内容全部由你的产品代码掌控:可以是一个「帮我填表」按钮、一条命令面板动作,也可以是特定页面状态下的自动辅助。这个模式下指令模板建议放进配置中心统一管理:改文案、改流程不用发版,还能按租户/角色下发自不同的辅助能力。
Step 5:跨页面任务——Chrome 扩展与 MCP Server
// 通道一(可选安装):官方 Chrome 扩展
// 让 Agent 的触达范围从「当前页」扩展到「多标签页任务」
// 通道二(Beta):官方 MCP Server
// 把浏览器里的 Page Agent 暴露给外部 Agent 客户端控制
// 适合让 Claude Code / OpenClaw 这类桌面 Agent 远程指挥浏览器干活
单页内嵌是 Page Agent 的主场,但官方准备了两条可选通道应对更大的场景:Chrome 扩展面向「任务横跨多个标签页」的用户侧场景(比如从列表页逐个打开详情处理);MCP Server(Beta)面向开发者侧集成——把浏览器中的 Page Agent 作为 MCP 工具暴露出去,你的桌面 Agent 就能指挥它操作浏览器。两条通道都是可选项,纯页内方案不装它们也完整可用;生产环境启用 Beta 通道前先在小范围验证稳定性。
扩展/MCP 通道放大了能力,也放大了风险面。让外部 Agent 能操作你的浏览器意味着它的每条指令都会真实作用于页面——接 MCP 前确认指令来源可信、限制可操作的站点范围,敏感页面(支付、管理后台)保持人工操作。
Step 6:上生产——密钥代理、范围约束与失败兜底
// 1) 密钥进后端:前端只请求你自己的代理端点
const agent = new PageAgent({
model: 'qwen3.5-plus',
baseURL: '/api/llm-proxy', // 你的后端代理:持有真实 Key 调服务商
language: 'zh-CN',
})
// 2) 范围约束:指令白名单 + 数据脱敏在代理层做
// 代理层校验 system 约束、限流、审计后再转发给模型
// 3) 失败兜底:execute 返回后校验页面状态,失败引导人工
const ok = await agent.execute('把审批意见填为「同意」并提交')
if (!ok) showFallbackToast('自动操作未完成,请手动检查表单')
生产化的底线清单:密钥不落地前端——模型请求走自家后端代理,代理层顺便做限流、审计与数据脱敏(页面上的敏感字段是否要进入模型上下文,这个决策必须在代理层可配置);能力范围有闸门——用指令模板约束 Agent 只做产品设计的辅助动作,删除、支付等高风险操作保持用户手动确认;失败有出口——每次 execute 后校验预期页面状态,失败时给用户明确提示与人工路径。做完这三件,这个「住在网页里的 Agent」才能从 demo 变成功能。
版本升级注意。Page Agent 迭代活跃(npm 上版本号跳动明显,MCP Server 还是 Beta),锁版本升级、升级后把 Step 3 的业务表单用例当回归测试跑一遍;API 面以你所用版本的官方文档为准,本篇参数名对照 1.12.x。
常见问题速查
| 你遇到的现象 | 大概率原因 & 解决 |
|---|---|
| demo 脚本加载后没有悬浮框 | 脚本位置放 body 尾部重试;或加 ?autoInit=false 后自行 new PageAgent 初始化 |
| execute 没有任何动作 | 模型端点/Key 不通。先在控制台看网络请求与报错,确认 baseURL 与模型名正确 |
| 自定义控件总是操作失败 | DOM 语义不足。补 label/aria 属性与占位文案;Canvas/虚拟滚动组件降低预期 |
| 担心前端泄露 API Key | 换后端代理:baseURL 指向自家 /api/llm-proxy,真实 Key 只存在服务端 |
| 模型调用成本偏高 | 文本 DOM 方案本身省 token;仍高就检查指令是否过泛导致多轮试探,用模板化指令收敛 |
| 想让桌面 Agent 指挥浏览器 | 走官方 MCP Server(Beta)通道;先限定可操作站点,敏感页面保持人工 |