自动化
在事件发生时自动执行动作:发一段提示去开新会话、调用一个 Webhook 或运行一段脚本,全部写在一个配置文件里。
开始使用
自动化把「事情发生了」和「于是做什么」连起来:当某个事件触发时,工作台按配置自动执行动作,可以是发一段提示去开新会话、调用一个外部地址,或者按固定节奏重复一段流程。
最省事的起点是直接用自然语言描述你想要什么,让工作台把它写成配置。下面这些说法都可以作为起点。
| 你想要 | 可以这样说 |
|---|---|
| 定时简报 | 每个工作日早上 9 点给我一份站会简报 |
| 标签分诊 | 会话加上 urgent 标签时,新建会话汇总需要处理的问题 |
| 审计记录 | 把每次权限模式变更记录到文件里 |
| 状态记录 | 会话状态变成 done 时,运行工作区脚本记录完成情况 |
| 周期报告 | 每周五下午 5 点新建一个会话,汇总本周完成的任务 |
| Webhook 集成 | 我标记会话时,向我的 Webhook 地址发一个请求 |
第一个自动化
先在工作区根目录创建下一节的 scripts/log-label.ts,再把下面配置保存为 automations.json。给任意会话添加一个标签,工作区根目录就会追加一条 label-events.jsonl 记录。配置保存后会重新加载,不需要重启。
{ "version": 2, "automations": { "LabelAdd": [ { "actions": [ { "type": "script", "script": "scripts/log-label.ts", "runtime": "bun", "timeoutMs": 60000 } ] } ] } }
创建事件记录脚本
让 Agent 在当前工作区创建 scripts/log-label.ts,内容如下。它只读取本次事件的标签、会话 ID 与事件名,向固定日志文件追加 JSON;不需要来源、Webhook 或模型连接。
脚本通过参数数组启动,不经过 shell。路径必须在工作区内,不能使用绝对路径或 ..,运行时也会检查符号链接是否越界。
import { appendFileSync } from 'node:fs' import { join } from 'node:path' const workspacePath = process.env.CRAFT_WORKSPACE_PATH if (!workspacePath) throw new Error('CRAFT_WORKSPACE_PATH is required') const entry = { at: new Date().toISOString(), event: process.env.CRAFT_EVENT, sessionId: process.env.CRAFT_SESSION_ID, label: process.env.CRAFT_LABEL, } appendFileSync(join(workspacePath, 'label-events.jsonl'), JSON.stringify(entry) + '\n')
按计划发送提示
想让工作台定时替你做点什么,就用提示动作加上 cron 表达式:命中计划时会新建一个会话,把提示发进去,Agent 就在新会话里开始干活。
提示里可以用 @ 提及来源,提及的来源会被自动启用;新会话还会自动贴上你指定的标签,方便之后筛选出来。
{ "version": 2, "automations": { "SchedulerTick": [ { "cron": "0 9 * * 1-5", "timezone": "Asia/Shanghai", "labels": ["定时"], "actions": [ { "type": "prompt", "prompt": "查看 @github 上指派给我的新 issue,并汇总成一段说明" } ] } ] } }
自动化如何工作
应用事件触发时(贴了标签、权限模式变了、cron 计划命中等),ZeroWorks 按事件键查找启用的匹配器,检查正则或计划及 conditions,然后把对应动作交给执行器。不要依赖不同动作完成的先后顺序。
动作有三种类型,各自适合不同的场景。
- 提示动作:给工作台发一段提示,新建一个会话。
- Webhook 动作:向指定地址发起 HTTP 请求,可选 JSON、表单或原始正文。
- 脚本动作:在运行工作区服务的主机上,用 bun、node 或 python3 运行工作区脚本,适合需要确定顺序的多步逻辑。远程工作区的脚本不在你的本机桌面执行。
配置文件
自动化按工作区配置,放在实际工作区根目录的 automations.json 里;默认目录示例如下,自定义工作区要使用它自己的 rootPath。结构是「事件 → 匹配器数组 → 动作数组」三层。
- version:可选的数字元数据,现有示例使用 2;执行仍以支持的字段和动作类型为准。
- automations:以事件名为键,每个事件下是一个匹配器数组。
- matcher:决定这条自动化在什么条件下被触发。
- actions:至少一个动作,只支持 prompt、webhook、script;旧 command 动作不能照搬。
~/.zero-works/workspaces/{workspaceSlug}/automations.json
配置字段与模型覆盖
下面是一个 prompt 动作片段。使用前从「设置 → AI」复制真实连接 slug 和该连接可用的模型 ID,再填写 llmConnection 与 model;示例中的 reporting-claude 是自定义连接名,不会自动创建连接。
| 位置 | 字段与含义 |
|---|---|
| 匹配器 | id 为稳定标识,name 为显示名,enabled 默认为 true;enabled: false 保留配置但停用 |
| 匹配器 | matcher 为事件值正则;SchedulerTick 改用 cron 和可选 IANA timezone |
| 匹配器 | permissionMode 为 safe、ask 或 allow-all,只控制提示动作新会话;省略时为 safe |
| 匹配器 | labels 贴到提示动作的新会话;conditions 全部成立后才执行,telegramTopic 用于 Telegram 话题路由 |
| prompt 动作 | prompt 必填;llmConnection、model、thinkingLevel 覆盖新会话的连接、模型和思考级别 |
| webhook 动作 | url 必填;method 默认 POST,可设 headers、bodyFormat: json/form/raw、body、auth、captureResponse |
| script 动作 | script 是工作区相对路径;args 为字符串数组,可设 runtime、timeoutMs,page 用于记录对应页面的刷新结果 |
{ "type": "prompt", "prompt": "读取当前项目的待办,列出今天需要人工处理的事项", "llmConnection": "reporting-claude", "model": "claude-sonnet-4-6", "thinkingLevel": "medium" }
事件
事件分两类:应用类事件描述工作台自身发生的事,Agent 类事件描述 Agent 在会话中的行为。事件名写在 automations 的键上。
当前版本里真正会触发动作的是应用类事件;Agent 类事件名可以写进配置并通过校验,但这一版还不执行动作。
| 事件键 | 匹配值或当前行为 |
|---|---|
| LabelAdd / LabelRemove | 新增或移除的标签值,例如 urgent 或 priority::3 |
| LabelConfigChange | 工作区标签配置变更,匹配值为空;通常省略 matcher |
| PermissionModeChange | 新的权限模式:safe、ask、allow-all |
| FlagChange | 新的标记值:字符串 true 或 false |
| SessionStatusChange | 新的会话状态 ID,例如 done;TodoStateChange 是会被归一化的旧别名 |
| SchedulerTick | 调度时刻,使用 cron 而非 matcher |
| PreToolUse / PostToolUse / PostToolUseFailure / Notification / UserPromptSubmit | Agent 事件键,当前不执行自动化动作 |
| SessionStart / SessionEnd / Stop / SubagentStart / SubagentStop / PreCompact / PermissionRequest / Setup | Agent 事件键,当前不执行自动化动作 |
匹配器
匹配器决定自动化在什么条件下被触发。可以用正则去匹配事件的值,也可以用 cron 表达式匹配时间。
- 正则匹配:按上一节列出的事件值匹配,例如 ^urgent$ 精确匹配 urgent;省略 matcher 时匹配该类所有事件。
- cron 匹配:按时间计划触发,可以配合时区,也可以限定只在某些工作日生效。
- 通用字段:可以指定提示动作新建会话时使用的权限模式,以及给这个会话附加的标签。
{ "version": 2, "automations": { "LabelAdd": [ { "matcher": "^urgent$", "name": "紧急事项分诊", "permissionMode": "safe", "labels": ["triage"], "actions": [{ "type": "prompt", "prompt": "汇总会话 $CRAFT_SESSION_NAME 里需要人工处理的紧急事项" }] } ] } }
条件
conditions 是数组,匹配器命中后其中每项都必须通过。状态条件读取当前事件载荷,不会任意查询工作区;SchedulerTick 没有某个会话的标签或状态,不能据此假定它满足会话条件。下面只在工作日办公时间,且未带 skip-review 标签的会话从进行中转为待复核时新建复核会话。
- 时间条件:condition: time,可设 after、before、weekday、timezone;after 包含起点、before 不包含终点,22:00 到 06:00 表示跨午夜。
- 状态条件:condition: state,field 指定事件字段;value、from/to、contains、not_value 四组比较操作每次只选一组。permissionMode 和 sessionStatus 的 from/to 会映射到事件的新旧值。
- 逻辑组合:用与、或、非把多个条件组合起来,最多可以嵌套 8 层。
- 语义条件:单独写在 semanticCondition 字段里,交给可选的语义判断能力回答一个是非问题,回答为「是」时才继续。
{ "version": 2, "automations": { "SessionStatusChange": [{ "matcher": "^needs-review$", "timezone": "Asia/Shanghai", "conditions": [ { "condition": "time", "after": "09:00", "before": "18:00", "weekday": ["mon", "tue", "wed", "thu", "fri"] }, { "condition": "state", "field": "sessionStatus", "from": "in-progress", "to": "needs-review" }, { "condition": "not", "conditions": [{ "condition": "state", "field": "labels", "contains": "skip-review" }] } ], "permissionMode": "safe", "actions": [{ "type": "prompt", "prompt": "复核会话 $CRAFT_SESSION_NAME 的结果,列出未覆盖验证" }] }] } }
环境变量与 Webhook
提示和 Webhook 字符串支持 $VAR 与 ${VAR} 展开;未定义变量会变成空字符串。事件变量包括 CRAFT_EVENT、CRAFT_EVENT_DATA、CRAFT_SESSION_ID、CRAFT_SESSION_NAME、CRAFT_WORKSPACE_ID,以及事件特有的 CRAFT_LABEL、CRAFT_OLD_MODE、CRAFT_NEW_MODE、CRAFT_OLD_STATE、CRAFT_NEW_STATE、CRAFT_IS_FLAGGED。调度事件提供 CRAFT_LOCAL_TIME 和 CRAFT_LOCAL_DATE。
Webhook 只接收事件生成的 CRAFT_* 变量及进程里 CRAFT_WH_* 前缀的自定义变量;URL、headers、body、auth 都可展开。先在运行 ZeroWorks 服务的进程环境中提供 CRAFT_WH_REPORT_URL 和 CRAFT_WH_REPORT_TOKEN,再使用下面配置,禁止把真实令牌填进示例或日志。
{ "version": 2, "automations": { "SessionStatusChange": [{ "matcher": "^done$", "actions": [{ "type": "webhook", "url": "${CRAFT_WH_REPORT_URL}", "method": "POST", "auth": { "type": "bearer", "token": "${CRAFT_WH_REPORT_TOKEN}" }, "bodyFormat": "json", "body": { "event": "$CRAFT_EVENT", "sessionId": "$CRAFT_SESSION_ID", "status": "$CRAFT_NEW_STATE" } }] }] } }
把自动化会话路由到 Telegram 话题
telegramTopic 写在匹配器上,只绑定提示动作创建的会话。首次使用会创建同名话题,之后复用;相同名字共用一个话题,大小写不同会分别处理。名称长度为 1 至 128 个字符。
- 在 Telegram 创建或转换一个启用 Topics 的 supergroup,把你已连接的机器人加进群。
- 将机器人设为管理员并授予 Manage Topics 权限;普通群和未启用 Topics 的群不能承载话题。
- 在「设置 → 消息 → Telegram」选择 Pair Supergroup,使用界面给出的短期配对码,在群的话题里发送 /pair 加该码;回到设置确认显示群名称。
- 保存下面配置并触发一次计划或使用测试入口。检查新会话的消息绑定和 Telegram 中对应话题;未绑定时先检查连接和群权限,再查看 messaging-gateway.log 的 automation_topic_bind_failed。
{ "version": 2, "automations": { "SchedulerTick": [{ "cron": "0 9 * * 1-5", "timezone": "Asia/Shanghai", "telegramTopic": "Daily reports", "permissionMode": "safe", "actions": [{ "type": "prompt", "prompt": "列出今天需要处理的事项;没有足够信息时说明缺口" }] }] } }
管理自动化
侧栏的自动化列表提供日常管理入口:可以单独启用或停用某条自动化,也可以复制一份再改,或者直接删除。
每条自动化都会记录执行结果,在详情页能看到成功与失败的时间线。历史记录保存在 automations-history.jsonl 里,每条自动化保留最近 20 次执行,全局上限 1000 条。
列表支持批量操作:可以一次选中多条自动化统一启用、停用或删除,并按最近执行时间排序,方便优先看还在活跃的那些。
- 启用 / 停用:临时关掉一条自动化而不用删除它。
- 复制:基于现有配置生成一份副本再调整。
- 删除:永久移除这条自动化。
- 测试:直接执行动作;提示会解析 @ 提及、启用对应来源,并使用配置连接、模型与思考级别。提示测试返回成功表示已启动会话,不表示整项工作完成,也不证明事件、cron 或条件实际命中。
校验与排障
- 让 Agent 调用 config_validate,参数为 { "target": "automations" },修复 JSON、空 actions、不支持的动作字段、无效正则、cron、时区和工作区外脚本路径;不要忽略未知事件被过滤的告警。
- 不触发时检查 enabled、精确事件键、匹配值、时区和 conditions;确认没有使用当前不执行的 Agent 事件。先触发一次真实应用事件,再对照历史与新会话。
- 连接或模型不对时检查 llmConnection 是已存在的 slug、model 属于该连接的可用模型,来源和技能引用确实存在且已认证。
- Webhook 失败时查看 HTTP 状态和错误;变量为空时检查执行主机进程环境,确认 URL 展开后为 http 或 https。测试/重放不展开变量,需要单独区分。
- 脚本失败时检查文件、运行时、工作区相对路径和 stderr。动作以真实进程权限运行,提示会话的 permissionMode 不会把脚本或 Webhook 包在同一权限审批里。
安全与限流
自动化会真实运行脚本并向外部地址发请求,因此权限边界与执行频率都需要有明确的约束。
- 权限模式:给提示动作新建会话时指定权限模式,避免它在执行档下自动触发。
- 标签策略:给自动化的产出打上标签,出问题时能快速定位是哪条规则干的。
- 执行历史:历史文件按条数上限自动截断,历史字段也会被截断长度,避免日志无限增长。
- 失败重试:仅 5xx、超时和连接错误等暂时故障可重试,先短时重试,再进入持久队列,按 5 分钟、30 分钟、1 小时退避;4xx 要修配置,不自动重试。历史条数上限与重试次数是不同约束。
- 事件限流:普通事件每类每分钟最多 10 次,SchedulerTick 为 60 次;超限事件会被丢弃。相同匹配器的脚本不重叠运行,Webhook 同一 origin 也有限流。