解析自然语言选择器
一个 .demo 脚本用口语化的方式描述其意图:
selectors:
composer = ask "the message box at the bottom of the chat"
你的任务是找到这些描述在实时 DOM 中真正对应的元素,并将其写入
scripts/<name>.selectors.json,以便 script.mjs 能将其编译为普通选择器,且 driver.mjs 能进行确定性录制。
你是解析者。 流水线中没有任何环节会调用模型——script.mjs 会读取侧边文件,如果没有则拒绝编译。解析发生在此处,一次完成,在录制开始前进行,因为录制的时机构成了视频的时间线,如果在运行期间进行模型往返,则会在完成的视频中产生“死空气”。
开始之前
- 应用必须可访问,并且如果需要登录,则必须在用户的 Chrome 中已登录。这就是使用扩展的意义所在:它连接到真实的会话,因此没有 SSO 仪式,也无需填写电子邮件验证码。
- 首先阅读脚本。你需要
app:/target:标头来获取 URL,还需要了解节拍,因为必须按流程顺序解析选择器(见下文)。
步骤
-
获取任务列表。
cd pipeline node script.mjs ../scripts/<name>.demo --intents标准输出 JSON:每个
ask选择器、其意图及其当前解析结果(如果未解析则为null)。解析所有的null。只有当用户明确表示流程已损坏时,才重新解析其余选择器。 -
在单次 ToolSearch 调用中加载浏览器工具,然后在执行其他任何操作之前调用
tabs_context_mcp。使用tabs_create_mcp创建一个新的标签页,除非用户已指定使用现有标签页。 -
按节拍顺序遍历流程。 这是容易出错的部分:许多选择器在着陆页上并不存在。只有在发送问题后,结果表格才会出现;“停止”控件只在生成过程中存在。因此,请逐步执行脚本的节拍,在每个
ask对应的元素出现在屏幕上时解析它。实时数据。在创建、修改、发送或删除任何内容的步骤之前,停下来询问用户。
feature.demo会向一个真实的 Salesmessage 账户发送四条消息。如果只需要定位某物,请选择悬停而不是点击,并且永远不要点击破坏性控件来“看看它是什么”。 -
找到元素并选择最持久的句柄。 优先级顺序如下:
首选 原因 [data-testid=...],data-test,data-qa专门用于被选择;能经受重新设计 稳定的 id很好,除非看起来是自动生成的(如 :r3a:,id-1a2b)按钮/链接按其精确的辅助功能名称 → btn:Name驱动程序的 btn:匹配文本、aria-label和内部img[alt][aria-label=...],[role=...]稳定,且通常具有语义 语义结构( main table,nav a[href="/teams"])能经受样式变更 切勿使用:
nth-child链、自动生成的类名(css-1a2b3c、长长的 Tailwind 堆栈)或任何依赖于生成内容的内容。最后一点正是此功能旨在避免的陷阱——模型答案每次运行时措辞都不同,因此根据其文本进行匹配的选择器就像掷硬币。应改为锚定在容器或完成信号上。 -
验证后再写入。 对于每个候选选择器,使用
javascript_tool确认它匹配恰好一个元素且该元素可见:const q = 'div[data-testid="chat-input"]'; const n = document.querySelectorAll(q); console.log(JSON.stringify({ q, count: n.length, visible: n[0] ? !!(n[0].offsetWidth || n[0].offsetHeight) : false, tag: n[0]?.tagName, editable: n[0]?.isContentEditable, }));通过
read_console_messages读取结果。如果匹配超过一个或为零,则继续查找——切勿写入你尚未亲眼确认匹配的选择器。 -
写入侧边文件
scripts/<name>.selectors.json:{ "resolvedAgainst": "https://claude.ai/new", "resolvedAt": "2026-08-10T09:00:00Z", "resolvedBy": "claude-in-chrome", "selectors": { "the message box at the bottom of the chat": { "spec": "div[data-testid=\"chat-input\"]", "evidence": "contenteditable tiptap div, aria-label 'Write a message', 1 match, visible" } } }以意图文本(逐字取自脚本)作为键——重命名选择器或重新排序块不能使解析失效。
spec是驱动程序接受的任何形式:原始的 puppeteer 选择器、btn:Text|Alt或::-p-text(substring)。evidence是你实际观察到的情况;将其写下来,以便下一个人能够判断它是否仍然成立。 -
确认并交回。
node script.mjs ../scripts/<name>.demo --intents # 期望 0 个未解析 node script.mjs ../scripts/<name>.demo --print # 编译报告你解析了哪些选择器、你必须在哪些选择器之间猜测,以及任何因需要破坏性步骤而跳过的内容。
当无法解析时
请告知用户并停止;不要编造选择器。一个未解析的意图是 Chrome 启动之前的干净编译错误。而一个错误的选择器会在中途失败,即在录制已经开始之后——这是代价高昂的失败时机。如果两个元素对描述匹配程度相同,请询问用户指的是哪一个,而不是随意选择。
注意事项
| 现象 | 原因 / 修复方法 |
|---|---|
| 元素不在页面上 | 它会在流程的后面出现。按顺序遍历节拍;在元素实际出现在屏幕上时解析每个意图 |
| 两个元素匹配 | 描述有歧义。与用户一起缩小范围,然后也重新措辞脚本中的 ask,以便下次解析时无歧义 |
| 选择器现在有效,下次运行失效 | 它依赖于生成内容或自动生成的类。重新锚定到 testid、role 或容器上 |
wait ... gone 锚点 | 这些本质上是瞬态的(例如生成过程中的“停止”按钮)。在它出现在屏幕上时解析,并优先使用 aria-label——它比文本变化更少 |
| 重新设计后解析漂移 | 仅从侧边文件中删除该键并重新运行。.demo 不会改变;这正是关键所在 |
| 对话框阻止扩展 | 不要点击会弹出 confirm/alert 的内容。模态对话框会冻结扩展,直到手动关闭 |