应用开发指南
自定义应用通过可视化步骤流程编辑器构建自动化任务。每个步骤代表一个原子操作,如点击、输入、数据提取、条件判断等。步骤之间按顺序执行,支持循环和条件分支。步骤的输出可保存到变量中,后续步骤通过 {{变量名}} 引用。
变量引用规则:在参数值中使用 {{变量名}} 可引用之前步骤保存的变量。支持点号访问子字段,如 {{videoInfo.author}}。运行参数(如搜索关键词、最大视频数等)通过 {{ctx.searchKeyword}}、{{ctx.maxVideos}} 等方式访问。
应用启动
启动应用(launchApp)
启动指定 Android 应用。会先强制停止应用再启动,确保从初始状态开始。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| uri | 目标应用包名 |
示例:
com.ss.android.ugc.aweme(抖音)com.tencent.mm(微信)com.xingin.xhs(小红书)
TIP
常见应用包名可在网上搜索获取。启动后通常需要配合「随机延迟」等待应用加载完成。
终止应用(terminateApp)
强制停止指定 Android 应用。用于在启动前清理应用状态。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| uri | 要终止的应用包名 |
TIP
launchApp 步骤已内置终止逻辑,一般不需要单独使用此步骤。
浏览器操作
启动浏览器(browserLaunch)
启动浏览器并导航到指定 URL。用于网页类任务的第一步。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| url | 要打开的网站 URL(支持 {{变量}}) |
| site(必填) | 站点标识:切换为该站点的指纹浏览器与登录态。站点在流程设计时写死,新增步骤时自动预填当前编辑会话站点。多个网站复用一套账号体系时,名称应该一样(如淘宝和天猫);对于无需账号访问的网站,也需要填写站点标识,用于生成指纹浏览器 |
| accountName(可选) | 该站点的已登录账号,可填运行参数/变量(如 {{accountVar}})实现同站点多账号运行;留空 = 该站点游客模式。新增账号:点击字段右侧 + 按钮,打开指纹浏览器人工登录,完成后自动保存登录信息 |
示例:
https://www.douyin.comhttps://www.xiaohongshu.com
多站点流程示例(一个应用在单个流程内操作多个站点):
启动浏览器 url=抖音站点A页面 site=抖音 accountName=账号A ← 抖音指纹+登录态
... 抖音操作 ...
关闭浏览器 ← 关闭并保存抖音登录态
启动浏览器 url=小红书页面 site=小红书 accountName=账号B ← 切换:小红书指纹+登录态
... 小红书操作 ...- 切换语义:命令带 site 且与当前站点/账号不同 → 用新站点的指纹浏览器与登录态启动并导航
- 切换时旧浏览器保留后台运行(登录态在关闭时才落盘);需要关闭旧站点浏览器时,在切换前放一步「关闭浏览器」
- 任务结束时,流程中打开过的所有站点浏览器统一保存登录态并关闭
TIP
仅适用于网页类应用。启动后配合「随机延迟」等待页面加载。同站点同账号的登录态会自动复用(指纹一致,避免平台风控)。
切换浏览器(browserSwitch)
切回该站点的指纹浏览器——复用后台打开的最后一次活跃窗口(不导航、不重开)。用于多站点/多设备流程中「回到之前打开过的浏览器」。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| site(必填) | 站点标识:切回该站点的指纹浏览器。复用该站点后台打开的最后一次活跃窗口。多个网站复用一套账号体系时,名称应该一样(如淘宝和天猫) |
| accountName(可选) | 该站点的已登录账号;留空 = 该站点游客模式 |
多站点切回示例:
启动浏览器 url=抖音页面 site=抖音 accountName=账号A ← 打开抖音
启动浏览器 url=哔哩哔哩页面 site=哔哩哔哩 accountName=账号B ← 切到B站,抖音后台保留
... B站操作 ...
切换浏览器 site=抖音 accountName=账号A ← 切回抖音:复用后台窗口,停留在离开时的页面
... 继续抖音操作 ...- 与「启动浏览器」的区别:启动 = 打开 + 导航(url 必填);切换 = 回到已打开窗口(无 url,停留在上次的页面)
- 切换回时直接复用后台浏览器窗口(不重开、不闪窗、登录态在内存中保持)
- 目标站点浏览器已被关闭(如流程中执行了「关闭浏览器」)时,切换会打开该站点的空白浏览器(同指纹/登录态)
- 多平台混合流程中同理:切换到手机用「启动应用」,切回浏览器用本指令
导航到 URL(browserNavigate)
在已打开的浏览器中导航到新的 URL,不重新启动浏览器。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| url | 目标 URL(支持 {{变量}}) |
| site(可选) | 同「启动浏览器」:带值且与当前不同时,切换为该站点的指纹浏览器与登录态后导航;留空 = 当前浏览器 |
| accountName(可选) | 同「启动浏览器」 |
TIP
不带 site 参数时不会创建新标签页,在当前页面内跳转;带 site 且与当前不同时执行站点切换。
关闭浏览器(browserClose)
关闭当前浏览器窗口及所有标签页,关闭前自动保存当前站点的登录态。流程中切换站点前可用本步骤显式关闭旧站点浏览器。
返回类型:无
无需参数。
WARNING
关闭后无法再执行浏览器操作,确保在所有步骤完成后再关闭。
关闭当前标签页(browserCloseTab)
仅关闭当前活跃的浏览器标签页,不影响其他标签页。
返回类型:无
无需参数。
TIP
如果只剩一个标签页则不会关闭,避免误操作。
浏览器后退(browserGoBack)
浏览器后退到上一页,相当于点击浏览器的「后退」按钮。
返回类型:无
无需参数。
TIP
仅网页端有效。不会创建新标签页,在当前页面内返回。
获取页面信息(browserInfo)
获取当前浏览器页面的 URL 和标题。返回 { url, title } 对象。
返回类型:对象
无需参数。输出保存到变量后,用 {{pageInfo.url}} 引用页面地址。
TIP
返回值需保存到变量中才能后续使用。
坐标点击(browserTap)
在网页指定坐标位置点击。适用于 AI 无法识别元素时的精确点击。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| x | 横坐标 |
| y | 纵坐标 |
TIP
坐标值会因屏幕分辨率不同而变化,不建议跨设备使用。优先使用 AI 点击步骤。
AI 操作
AI 点击(aiTap)
AI 自动识别并点击指定元素。最常用的操作步骤,通过自然语言描述要点击的目标。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| prompt | 要点击的元素描述(AI 视觉定位,推荐) |
| deepLocate | 是否启用深度定位(可选) |
| selector | 元素选择器:CSS/XPath(web)、resource-id(Android)或 uia:/msaa:/img:(Windows 桌面,捕获控件自动生成)。优先于 prompt,可通过「捕获元素」按钮获取 |
示例:
点击右上角的搜索图标点击"发布"按钮点击评论图标
TIP
描述要具体明确:「点击视频右侧的评论图标」比「点击评论」更好。开启深度定位可提高定位精度但速度稍慢。
XPath/CSS 元素定位与捕获
AI 点击、悬停、输入等步骤以自然语言定位(prompt)为主,AI 自动理解界面元素并执行操作。当自然语言定位不准确时,可借助 XPath/CSS 元素定位(selector) 作为辅助手段。selector 和 prompt 同时填写时,优先使用 selector 精确定位,prompt 作为回退方案。
捕获元素功能:在流程编辑器中,点击 AI 操作步骤参数旁的「捕获元素」按钮(🔍图标),可以可视化的方式选取目标元素:
Android 应用:自动连接设备并截取当前界面,显示所有可交互元素的高亮框
- 鼠标移动:实时高亮命中的元素(绿色框)
- ⌘/Ctrl + 点击:捕获元素,自动生成定位表达式(如
com.app:id/button)并回填到选择器输入框 - 普通点击:在设备对应位置执行点击,用于在实时界面中导航(如打开菜单、进入子页面等)
- 点击「刷新元素」:重新获取当前界面的控件树,更新元素列表
- 右侧列表支持搜索,点击列表项选中,⌘/Ctrl + 点击列表项直接捕获
- 属性 / XPath 模式切换(默认 XPath):属性模式生成 resource-id/text/content-desc 表达式;XPath 模式生成
//*[@resource-id='com.app:id/button']等 Appium 风格表达式,选中「XPath」后 ⌘/Ctrl + 点击捕获 XPath 定位符 - Shift + 点击:设置锚点(橙色高亮),之后 ⌘/Ctrl + 点击目标元素生成「锚点 >> 目标」组合定位符,用于父子结构元素
- 若当前界面无法获取控件树(如 Compose/WebView 应用),⌘/Ctrl + 点击会捕获屏幕坐标(格式
x,y)作为定位符
网页应用:在浏览器中进入交互拾取模式
- 鼠标悬停实时高亮元素,按 ESC 或点击空白退出拾取模式
- CSS / XPath 模式切换(拾取提示条右上角,默认 XPath):点击元素按当前模式生成 CSS 或 XPath 选择器并回填
- ⌘/Ctrl + 点击:按当前模式捕获;Alt/Option + 点击:直接以 XPath 捕获(无需切换模式)
- Shift + 点击:设置锚点(橙色高亮),之后 ⌘/Ctrl + 点击目标生成「锚点 >> 目标」组合定位符
- 提示条可按住文字部分拖动,避免遮挡要捕获的元素
Windows 桌面应用(仅 Windows 客户端):可视化捕获窗口控件,按 UIA 原生树 > 无障碍(MSAA) > 图像模板 三级链路生成
uia:{...}/msaa:{...}/img:{...}选择器- 捕获弹窗展示当前屏幕截图与控件树(按窗口分组),点击控件即生成定位路径;「测试定位」可即时验证匹配
- UIA 原生树定位不到时(如自绘控件、WebView),自动回退到无障碍树,再回退到图像模板匹配(截取控件小图 + 锚点,运行时按分辨率比例缩放匹配)
- 图像圈选与捕获模式的悬浮提示条若遮挡目标控件,按住提示条拖走即可(框选/捕获不受影响)
校验元素:点击 AI 操作步骤参数旁的「校验元素」按钮(🧪图标),可测试选择器(含 XPath)是否能在当前页面找到匹配元素。成功时显示红框标注匹配位置,失败时提示未找到。
XPath 定位(推荐用于结构稳定的元素):
| 平台 | 示例 | 说明 |
|---|---|---|
| Android | //*[@resource-id='com.app:id/btn'] | 按 resource-id 定位 |
| Android | //android.widget.TextView[@text='确定'] | 按类名 + 文本定位 |
| Android | com.app:id/title >> .//android.widget.Button | 锚点 + 相对 XPath(子元素定位) |
| Web | //*[@id='search-btn']、//h3[contains(@class,'card-title')] | 任意 XPath 表达式 |
| Web | #header >> .search-btn | CSS 锚点链(Playwright 原生支持) |
| 桌面(Windows) | uia:{...}、msaa:{...}、img:{...} | 捕获控件自动生成:UIA 原生树 > 无障碍 > 图像模板三级链 |
TIP
自然语言定位(prompt)为主:适用于绝大多数场景,AI 自动理解界面元素的语义并执行操作。XPath/CSS 定位(selector)为辅:当自然语言定位不准确时,可通过元素选择器精确指定目标元素。两者都填时 selector 优先,prompt 作为兜底,兼顾精确性与灵活性。
AI 悬停(aiHover)
AI 识别并将鼠标悬停在指定元素上。网页端用于触发浮层或下拉菜单。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| prompt | 要悬停的元素描述 |
| selector | 元素选择器,优先于 prompt(可选) |
示例:
悬停在用户头像上悬停在"筛选"按钮上
TIP
仅网页端有效,Android 设备无悬停操作。
AI 操作(aiAct)
AI 执行一段自然语言描述的复合操作。可描述多步操作,AI 会自动执行。是最灵活的操作步骤。
返回类型:对象
参数:
| 参数 | 说明 |
|---|---|
| prompt | 操作描述 |
示例:
点击评论"你好"下方的回复按钮,输入"谢谢关注",点击发送按钮如果当前没打开评论浮层,则点击视频右侧的评论图标
TIP
可以描述条件逻辑(「如果...则...」),AI 会根据页面状态判断。操作描述要清晰、步骤明确。
AI 输入(aiInput)
AI 识别输入框并输入指定文本。先定位输入框,再输入内容。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| prompt | 输入框描述 |
| value | 要输入的文本 |
| selector | 输入框选择器,优先于 prompt(可选) |
示例:
- prompt:
搜索输入框, value:获客技巧 - prompt:
评论输入框, value:{{replyContent}}
TIP
value 中使用 {{变量名}} 可以引用之前步骤的输出。
AI 滚动(aiScroll)
AI 在页面上执行滚动操作。可指定方向和距离。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| direction | 滚动方向(上/下/左/右) |
| scrollType | 滚动类型(可选) |
| distance | 滚动距离(可选) |
| locate | 滚动区域描述(可选) |
示例:
- direction:
下, scrollType:单次, distance:500 - direction:
下, locate:评论区
TIP
不填 distance 时 AI 会自动判断滚动距离。填写 locate 可指定在特定区域滚动。
AI 清空输入(aiClearInput)
AI 识别输入框并清空其内容。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| prompt | 输入框描述 |
AI 数据提取
AI 查询(aiQuery)
AI 从屏幕中提取结构化数据,返回 JSON 对象。是最核心的数据提取步骤。
返回类型:对象
参数:
| 参数 | 说明 |
|---|---|
| demand | 数据提取需求描述,包含 JSON 格式示例 |
示例:
获取视频基本信息,返回JSON格式:{ "author": "作者昵称", "description": "视频描述", "commentCount": "评论数" }获取所有评论,包括用户名和评论内容,返回JSON数组:[{"username":"用户名","content":"评论内容"}]
TIP
在 demand 中明确给出 JSON 格式示例,AI 返回结果更准确。输出保存到变量后,用 {{变量名.字段名}} 访问子字段,如 {{videoInfo.author}}。
AI 提取字符串(aiString)
AI 从屏幕中提取一段文本字符串。返回纯文本而非 JSON。
返回类型:字符串
参数:
| 参数 | 说明 |
|---|---|
| prompt | 要提取的文本描述 |
示例:
获取用户主页的抖音号判断当前页面状态,返回"视频详情"或"其他"
TIP
只返回一个字符串值。如需提取多个字段,请使用「AI 查询」步骤。
AI 布尔判断(aiBoolean)
AI 判断一个条件是否成立。返回 true 或 false,常用于条件判断步骤。
返回类型:布尔值
参数:
| 参数 | 说明 |
|---|---|
| prompt | 判断条件描述 |
示例:
当前是否在视频播放页面评论浮层是否已打开
TIP
描述要清晰无歧义。输出保存到变量后,可在「条件判断」步骤中使用。
AI 选择下拉选项(aiSelect)
设置下拉框/组合框的选中项,输出实际选中的选项文本。优先按选择器走确定性路径(零 token):web 原生 <select> 程序化设值、自定义组件库下拉自动「展开→点选项」、Windows 桌面走 UIA 控件树选中;失败回退 AI 视觉操作(打开下拉+点击选项,消耗 token)。
返回类型:字符串(实际选中的选项文本)
参数:
| 参数 | 说明 |
|---|---|
| selector | 元素选择器(CSS/XPath web、uia: windows 桌面捕获生成)。仅 web 与 Windows 桌面支持;android/macOS 平台用 prompt 描述 |
| prompt | 下拉框描述(selector 为空时必填;未命中时作为 AI 兜底) |
| value | 要选中的选项(默认按显示文本包含匹配,支持 {{变量}}) |
| by | 匹配方式:显示文本 / 选项值(原生 select 的 option value)/ 序号(从 0 开始) |
| exact | 精确匹配开关(默认包含匹配,多处匹配取第一个) |
示例:value 填 上海(按显示文本匹配);按序号选第 2 项:by=序号、value=1。
AI 获取下拉选项(aiSelectOptions)
获取下拉框/组合框的全部选项列表(含当前选中项)。web 原生 select 直接读取;自定义组件库下拉自动展开读取后关闭;Windows 桌面走 UIA 控件树枚举;其余平台 AI 视觉读取。
返回类型:数组 [{ label, value, selected }]——绑定变量后用 {{变量名.0.label}}、{{变量名.1.value}} 取值,或循环遍历。
参数:与 AI 选择下拉选项相同(selector/prompt,无需 value)。
列表操作
列表类页面的专用指令:提取整屏列表数据、按行内文本点击列表项。配合「循环」「AI 操作」「代码块」实现逐屏批量处理。
点击列表项(tapListItem)
点击列表中指定某一行内的元素。选择器匹配所有行(捕获时自动生成 class 多匹配选择器),行内文本指定目标行——与滚动位置无关,列表上下滑动后依然准确。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| selector | 行内要点击的元素选择器。点击「捕获元素」自动生成匹配所有行的多匹配选择器(如 //h3[contains(@class,'card-title')]) |
| itemText | 行内文本:点击所在行包含该文本的元素。可填该行任意元素的文本(标题/作者名等,部分文本也可,包含匹配)。支持 {{变量}} 引用 |
| prompt | AI 兜底描述(可选):行内文本未命中时交给 AI 视觉定位的自然语言描述 |
示例:
- 列表场景:selector 捕获每张卡片的「关注」按钮(class 多匹配),itemText 填标题
{{comment.title}}—— 循环中对每一项点击 - 行内文本填部分文本即可:「怎么危险的路」能命中完整标题「怎么危险的路应该先付定金…」
TIP
itemText 建议填行内唯一文本(标题、作者名等),多处匹配时取第一个。与「提取列表数据」配合:提取出列表 JSON 后,循环里用 {{item.title}} 作为 itemText 逐项点击,天然适配上下滑动。
提取列表数据(getListData)
按 XPath/CSS 提取当前屏幕可见的列表数据。每个字段一个选择器(匹配所有行),自动按行对齐成 JSON 数组 [{字段:值}, ...]。
返回类型:数组(JSON 对象数组)
参数:
| 参数 | 说明 |
|---|---|
| fields | 字段列表,每项含 name(字段名)和 selector(匹配所有行的 XPath/CSS)。点击字段行的「捕获元素」自动生成多匹配选择器,字段名留空自动推导 |
| limit | 最多提取行数(默认 50,上限 200) |
示例:
json
[
{ "title": "怎么危险的路应该先付定金", "author": "晴空万里", "time": "06-19 江西" },
{ "title": "小孩子不会说话", "author": "🍁云儿🍁", "time": "06-18 广东" }
]TIP
- 只提取屏幕内可见的行(横竖方向都在屏幕内)——配合「循环 + 点击列表项/AI 点击 + AI 滚动」逐屏处理:提取 → 逐项处理 → 滚动 → 再提取
- 某行缺少任一字段时整行丢弃(不错位、不补空)
- 输出绑定到变量后,循环中通过
{{list}}遍历,每项用{{item.title}}等引用字段 - 与 AI 指令混用的典型闭环:提取列表 → 循环内用 AI 指令处理每一项 → 滚动一屏 → 再提取,直到列表结束
设备控制
获取屏幕尺寸(screenSize)
获取当前屏幕/页面尺寸(全平台:Android 设备屏幕 / Web 页面视口 / Desktop 当前显示器)。返回 { width, height } 对象。
返回类型:对象
无需参数。输出绑定到变量后(如 screen),用 {{screen.width}} 和 {{screen.height}} 访问,或在代码块中用 screen.width 读取。
单位与 AI 滚动的距离参数一致,可直接按比例计算滚动距离。
TIP
典型用法:输出绑定到变量 → 代码块计算 scrollDist = screen.height * 0.8 → AI 滚动的滚动距离填 {{scrollDist}}(滚动一屏的 80%)。
设备滑动(deviceSwipe)
在 Android 设备上从一点滑到另一点,最精确的滑动控制。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| startX | 起点 X 坐标 |
| startY | 起点 Y 坐标 |
| endX | 终点 X 坐标 |
| endY | 终点 Y 坐标 |
| duration | 滑动时长(毫秒,可选) |
示例:
- startX:
540, startY:1500, endX:540, endY:500, duration:300
TIP
推荐在需要精确控制滑动时配合「代码块」计算坐标后使用。duration 控制滑动速度,300ms 接近真人操作。
桌面自动化
桌面自动化步骤仅适用于桌面平台(platform=desktop),用于操控电脑桌面上的鼠标、键盘和显示器。
点击(computerMouseClick)
点击(单击/双击/右键/长按,跨平台)。优先按元素定位点击(桌面捕获控件生成 uia:/msaa:/img: 选择器、web 填 CSS、android 填 resource-id,零 Token),元素选择器为空时按坐标 x/y 点击。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| clickType | 点击类型:click(单击)/ doubleClick(双击)/ rightClick(右键)/ longPress(长按)。单击=全平台,双击/右键=桌面与 web,长按=android |
| selector | 元素选择器(桌面=uia:/msaa:/img:,web=CSS,android=resource-id),优先于坐标 |
| programmatic | 程序化触发(仅 uia: 选择器+单击):优先 UIA 程序化调用(Invoke/Toggle/Select 链)——零输入注入、不移动光标,可规避应用的远控检测;控件不支持 Pattern 时自动回退真实点击。适合标准控件应用(记事本/WinForms 等);自绘 UI(企业微信等)通常不支持会自动回退 |
| x / y | 元素选择器为空时按裸坐标点击(android 为逻辑坐标) |
| prompt | AI 兜底描述(仅单击支持;selector 未命中时交给 AI 视觉定位) |
键盘快捷键(computerKeyboardShortcut)
发送键盘快捷键组合,模拟键盘操作。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| keyName | 快捷键组合(如 Command+C、Control+V、Alt+F4) |
示例:
Command+C(Mac 复制)Control+V(Windows 粘贴)Alt+Tab(切换窗口)
输入文本(computerType)
在桌面上输入文本,通过剪贴板粘贴实现,支持中文。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| text | 要输入的文本内容 |
拖拽(computerDrag)
在桌面上从一点拖拽到另一点,模拟鼠标拖拽操作。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| fromX | 起点 X 坐标 |
| fromY | 起点 Y 坐标 |
| toX | 终点 X 坐标 |
| toY | 终点 Y 坐标 |
| duration | 拖拽时长(毫秒,默认 500) |
列出显示器(computerListDisplays)
列出所有可用的显示器信息。返回显示器数组。
返回类型:数组
无需参数。输出保存到变量后,每项包含显示器 ID 等信息。
元素存在/等待(computerElementExist)
判断指定元素是否存在(输出布尔值,常用于条件分支),跨平台:优先按元素选择器确定性判断(web=CSS、android=resource-id、desktop=uia:/msaa:/img: 捕获生成,零 Token),可设置轮询等待出现;元素选择器为空时按 AI 兜底描述视觉判断一次。
返回类型:布尔
参数:
| 参数 | 说明 |
|---|---|
| selector | 元素选择器(多平台格式),优先于 prompt |
| prompt | AI 兜底描述(selector 为空时必填;AI 视觉判断一次,不轮询) |
| timeoutSec | 轮询等待秒数:0=只判断一次;>0=轮询等待元素出现(间隔 1s),超时输出 false;AI 模式忽略 |
打开软件(computerLaunchApp)
启动桌面应用程序(Windows/macOS 桌面客户端):Windows 按可执行文件路径或命令名启动;macOS 支持 .app 路径或应用名(经 open 启动)。可传启动参数与等待时间。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| appPath | 应用路径:Windows 填可执行文件完整路径或命令名(如 D:\WXWork\WXWork.exe 或 notepad);macOS 填 .app 完整路径或应用名(如 /Applications/TextEdit.app 或 TextEdit)。支持 {{变量}},参数旁「选择文件/目录」按钮可直接弹窗选取 |
| args | 启动参数(可选):传给应用的命令行参数;macOS 经 open --args 透传 |
| waitSeconds | 启动后等待秒数(0-60,默认 0 不等待) |
不同电脑路径不同怎么办
每台电脑安装路径不一致时,不要写死路径:在「变量与参数」面板定义输入变量(如 appPath,类型选 file),本步骤路径填 {{appPath}}。运行任务时,参数表单会显示该输入项,右侧「选择文件/目录」按钮可弹窗选取本机实际路径(Windows 选 exe,macOS 选 .app 目录),一次填写、多台电脑各填各的。
时间控制
随机延迟(randomSleep)
在最小值和最大值之间均匀随机延迟一段时间。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| minMs | 最小延迟(毫秒) |
| maxMs | 最大延迟(毫秒) |
示例:
- minMs:
3000, maxMs:8000(延迟 3-8 秒,均匀随机) - minMs:
500, maxMs:1500(延迟 0.5-1.5 秒)
TIP
建议在启动应用、打开评论等耗时操作后添加延迟等待。Android 端通常 3-8 秒,网页端 2-5 秒。硬编码应用(如抖音评论回复)内部的拟人延迟不受此步骤影响。
服务端交互
写入任务输出(writeOutput)
将一条数据写入任务输出表,可在任务详情的「总体输出」中查看。支持变量引用。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| data | 输出数据(JSON,支持 {{变量名}}) |
示例:
json
{ "source": "wechat_video", "username": "{ {comment.username} }", "content": "{ {comment.content} }", "replyContent": "{ {replyContent} }" }TIP
这是记录任务成果的关键步骤。每次调用写入一条记录。
增加统计(incrementStat)
累加任务统计数据。统计值会在运行时实时显示在界面上。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| field | 统计字段名 |
| value | 增量(默认 1) |
示例:
- field:
totalCommentsFetched, value:1 - field:
totalFollowed
TIP
每 5 次操作自动刷新到数据库。常用统计字段:totalVideosBrowsed、totalCommentsFetched、totalFollowed、totalReplied、totalIntentionCustomers。
刷新统计(flushStats)
立即将缓存的统计数据写入数据库。
返回类型:无
无需参数。
TIP
统计数据会自动每 5 次操作刷新一次,一般不需要手动调用。
Redis 读取(redisGet)
从 Redis 读取指定键的值。键名会自动加上应用和用户维度的前缀,实现数据隔离。
返回类型:对象
参数:
| 参数 | 说明 |
|---|---|
| key | 业务键名 |
示例:
- key:
lastProcessedId - key:
searchCursor
TIP
同一应用同一用户的 key 互相隔离,不同用户/应用之间不会冲突。输出保存到变量中后续使用。
Redis 写入(redisSet)
向 Redis 写入键值对,支持设置过期时间。用于持久化存储任务状态。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| key | 业务键名 |
| value | 值(支持变量引用) |
| ttlSeconds | 过期时间(秒,默认 7 天) |
示例:
- key:
processedUsers, value:{ "user123": true }, ttlSeconds:86400(1 天后过期)
TIP
默认 7 天过期,适合日常去重。设为 0 表示永不过期。
Redis 计数(redisIncr)
递增 Redis 计数器并返回递增后的值。适合统计每日操作次数、限流控制。
返回类型:数字
参数:
| 参数 | 说明 |
|---|---|
| key | 业务键名 |
| value | 增量(默认 1) |
| ttlSeconds | 过期时间(秒) |
示例:
- key:
dailyFollows, value:1, ttlSeconds:86400 - 输出 → 变量
todayFollows - 条件判断:
todayFollows < 50
TIP
配合条件判断步骤可实现「每日最多关注50人」等限流逻辑。
Redis 键是否存在(redisKeyExists)
检查 Redis 中是否存在指定键。返回 true 或 false,常用于去重判断。
返回类型:布尔值
参数:
| 参数 | 说明 |
|---|---|
| key | 业务键名(支持变量引用) |
示例:
- key:
user_{{comment.username}} - 输出 → 变量
alreadyProcessed - 条件判断:
alreadyProcessed === false
TIP
配合 redisSet 实现完整去重:先检查是否存在 → 不存在则处理并写入标记。
追加到 Excel(excelAppend)
将数据追加写入 Excel 文件指定 Sheet 中。文件不存在则自动创建,Sheet 不存在则自动新建。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| filePath | Excel 文件路径(如 /data/output.xlsx,支持 {{变量名}} 引用) |
| sheetName | 目标 Sheet 名称,默认 Sheet1 |
| data | 要追加的数据。单条对象写入一行,对象数组写入多行。支持 {{变量名}} 引用 |
| headers | 列名数组(如 ["姓名","年龄","时间"])。新建 Sheet 时写入第一行 |
示例:
- filePath:
/data/leads.xlsx, sheetName:抖音意向客户, data:{{outData}}, headers:["用户名","评论内容","意向等级","时间"]
TIP
在服务器端执行,写入的是服务器上的文件路径。建议在关键数据写入后追加此步骤,用于导出分析。
多平台混合流程
创建应用时平台留空即为多平台应用(platform=multi),指令面板显示全部步骤类型,一个流程可同时操作浏览器、Android 手机与桌面。
当前设备切换
AI 操作步骤(aiTap / aiInput / aiScroll 等)作用于当前设备,由启动步骤切换:
| 步骤 | 执行后当前设备 |
|---|---|
| 启动浏览器(browserLaunch) | 浏览器 |
| 切换浏览器(browserSwitch) | 浏览器(复用该站点后台窗口,不导航) |
| 启动应用(launchApp) | Android 手机 |
| 打开软件(computerLaunchApp)/ 电脑操作(computerMouseClick 等) | PC 桌面 |
WARNING
每个 AI 操作步骤之前必须有对应平台的启动步骤明确操作对象,否则运行时报错「流程未明确当前操作设备」。同一平台的连续操作之间不要重复插入启动步骤。
示例(浏览器 → 手机 → 桌面):
- 启动浏览器(browserLaunch,url=抖音) → AI 点击「搜索」 → AI 输入关键词
- 启动应用(launchApp,uri=手机包名) → AI 点击「我的」 → 设备滑动
- 打开软件(computerLaunchApp,路径=记事本) → 点击(computerMouseClick,uia: 选择器) → 键盘快捷键
步骤级操作平台声明
AI 操作步骤(aiTap / aiAct / aiInput / aiQuery / aiString / aiBoolean / aiScroll / aiClearInput / aiHover 等)及列表步骤(tapListItem / getListData / computerMouseClick / computerElementExist)新增操作平台参数:
| 选项 | 行为 |
|---|---|
| 自动(默认) | 按前置启动步骤/最近会话推断(即上表的「当前设备」机制) |
| 浏览器 / 手机 / 桌面 | 显式指定该步骤操作的设备,客户端按平台分流到对应会话,无需前置启动步骤切换 |
适合「同一屏先后操作两个平台」的场景,例如:浏览器提取数据 → 桌面软件处理,两个平台会话常驻,用 platform 参数逐步骤指定操作对象。
任务目标引用(__target)
运行表单/定时任务所选的目标注入为特殊标记参数 __target(kind: "task_target"),流程中可直接引用:
| 表达式 | 含义 |
|---|---|
{{__target.accounts[0].site}} | 默认站点标识 |
{{__target.accounts[0].accountName}} | 默认站点账号 |
{{__target.accounts[1].site}} | 第二个站点(多站点流程配合「启动浏览器」步骤的 accountName 参数) |
{{__target.deviceUdid}} | 所选 Android 设备 |
大模型调用
LLM 对话(llmChat)
调用文本大模型,自定义提示词,获取 AI 生成的文本结果。用于意向判断、内容生成等场景。在服务端执行(用平台配置的文本模型,不占客户端设备)。
返回类型:字符串
参数:
| 参数 | 说明 |
|---|---|
| systemPrompt | 系统提示词(定义 AI 角色) |
| userPrompt | 用户消息(待分析内容) |
| messages | 高级:直接传入 [{ role, content }] 消息数组(多轮对话) |
示例:
- systemPrompt:
你是一个销售意向判断助手。分析用户评论判断意向等级。 - userPrompt:
分析以下评论:{{comments}} - 输出 → 变量
llmResult - 代码块解析:
JSON.parse(vars.get("llmResult"))
TIP
在提示词中明确要求 AI 返回 JSON 格式,方便后续用代码块解析。使用 {{变量名}} 可将之前步骤的数据传入提示词。此步骤消耗 Token 余额。
MCP 调用(mcpCall)
通过 Streamable HTTP 连接远程 MCP 服务器并调用工具——查询外部数据(数据库/接口/知识库)或执行远端操作,返回结果绑定到变量。
返回类型:对象
参数:
| 参数 | 说明 |
|---|---|
| url | MCP 服务端点(Streamable HTTP 地址,如 https://mcp.example.com/mcp) |
| toolName | 要调用的工具名 |
| apiKey | Bearer Token 认证(该服务需要时填) |
| arguments | 工具参数(JSON 字符串,如 {"owner":"xxx","repo":"yyy"},支持 {{变量}}) |
示例:url 填 MCP 端点,toolName 填 search_docs,arguments 填 {"query": "{{searchKeyword}}"},输出绑定变量后用 {{result.0.title}} 取字段。
WARNING
MCP 调用暂不支持单步测试运行,请通过运行整个流程验证。
脚本执行
代码块(codeBlock)
在客户端本地执行 JavaScript(默认)或 Python 代码。可读写变量、解析 JSON、进行数值计算等。是最灵活的数据处理步骤。
返回类型:对象(JS:return 的值或末行表达式;Python:result 变量)
参数:
| 参数 | 说明 |
|---|---|
| language | 语言:javascript(默认)/ python |
| code | 代码。JS 整体包在 async 函数中执行,支持顶层 await;Python 详见下方「Python 代码块」 |
如何读取变量
使用 vars.get(变量名) 读取流程变量。可读取的内容包括:
- 「变量与参数」面板中声明的变量(含默认值)
- 之前步骤通过「输出」绑定保存的变量
- 之前代码块通过
vars.set写入的变量
javascript
// 读取单个变量
const countStr = vars.get("commentCount");
// 读取对象字段(点号或下标访问)
const width = vars.get("screen").width;
const title = vars.get("videoInfo")["title"];两个注意点
vars.get()返回的可能是字符串(取决于变量来源),数值运算前用Number()转换:javascriptconst current = Number(vars.get("totalComments")) || 0;- 运行参数
ctx不传入沙箱,代码块内vars.get("ctx")拿不到。引用运行参数请用{{ctx.xxx}}文本替换方式(见下文「变量替换 vs vars.get」)。
如何设置变量
使用 vars.set(变量名, 值) 写入变量。代码块执行结束后,set 过的变量会自动写回流程,后续步骤即可通过 {{变量名}} 引用。
javascript
// 写入新变量(无需预先声明,自动创建)
vars.set("scrollDist", Math.round(width * 0.8));
// 覆盖已有变量(常用于跨循环累计)
const current = Number(vars.get("total")) || 0;
vars.set("total", current + newCount);支持的类型:字符串、数字、布尔、对象、数组,以及 Set / Map(会被特殊序列化,下次 vars.get 时还原)。
Python 代码块
language 选择 python 后,代码在客户端内置 Python 运行时执行(无需本机安装 Python;旧客户端回退服务端沙箱,要求服务器 python3 3.9+)。
python
import json, math, random, hashlib, base64, collections
# 变量读写与 JS 一致
name = vars.get("name")
n = vars.get("count") or 0
vars.set("count", n + 1)
vars.set("hello", "hi " + name)
# 给全局变量 result 赋值 = 步骤输出(可绑定到变量)
result = {"sha": hashlib.md5(name.encode()).hexdigest(), "pi": round(math.pi, 3)}与 JS 版本的区别:
| 项 | 说明 |
|---|---|
| 步骤输出 | 给全局变量 result 赋值(不赋值则为 null) |
| 自定义函数 | 支持:language=python 的自定义函数以 def 名(参数): 前缀注入,仅 Python 代码块可调用(JS 函数仅 JS 代码块可调用,互不可见) |
| 可用库 | 仅标准库白名单:json / math / re / random / time / datetime / hashlib / base64 / string / collections / itertools / functools / copy / decimal / urllib.parse / statistics / fractions / secrets / textwrap / heapq / operator / unicodedata / difflib / bisect / types / zlib(含子模块)。os/sys/socket/io/pathlib 等不可导入 |
| 执行限制 | 10 秒超时、内存 512MB、无网络访问、无文件读写(open 被禁用) |
| 变量类型 | 仅 JSON 类型(字符串/数字/布尔/对象/数组/null),数值范围 2^53 内 |
编辑器内置模块提示
自定义函数编辑器中,代码输入框下方有「JavaScript/Python 沙箱:可用模块与限制」折叠面板,按语言列出可用全局与模块白名单——数据由服务端接口实时下发,与实现保持同步。object/array 类型的输入变量会自动 parse 为对象/数组,无需手动 JSON.parse / json.loads。
安全说明:Python 沙箱采用「import 白名单 + 受限内置函数 + 资源限制 + 超时强杀 + 降权运行」多层防护,可挡住脚本化攻击与绝大多数恶意代码;但由于 Python 语言特性,极端深度的沙箱逃逸无法 100% 杜绝(流程作者为受信用户,平台为私有部署)。如需强隔离请联系管理员评估容器化方案。
变量和函数需要在面板中预先定义吗?
变量:不需要预先定义。 vars.set 任意名字的变量都会自动创建并写回流程,后续步骤可直接引用。但建议在「变量与参数」面板中声明,原因是:
- 运行参数(isInput 勾选):任务启动时由用户填写,通过
{{ctx.变量名}}传入流程 - 默认值:变量在首次赋值前就有初始值
- 可读性:面板中能看到流程用到的所有变量
未声明的变量只影响「看不到」,不影响使用。
函数:需要在「变量」面板的「自定义函数」Tab 中定义。 自定义函数会预先载入沙箱,代码块中才能按函数名直接调用。代码块内部自己声明的 function 只在该代码块内有效,跨代码块复用必须走「自定义函数」面板。
变量替换 {{var}} vs vars.get()
两者的机制不同:
| 方式 | 时机 | 行为 |
|---|---|---|
{{变量名}} | 代码执行前 | 对 code 文本做替换:整个参数就是一个 {{var}} 时保持原始类型;嵌入在字符串中时,对象会被序列化为 JSON 字符串 |
vars.get/set | 代码执行时 | 运行时读写真实变量值,类型完整保留 |
推荐:代码块内统一用 vars.get/set 读写变量,避免文本替换带来的引号转义问题; 替换仅用于引用运行参数({{ctx.searchKeyword}})或个别标量值。
javascript
// 不推荐:把对象用模板替换嵌入代码(依赖 JSON 序列化结果)
// const list = 模板变量替换;
// 推荐:运行时读取,类型安全
const list = vars.get("listData");运行环境与依赖管理
代码块在客户端本地执行(新版客户端上报 codeblock-js/py 能力后自动走本地;旧客户端回退服务端沙箱):
JavaScript(本地 Node 环境):
- 可直接
requireNode 内置模块(fs、path、crypto、https等,具备完整本地能力:读写文件、发网络请求) - 第三方 npm 包:点击代码块编辑器下方的「依赖管理」安装(如
dayjs、xlsx),安装后在代码中直接require - 可用全局:
vars.get/set、platform(客户端系统)、md5(文本)、console.log(运行日志可见) - 默认超时 60 秒(可在「超时」处调整 5-600 秒)
Python(本地运行时):
- 客户端内置 Python 运行时(无需本机安装),仅标准库白名单:
json、math、re、random、time、datetime、hashlib、base64、collections等 - 无网络与文件系统访问(如需要,用 JavaScript 代码块或「本地文件操作」指令族)
- 可用全局:
vars.get/set、platform、md5(文本);给result变量赋值作为输出
TIP
自定义函数(JS/Python)在代码块中按函数名直接调用,与代码块同环境执行。
示例:
javascript
// 解析 LLM 返回的 JSON
const raw = vars.get("llmResult");
const parsed = JSON.parse(raw.match(/\{[\s\S]*\}/)[0]);
vars.set("intentionLevel", parsed.intentionLevel);
return parsed;javascript
// 数值计算(配合「循环」做跨屏累计)
const current = Number(vars.get("totalComments")) || 0;
const newCount = Number(vars.get("currentScreenCount")) || 0;
vars.set("totalComments", current + newCount);
return current + newCount;自定义函数
在应用编辑器的「变量」面板中切换到「自定义函数」Tab,可定义可复用的函数(支持 JavaScript 与 Python 两种语言)。定义后会预载入对应语言的沙箱,在「代码块」中按函数名直接调用。不使用自定义函数的代码块无需定义任何函数。
函数定义
每个函数包含以下字段:
| 字段 | 说明 |
|---|---|
| name | 函数名,在 codeBlock 中直接作为标识符调用 |
| language | 实现语言:javascript(默认)/ python。JS 函数仅 JS 代码块可调用,Python 函数仅 Python 代码块可调用,互不可见 |
| params | 参数名列表(可选,逗号分隔)。动态类型:不声明参数类型,类型由调用方实参决定,对象/数组直接传参 |
| description | 函数描述(可选) |
| code | 函数体。JS:async 函数体(自动包装 var 名 = async (参数) => {...});Python:只写 def 函数体(自动包装 def 名(参数): 并整体缩进 4 空格),返回值用 return |
使用示例
JS 函数 parseCommentCount:
name: parseCommentCount
language: javascript(默认)
params: text
code:
if (!text) return 0;
text = String(text).trim();
if (text.includes('万')) { ... }在 JS 代码块中调用(自定义函数是 async 函数,调用时建议 await):
javascript
const count = await parseCommentCount(vars.get("commentCountStr"));
vars.set('commentCount', count);Python 函数 formatTitle:
python
name: formatTitle
language: python
params: title
code:
if not title:
return ""
return title.strip().replace(" ", "")在 Python 代码块中调用:
python
title = formatTitle(vars.get("rawTitle"))
vars.set("title", title)
result = title函数与代码块的执行环境
- 函数体和代码块共享同一个沙箱:JS 函数内同样可用
vars.get/set、md5、crypto、JSON等全局对象;Python 函数内可用vars.get/set、白名单标准库 - 同语言函数之间可以相互调用(JS:
await otherFn(...);Python:other_fn(...)) - 代码块内直接声明的普通
function/def仅该代码块内可用,无法跨代码块复用 - 函数定义保存在应用的
flow_definition中,随流程一起导入导出
流程控制
循环(loop)
循环执行子步骤。支持三种模式:计数循环、遍历数组(for-each)和条件循环(while)。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| loopSource | 数组变量名(遍历数组模式) |
| loopVariable | 循环变量名(默认 item) |
| breakCondition | 退出条件表达式 |
| maxIterations | 最大迭代次数(默认 1000,防无限循环) |
| maxConsecutiveFailures | 子步骤最大连续失败次数(超过后退出循环,可选) |
三种循环模式:
| 模式 | 配置方式 | breakCondition 语义 | 典型用法 |
|---|---|---|---|
| 计数循环 | 仅填 maxIterations + loopVariable | 无(用 maxIterations 限制) | 遍历 N 个视频 |
| 遍历数组 | 填 loopSource | 「退出条件」,为 true 时提前退出 | 遍历评论列表 |
| 条件循环 | 填 breakCondition | 「继续条件」,为 true 时继续循环 | 滚动加载更多 |
示例:
- 计数循环:maxIterations:
{{maxVideos}}, loopVariable:videoIndex - 遍历数组:loopSource:
comments, loopVariable:comment,循环体中用{{comment.username}}、{{comment.content}} - 条件循环:breakCondition:
noNewCount < 1 && totalProcessed < 20, maxIterations:100
循环体中的变量:
| 变量 | 说明 |
|---|---|
{{loopVariable}} | 当前项。如 {{comment.username}} |
{{loopVariable.__index}} | 当前索引(从 0 开始) |
{{loopVariable.__length}} | 数组长度(仅遍历数组模式) |
{{loopSource}} | 同 {{loopVariable}},可互换使用 |
TIP
- 计数循环中
loopVariable的值从 1 开始(如遍历第 1 个视频时videoIndex === 1) - 遍历数组模式中「退出条件」为 true 时立即退出,与条件循环的「继续条件」语义相反
maxConsecutiveFailures用于子步骤连续报错时自动退出,避免死循环maxIterations为数字或{{变量名}},支持引用运行参数
条件判断(conditional)
根据条件表达式执行不同的步骤分支。支持 IF/THEN/ELSE 结构。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| condition | 条件表达式(直接引用变量名,无需 ) |
条件表达式语法:
| 类型 | 示例 |
|---|---|
| 字符串比较 | intentionLevel === "高" |
| 数值比较 | totalFollowed >= 50 |
| 布尔判断 | alreadyProcessed === false、isProfile === true |
| 逻辑组合 | count < 10 && hasMore === true、failed === true || isProfile === false |
| 取反 | !alreadySeen |
分支结构:
- then 分支:条件为 true 时执行的步骤
- else 分支:条件为 false 时执行的步骤(可选,在编辑器中点击「+ else」添加)
示例:
- condition:
intentionLevel === "高"→ then:[关注用户、发送回复] - condition:
totalFollowed >= 50 || totalReplied >= 30→ then:[停止循环] - condition:
alreadyProcessed === false→ then:[关注并回复]else:[跳过]
TIP
条件表达式中变量直接引用名称,不需要 包裹(如 commentCount > 0,不是 {{commentCount}} > 0)。
输出日志(log)
输出一条日志信息到任务日志中,用于调试和记录关键节点。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| message | 日志消息(支持变量引用) |
示例:
开始处理第 {{i}} 个视频找到意向客户: {{comment.username}}
TIP
停止信号无需手动检查——系统在每个步骤执行前自动检查,用户点「停止」时立即生效。
设置变量(setVariable)
设置流程变量的值。用于初始化变量或更新变量值。
返回类型:无
参数:
| 参数 | 说明 |
|---|---|
| name | 变量名 |
| value | 变量值(支持 {{变量名}} 引用) |
示例:
- name:
totalProcessed, value:0 - name:
searchKeyword, value:{{ctx.searchKeyword}}
子流程(subFlows + callSubflow)
把一段可复用的步骤抽象为子流程,主流程通过「调用子流程」步骤引用——实现复用、抽象,并提升流程可读性。子流程在本应用内定义与复用(编辑器底部面板「子流程」tab 管理)。
创建子流程:
- 底部面板 → 「子流程」tab → 新建子流程(命名、可加用途描述)
- 头部下拉(或子流程列表的「编辑」)切换到该子流程——画布、变量面板、复制粘贴均作用于它(顶部有青色横幅提示当前作用域),变更随整体保存
- 在「变量与参数」中声明变量:勾选**「运行参数」= 输入**(调用时传值)、勾选**「输出」= 输出**(调用结束回传)
- 像编排主流程一样编排子流程步骤(支持循环/条件/try 等所有指令)
在主流程中调用:指令面板「流程控制」分组拖入**「调用子流程」**,选择目标子流程并填入参(支持 {{变量}});在「输出绑定」中接收返回——整体绑定得到 {输出变量名: 值} 对象,或「取值路径」填某个输出名取单个。
变量与函数可见性(完全隔离):
子流程与主流程完全隔离——子流程读不到主流程变量(要传值必须声明为输入并在调用时传入参)、内部写入不影响主流程、自带独立的自定义函数(编辑子流程时在「自定义函数」面板维护,仅子流程内可见)。跨边界传值只有两条通道:输入(入参)与输出(输出绑定)。
WARNING
2026-09-05 起子流程为完全隔离语义(此前支持「读穿透」直接读主流程同名变量,已移除)。存量子流程若依赖读穿透,请把用到的主流程变量声明为子流程的输入变量并填入参。
WARNING
- 子流程不允许递归(直接或间接调用自己,保存时校验报错);嵌套深度上限 8 层
- 子流程内的「退出循环 (break)」只作用于子流程内部的循环,不会退出主流程的循环
- 「调用子流程」步骤支持重试/出错时策略(子流程内部失败按该步骤的策略统一处理),但不限整体超时——子流程是完整流程段,内部步骤各自受命令级超时约束
- 子流程不支持单步「测试运行」,请运行整个流程验证(日志中有
enter subflow/exit subflow标记) - 引用按子流程 ID 记录——重命名不影响已有调用;删除被引用的子流程会校验报错
自定义指令(指令市场,callInstruction)
三方开发者可以把一段流程编排发布为自定义指令,供全平台用户在流程编排中直接引用——免安装、拖入即用,与内置标准指令同级体验。
开发者(创建指令):侧边栏「我的指令」→「新建指令」(填名称/描述/子分类/平台)→ 进入与流程编辑器一致的编排页(编排步骤、声明输入/输出变量、自带函数)→「保存草稿」→「发布」。发布后:
- 全平台用户的流程编辑器 palette 出现**「指令市场」**分组(按子分类二级归类),可直接拖入
- 作者自己额外有**「我的指令」**分组(草稿+已发布),可确认上架状态
- 引用·自动更新:引用方只存指令 ID,运行时加载已发布版本——作者重新发布后,所有引用它的应用下次运行自动使用新版
- 下架后引用方运行报「指令已下架」(编辑器校验也会提示)
使用方(引用指令):palette「指令市场」分组拖入 → 属性面板按指令声明的输入变量动态填入参(支持 {{变量}})→「输出绑定」接收返回(整体对象或按输出名取单个)。画布显示橙色 🔧 指令名 (N 入参) 卡片。
隔离语义(与子流程一致且更强):指令内部变量/函数与引用方应用完全隔离——不读应用变量、写入不泄漏、函数独立。传值只走输入/输出。指令可嵌套引用其它市场指令(深度上限 8 层)。
单步测试:「调用自定义指令」步骤支持测试运行——服务端按已发布定义顺序执行指令步骤(服务端步骤本地跑、设备步骤下发客户端),返回输出对象。
WARNING
- 草稿指令引用后运行会报错(校验提示先发布)
- 指令定义源码仅作者可见;市场列表只暴露名称/描述/入参出参摘要
添加备注说明(comment)
在流程中插入一条纯文字备注,记录当前指令或指令块的作用、注意问题等。画布中显示为黄色便签样式,大纲中直接显示备注文字。
仅作文字提醒和阅读参考,不参与流程执行——执行时直接跳过,零开销。备注内容支持多行,写 {{变量}} 之类的文字也不会被解析或校验。
退出循环(break)
放在循环体内,执行到该指令时立即退出当前所在的循环(嵌套时只退出最内层),继续执行循环之后的步骤。画布中显示为红色 ⛔ 退出循环 标记。
- 常与条件判断配合:满足某条件时提前结束循环(如「已找到目标 / 已处理够 N 条」),比
breakCondition更直观 - 放在循环外无效果(编辑器校验会警告)
- 放在 try/catch 内也能正常穿透(不会被当作错误捕获);在子流程内只作用于子流程内部的循环
异常捕获(try/catch)
块级错误处理:try 分支中的任一步骤失败时,跳过剩余步骤、执行 catch 分支,之后继续后续流程(不中断任务)。错误信息可写入指定变量(默认 error),catch 分支中用 {{error}} 引用(如写入输出、记录日志)。
WARNING
步骤自身配置了「出错时 = 继续 / 存失败信息到变量」的,优先按步骤策略处理,不会触发 catch。catch 分支不能为空(保存时校验)。
出错处理(onError)
每个 Action 步骤都支持「出错时」策略,控制步骤失败(含超时)时的行为:
| onError 值 | 行为 |
|---|---|
abort(默认) | 终止整个流程 |
continue | 跳过该步骤,继续执行后续步骤 |
retry | 重试(配合最大重试次数,默认 1 次) |
setVariable | 把失败信息保存到变量后继续 |
保存失败信息到变量(onError = setVariable):指定「失败信息保存到变量」的变量名后,步骤失败时**失败原因(错误消息文本)**会自动写入该变量——后续步骤可用 {{变量名}} 引用(写入任务输出、条件判断分支恢复等)。变量会自动注册到变量面板。也可在「自定义写入值」填 {{某变量}} 或固定文本覆盖默认的错误消息。
示例:
- aiTap
prompt: 用户昵称, 出错时setVariable, 变量tapError - 后续条件判断:
{{tapError}} !== ''→ 执行恢复操作(或直接把{{tapError}}写入任务输出排查问题)
步骤超时(timeoutSeconds):每个步骤可配置「超时时间(秒)」(5-3600,留空=默认:AI 指令约 5 分钟、其余约 10 分钟)。超时后按「出错时」策略处理——例如 配合 setVariable 可捕获「页面卡死导致某步骤长时间无响应」的情况。注意「代码块」有自己的沙箱超时设置、「调用子流程」不限整体超时,两者不显示此配置。
WARNING
注意:{{a}} + {{b}} 会变成字符串拼接(如 "3" + "5" = "35"),不是数值运算。需要数值运算请使用「代码块」步骤。
文件操作
文件类指令全部在客户端本地执行(读取/写入都发生在运行任务的客户端机器上),路径支持 {{变量}}。适合读取批量输入、写入结果数据、管理下载文件等场景。
读取本地文件(loadFile)
读取客户端本地文件内容并存入变量,支持多种格式解析。适用于从文件批量加载关键词列表、配置等场景。
返回类型:根据 format 参数返回 array 或 string
参数:
| 参数 | 说明 |
|---|---|
| path | 文件路径(支持 {{变量名}} 引用) |
| format | 解析格式:lines(每行一个,返回数组)、csv(返回对象数组)、json(返回对象/数组)、text(原始字符串) |
使用方式:
- 在变量面板声明一个
file类型的输入变量(如keywordFile),勾选"运行参数" - 运行任务时选择本地文件,文件路径会存入该变量
- 在流程中添加
loadFile步骤,path 设为{{keywordFile}},选择 format - 将结果绑定到变量(如
keywords) - 后续用
loop遍历{{keywords}}
示例流程:
变量: keywordFile (file, isInput=true)
步骤:
1. loadFile path={ {keywordFile} } format=lines -> keywords
2. loop loopSource={ {keywords} } loopVariable=keyword
2.1. aiInput 输入框 { {keyword} }
2.2. aiTap 搜索按钮
2.3. ... 后续操作TIP
MCP 调用时,外部 Agent 可直接传文件内容字符串(跳过 loadFile),或传文件路径(客户端机器需有该文件)。
读取 Excel 区域(excelRead)
读取 Excel 指定 Sheet 的任意区域内容,输出二维数组(每行是单元格值数组),绑定到变量供循环/代码块使用。
返回类型:数组(二维)
参数:
| 参数 | 说明 |
|---|---|
| filePath | Excel 文件路径(支持 {{变量}}) |
| sheetName | Sheet 名称(留空=第一个 Sheet) |
| startRow / endRow | 起始/结束行(1 起,含结束行;-n=倒数第 n 行,-1=最后一行) |
| startCol / endCol | 起始/结束列(字母如 A、AB,或数字 1 起;支持负数倒数) |
说明:默认读取整个 Sheet;文件或 Sheet 不存在时报错。
写入 Excel 区域(excelWrite)
向 Excel 写入二维数组数据。
| 参数 | 说明 |
|---|---|
| filePath | 文件路径(不存在自动创建) |
| sheetName | 目标 Sheet(留空=Sheet1,不存在自动新建) |
| data | 二维数组(对象数组先用代码块转换,或改用「追加到 Excel」) |
| mode | append=写到已有数据末尾;region=从起始行列位置覆盖写入(越界自动扩展) |
| startRow / startCol | region 模式的起始行列(1 起,支持负数倒数) |
读取 Word(wordRead)
读取 Word 文档的文本内容,段落以换行分隔,绑定到变量使用。仅支持 .docx(不支持旧版 .doc)。
写入 Word(wordWrite)
向 .docx 写入文本(每行一个段落):append=末尾续写(保留原有内容,文件不存在自动新建);overwrite=重写整个文档。
读取 PDF(pdfRead)
提取 PDF 的文本内容(逐页提取,页间空行分隔)。扫描件(图片型 PDF)无法提取文字——请用「AI 识别文件」。
写入 PDF(pdfWrite)
向 PDF 写入文本(自动折行、自动分页,支持中英文):append=末尾新增内容页(文件不存在自动新建);overwrite=重写整个文档。
AI 识别文件(aiFileRecognize)
用视觉大模型识别本地图片或 PDF 文件的内容。图片文件(png/jpg/jpeg/bmp/webp)直接识别;PDF 逐页转图识别——扫描件/图片型 PDF 也可识别(如发票、证件、截图)。
返回类型:字符串(模型按识别要求输出的内容)
参数:
| 参数 | 说明 |
|---|---|
| filePath | 本地文件路径(支持 {{变量}},可配合 file 类型输入变量) |
| prompt | 识别要求:写清楚要提取什么及输出格式(如 提取发票号码、开票日期和金额,以 JSON 输出) |
| pages | 仅 PDF 生效:页码范围(如 1-4),默认前 8 页、最多 20 页 |
示例:识别发票 → prompt 提取开票号码和开票日期,以JSON输出 → 输出绑定变量 result → 代码块 JSON.parse(vars.get("result")) 取字段。
TIP
文字版 PDF(可复制文字的)用「读取 PDF」+「LLM 对话」更省 token;AI 识别文件适合扫描件与图片。此步骤消耗 Token 余额。
上传文件(uploadFile)
上传本地文件到网页或桌面软件(web 与 Windows 桌面支持,android 不支持)。
- web:自动识别
input[type=file]直接设值(隐藏输入框也可),或拦截文件选择框后点击上传按钮注入文件 - 桌面(Windows):点击上传按钮后自动操作系统文件对话框填入路径并确认
参数:filePath(本地文件路径)、selector / prompt(上传按钮的定位,至少填一个)。
下载文件(downloadFile)
点击下载并保存到本地指定目录(web 与 Windows 桌面支持,android 不支持)。
- web:先监听下载事件再点击下载按钮(避免漏事件),保存到指定目录——web 平台不弹保存对话框,由本参数直接控制保存位置(Playwright 接管下载)
- 桌面(Windows):点击另存为/导出/下载按钮后自动操作系统保存对话框填入路径并确认(静默保存到固定目录的应用不支持)
返回类型:{ path, fileName }——绑定变量后用 {{file.path}} 取值。
参数:saveDir(保存目录,必填)、rename(指定文件名,缺省用网站建议名)、selector / prompt(下载按钮定位)、timeoutSec(等待下载完成,默认 180 秒)。不填选择器时仅等待下载(用于前序步骤已触发下载的场景,可能漏事件,建议填写)。目标文件已存在会直接覆盖。
处理下载对话框(downloadDialog)
保存「前序步骤触发的下载」到本地目录,本指令不点击任何按钮(web 与 Windows 桌面支持)。
- web:接收浏览器下载并保存——验证码通过后异步触发的、甚至本步骤开始前已触发的下载都能兜住(浏览器启动时即挂持久下载监听)
- 桌面(Windows):驱动已弹出的另存为对话框(填入路径并点保存)
返回类型:{ path, fileName }。典型编排(下载有验证码时):
1. AI 点击 下载按钮
2. AI 处理验证码(滑块/点选等)
3. downloadDialog saveDir=D:\downloads rename={ {orderId} }.pdf本地文件管理(openFolder / listFiles / createFolder / copyFile / moveFile / deleteFile / waitFile)
对客户端本地文件/文件夹的操作,全部零 token、跨平台(含 mac):
| 指令 | 说明 |
|---|---|
| openFolder | 在资源管理器/访达中打开文件夹(路径含空格安全) |
| listFiles | 列出文件夹内文件,输出 [{name, path, size, mtime, isDir}] 数组;pattern 通配符过滤(如 *.xlsx)、recursive 递归子目录。配合循环逐个处理 |
| createFolder | 创建文件夹(多级逐级创建,已存在不报错) |
| copyFile | 复制文件:targetPath 为已存在目录时复制入内保持原名,否则视为完整目标路径。已存在直接覆盖 |
| moveFile | 移动/重命名(跨盘符可),路径规则同 copyFile。已存在直接覆盖 |
| deleteFile | 删除文件/文件夹(文件夹需勾选 recursive)。⚠️ 不进回收站、不可恢复 |
| waitFile | 轮询等待文件出现(500ms 间隔):等到输出 true、超时输出 false(不报错,常与条件分支配合:下载/导出完成后处理文件) |
多实体输出
输出数据到指定实体(writeOutput)
writeOutput 步骤支持 entity 参数,可将不同类型的数据写入不同实体,在任务详情中分 Tab 展示。
参数:
| 参数 | 说明 |
|---|---|
| data | 要输出的数据对象(支持 {{变量名}} 引用) |
| entity | (可选)实体名称,如 video、comment、author。不填则写入默认实体 |
配置 output_schema:
在应用编辑器中配置 output_schema 的 entities 数组,定义多个实体:
json
{
"entities": [
{
"name": "video",
"title": "视频",
"columns": [
{"key": "title", "title": "标题"},
{"key": "author", "title": "作者"}
]
},
{
"name": "comment",
"title": "评论",
"columns": [
{"key": "text", "title": "评论内容"},
{"key": "user", "title": "评论用户"}
]
}
]
}示例:
步骤:
1. aiQuery 提取视频标题和作者 -> videoInfo
2. writeOutput entity=video data={title: { {videoInfo.title} }, author: { {videoInfo.author} }}
3. loop loopSource={ {comments} }
3.1. writeOutput entity=comment data={text: { {item.text} }, user: { {item.user} }}任务详情页会展示「视频」「评论」两个 Tab,各自独立分页。
TIP
旧格式 {columns: [...]} 仍兼容,自动归入默认实体。
分布式定时调度
平台支持定时自动执行任务,支持多设备并行、cron 表达式和简单间隔两种触发方式。
创建定时调度
在左侧导航进入「定时调度」页面,点击「创建调度」:
- 选择应用:选择要定时执行的应用
- 填写参数:填写运行参数(同手动运行)
- 选择目标:
- Android 应用:选择一台或多台已连接的 Android 设备
- 网页应用:选择已登录的账号(按站点过滤,如抖音应用只显示抖音账号)
- 桌面应用:固定为本机
- 多平台应用:账号(可选,浏览器步骤用)+ 手机设备(可选,手机步骤用);都未选则目标为本机桌面。单目标 = 一台客户端执行整个混合流程
任务目标统一注入
__target特殊标记参数(kind: "task_target",含deviceUdid/accounts/desktop字段),分布式定时调度等场景可直接识别,流程内也可用{{__target.accounts[0].accountName}}引用目标账号。
- 配置调度规则:用可视化界面配置(无需了解 cron 语法)
- 设置重试次数:设备离线时重试次数(默认 3 次,每次间隔 5 分钟)
调度规则配置
支持两种模式二选一(顶部按钮组切换):
1. 可视化配置(默认,无需了解 cron 语法):
| 频率 | 配置方式 | 示例 |
|---|---|---|
| 每一分钟 | 一键选择 | 每分钟执行一次 |
| 每 N 分钟 | 填写分钟数 | 每 30 分钟执行一次 |
| 每小时 | 选全天每小时 或 指定小时范围 | 每天 9-18 点每小时执行 |
| 每天 | 选执行时间 或 小时范围 | 每天 9:00 执行 |
| 每周 | 选执行时间 + 星期多选 | 每周一、三、五 9:00 执行 |
2. Cron 表达式:直接编辑标准 5 段表达式(分 时 日 月 周),带语法与范围校验(支持 *、数字、A-B 范围、X/Y 步长、逗号列表),适合复杂规则:
*/10 * * * *— 每 10 分钟0 9 * * 1-5— 工作日每天 9:000 9,12,18 * * *— 每天 9/12/18 点
TIP
两种模式切换不会丢配置:从「Cron 表达式」切回「可视化配置」时,若当前表达式无法用表单表示(如上面最后一种多时刻规则),表单不会自动覆写,改动任一表单项后才按表单配置覆盖。
多设备并行
一个调度可绑定多个目标(设备/账号),到点时所有目标并行执行,各自创建独立的任务实例。
设备离线处理
到点触发时如果目标设备/账号不在线:
- 自动重试(默认 3 次,间隔 5 分钟)
- 重试耗尽后标记为「跳过」,不影响其他目标
- 执行记录可在调度详情中查看
执行历史
每个调度有独立的执行历史页面,记录每次触发的:
- 触发时间
- 目标设备/账号
- 状态(已派发 / 重试中 / 跳过 / 失败)
- 创建的任务实例 ID
分布式支持
- 调度器嵌入 server 主进程,每个实例都扫描调度表
- 通过 Redis 分布式锁保证同一调度同一时刻只有一个实例派发
- 多 server 实例部署时自动协调,不重复执行
编辑器操作
保存与校验:
- Ctrl/Cmd + S 强制保存:即使存在校验错误也直接保存(问题步骤仍会红色高亮,保存后提示错误数量)
- 点击「保存草稿/保存流程」时若有校验错误:弹窗列出全部问题明细,可选择**「仍要保存」**(草稿允许带错保存,发布/执行前修正即可)或返回修改;仅有警告时同样可确认后保存
拖拽编排:
- 指令之间的缝隙是固定宽度的投放热区——拖到目标位置出现蓝色横杠即可松手放入,无需等它展开
- 画布底部常驻一行灰色**「拖拽到此处添加到最后」**空白落区,拖入即追加到流程末尾
- 支持多选(Ctrl/Cmd + 点击)、Ctrl+C/V 复制粘贴、Ctrl+Z 撤销、Delete 删除选中
- 复制粘贴支持主流程 ↔ 子流程之间互贴(一行或多行):复制后切换编辑目标直接粘贴即可。变量按方向智能处理——贴进子流程不搬变量(主流程变量读穿透天然可读);从子流程贴回主流程时缺失变量自动按普通变量补建(不带输入/输出标志,不影响运行表单);跨应用粘贴仍完整迁移变量与函数
附录:变量与表达式参考
变量类型
流程变量在编辑器「变量」面板中管理,分为三种类型:
| 类型 | 说明 | 示例 |
|---|---|---|
string | 字符串 | "高"、"com.xxx.aweme" |
number | 数值 | 0、50 |
boolean | 布尔值 | true、false |
object | 对象 | { "author": "张三" } |
array | 数组 | ["张三","李四"] |
输入变量(isInput = true):勾选「运行参数」后,变量会在参数配置界面中显示,用户可手动填写。如 searchKeyword、maxVideos 等。
运行时变量(isInput = false):流程执行过程中产生的中间变量,不暴露给用户。如 videoInfo、intentionResults 等。
文件变量(type: file):特殊类型,允许用户选择本地文件,文件路径自动填充到变量中,配合 loadFile 步骤使用。
变量作用域
所有变量默认在流程级别(scope: flow)生效,循环体内可以读写外层变量。循环变量(loopVariable)和 __index/__length 仅在当前循环体内可访问。
变量引用
在参数值中使用 {{变量名}} 引用变量:
| 语法 | 说明 |
|---|---|
{{videoInfo.author}} | 引用 videoInfo 变量的 author 子字段 |
{{ctx.searchKeyword}} | 引用运行参数中的搜索关键词 |
{{ctx.maxVideos}} | 引用运行参数中的最大视频数 |
{{item.username}} | 循环中引用当前项的 username 字段 |
{{item.__index}} | 循环中引用当前索引 |
条件表达式
条件判断步骤和循环条件中可使用的表达式:
- 比较运算符:
===、!==、>、<、>=、<= - 逻辑运算符:
&&、|| - 字符串比较:
intentionLevel === "高" - 数值比较:
totalFollowed >= 50 - 布尔判断:
alreadyProcessed === false - 组合条件:
count < 10 && hasMore === true
