让它自己动,再伸向外面
自动化在数据一变时执行 Action,策略写在提交条件里;要真的改外部系统,就给 Action 配 writeback webhook;上线前的检查单。

本课目标
读完这一课,你将能够
- 写一个「数据一变就触发」的自动化,把 Action 交给它执行
- 把策略写进 Action 的提交条件,让自动化即使配错了也越不了权
- 说出 writeback webhook 什么时候用、失败时会怎样,并在上线前过一遍检查单
数据一变就做:Automate
自动化 = 条件加效果。条件是「seatRequest 新建或修改、状态仍是待审批,且座位数在 50 以内」,效果是「执行 Action auto-approve-seat-request」。它是一个没有界面的应用,只有一份清单。它读的是 Semantic 的变化账(和界面的实时订阅是同一条),不轮询,也不写定时任务。
先给它一个专用的 Action,存成 ontology/07-auto-approve-seat-request.json。它和批准很像,区别在下一节讲:
{
"kind": "actionType",
"apiName": "auto-approve-seat-request",
"title": "自动批准座位申请(50 座以内)",
"description": "给自动化用的批准:申请的座位数不超过 50 才能批。策略写在提交条件里,所以成员角色(自动化)也能执行,越权的申请一律拒绝。",
"schema": {
"parameters": [
{ "name": "request", "type": "object", "objectType": "seatRequest", "title": "申请", "required": true },
{ "name": "customer", "type": "object", "objectType": "customer", "title": "公司", "required": true }
],
"submissionCriteria": [
{
"condition": { "type": "comparison", "left": { "param": "request", "property": "status" }, "operator": "is", "right": { "literal": "submitted" } },
"failureMessage": "只能批准待审批的申请"
},
{
"condition": { "type": "comparison", "left": { "param": "request", "property": "customerId" }, "operator": "is", "right": { "param": "customer", "property": "customerId" } },
"failureMessage": "这张申请不是这家公司的"
},
{
"condition": { "type": "comparison", "left": { "param": "request", "property": "seats" }, "operator": "gt", "right": { "param": "customer", "property": "seats" } },
"failureMessage": "这张申请已经过时:公司现有的座位数不比它少"
},
{
"condition": { "type": "comparison", "left": { "param": "request", "property": "seats" }, "operator": "lte", "right": { "literal": 50 } },
"failureMessage": "超过 50 座的申请要人工批准"
},
{
"condition": { "type": "comparison", "left": { "param": "request", "property": "seats" }, "operator": "gte", "right": { "param": "customer", "property": "members" } },
"failureMessage": "座位数不能少于成员数"
}
],
"rules": [
{
"type": "modifyObject",
"objectType": "seatRequest",
"object": "$request",
"values": { "status": "approved", "decidedBy": "$user", "decidedAt": "$now", "decisionNote": "50 座以内自动批准" }
},
{
"type": "modifyObject",
"objectType": "customer",
"object": "$customer",
"values": { "seats": "$request.seats" }
}
],
"roles": ["developer", "member"],
"summary": "自动批准 {customer} 的座位申请:{seats} 座",
"actionLog": true,
"toolDescription": "Auto-approve a small (at most 50 seats) pending seat request and set the customer's seat count. Refuses anything larger. Meant for automations."
}
}
再建一个目录 seat-auto-approve,把下面的清单存成里面的 aidc.app.json(这是旧的清单写法,内容就是 plugin.json 里 extensions["ai.aidc"].app 那一段,两种写法平台都收)。自动化就在 workflow 里:
{
"$schema": "https://www.ai-dc.ai/developer/schemas/aidc.app.schema.json",
"manifestVersion": 1,
"slug": "seat-auto-approve",
"version": "1.0.1",
"title": "座位自动批准",
"summary": "座位申请一提交,座位数在 50 以内就自动批准:数据一变就跑,不轮询、不写定时任务。策略写在 Action 的提交条件里。",
"description": "Academy「做一个会执行的 Action」的配套自动化。条件:seatRequest 新建或修改,状态是待审批(submitted)且申请的座位数不超过 50。效果:执行 Action auto-approve-seat-request,申请标为已批准,同一个事务里公司的座位数改成申请的数目。判断在两处都做:触发条件(when)先筛,Action 的提交条件再核一遍——所以就算有人手动把超过 50 座的申请交给它,Action 也会拒绝。它没有界面,只有清单;每次运行都能在 seat-desk 的 Action Log 里看到(执行人是 workflow:seat-auto-approve)。",
"examples": [
"最近自动批准了哪些座位申请?",
"座位自动批准这条自动化最近一次运行成功了吗?"
],
"owner": {
"department": "AIDC Developer",
"team": "官方样板"
},
"category": "data",
"entry": null,
"sdk": [
"semantic"
],
"permissions": {
"camera": false,
"microphone": false
},
"models": [],
"datasets": [],
"semantic": {
"types": [
"seatRequest",
"customer"
],
"actions": [
"auto-approve-seat-request"
],
"write": []
},
"agents": false,
"auth": {
"access": "restricted"
},
"storage": {
"type": "none"
},
"limits": {
"dailyTokens": 1000,
"requestsPerMinute": 30,
"realtimeSessionsPerDay": 0,
"computeMinutesPerMonth": 2000,
"notificationsPerDay": 20
},
"cdn": [],
"workflow": {
"description": "Automate 的「数据一变」条件(trigger.change)+ Action 效果:seatRequest 一有新的或改动过的待审批申请,座位数在 50 以内的就执行 auto-approve-seat-request;超过 50 座的不碰,留给人批。",
"input": {
"request": {
"type": "text",
"title": "申请编号",
"description": "要自动批准的座位申请(seatRequest 的主键)",
"required": true
},
"customer": {
"type": "text",
"title": "公司 ID",
"description": "申请所属的客户公司(customer 的主键)",
"required": true
}
},
"trigger": {
"manual": {
"roles": [
"developer"
],
"preview": "members"
},
"change": {
"type": "seatRequest",
"when": "=object.status == 'submitted' && object.seats <= 50",
"input": {
"request": "=object.requestId",
"customer": "=object.customerId"
},
"cooldown": "1m"
}
},
"lanes": [
"AIDC Developer"
],
"steps": [
{
"id": "approve",
"kind": "use",
"title": "自动批准",
"description": "Action auto-approve-seat-request:提交条件再核一遍(待审批、公司对得上、不超过 50 座、不少于成员数),通过了同一个事务里改申请和公司",
"use": "seat-auto-approve/auto-approve-seat-request",
"with": {
"request": "=input.request",
"customer": "=input.customer"
}
}
],
"output": {
"customer": "=steps.approve.object.customerName",
"seats": "=steps.approve.object.seats",
"summary": "=steps.approve.event.summary"
},
"outputLabels": [
{
"key": "customer",
"title": "公司"
},
{
"key": "seats",
"title": "批准的座位数"
},
{
"key": "summary",
"title": "摘要"
}
]
}
}
- change.type + when:这个类型的对象新建或修改,并且
when为真才开跑;条件里object是变化之后的对象。 - use:
<应用>/<Action>;with把工作流的input填进 Action 的参数(input又来自trigger.change.input,从对象里取值)。清单semantic.actions要登记这个 Action。 - cooldown:同一个对象两次自动运行至少隔这么久,告警类的自动化一定要写。
aidc semantic define ontology && aidc semantic publish --notes "自动批准"
aidc app deploy seat-auto-approve && aidc app publish seat-auto-approve
aidc semantic apply submit-seat-request --param customer=acme-north --param seats=44 # 50 座以内
aidc semantic apply submit-seat-request --param customer=acme-east --param seats=55 # 超过 50 座
aidc semantic objects seatRequest --order-by requestedAt:desc --page-size 2 \
--select customerName,currentSeats,seats,status,decidedBy
申请的座位数要比这家公司现有的多,提交前可以用 aidc semantic objects customer 看一眼(ACME East 现在是 30 座,ACME North 是 36 座)。过几秒再看一遍,结果是这样(整理成了两行):
ACME East 30 → 55 submitted (还没人批) ← 超过 50 座,留给人
ACME North 36 → 44 approved workflow:seat-auto-approve ← 自己批了
策略写在 Action 里,自动化只是按钮
为什么不让自动化直接调第 4 课的 approve-seat-request?因为自动化以「提供方应用 × 成员角色」执行,不会越权成开发者,而批准只给开发者。所以另做一个 auto-approve-seat-request:roles 放宽到 member,把策略——不超过 50 座——写死在它的提交条件里。
| approve-seat-request | auto-approve-seat-request | |
|---|---|---|
| 谁能执行 | 开发者 | 开发者、成员(自动化) |
| 多大的申请 | 不限 | 50 座以内;再大的一律拒绝 |
| 批复意见 | 人写 | 固定一句「50 座以内自动批准」 |
判断在两处做:触发条件 when 先筛,Action 的提交条件再核一遍。这样即使有人把触发条件改松了,或者手动把一张 60 座的申请交给它,Action 也会回一句「超过 50 座的申请要人工批准」,不会批出去。被拒绝的那次运行是失败的,申请还是待审批,留在列表里等人处理。
要改外面的系统:writeback webhook
到目前为止,Action 改的都是 Semantic 里的对象。要真的改外面的系统——关一台服务器、回写一张单据——就给 Action 配一个 writeback webhook。它在校验通过之后、规则之前调用外部系统:外部系统接受了,语义层才记这一笔;被拒绝或超时,整个 Action 不生效,一条都不改,原因原样还给执行的人。
下面两段取自 AIDC 自己的真实 Action auto-stop-ec2-instance:第一段是它 schema.webhooks 里的 webhook,第二段是它 schema.rules 里的规则。(这个样板的 lastActionBy 存的是执行人的名字,$userName;你自己的定义,按第 4 课的做法存账号 ID。)
{
"writeback": {
"webhook": "aws/ec2-stop-instances",
"inputs": {
"region": "$instance.region",
"instanceId": "$instance.instanceId",
"expectedLaunchTime": "$instance.launchTime"
}
}
}
[
{
"type": "modifyObject",
"objectType": "ec2Instance",
"object": "$instance",
"values": {
"lastAction": "auto-stop",
"lastActionAt": "$now",
"lastActionBy": "$userName",
"lastActionNote": "$reason",
"lastActionResult": "$writeback.currentState"
}
}
]
- 输入:
$instance.instanceId这类取值,和规则里是同一种写法;<数据源>/<webhook>是它的名字。 - 输出:webhook 的返回值在规则里用
$writeback.<输出名>取,上面就把 AWS 返回的状态写回了对象。 - 只校验不调用:
$validateOnly和预演都不会真的去调外部系统;有 writeback 的 Action 一次只能提交一个请求,不收批量(外部系统的改动没法和语义层一起回滚)。 - 定义时就校验:webhook 存在、必填输入都给了、没有多余的输入、
$writeback.…是它的输出、这个公司能用那个数据源。
这不是纸上谈兵。2026-09-30,在 AIDC 自己的 AWS 账号里,一台设了「开机 0.1 小时后自动关机」的一次性测试机,在平台每小时一次的同步之后,被自动化执行 auto-stop-ec2-instance 关掉了:没有人再动手,AWS 的事件记录是 User initiated,Action Log 里的执行人是这条自动化。
但要老实说清现状:现在有的数据源只有 AIDC 平台提供的 aws,而且只给 AIDC 自己的组织用;你自己公司的数据源在做。所以在你的公司里,你能练的是「定义时的校验」——把下面这份存成 try-writeback.json,define 它:
{
"kind": "actionType",
"apiName": "stop-server",
"title": "关机(试写)",
"description": "试写一个 writeback webhook,只为看平台怎么校验。在你自己的公司里 define 会被拒绝:数据源 aws 是 AIDC 平台的,只给 AIDC 组织。",
"schema": {
"parameters": [
{ "name": "customer", "type": "object", "objectType": "customer", "title": "公司", "required": true }
],
"webhooks": {
"writeback": {
"webhook": "aws/ec2-stop-instances",
"inputs": { "region": "ap-southeast-1", "instanceId": "$customer.customerId" }
}
},
"rules": [
{ "type": "modifyObject", "objectType": "customer", "object": "$customer", "values": { "stage": "暂停" } }
]
}
}
aidc semantic define try-writeback.json --dry-run
# 422 webhook 用不了:stop-server:数据源 aws 在 cell-acme 不可用(平台的 AWS 账号只给 AIDC 组织;客户自己的数据源要补)
# 把 inputs 里的 instanceId 删掉再试,先被拦下的是别的:
# 422 定义引用不闭合:stop-server: webhooks.writeback aws/ec2-stop-instances 的必填输入 instanceId 没给
第一句是「这个公司用不了那个数据源」,第二句是「写法上缺了必填的输入」。平台按这个顺序查:先查写得对不对,再查你有没有资格用。
上线前的检查单
- 每条失败都跑过
每个 Action 的每条提交条件,都用
--validate-only让它不通过一次,读一遍信息:像操作指引,不像报错。 - 角色最小
roles只给该给的人;给自动化的 Action 单独做,把策略写进提交条件。 - 拦人的规则只在 Action 里
界面、自动化和 Skills 里的判断只是筛选和提示,真正拦人的规则只写在 Action 里。
- 留着日志
actionLog开着,summary写成一句能读懂的话;出了事凭operationId一头查到另一头。 - 改定义先 dry-run
破坏性变更(删参数、加必填参数…)在
define里就会列出来;publish之前确认调用它的应用都改好了。 - 先预演,再放开
应用和 Skills 里写入先
--preview;自动化先只对小范围生效,看几次运行记录再放开。
要点
- 自动化 = 条件加效果:
trigger.change加when,效果是use一个 Action;数据一变就跑,不轮询。 - 给自动化单做一个 Action,把策略写死在提交条件里;触发条件先筛、提交条件再核,两处都判断。
- writeback webhook 在校验之后、规则之前调外部系统:失败则整个 Action 不生效;只校验和预演不调用。
- 现在的数据源只有平台的
aws(只给 AIDC 自己);客户自己的数据源在做,定义时的校验可以先练。
练一练
让申请自己被批准
在你自己的公司里做;自动化在正式通道才生效,先 deploy 再 publish。
存好 07-…json 并 define,写好自动化清单,deploy 加 publish。等半分钟,提交一张 44 座、一张 55 座的申请(座位数都要比这家公司现有的多,提交前用 aidc semantic objects customer 看一眼):哪张被批了?执行人是谁?
手动把 55 座那张交给自动批准(申请编号在 aidc semantic objects seatRequest 的第一列 __primaryKey):aidc semantic apply auto-approve-seat-request --param request=… --param customer=…。得到什么?申请状态变了吗?
define try-writeback.json --dry-run,读那句提示;再把 instanceId 的输入删掉,提示变成了什么?先被拦下的是哪一种问题?
小测
选一个答案,马上看解析。
Q1为什么自动化调用的是 auto-approve-seat-request,而不是 approve-seat-request?
角色管谁能做,提交条件管做得对不对。给自动化的 Action 放宽角色,同时用提交条件把它能批的范围卡死。
Q2一个 writeback webhook 调外部系统失败了,语义层里会留下什么?
webhook 在规则之前执行;它失败,规则根本不会跑,语义层一条都不改。错误码是 webhook_failed。
Q3触发条件里已经写了 seats <= 50,为什么 Action 的提交条件还要再写一遍?
规则要放在所有入口都绕不开的地方——Action 里。触发条件只是让自动化少跑无用的运行。