Communitygithub.com

产品视频 - 解析自然语言选择器

解析流水线 .demo 脚本中的自然语言 `ask "..."` 选择器,在实时的应用中使用 Claude in Chrome 扩展进行查找,并写入 <script>.selectors.json 侧边文件。当脚本存在未解析意图、编译或录制失败提示“选择器仍表示意图”,或重新设计导致原先可用的选择器失效时使用。

产品视频 - 解析自然语言选择器 是什么?

该技能旨在解决 AI Agent 视频录制工作流中的一个关键难题:将 `.demo` 脚本中描述性的自然语言选择器(如 `ask "the message box at the bottom of the chat"`)解析为稳定、可复用的 DOM 选择器。它作为视频流水线的一部分,在录制开始前一次性完成该解析,避免了在运行期间调用模型导致的延迟和“死空气”问题。技能支持通过 Claude in Chrome 扩展在实时、已登录的 Web 应用上运行,并提供了详细的步骤:获取未解析意图列表、按脚本流程顺次定位元素、优先使用 `data-testid` 或语义角色等持久化句柄,最终写入侧边文件。它还包含验证机制,确保选择器在 DOM 中唯一且可见,并提供了处理常见陷阱的指导,如仅依赖于生成内容或类名。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ssunduko/product-video/tree/HEAD/.claude/skills/resolve-selectors

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

解析自然语言选择器

一个 .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,还需要了解节拍,因为必须按流程顺序解析选择器(见下文)。

步骤

  1. 获取任务列表。

    cd pipeline
    node script.mjs ../scripts/<name>.demo --intents
    

    标准输出 JSON:每个 ask 选择器、其意图及其当前解析结果(如果未解析则为 null)。解析所有的 null。只有当用户明确表示流程已损坏时,才重新解析其余选择器。

  2. 在单次 ToolSearch 调用中加载浏览器工具,然后在执行其他任何操作之前调用 tabs_context_mcp。使用 tabs_create_mcp 创建一个新的标签页,除非用户已指定使用现有标签页。

  3. 按节拍顺序遍历流程。 这是容易出错的部分:许多选择器在着陆页上并不存在。只有在发送问题后,结果表格才会出现;“停止”控件只在生成过程中存在。因此,请逐步执行脚本的节拍,在每个 ask 对应的元素出现在屏幕上时解析它。

    实时数据。在创建、修改、发送或删除任何内容的步骤之前,停下来询问用户。 feature.demo 会向一个真实的 Salesmessage 账户发送四条消息。如果只需要定位某物,请选择悬停而不是点击,并且永远不要点击破坏性控件来“看看它是什么”。

  4. 找到元素并选择最持久的句柄。 优先级顺序如下:

    首选原因
    [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 堆栈)或任何依赖于生成内容的内容。最后一点正是此功能旨在避免的陷阱——模型答案每次运行时措辞都不同,因此根据其文本进行匹配的选择器就像掷硬币。应改为锚定在容器或完成信号上。

  5. 验证后再写入。 对于每个候选选择器,使用 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 读取结果。如果匹配超过一个或为零,则继续查找——切勿写入你尚未亲眼确认匹配的选择器。

  6. 写入侧边文件 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 是你实际观察到的情况;将其写下来,以便下一个人能够判断它是否仍然成立。

  7. 确认并交回。

    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 的内容。模态对话框会冻结扩展,直到手动关闭

相关技能