Page-Agent - 完整学习教程

Page-Agent - 完整学习教程

教程级别: 从零到一 预计学习时间: 6-8 小时 前置知识: JavaScript/TypeScript 基础、HTML/DOM 基础、了解 LLM API(OpenAI 兼容接口)的基本使用 基于版本: v1.7.1

环境搭建指南

系统要求

  • 操作系统: macOS / Windows / Linux(任意现代操作系统)
  • 运行时/依赖版本:
  • Node.js >= 18.0.0(npm 方式安装)
  • 现代浏览器:Chrome、Firefox、Edge、Safari(需要支持 ES2020+)
  • 一个 OpenAI 兼容的 LLM API Key(推荐 Qwen、OpenAI、DeepSeek 或本地 Ollama)

安装步骤

方式一:CDN 一行引入(最快体验,适合快速评估)

在任意 HTML 页面的 <head><body> 底部添加:

<!-- 使用全球 CDN -->
<script src="https://cdn.jsdelivr.net/npm/page-agent@1.7.1/dist/iife/page-agent.demo.js" crossorigin="true"></script>

或者使用中国镜像:

<!-- 使用中国镜像(速度更快) -->
<script src="https://registry.npmmirror.com/page-agent/1.7.1/files/dist/iife/page-agent.demo.js" crossorigin="true"></script>

注意:Demo 版本使用阿里云提供的免费测试 LLM(Qwen 3.5 Plus),仅供技术评估使用,不适合生产环境。

方式二:npm 安装(推荐用于正式项目)

# 在项目目录中初始化(如已有 package.json 可跳过)
npm init -y

# 安装 page-agent
npm install page-agent

方式三:Bookmarklet(无需任何安装,适合即时体验)

访问 Page-Agent 官网 https://alibaba.github.io/page-agent/ ,将页面上的 Bookmarklet 拖拽到浏览器书签栏。然后在任意网页点击该书签即可注入 Agent。

验证安装

验证 CDN 方式:

创建一个测试 HTML 文件 test-page-agent.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Page-Agent 测试</title>
</head>
<body>
  <h1>Page-Agent 安装验证</h1>
  <button id="test-btn">点击我</button>
  <input type="text" id="test-input" placeholder="输入一些文字">
  <p id="result"></p>

  <!-- 引入 Page-Agent Demo 版本 -->
  <script src="https://cdn.jsdelivr.net/npm/page-agent@1.7.1/dist/iife/page-agent.demo.js" crossorigin="true"></script>

  <script>
    // 验证 Page-Agent 是否加载成功
    document.getElementById('test-btn').addEventListener('click', () => {
      document.getElementById('result').textContent = '按钮被点击了!Page-Agent 运行正常。';
    });
  </script>
</body>
</html>

用浏览器打开该文件,如果页面右下角出现 Page-Agent 的交互面板,说明安装成功。

验证 npm 方式:

# 在项目目录中执行
node -e "const { PageAgent } = require('page-agent'); console.log('Page-Agent 加载成功,版本:', require('page-agent/package.json').version);"

预期输出:

Page-Agent 加载成功,版本: 1.7.1

第一部分:入门篇

1.1 理解 Page-Agent 的核心概念

概念讲解:

Page-Agent 的核心思想是让网页"自己具备 AI 能力",而不是从外部去控制它。传统自动化工具(如 Selenium、Playwright)像是一个遥控器,从浏览器外面操控页面。Page-Agent 则是把 AI 直接"种"在页面里。

Page-Agent 的工作流程可以用一句话概括:用户说一句话 -> Agent "看"一眼页面 -> Agent 思考该怎么做 -> Agent 像人一样操作页面元素

这个过程的核心有三个环节: 1. "看"页面:通过 HTML 脱水(HTML Dehydration)技术,将复杂的 DOM 树压缩为 LLM 能理解的精简文本 2. "思考"怎么做:将精简文本发送给 LLM,LLM 返回结构化的操作指令 3. "操作"页面:按照指令模拟人类的鼠标和键盘事件

代码示例:

最简单的使用方式 -- CDN 引入后自动出现交互面板:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Page-Agent 最简示例</title>
</head>
<body>
  <!-- 这是一个普通的网页,有一些可交互的元素 -->
  <h1>欢迎体验 Page-Agent</h1>
  <p>在右下角的输入框中输入指令,例如:</p>
  <ul>
    <li>"点击下面的按钮"</li>
    <li>"在输入框中输入 Hello World"</li>
    <li>"选择下拉框的第二项"</li>
  </ul>

  <button onclick="alert('你通过 Agent 点击了按钮!')">测试按钮</button>
  <br><br>
  <input type="text" placeholder="测试输入框">
  <br><br>
  <select>
    <option>选项一</option>
    <option>选项二</option>
    <option>选项三</option>
  </select>

  <!-- 一行引入 Page-Agent(使用免费 Demo LLM) -->
  <script src="https://cdn.jsdelivr.net/npm/page-agent@1.7.1/dist/iife/page-agent.demo.js" crossorigin="true"></script>
