Skip to content

站点配方、技能与异步作业 ​

本文讲三件与「让智能体少踩坑」有关的事:把站点经验固化成可执行序列的配方、给人读也给智能体读的技能文档,以及长批次用的异步作业。

一、站点配方(recipes) ​

把「某个站点上必须这么点」的经验固化成服务端的 JSON 命令序列。每个配方一个 .json 文件,文件名(不含扩展名)就是配方名。

项说明
目录默认 <服务启动目录>/recipes,可用 browser.recipes.dir 改;get_config 能查到实际目录
列出list_recipes(返回名称、说明、参数、默认值、步数、命令序列)
执行{"method":"run_recipe","params":{"name":"配方名","vars":{...}}}
生效方式必须显式点名才执行,不做「看到这个域名就自动套用」的隐式推断

文件格式 ​

json
{
  "name": "配方名(可省,默认取文件名)",
  "description": "一句话说明它解决什么问题",
  "params": { "变量名": "给人看的说明" },
  "defaults": { "变量名": "默认值,调用方不传时用这个" },
  "commands": [
    { "命令名": { "参数": "值" } },
    { "close_modal": { "title": "{{title}}" } }
  ]
}
  • commands 与批量接口 commands 的格式完全一致:每项一个键(外加可选的 expect 断言)。所以配方里的每一步都能单独贴出来手跑,排障时不用整段重来。
  • 由 vars 注入,走 JSON 编码:字符串自动带引号并转义,中文、引号、换行都不用自己转义。带引号的写法("")与裸写法()都支持。
  • 变量先取 defaults,再用调用方 vars 覆盖 —— 写好了默认值的配方可以不传参数直接跑。
  • 回执与 commands 批量一致(count / succeeded / failed / results),另加 recipe 与 recipeDescription。

仓库自带的配方 ​

配方用途
close-all-modals清掉页面上所有可见的 DOM 弹窗(ant 确认框、用户服务协议层、抽屉)。这类按钮只认真实鼠标事件,JS 派发点了没反应
query-and-read-table点「查询」→ 等表格内容稳定 → 读表格。搜索结果是异步刷新的,点完立刻读会读到上一次的结果
cnipa-list-drafts中国商标网:「我的账户 → 申请管理 → 未提交」并把日期筛选切到「近三个月」再读列表。默认筛选是「今天」,只看默认视图很容易得出「没保存成功」的错误结论
open-console-from-iframe主站把第三方控制台套在跨域 iframe 里时的通用套路
wework-qykit-open-console企业微信后台的「邮件 / 微盘 / 文档 / 会议」:现取一个未被消费的 token,把第三方控制台当顶层页面打开
gov-site-search国产政府/事业单位站的站内全文检索:进高级搜索 → 选「包含完整关键词」→ 填词 → 可选日期区间 → 每页 50 → 点搜索。这类站的检索参数全在页面 JS 状态里,直接拼 URL 会被静默重置成 0 条结果,必须走表单

站点技能(.agents/skills/) ​

仓库自带的技能文档除了主技能,还有一批站点操作手册。它们在仓库里启动会话时会被自动发现,所以做对应站点时直接点名即可,不必把踩过的坑重新踩一遍:

技能什么时候读
cn-gov-site-search在省/市/县/局各级政务网站上查公告、批复、征地信息。讲透检索必须走表单、精确短语与日期区间怎么开、公告正文是图片时怎么读、征地类信息在哪个栏目,以及站上确实没有时怎么转依申请公开
web-data-as-text任何「页面在数字上」的任务(行情、账单、后台列表、看板)。给出文本来源的优先次序、交叉校验与时间基准,并写明例外:内容本身就是图片时,读图才是第一条路
wecom-* / cnipa-* / bilibili-* …各自站点的手册(企业微信后台、中国商标网、B 站投稿等)

技能文档里单个反引号包起来的名字会被当成命令名做构建期检查,所以页面里的标识符(CSS 类名、表单字段、URL 参数)要写成双反引号 —— 约定见仓库的 docs/SKILL-CONVENTIONS.md。

套路:主站套第三方控制台 ​

企业微信后台把「邮件」套在第三方域名的跨域 iframe 里(微盘 / 文档 / 会议同理)。这是一整类站点,不是个例。症状是 get_browser_state 只拿到主站外壳、iframe 内部一个元素都没有。

