任务录制
录制不是"录宏",而是采集你在界面上的焦点与点击轨迹,再把它变成一份可读、可改的 skill 草稿。
怎么录
node apps/recorder/main.ts --seconds 15 --out .demo/recordings/rec.jsonl
录制期间正常操作电脑。它采集三类事件:
- focus:每 300ms 采样一次 UIA 焦点元素(窗口、控件类型、名称、AutomationId、当前值);
- click:通过
WH_MOUSE_LL低级鼠标钩子捕获每一次左键按下,用AutomationElement.FromPoint反查坐标下的控件; - window-open:顶层窗口集合出现新成员时记录一条,用来推断"这次点击生效了"。
鼠标钩子跑在独立 STA 线程上,带自己的消息循环——这是 PowerShell 里能让低级钩子生效的关键。
两种模式
| 模式 | 做法 | 代价 | | --- | --- | --- | | events(默认) | UIA 焦点 + 结构变化回调,靠 Application.DoEvents() 派发 | 15 秒会话只产出 8 条事件;同样时长轮询会刷出约 50 条重复焦点 | | poll(-Poll) | 每 300 毫秒采样焦点 + 顶层窗口 | 事件注册失败时自动回退;受限机器上也能用 |
注册失败时录制器会把原因打到 stderr 并自己退回轮询——一次录制不会因为事件 API 的问题直接失败。
只写三类事件,都是真有信息量的:焦点变化、顶层窗口出现、左键点击(带光标下的元素)。
怎么变成 skill
node scripts/draft-skill.ts --recording .demo/recordings/rec.jsonl --name my-skill --only "Notepad"
生成规则:
- 点击 →
ui.click,选择器取控件类型 + 名称(有 AutomationId 时优先用它); - 输入 →
ui.type,并自动推断一条ui.value断言("输入框里的值必须等于我打进去的字"); - 点击后 3 秒内出现新窗口 → 给这条点击补一条
window.exists断言("点了它,应该弹出某个窗口"); - 点击后 2.5 秒内焦点落到另一个控件 → 补一条
ui.exists断言("点了它,那个控件应该出现")——这是不弹窗的点击唯一能自动推断的信号; - 连续采样的前缀值会合并成一步,不会生成一串重复的
ui.type。
窗口检测按 窗口标题 + 进程号 去重,所以"同一个应用又开了一个同名窗口"也算新窗口——这是实测踩出来的:只按标题去重会把第二个记事本窗口漏掉。
预览:
node apps/cli-demo/run.ts --skill packages/skills/my-skill/index.ts --mode preview
草稿不是成品
自动推断只覆盖"输入"这一类。点击之后的断言仍然是空的(post: []),因为"点了以后应该发生什么"需要人判断:
- 点了"保存",应该出现保存对话框还是直接落盘?
- 点了标签页,地址栏应该变吗?
- 出错了怎么退回去?
补完这些,草稿才是 skill。
已知限制
- 只采集左键点击,不采集拖拽、右键菜单、滚轮;
- 未聚焦的窗口不会出现在 focus 事件里;
- 窗口标题带文件名时会变化,草稿自动取
" - "之后的部分作为稳定匹配串; - 录制期间切到别的应用,用
--only过滤。
下一步
- 用 UIA 的 FocusChanged / StructureChanged 事件替代轮询,降低漏采;
- 记录窗口结构变化(新窗口出现 = 点击生效的信号),自动生成点击断言;
- 拖拽与右键菜单支持。