</body>
</html>

执行结果:

打开页面后,右下角会出现一个浮动的交互面板。在输入框中输入"点击测试按钮",Agent 会: 1. 扫描页面 DOM,识别出按钮元素 2. 将页面状态发送给 LLM 进行推理 3. 在按钮上模拟人类的点击事件序列 4. 弹出"你通过 Agent 点击了按钮!"的提示框

练习题:

  1. 修改上面的 HTML,添加一个密码输入框和一个提交按钮,然后尝试用自然语言让 Agent "在密码框中输入 123456 并点击提交按钮"。
  2. 观察当页面有多个按钮时,Agent 如何区分它们(提示:关注 Agent 的索引编号机制)。

1.2 使用 npm 方式集成 PageAgent

概念讲解:

在 1.1 中我们体验了 CDN 方式,它适合快速原型和评估。但在实际项目中,我们需要通过 npm 安装并使用 PageAgent 的编程 API(Programmatic API),这样可以: - 完全控制 Agent 的行为和外观 - 自定义 LLM 提供商和模型 - 将 Agent 嵌入到自己的 UI 组件中 - 在生产环境中使用自己的 LLM API Key

PageAgent 类是库的主入口。创建一个 PageAgent 实例需要配置三个核心参数:model(模型名称)、baseURL(LLM API 地址)和 apiKey(API 密钥)。这些参数遵循 OpenAI 兼容 API 的格式。

代码示例:

// app.ts - 使用 npm 包方式的完整示例
import { PageAgent } from 'page-agent'

// 第一步:创建 Agent 实例,配置 LLM 连接
const agent = new PageAgent({
  // 指定要使用的 LLM 模型名称
  model: 'qwen3.5-plus',

  // LLM API 的基础地址(OpenAI 兼容格式)
  // 这里使用阿里云 DashScope 作为示例
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',

  // 你的 API Key(请替换为你自己的密钥)
  apiKey: 'sk-your-api-key-here',

  // Agent 使用的语言(影响 UI 面板和提示词语言)
  language: 'zh-CN',

  // 最大执行步数(防止无限循环)
  maxSteps: 20,
})

// 第二步:执行自然语言指令
async function runDemo() {
  try {
    // Agent 会扫描当前页面的 DOM,理解页面结构,
    // 然后模拟人类操作来完成任务
    const result = await agent.execute('点击页面上的第一个按钮')
    console.log('任务执行结果:', result)
  } catch (error) {
    console.error('任务执行失败:', error)
  }
}

// 第三步:触发执行(例如绑定到一个按钮点击事件)
document.getElementById('run-agent-btn')?.addEventListener('click', runDemo)

使用 OpenAI 作为 LLM 的配置:

import { PageAgent } from 'page-agent'

const agent = new PageAgent({
  model: 'gpt-4.1',
  baseURL: 'https://api.openai.com/v1',
  apiKey: 'sk-your-openai-key-here',
  language: 'en-US',
  maxSteps: 15,
})

await agent.execute('Fill the username field with admin and click login')

使用本地 Ollama 作为 LLM(完全离线,数据不出本机):

import { PageAgent } from 'page-agent'

const agent = new PageAgent({
  model: 'qwen3.5:4b',  // Ollama 中的模型名称
  baseURL: 'http://localhost:11434/v1',  // Ollama 的本地 API 地址
  apiKey: 'ollama',  // Ollama 不需要真实 API Key,随便填
  language: 'zh-CN',
  maxSteps: 10,
})

await agent.execute('点击页面上的搜索按钮')

执行结果:

任务执行结果: { success: true, text: "已成功点击页面上的第一个按钮" }

注意事项:

  • apiKey 不要硬编码在代码中,推荐使用环境变量或后端代理
  • Ollama 本地模型需要至少 9B 参数才能较好地处理 Page-Agent 的复杂工具调用 schema,4B 模型的 JSON 输出经常格式错误
  • maxSteps 建议设置在 10-30 之间,过低可能导致复杂任务无法完成,过高可能浪费 LLM 调用费用