先用正规解法:get_browser_state 传 includeFrames: true,或先 list_frames 看有哪些 frame。Playwright 能跨 frame(走浏览器协议取内容,不依赖往页面里注入脚本),拿到 frame 之后读、点、填都能做。

为什么还需要这个配方:有些控制台在 iframe 里的可用性受主站外壳影响(iframe 尺寸小、控件被裁掉、主站轮询把状态重置),这时「把控制台当顶层页面打开」更稳。步骤是:

  1. 从 iframe.src 反查第三方 URL 与参数(list_frames,或用 execute_js 列出页面上所有 iframe 的 src);
  2. 在 get_requests 里按 token / oauth / sso 等关键字找出主站发 token 或换登录态的接口;
  3. 用同源 execute_js 调它(credentials: 'same-origin' 会自动带上主站的登录 Cookie),拿一个新的、还没被消费的 token;
  4. 用这个 token 把控制台当顶层页面打开,之后就能当普通页面读写。

直接 go_to_url 打开 iframe 的 src 会失败:那里面的 token 是一次性的,已经被 iframe 消费掉了。这是这类站点最容易踩的一步。

写配方的纪律 ​

  • 只固化稳定的步骤:菜单文本、按钮文本这类会随站点改版变化的东西尽量放进 params 或 defaults,别硬编码在 commands 里;
  • 每一步都要能解释「为什么必须这样」;靠猜试出来的步骤不要写进配方;
  • 涉及不可逆操作的站点,配方里不要带自动提交类步骤。

二、技能文档(skills) ​

技能是给智能体读的方法论,配方是给服务执行的命令序列。两者互补:

技能配方
形态Markdown 文档JSON 命令序列
谁来用智能体读进去,自己决定怎么做服务端按名字执行
适用需要判断、分支、排障的流程步骤固定、可以照抄的操作
目录.agents/skills/recipes/

当前仓库的技能组织方式:

位置内容
.agents/skills/deepseek-browser-use/SKILL.md总纲:核心命令用法与参考文档入口
.agents/skills/deepseek-browser-use/references/协议、客户端、人机协作、排障等按需读取的分册;商户表单经验位于 payment-onboarding.md
.agents/skills/<技能名>/SKILL.md具体站点的实操手册,记录该站点特有的坑与验证过的步骤

技能能否被自动发现取决于宿主支持的目录约定;当前仓库使用 .agents/skills/。技能只提供使用规则,后端服务仍需单独启动。公共教程中的操作经验见表单与多层弹窗排障。

技能文档与命令表有防漂移检查:测试会校验技能文档覆盖了命令表里的每一个方法,且文档里不出现已废弃的方法名。所以新增命令时不要忘了补技能文档,否则构建会失败。

三、异步作业 ​

批次可能跑几十秒以上,超过客户端自己的超时时间。在批次参数里加 "async": true:

json
{
  "id":"1001",
  "method":"commands",
  "params":{"async":true,"commands":[
    {"go_to_url":{"url":"https://example.com"}},
    {"wait_for_stable":{"selector":".vxe-table--body-wrapper","quietMs":900,"timeoutSeconds":20}},
    {"get_browser_state":{}}
  ]}
}

接口立刻返回:

json
{"data":{"jobId":"<作业 ID>","status":"running","browserId":"1001"}}

之后用 get_job 取结果、list_jobs 看全部、cancel_job 取消。

项说明
状态running / done / failed / cancelled
取消协作式的:批次会在每一步之间检查取消标记,所以取消在「下一步之前」生效,当前这一步会跑完
保留数量只保留最近 50 个,更早的被淘汰
存储结果在内存里,服务重启即丢。它是「一次调用的结果暂存」,不是持久化的任务队列
异常批次内部抛异常会被记成 failed,不会把线程池打挂

最大的好处是:客户端超时不再等于任务失败。 超时之后批次其实还在服务端继续跑,重新用 get_job 取结果即可,不必重发整批 —— 重发在有副作用的操作(提交、下单)上会出问题。

需要跨服务重启保留任务、或需要人工在长时间等待后接着跑的场景,不要依赖作业:把进度写进任务自己的上下文(例如用 execute_js 存进 LocalStorage,或让智能体自己记录),或用站点配方把这段固定步骤固化下来。