本节摘要:本节论证一个判断:网页智能体框架再多,公共语法只有三个动词。act 把自然语言指令变成页面动作——经历指令解析、候选发现、目标选择、动作执行四步;extract 把页面变成结构化数据——指令加数据模式,不写选择器只描述「要什么」;observe 只看不动——产出候选动作清单,服务干跑、调试与缓存。三原语的划分依据是网页操作的本体:改变页面、读取页面、以及读取「页面能被怎样改变」。本节同时诚实列出三原语覆盖不了的部分:多步任务的规划与循环、结果验证、跨页上下文,它们分别指向第 2、6、7 章。读懂本节,你换任何一个新框架都只需要认一次 API 方言。
对「用程序操作网页」做一次本体盘点,只剩三类不可再分的活动:改变页面状态(点击、输入、提交、滚动)、从页面取数据(读文本、读结构)、了解页面能被怎样操作(哪些元素可点、当前在哪一屏)。三类活动各对应一个原语:
| 原语 | 一句话职责 | 输入 | 输出 | 典型失败模式 |
|---|---|---|---|---|
| act | 把意图变成页面动作 | 自然语言指令 | 动作执行结果 | 目标歧义、元素找不到、点不动 |
| extract | 把页面变成结构化数据 | 指令加数据模式 | 符合模式的 JSON | 模式过宽、字段漏、页面没渲染完 |
| observe | 只看不动,产出候选 | 指令或空 | 候选动作清单 | 候选为空、候选过泛 |
把同一个靶场任务分别用机械动作与三原语写出来,抽象层级的落差一目了然:
# compare_task.txt —— 同一任务的两种表述(靶场:添加一条待办) 机械动作序列: goto(url) → locator(输入框).fill("买牛奶") → locator(按钮).click() → waitFor(列表变化) 三原语表述: act("在「新待办」输入框填入 买牛奶,点击「添加」按钮") extract("提取待办列表条目")
上排每个符号都要人维护(选哪个定位器、等多久);下排每个词都是业务语义(哪个框、哪个按钮、要什么数据)。这就是抽象层级的差别:Selenium 与 Playwright 的 click、fill 是机械动作——调用者自己负责「找到元素」;三原语是意图动作——调用者只负责「说清目标」。机械动作的原语有十几个(click、fill、select、hover、drag、press……),意图动作收敛到三个,因为「找目标」的复杂性被转移给了框架与模型。
这一转移正是第 0.1 节「语义缺失」天花板的解药:指令文本(「点击登录」)把语义重新带回代码,维护点从「元素在哪」变成「意图是什么」——前者随页面改版漂移,后者只随业务漂移,而业务漂移的频率低得多。三原语也不是某个委员会的发明,而是收敛的结果:browser-use 的动作序列、Stagehand 的三个核心 API、Playwright MCP 的工具分组,都能一一映射到这三个动词上(第五节给出对照表)。
act 是三原语里唯一会改变世界的一个,也因此最值得拆。一条指令从文本到点击落地,经历四步:
「在「新待办」输入框填入 买牛奶,点击「添加」按钮」 │ ▼ ① 指令解析:拆成两个子意图(填输入框 / 点按钮) ▼ ② 候选发现:枚举页面上与子意图相关的可交互元素(1.2 节机制) ▼ ③ 目标选择:按可见文本、角色、位置对候选打分,选定目标 ▼ ④ 动作执行:驱动浏览器在目标上执行 fill / click,并等待响应 │ ▼ 产物:动作执行结果(成功 / 失败及原因)
四步里只有第 4 步是纯工程,前三步都在处理自然语言与页面语义的对接。把四步的中间产物列成表,排障时就知道该看哪里:
| 步骤 | 中间产物 | 排障时看什么 |
|---|---|---|
| 指令解析 | 子意图列表 | 拆解是否符合预期、有没有丢分支 |
| 候选发现 | 可交互元素清单 | 目标在不在候选里(第 1.2 节机制) |
| 目标选择 | 打分与选中结果 | 为什么选了别的元素(歧义) |
| 动作执行 | 执行结果与页面响应 | 是否被遮挡、是否需要等待 |
写好 act 指令的三条经验:说目标不说路径(「点击添加按钮」优于「点击页面右下角那个蓝色元素」);引用可见文本(用户能看见的字样是页面上最稳的语义锚点);一次一个意图(复合指令交给框架拆解,别在一句话里藏三个条件分支)。指令质量直接决定第 3 步的命中率,第 3.2 节有一张好指令对照表。
extract 回答「页面上有什么」,但输出不是页面转储,而是按你给的数据模式裁剪过的 JSON。调用侧只写两样东西:一句指令说明要什么,一个模式说明长什么样。以 Stagehand 风格为例(完整走查见 1.3 节):
// extract_shape.mjs —— extract 的调用形态:指令 + 数据模式(写法示意) import { z } from "zod"; const data = await page.extract({ instruction: "提取待办列表的全部条目文本", schema: z.object({ items: z.array(z.string()), }), });
与「写爬虫」的本质区别在于故障面:爬虫的维护点是选择器(页面一改就断,第 0.1 节的结构脆断),extract 的维护点是指令与模式(页面一改,多数情况下指令仍然成立,因为「待办列表」这个语义没变)。代价是每步一次模型调用与输出不完全可控——所以第 3.3 节会把高频 extract 固化成普通查询代码。
拿靶场做个对照实验帮助消化:同一份列表数据,用爬虫思路要写「列表容器选择器加子项遍历加文本清洗」三段代码;用 extract 思路只剩一句指令加一个模式。页面把列表从「表格」改成「卡片流」时,爬虫的三段代码全废,extract 的指令多半照旧成立——语义没变,变的只是它的呈现。这个实验也划出了 extract 的边界:当「待办列表」这个业务概念本身在页面上消失或改名时(语义变更而非结构变更),指令同样会失效——语义层比结构层活得久,但不是永生。
extract 的两条工程要领:模式宁窄勿宽(字段名、类型、嵌套都写死,宽模式会诱发模型脑补);抽取前确认页面已稳定(懒加载未完成时抽取,漏的是真数据,且不报错——这类「静默漏」归第 6 章验证兜底)。
observe 产出「候选动作清单」:给定一句指令(甚至不给),框架枚举页面上相关可交互元素,返回每项的定位信息与建议动作,但不执行任何一步。它单独存在,因为三个高频场景都需要「看而不动」:
| 场景 | 用法 | 后续去向 |
|---|---|---|
| 干跑 | 上线前先 observe 一遍,检查指令会命中哪些元素 | 命中不对就改指令,成本为零 |
| 调试 | 执行失败后 observe 当前页,对比预期候选 | 定位是页面变了还是指令歧义 |
| 缓存 | observe 的产物含定位信息,可落盘复用 | 第 3.3 节渐进式固化的原料 |
observe 与 act 的关系是「预览与提交」:observe 给你否决权,act 承担后果。凡是不可逆动作(提交、支付、删除),工程上值得先 observe 再 act——这是第 2.2 节敏感数据确认机制的一个朴素雏形。
再看一眼observe 在三原语里的特殊地位:act 与 extract 都改变状态或产出数据,observe 什么都不产出给业务——它产出的是确定性。候选清单把「模型会点什么」从黑盒变成可检视的白盒,这份可检视性正是第 3.3 节固化流程的原料(observe 结果可缓存),也是第 6 章验证策略的观测点(对比预期候选与实际候选)。换句话说,前半本所有「把智能体变得可运维」的努力,入口都在 observe。
语法相同,方言各异。把前半本的主角放进同一张表,你会看到三原语如何被各家实现——这也是「读懂本章,换框架只需认一次方言」的凭证:
| 框架 | act 的形态 | extract 的形态 | observe 的形态 |
|---|---|---|---|
| browser-use | 循环内的动作输出(编号加类型) | 循环内的最终结果动作 | 每轮感知的编号清单 |
| Stagehand | page.act(指令或候选) | page.extract(指令加 zod 模式) | page.observe(候选数组) |
| Playwright MCP | browser_click 等交互工具 | 快照文本交由 Agent 阅读抽取 | browser_snapshot(带 ref 清单) |
| Midscene | ai(视觉定位执行) | aiQuery(自然语言抽取) | 截图与定位框(视觉中间产物) |
对照中有一处值得停留:Playwright MCP 没有独立的 extract 工具,抽取靠「快照文本加 Agent 的阅读理解」完成——因为它的服务对象本来就是编码 Agent,读文本正是它的强项;而 Stagehand 的 extract 内建了模式校验,服务于无人值守的批量任务。同一原语在不同形态下的肥瘦取舍,取决于「谁来消费结果」:模型消费则松,程序消费则严。这个视角在后续章节会反复出现。
三原语覆盖「单步」与「单页」,超出这个范围的三类需求,各有专门章节:
| 不覆盖 | 为什么 | 去处 |
|---|---|---|
| 多步规划与循环 | 三原语没有「任务」概念,谁编排步骤、何时终止需上层结构 | 第 2 章 Agent 循环 |
| 结果验证 | act 只报「点了」,不报「点对了」;extract 只管取,不管取得对 | 第 6 章验证与重试 |
| 跨页与会话上下文 | 三原语是无状态的,登录态、会话复用、并发隔离在其之下 | 第 7 章会话与基建 |
把这张表当成全书的导航图之一:第 1 章往下是「单步怎么做对」,第 2 章往上加一层「多步怎么串起来」,第 6 章往右补一层「做完了怎么确认」。
三原语的语义已经立住,但 act 的管道里留了一个黑盒:第②步「候选发现」与第③步「目标选择」到底怎么把 DOM 变成模型可挑的清单?下一节打开这个黑盒,讲 DOM 流派的元素定位与编号点击。