练习题:

  1. 分别配置使用 DeepSeek(https://api.deepseek.com/v1)和本地 Ollama 作为 LLM 后端,对比两者对同一指令的响应速度和准确度。
  2. 测试将 maxSteps 设置为 3,观察当 Agent 无法在 3 步内完成任务时的行为(它会如何处理)。

第二部分:进阶篇

2.1 理解 HTML 脱水与 DOM 感知

概念讲解:

在入门篇中我们学会了如何让 Agent 执行简单的点击和输入操作。但要真正理解 Page-Agent 的工作原理并写出高效的指令,需要理解它如何"看"页面。

Page-Agent 不会截图然后交给视觉模型分析,而是将 DOM 树"脱水"(Dehydration)成一段精简文本,发送给文本 LLM。这个过程类似于把一棵枝繁叶茂的树去掉叶子和小枝,只保留主干和关键节点。

脱水后的文本格式如下:

[0]<button>提交</>
[1]<input type=text placeholder=请输入用户名 />
[2]<a href="/help">帮助中心</>
    当前有 3 个待处理任务
[3]<select>
  [3.1]<option>选项A</>
  [3.2]<option>选项B</>

每个可交互元素被赋予一个 [index] 编号。LLM 通过索引引用元素(如"点击 [0]"),Agent 根据索引找到对应的 DOM 元素并执行操作。

理解这一点非常重要,因为: - 你可以通过 data-page-agent-ignore 属性让 Agent 忽略某些元素 - Agent 只能操作被它识别为"可交互"的元素 - 良好的语义 HTML 和 ARIA 标记会显著提升 Agent 的准确率

代码示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>DOM 感知演示</title>
</head>
<body>
  <h2>用户注册表单</h2>

  <!-- 良好的 ARIA 标记让 Agent 更容易识别 -->
  <form id="register-form">
    <label for="username">用户名</label>
    <input type="text" id="username" name="username"
           aria-label="用户名输入框" placeholder="请输入用户名">

    <label for="email">邮箱</label>
    <input type="email" id="email" name="email"
           aria-label="邮箱输入框" placeholder="请输入邮箱">

    <label for="password">密码</label>
    <input type="password" id="password" name="password"
           aria-label="密码输入框" placeholder="请输入密码">

    <button type="submit" aria-label="注册按钮">注册</button>
  </form>

  <!-- 这个元素会被 Agent 忽略 -->
  <div data-page-agent-ignore>
    <p>这是内部调试信息,Agent 不会看到这些内容。</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/page-agent@1.7.1/dist/iife/page-agent.demo.js" crossorigin="true"></script>
</body>
</html>

在 Agent 面板中输入:"帮我在表单中填写注册信息,用户名 testuser,邮箱 test@example.com,密码 abc123,然后点击注册按钮"。

执行结果:

Agent 会按顺序: 1. 在用户名输入框中输入 "testuser" 2. 在邮箱输入框中输入 "test@example.com" 3. 在密码输入框中输入 "abc123" 4. 点击"注册"按钮

带有 data-page-agent-ignore 的调试信息区域不会被 Agent 看到,也不会被操作。

注意事项:

  • 坑点 1:不可见元素不会被识别。 如果元素设置了 display: nonevisibility: hidden,Agent 的 DOM 扫描会跳过它。如果需要 Agent 操作一个当前隐藏的元素,需要先让它变为可见(例如展开一个折叠面板)。
  • 坑点 2:动态加载的内容需要等待。 如果页面使用了懒加载或异步渲染,Agent 在扫描 DOM 时可能看不到尚未渲染的元素。此时可以让 Agent 先执行滚动操作触发加载,或使用 wait 等待。
  • 坑点 3:纯 CSS 交互元素可能被遗漏。 如果一个 div 仅通过 CSS cursor: pointer 和 JS 事件监听器实现点击,Agent 可能无法可靠地识别它。建议使用语义化标签(buttona)或添加 ARIA 属性。

练习题:

  1. 创建一个包含 10 个不同类型交互元素(按钮、链接、输入框、下拉框、复选框、单选按钮等)的页面,让 Agent 执行一个涉及所有元素类型的综合操作指令。
  2. 在页面上添加一个隐藏的 divdisplay: none),尝试让 Agent 操作它,观察会发生什么。

2.2 自定义配置与多模型支持

概念讲解:

在实际项目中,我们通常需要对 Agent 进行更精细的配置。Page-Agent 提供了丰富的配置选项,包括 LLM 模型选择、系统提示词自定义、最大步数控制等。

关键配置项:

  • model:LLM 模型名称,如 gpt-4.1qwen3.5-plusdeepseek-chat
  • baseURL:OpenAI 兼容 API 的基础地址
  • apiKey:API 密钥
  • language:界面和提示词语言(en-USzh-CN
  • maxSteps:最大执行步数(默认值和推荐值因场景而异)
  • systemPrompt:自定义系统提示词,可以约束 Agent 的行为范围

代码示例:

import { PageAgent } from 'page-agent'

// 创建一个用于 CRM 系统的定制化 Agent
const crmAgent = new PageAgent({
  model: 'qwen3.5-plus',
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
  apiKey: 'sk-your-api-key',

  // 自定义系统提示词,约束 Agent 的操作范围
  systemPrompt: `你是一个 CRM 系统的操作助手。
你的任务是帮助用户完成客户信息管理操作。
规则:
1. 只操作客户信息相关的表单和按钮
2. 不要修改系统设置或管理员相关页面
3. 删除操作前必须先向用户确认
4. 如果遇到不确定的操作,使用 ask_user 工具询问用户`,

  language: 'zh-CN',
  maxSteps: 25,  // CRM 操作通常步骤较多
})

// 执行 CRM 相关操作
await crmAgent.execute('在客户列表中搜索"张三",然后将他的联系方式更新为 13800138000')

使用后端代理保护 API Key:

// 在生产环境中,不建议将 API Key 暴露在前端代码中
// 推荐通过后端代理转发 LLM 请求

import { PageAgent } from 'page-agent'

const agent = new PageAgent({
  model: 'gpt-4.1',

  // 指向你自己的后端代理地址
  baseURL: '/api/llm-proxy',

  // 后端通过 cookie 或 token 鉴权,不需要真实 API Key
  apiKey: 'authenticated',

  language: 'zh-CN',
  maxSteps: 20,
})

await agent.execute('帮我填写本周的周报')

执行结果:

// CRM Agent 执行结果示例
{ success: true, text: "已找到客户张三,联系方式已更新为 13800138000" }

注意事项:

  • 坑点 1:API Key 安全。 前端代码中的 API Key 对用户是可见的。生产环境务必使用后端代理。作者在 HN 讨论中明确建议:"如果你打算将 Agent 集成到 Web 应用中,最好通过 cookie 或类似方式鉴权的代理 API 来调用 LLM。"
  • 坑点 2:系统提示词的效果因模型而异。 小模型(如 Qwen3.5 4B)对复杂系统提示词的遵循度较低,大模型(如 GPT-4.1、Qwen3.5 Plus)效果更好。
  • 坑点 3:maxSteps 过低会导致任务中断。 复杂表单填写可能需要 10-20 步,建议根据实际任务复杂度设置合理的上限。

练习题:

  1. 为一个虚拟的"在线商城"编写一个自定义系统提示词,限制 Agent 只能执行"浏览商品"和"加入购物车"操作,禁止执行"结算"和"支付"操作。
  2. 搭建一个简单的 Node.js Express 代理服务器,将前端 LLM 请求转发到实际的 API 服务,并验证 Page-Agent 可以通过该代理正常工作。

第三部分:高级篇

3.1 使用无头核心 @page-agent/core

Page-Agent 提供了 @page-agent/core 包,允许开发者跳过内置 UI 面板,直接使用 Agent 的核心逻辑。这在你需要自定义 UI 或将 Agent 嵌入已有界面时非常有用。

// 使用 @page-agent/core 无头核心,完全自定义交互界面
import { PageAgentCore } from '@page-agent/core'

// 创建无头 Agent 实例
const core = new PageAgentCore({
  model: 'qwen3.5-plus',
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
  apiKey: 'sk-your-api-key',
  language: 'zh-CN',
  maxSteps: 20,
})

// 监听 Agent 的状态变化,用于更新自定义 UI
core.onStatusChange((status) => {
  switch (status) {
    case 'thinking':
      console.log('Agent 正在思考...')
      updateMyUI({ state: 'loading', message: 'AI 正在分析页面...' })
      break
    case 'executing':
      console.log('Agent 正在执行操作...')
      updateMyUI({ state: 'active', message: '正在执行操作...' })
      break
    case 'completed':
      console.log('Agent 任务完成')
      updateMyUI({ state: 'done', message: '任务已完成!' })
      break
    case 'error':
      console.log('Agent 执行出错')
      updateMyUI({ state: 'error', message: '执行出错,请重试' })
      break
  }
})

// 监听 Agent 的每一步操作,用于展示执行过程
core.onStep((step) => {
  console.log(`步骤 ${step.number}: ${step.description}`)
  appendToLog(`[${step.number}] ${step.action} -> ${step.target}`)
})

// 执行任务
const result = await core.execute('在搜索框中输入"Page-Agent 教程"并点击搜索')

// 自定义 UI 更新函数
function updateMyUI(state) {
  // 将 Agent 状态映射到你自己的 UI 组件
  const indicator = document.getElementById('my-agent-status')
  if (indicator) {
    indicator.textContent = state.message
    indicator.className = `status-${state.state}`
  }
}

function appendToLog(message) {
  const log = document.getElementById('my-agent-log')
  if (log) {
    const entry = document.createElement('div')
    entry.textContent = message
    log.appendChild(entry)
  }
}

执行结果:

步骤 1: 找到搜索框
[1] input_text -> [搜索输入框]
步骤 2: 输入搜索关键词
[2] input_text -> 在搜索框中输入 "Page-Agent 教程"
步骤 3: 点击搜索按钮
[3] click_element_by_index -> [搜索按钮]

3.2 性能优化

Page-Agent 的性能瓶颈主要在 LLM API 调用上(每步 2-5 秒)。以下是几个实用的优化策略。

优化策略 1:减少 DOM 扫描范围

通过 data-page-agent-ignore 属性标记不相关的页面区域,减少 Agent 需要处理的 DOM 节点数量,从而降低 Token 消耗和推理时间。

<!-- 导航栏:Agent 不需要操作 -->
<nav data-page-agent-ignore>
  <a href="/home">首页</a>
  <a href="/about">关于</a>
  <a href="/contact">联系我们</a>
  <!-- ...更多导航链接... -->
</nav>

<!-- 侧边栏:Agent 不需要操作 -->
<aside data-page-agent-ignore>
  <div class="ad-banner">广告</div>
  <div class="related-links">相关链接</div>
</aside>

<!-- 主内容区:只有这里需要 Agent 操作 -->
<main>
  <form id="task-form">
    <!-- 表单内容 -->
  </form>
</main>

<!-- 页脚:Agent 不需要操作 -->
<footer data-page-agent-ignore>
  <p>版权信息</p>
</footer>

优化策略 2:合理设置 maxSteps

根据任务的预期复杂度设置合理的步数上限,避免 Agent 在失败时无意义地消耗 LLM 调用次数。

// 简单任务(1-3 步)
const simpleAgent = new PageAgent({
  /* ...其他配置... */
  maxSteps: 5,  // 给简单任务留 2 步余量
})

// 中等任务(5-10 步)
const mediumAgent = new PageAgent({
  /* ...其他配置... */
  maxSteps: 15,
})

// 复杂任务(10-20 步)
const complexAgent = new PageAgent({
  /* ...其他配置... */
  maxSteps: 30,
})

优化策略 3:编写精确的指令

模糊的指令会导致 Agent 需要更多步骤来探索和纠错。精确的指令可以减少 LLM 调用次数。

// 不好的写法:模糊指令,Agent 需要多次尝试
await agent.execute('帮我处理一下那个表单')

// 好的写法:精确指令,Agent 可以一步到位
await agent.execute('在用户名输入框中输入 admin,在密码输入框中输入 password123,然后点击登录按钮')

3.3 最佳实践

  1. 使用语义化 HTML。 使用 <button> 而非带点击事件的 <div>,使用 <label> 关联表单控件,添加 ARIA 属性。Agent 的交互元素识别器会优先依赖语义标签和 ARIA 角色。

  2. 生产环境使用后端代理。 不要在前端代码中暴露 LLM API Key。创建一个后端 API 端点,通过用户的 session cookie 鉴权后转发 LLM 请求。

  3. 使用白名单/黑名单限制操作范围。 通过 data-page-agent-ignore 属性排除不需要操作的元素,通过自定义系统提示词约束 Agent 的行为边界。

  4. 选择合适的 LLM 模型。 复杂的 AgentOutput schema(包含 reflection、memory、goal、action 四个维度)需要较强的指令遵循能力。推荐使用 9B 参数以上的模型。Qwen3.5 Plus 和 GPT-4.1 表现最佳。

  5. 为 Agent 提供操作反馈。 当 Agent 执行操作后页面应有明显的视觉反馈(如加载动画、成功提示),这样 Agent 在下一步观察时可以确认操作是否成功。


第四部分:实战项目

项目需求

构建一个 "智能表单助手" 组件,可以嵌入到任何包含表单的 Web 页面中。用户只需要用自然语言描述要填写的表单内容,Agent 就会自动完成表单填写并提交。

具体要求: - 支持多种表单元素:文本输入、下拉选择、复选框、文本域 - 提供自定义 UI 面板(不使用内置面板) - 显示 Agent 的执行进度和每一步操作日志 - 支持操作前的确认机制(用户可以在执行前审核 Agent 的计划)

项目设计

架构设计:

┌─────────────────────────────────────────────┐
│              智能表单助手 UI                   │
│  ┌─────────────────────────────────────┐    │
│  │  输入框:自然语言描述表单内容           │    │
│  ├─────────────────────────────────────┤    │
│  │  执行按钮 | 状态指示器               │    │
│  ├─────────────────────────────────────┤    │
│  │  操作日志面板                        │    │
│  │  [1] 正在输入用户名...               │    │
│  │  [2] 正在输入邮箱...                 │    │
│  │  [3] 正在选择部门...                 │    │
│  │  [4] 正在点击提交...                 │    │
│  └─────────────────────────────────────┘    │
│                                             │
│  目标表单(用户真正要填写的表单)              │
│  ┌─────────────────────────────────────┐    │
│  │  用户名: [          ]               │    │
│  │  邮  箱: [          ]               │    │
│  │  部  门: [请选择 ▼ ]                │    │
│  │  备  注: [          ]               │    │
│  │  [提交]                             │    │
│  └─────────────────────────────────────┘    │
└─────────────────────────────────────────────┘

技术选型: - Page-Agent npm 包(核心 Agent 能力) - 纯 JavaScript + DOM API(无框架依赖) - OpenAI 兼容 API(可替换为任意 LLM 提供商)

完整实现代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>智能表单助手 - Page-Agent 实战项目</title>
  <style>
    /* 页面基础样式 */
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      background: #f5f5f5;
      padding: 20px;
      max-width: 800px;
      margin: 0 auto;
    }

    /* Agent 助手面板样式 */
    .agent-panel {
      background: white;
      border: 1px solid #e0e0e0;
      border-radius: 12px;
      padding: 20px;
      margin-bottom: 30px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.08);
    }
    .agent-panel h2 {
      margin-bottom: 15px;
      color: #333;
    }
    .agent-input-group {
      display: flex;
      gap: 10px;
      margin-bottom: 15px;
    }
    .agent-input-group textarea {
      flex: 1;
      padding: 12px;
      border: 1px solid #ddd;
      border-radius: 8px;
      font-size: 14px;
      resize: vertical;
      min-height: 80px;
      font-family: inherit;
    }
    .agent-input-group button {
      padding: 12px 24px;
      background: #4a90d9;
      color: white;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 14px;
      white-space: nowrap;
      transition: background 0.2s;
    }
    .agent-input-group button:hover { background: #357abd; }
    .agent-input-group button:disabled {
      background: #ccc;
      cursor: not-allowed;
    }

    /* 状态指示器 */
    .status-bar {
      padding: 8px 12px;
      border-radius: 6px;
      margin-bottom: 15px;
      font-size: 13px;
      display: none;
    }
    .status-bar.thinking {
      display: block;
      background: #fff3cd;
      color: #856404;
    }
    .status-bar.executing {
      display: block;
      background: #cce5ff;
      color: #004085;
    }
    .status-bar.completed {
      display: block;
      background: #d4edda;
      color: #155724;
    }
    .status-bar.error {
      display: block;
      background: #f8d7da;
      color: #721c24;
    }

    /* 操作日志面板 */
    .log-panel {
      background: #f8f9fa;
      border: 1px solid #e9ecef;
      border-radius: 8px;
      padding: 12px;
      max-height: 200px;
      overflow-y: auto;
      font-size: 13px;
      font-family: 'Menlo', 'Consolas', monospace;
    }
    .log-entry {
      padding: 4px 0;
      border-bottom: 1px solid #eee;
    }
    .log-entry:last-child { border-bottom: none; }
    .log-entry .step-num { color: #4a90d9; font-weight: bold; }
    .log-entry .step-action { color: #666; }

    /* 目标表单样式 */
    .target-form {
      background: white;
      border: 1px solid #e0e0e0;
      border-radius: 12px;
      padding: 20px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.08);
    }
    .target-form h2 {
      margin-bottom: 20px;
      color: #333;
    }
    .form-group {
      margin-bottom: 15px;
    }
    .form-group label {
      display: block;
      margin-bottom: 5px;
      font-weight: 500;
      color: #555;
    }
    .form-group input,
    .form-group select,
    .form-group textarea {
      width: 100%;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 6px;
      font-size: 14px;
      font-family: inherit;
    }
    .form-group input:focus,
    .form-group select:focus,
    .form-group textarea:focus {
      outline: none;
      border-color: #4a90d9;
      box-shadow: 0 0 0 2px rgba(74,144,217,0.2);
    }
    .submit-btn {
      padding: 12px 30px;
      background: #28a745;
      color: white;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 15px;
      margin-top: 10px;
    }
    .submit-btn:hover { background: #218838; }
  </style>
</head>
<body>
  <!-- 智能表单助手面板 -->
  <div class="agent-panel">
    <h2>智能表单助手</h2>
    <p style="color: #666; margin-bottom: 15px;">
      用自然语言描述你要填写的表单内容,AI 会自动帮你完成填写。
    </p>

    <div class="agent-input-group">
      <textarea id="agent-instruction" placeholder="例如:帮我填写表单,用户名 zhangsan,邮箱 zhangsan@example.com,选择技术部,备注中写"希望参加前端培训""></textarea>
      <button id="agent-run-btn">执行填写</button>
    </div>

    <!-- 状态指示器 -->
    <div id="status-bar" class="status-bar"></div>

    <!-- 操作日志 -->
    <div id="log-panel" class="log-panel" style="display:none;"></div>
  </div>

  <!-- 目标表单(Agent 要操作的表单) -->
  <div class="target-form">
    <h2>员工信息登记表</h2>
    <form id="employee-form" onsubmit="handleSubmit(event)">
      <div class="form-group">
        <label for="name">用户名</label>
        <input type="text" id="name" name="name" aria-label="用户名" placeholder="请输入用户名">
      </div>

      <div class="form-group">
        <label for="email">邮箱</label>
        <input type="email" id="email" name="email" aria-label="邮箱" placeholder="请输入邮箱">
      </div>

      <div class="form-group">
        <label for="department">部门</label>
        <select id="department" name="department" aria-label="部门选择">
          <option value="">请选择部门</option>
          <option value="tech">技术部</option>
          <option value="product">产品部</option>
          <option value="design">设计部</option>
          <option value="marketing">市场部</option>
          <option value="hr">人力资源部</option>
        </select>
      </div>

      <div class="form-group">
        <label for="notes">备注</label>
        <textarea id="notes" name="notes" aria-label="备注" rows="3" placeholder="请输入备注信息"></textarea>
      </div>

      <div class="form-group">
        <label>
          <input type="checkbox" id="agree" name="agree" aria-label="同意协议">
          我已阅读并同意《员工信息使用协议》
        </label>
      </div>

      <button type="submit" class="submit-btn" aria-label="提交表单">提交</button>
    </form>
  </div>

  <!-- 引入 Page-Agent(使用 npm 方式时请用 import 语句) -->
  <!-- 这里使用 CDN Demo 模式简化演示,实际项目请使用 npm + 自定义 LLM -->
  <script src="https://cdn.jsdelivr.net/npm/page-agent@1.7.1/dist/iife/page-agent.demo.js" crossorigin="true"></script>

  <script>
    // 表单提交处理(用于验证 Agent 的操作结果)
    function handleSubmit(event) {
      event.preventDefault()
      const formData = new FormData(event.target)
      const data = Object.fromEntries(formData.entries())
      console.log('表单已提交,数据为:', data)

      // 在页面上显示提交成功的信息
      const statusDiv = document.getElementById('status-bar')
      statusDiv.className = 'status-bar completed'
      statusDiv.textContent = `表单提交成功!用户名: ${data.name}, 邮箱: ${data.email}, 部门: ${data.department}`
    }

    // 绑定执行按钮点击事件
    document.getElementById('agent-run-btn').addEventListener('click', function() {
      const instruction = document.getElementById('agent-instruction').value.trim()
      if (!instruction) {
        alert('请输入表单填写指令')
        return
      }

      const statusBar = document.getElementById('status-bar')
      const logPanel = document.getElementById('log-panel')
      const runBtn = this

      // 禁用按钮,防止重复点击
      runBtn.disabled = true
      runBtn.textContent = '执行中...'

      // 显示状态
      statusBar.className = 'status-bar thinking'
      statusBar.textContent = 'Agent 正在分析页面和你的指令...'
      logPanel.style.display = 'block'
      logPanel.innerHTML = ''

      // 添加日志条目
      function addLog(step, message) {
        const entry = document.createElement('div')
        entry.className = 'log-entry'
        entry.innerHTML = `<span class="step-num">[${step}]</span> <span class="step-action">${message}</span>`
        logPanel.appendChild(entry)
        logPanel.scrollTop = logPanel.scrollHeight
      }

      addLog('准备', '指令已发送,等待 Agent 响应...')

      // 注意:Demo 模式下 Page-Agent 会自动弹出内置面板
      // 在 npm + @page-agent/core 模式下,可以通过 onStep 回调捕获每一步操作
      // 这里仅演示 UI 集成模式
      setTimeout(() => {
        statusBar.className = 'status-bar completed'
        statusBar.textContent = '指令已发送给 Agent。请在 Page-Agent 内置面板中观察执行过程。'
        runBtn.disabled = false
        runBtn.textContent = '执行填写'
        addLog('完成', '指令已交给 Agent,请在内置面板中交互。')
      }, 2000)
    })
  </script>
</body>
</html>

代码解析

知识点 1:DOM 感知优化(2.1 节) 每个表单元素都使用了语义化标签(<label><input><select><textarea>)并添加了 aria-label 属性,确保 Agent 的 DOM 智能感知层可以准确识别每个交互元素。这是 2.1 节"HTML 脱水与 DOM 感知"知识点的实际应用。

知识点 2:多模型配置(2.2 节) 代码中通过 CDN 引入了 Demo 版本(使用免费测试 LLM)。在实际项目中,你可以替换为 npm 方式并配置自己的 LLM 提供商(如 2.2 节所述)。handleSubmit 函数展示了如何接收 Agent 操作后的表单数据。

知识点 3:自定义 UI 集成(3.1 节) 智能表单助手面板是一个完全自定义的 UI,不依赖 Page-Agent 的内置面板。状态指示器(thinking/executing/completed/error)和操作日志面板展示了如何将 Agent 的执行过程可视化。在 npm + @page-agent/core 模式下,你可以通过 onStatusChangeonStep 回调获取实时状态,替换掉当前的简化演示逻辑。

扩展挑战

  1. 将 CDN Demo 模式替换为 npm + @page-agent/core 无头核心模式。 移除 CDN 引入,通过 npm 安装 Page-Agent,使用 PageAgentCore 类替换 Demo 模式,并通过 onStatusChangeonStep 回调将 Agent 的执行过程实时展示在自定义 UI 中。
  2. 添加操作确认机制。 在 Agent 执行每一步操作前,在日志面板中显示即将执行的操作,并让用户点击"确认"后才真正执行。这需要使用 Agent 的生命周期钩子。
  3. 添加表单验证。 在 Agent 完成填写后,自动验证表单数据的合法性(如邮箱格式、必填字段检查),并在验证失败时让 Agent 自动修正。

第五部分:常见问题与排查指南

常见错误及解决方案

错误信息 原因 解决方案
Agent 加载后没有出现交互面板 CDN 链接不正确或网络不通 检查 CDN URL 是否正确(注意版本号),尝试切换全球/中国镜像
Agent 提示"无法找到该元素" 元素被 display:none 隐藏,或不在视口内 确保目标元素可见,或先让 Agent 滚动页面到目标位置
Agent 操作后页面没有反应 元素事件被前端框架(React/Vue)劫持 Page-Agent 已内置 React Fiber 绕过机制,如果仍有问题,检查元素是否使用了非标准的自定义事件
LLM 返回格式错误导致任务中断 使用的模型太小(<9B),无法正确生成 AgentOutput JSON 切换到更大的模型(如 Qwen3.5 Plus、GPT-4.1),Page-Agent 内置的 5 级容错会尝试修复
CSP 阻止脚本加载 页面设置了严格的 Content-Security-Policy 需要在 CSP 中白名单 Page-Agent 的脚本源,或通过 npm 方式打包到项目构建产物中
中文输入法导致输入内容异常 中文输入法的 compositionstart/end 事件未被正确处理 Page-Agent 已处理 isComposing 状态,如果仍有问题,尝试在指令中明确指定要输入的具体文字
Agent 执行了非预期的操作 指令模糊,LLM 推理出现偏差 编写更精确的指令,添加系统提示词约束 Agent 行为范围
execute() 方法长时间无返回 maxSteps 设置过高,Agent 在复杂页面上反复尝试 降低 maxSteps(推荐 10-20),编写更明确的指令减少探索步骤

调试技巧

  1. 观察 DOM 脱水输出。 在浏览器开发者工具的 Console 中检查 Agent 发送给 LLM 的页面快照。你可以通过在代码中临时添加 console.log 来查看 Agent 感知到的页面结构是否正确。如果 Agent "看"不到某个元素,很可能是该元素在 DOM 扫描时被过滤了(不可见、被忽略属性标记等)。

  2. 使用 Bookmarklet 在目标页面上快速测试。 在集成到正式项目之前,先在目标页面上使用 Bookmarklet 注入 Agent,测试指令是否能被正确理解和执行。这可以帮助你快速排查是 Agent 本身的问题还是集成代码的问题。

  3. 检查 LLM API 连通性。 如果 Agent 完全没有响应,首先检查 LLM API 是否可达。在浏览器 Console 中执行以下代码测试:

// 测试 LLM API 连通性
fetch('https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer sk-your-api-key',
  },
  body: JSON.stringify({
    model: 'qwen3.5-plus',
    messages: [{ role: 'user', content: 'Hello' }],
  }),
})
.then(r => r.json())
.then(data => console.log('LLM API 连通正常:', data))
.catch(err => console.error('LLM API 连接失败:', err))

第六部分:学习路线推荐

官方文档推荐阅读顺序

  1. Introduction / Overview - 理解项目定位和核心概念,了解页内 Agent 和 Chrome 扩展的区别
  2. Getting Started / Quick Start - 跟着官方快速入门跑通第一个示例(CDN 和 npm 两种方式都试一遍)
  3. Programmatic API - 学习 PageAgent 类的完整配置选项,重点关注 systemPromptmaxSteps 和生命周期回调
  4. Chrome Extension - 了解可选的跨页扩展功能,学习 window.PAGE_AGENT_EXT API 的使用
  5. Developer Guide - 本地开发环境搭建、贡献指南、架构设计文档
  6. AGENTS.md - 为 AI 编程助手提供的上下文信息,有助于理解项目设计意图

推荐进阶资源

  • 四行代码背后的完整 AI 浏览器 Agent:page-agent 源码深度拆解 - 对 Page-Agent 源码的五层架构进行了逐行分析,包括 HTML 脱水机制、ReAct 推理循环、响应容错等核心实现。适合想深入理解内部原理的开发者。
  • Hacker News Show HN 讨论 - 项目作者与社区的完整讨论记录,涵盖安全性、CSP 兼容性、架构权衡、与竞品对比等深度话题。推荐阅读以获取真实的生产环境考量。
  • browser-use 项目 - Page-Agent 的 DOM 处理逻辑源自该项目。了解 browser-use 的架构有助于理解 Page-Agent 的设计灵感来源和两者在服务端 vs 客户端定位上的差异。

教程版本说明: 本教程基于 Page-Agent v1.7.1 编写,API 可能随版本更新而变化。如遇与教程不符的行为,请查阅官方文档获取最新信息。