aitable-workflow-core 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +16 -4
- package/dist/index.js +190 -188
- package/package.json +2 -2
- package/skills/aitable-workflow/SKILL.md +18 -28
- package/skills/aitable-workflow/{reference → references}/config-surface.md +15 -1
- package/skills/aitable-workflow/{reference → references}/event-handlers.md +3 -1
- package/skills/aitable-workflow/references/event-sources.md +229 -0
- package/skills/aitable-workflow/references/field-types.md +73 -0
- package/skills/aitable-workflow/{reference → references}/generated/event-sources.md +16 -17
- package/skills/aitable-workflow/{reference → references}/invariants.md +4 -1
- package/skills/aitable-workflow/{reference → references}/multi-workflow.md +13 -7
- package/skills/aitable-workflow/{reference → references}/profile-and-roles.md +9 -3
- package/skills/aitable-workflow/{reference → references}/runtime-refs.md +8 -40
- package/skills/aitable-workflow/{reference → references}/workflow-yml.md +12 -10
- package/skills/aitable-workflow-design/SKILL.md +7 -5
- package/skills/aitable-workflow-design/patterns/brownfield-editing.md +2 -2
- package/skills/aitable-workflow-design/patterns/clarification-checklist.md +21 -28
- package/skills/aitable-workflow-design/patterns/config-in-aitable.md +4 -5
- package/skills/aitable-workflow-design/patterns/dingtalk-bot-patterns.md +129 -0
- package/skills/aitable-workflow-design/patterns/external-aitable-read.md +132 -0
- package/skills/aitable-workflow-design/patterns/greenfield-checklist.md +10 -10
- package/skills/aitable-workflow-design/patterns/output-and-report.md +2 -2
- package/skills/aitable-workflow/reference/event-sources.md +0 -217
- /package/skills/aitable-workflow/{reference → references}/aitable-cell-values.md +0 -0
- /package/skills/aitable-workflow/{reference → references}/automations.md +0 -0
- /package/skills/aitable-workflow/{reference → references}/generated/core-api.md +0 -0
- /package/skills/aitable-workflow/{reference → references}/generated/toolkits.md +0 -0
- /package/skills/aitable-workflow/{reference → references}/recipe-contract.md +0 -0
- /package/skills/aitable-workflow/{reference → references}/workspace-context.md +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aitable-workflow-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"description": "配置驱动的 AI 表格工作流自动化引擎(core):YAML 定义业务流程,AI 逐步执行并回写钉钉 AI 表格;含 CLI 与工作流引擎。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"aitable-workflow-base": "0.1.
|
|
27
|
+
"aitable-workflow-base": "0.1.24",
|
|
28
28
|
"@inquirer/prompts": "^8.5.2",
|
|
29
29
|
"cron-parser": "^5.6.2",
|
|
30
30
|
"cross-spawn": "^7.0.6",
|
|
@@ -14,43 +14,33 @@ description: aitable-workflow 框架的契约与语法参考。当需要读写 a
|
|
|
14
14
|
|
|
15
15
|
不要预先读取全部文件。只读命中的 2-4 个,减少无关上下文。
|
|
16
16
|
|
|
17
|
-
动手之前先读 `
|
|
17
|
+
动手之前先读 `references/invariants.md`:那是不可违反项,违反后果从「校验失败」到「线上静默停摆」。
|
|
18
18
|
</IMPORTANT>
|
|
19
19
|
|
|
20
20
|
## 路由表
|
|
21
21
|
|
|
22
22
|
| 我要做什么 | 识别信号 | 加载 |
|
|
23
23
|
| --- | --- | --- |
|
|
24
|
-
| 确认不可违反项 | **动手前必读**;结构硬约束与「绝不」清单 | `
|
|
25
|
-
| 写 workflow.yml 骨架 | meta / steps / edges / needs_human / input_mapping / record_policy / field_definitions
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
24
|
+
| 确认不可违反项 | **动手前必读**;结构硬约束与「绝不」清单 | `references/invariants.md` |
|
|
25
|
+
| 写 workflow.yml 骨架 | meta / steps / edges / needs_human / input_mapping / record_policy / field_definitions | `references/workflow-yml.md` |
|
|
26
|
+
| 查字段类型合法值 | `field_definitions` 里该写 `text` 还是 `singleSelect`、`currency` 是否合法等 | `references/field-types.md` |
|
|
27
|
+
| 写/读 AI 表格记录 cellValue | record create/update/query 的 cells 值、user/group/attachment 写入格式 | `references/aitable-cell-values.md` ⚠️ **写错键会静默为空** |
|
|
28
|
+
| 写 automations/*.json(DWS 自动化) | workflow-dsl/v1、SendMessage、rowUrl、FindRecords、CreateRecord、跨表 _tables | `references/automations.md` ⚠️ **变量类型错会静默失效** |
|
|
29
|
+
| 声明 workflow 内视图 | `tracker.views`、表单入口、看板、字段顺序、筛选 | `references/workflow-yml.md` |
|
|
30
|
+
| 写 recipe | .recipe.ts、RecipeDefinition、ctx.toolkit、执行策略 | `references/recipe-contract.md` |
|
|
31
|
+
| 查框架类型签名 | RecipeContext / RecipeResult / WorkflowTemplate 的准确字段 | `references/generated/core-api.md` |
|
|
32
|
+
| 查 toolkit 方法与配置 | ctx.toolkit.<name>.<method>、toolkit_config 的字段 schema | `references/generated/toolkits.md` |
|
|
33
|
+
| 声明事件源、选择机制、筛选条件 | event_sources、群消息、@机器人、定时、AI 表格记录变更、filter | `references/event-sources.md` ⚠️ **必读,filter 是硬要求** |
|
|
34
|
+
| 查事件源 config 准确字段 | event_sources[].config、字段名、类型、默认行为 | `references/generated/event-sources.md`(由 Descriptor.configSchema 生成) |
|
|
35
|
+
| 写 EventHandler | handler / handler_config、*_handler.recipe.ts、pushEvent | `references/event-handlers.md` |
|
|
36
|
+
| 决定哪些配置给用户看 | meta.config、tableRefConfigSchema、expose、config 面板 | `references/config-surface.md` |
|
|
37
|
+
| 多 workflow 拆分 / 跨 workflow 引用 | workflows: { dir }、多个触发节律、{ ref } | `references/multi-workflow.md` |
|
|
38
|
+
| 引用 env / 配置 / 全局变量 | ${env.X}、${config.a.b}、${globalVar.x}、.env.example | `references/runtime-refs.md` |
|
|
39
|
+
| 给 Agent 提供复杂上下文 | workspaces/、AGENTS.md、知识库、长上下文 | `references/workspace-context.md` |
|
|
40
|
+
| 写 roles / profile.yml | 角色、persona、knowledge_scope、profile v2 | `references/profile-and-roles.md` |
|
|
40
41
|
|
|
41
42
|
## 冲突判定
|
|
42
43
|
|
|
43
44
|
- 存在 `profile/profile.yml` 时,`profile-and-roles.md` 关于「配置写哪儿」的结论**覆盖** `workflow-yml.md` 里的内联写法。
|
|
44
45
|
- 用户明确指定要读某文件时,直接读,不重新路由。
|
|
45
46
|
|
|
46
|
-
## 本包不含什么
|
|
47
|
-
|
|
48
|
-
本包只讲**框架契约与语法**(怎么写才合法)。以下不在这里:
|
|
49
|
-
|
|
50
|
-
- 需求澄清、形态判定(全新 / 改已有)、架构决策模式、产出清单与汇报规范 → `../aitable-workflow-design/SKILL.md`
|
|
51
|
-
|
|
52
|
-
## 维护
|
|
53
|
-
|
|
54
|
-
新增、删除或重命名 `reference/` 下的文件时,必须同步更新本路由表;`reference/` 下不允许存在未被路由表引用的孤儿文件(有测试守护)。
|
|
55
|
-
|
|
56
|
-
`reference/generated/**` 是构建期产物,**禁止手改**:由 `packages/core/scripts/gen-skill-reference.ts` 统一生成。
|
|
@@ -60,10 +60,24 @@ config: z.object({
|
|
|
60
60
|
- **`reference`** —— 引用本 Base 内已有的表(用户自己的业务表,不属本 setup 管理)。setup 不会建表;`baseId` / `tableId` 在 `config --json` 中作为必填项出现,用户通过 `config set --set step.<id>.config.<key>.tableId=<...>` 或配置页面填入。**这是「读用户已有表」的标准通道**。
|
|
61
61
|
- **`external`** —— 引用跨 Base / 跨项目的表,或本 recipe 不应写入的只读数据源。setup 同样不建表;配置面的处理方式与 `reference` 一致,但语义上意味着「绝不尝试在该 Base 内建表或写回 ID」。
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
设计期尚未绑定的 `reference` / `external` 表引用,外层 schema 要声明 `.optional()`,否则 `validate`
|
|
64
|
+
会在 setup 写入 `baseId/tableId` 前先被 Zod 必填拦住。运行期 recipe 必须自己检查未配置态,并返回
|
|
65
|
+
可读错误。
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
config: z.object({
|
|
69
|
+
faq_table: tableRefConfigSchema({
|
|
70
|
+
label: "FAQ 表",
|
|
71
|
+
description: "用户已有的 FAQ 数据表",
|
|
72
|
+
ownership: "external",
|
|
73
|
+
access: "read",
|
|
74
|
+
}).optional(),
|
|
75
|
+
})
|
|
76
|
+
```
|
|
64
77
|
|
|
65
78
|
`tableRefConfigSchema` 声明的字段**不需要**额外打 `expose`——它走独立的表引用处理分支。
|
|
66
79
|
|
|
80
|
+
|
|
67
81
|
文件引用同理用 `fileRefConfigSchema`。
|
|
68
82
|
|
|
69
83
|
## 「绝不暴露」清单
|
|
@@ -68,6 +68,8 @@ z.array(z.string()).optional()
|
|
|
68
68
|
|
|
69
69
|
详见 `config-surface.md`。
|
|
70
70
|
|
|
71
|
+
机器人回复(`replyToConversation`)与 typing 表情反馈(`beginDingTalkProcessingFeedback`)的设计模式见 `aitable-workflow-design/patterns/dingtalk-bot-patterns.md`。
|
|
72
|
+
|
|
71
73
|
## 示例:同步快路径
|
|
72
74
|
|
|
73
75
|
```typescript
|
|
@@ -76,7 +78,7 @@ import type {
|
|
|
76
78
|
} from "aitable-workflow-core";
|
|
77
79
|
|
|
78
80
|
const recipe: EventHandlerRecipeDefinition = {
|
|
79
|
-
meta: { id: "
|
|
81
|
+
meta: { id: "conversation", name: "会话消息处理器" },
|
|
80
82
|
|
|
81
83
|
create(ctx: HandlerInitContext): HandlerInstance {
|
|
82
84
|
return {
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# 事件源(event_sources)
|
|
2
|
+
|
|
3
|
+
事件源在 `workflow.yml` 的 `event_sources` 段声明,用于把外部变化转换为 handler recipe 的输入事件。
|
|
4
|
+
|
|
5
|
+
**只有用户明确要求外部事件驱动入口时才生成 `event_sources`。** 表单提交、手动新建记录 → tracker 自身轮询即可。
|
|
6
|
+
|
|
7
|
+
`event_sources[].config` 的字段名、类型、默认值、引用形式和组合规则以 [生成的配置字段参考](generated/event-sources.md) 为准;本页只说明选型、机制、示例和运行约束。
|
|
8
|
+
|
|
9
|
+
## 内置类型一览
|
|
10
|
+
|
|
11
|
+
| type | 机制 | 用在哪 |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `dingtalk_group_message` | dws 逐群轮询 | 监听指定群的全部消息,不需要机器人 |
|
|
14
|
+
| `dingtalk_user_message` | dws `list-all` 单次批量拉取 | 拉取非群聊消息,或群多到逐群轮询扛不住时(约 200+ 群) |
|
|
15
|
+
| `dingtalk_stream` | WebSocket 推送 | @机器人触发、需要支持单聊 |
|
|
16
|
+
| `cron_scheduler` | 定时 | 日报、巡检、定时汇总 |
|
|
17
|
+
| `aitable_record` | AI 表格记录轮询 | 监听表格记录新增或变更 |
|
|
18
|
+
|
|
19
|
+
三个 `dingtalk_*` 消息类型产出**同构**的 `message_received` 事件——换入站方式只改 `type`,下游无需变更。
|
|
20
|
+
|
|
21
|
+
### `message_received` payload 契约
|
|
22
|
+
|
|
23
|
+
钉钉消息事件源产出的事件形态固定为:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
type MessageReceivedEvent = {
|
|
27
|
+
type: "message_received";
|
|
28
|
+
itemId: null;
|
|
29
|
+
timestamp: string;
|
|
30
|
+
payload: {
|
|
31
|
+
conversationId: string;
|
|
32
|
+
messages: Array<{
|
|
33
|
+
sender: string;
|
|
34
|
+
senderName?: string;
|
|
35
|
+
senderOpenDingTalkId?: string;
|
|
36
|
+
createTime: string;
|
|
37
|
+
content: string;
|
|
38
|
+
messageId?: string;
|
|
39
|
+
msgType?: string;
|
|
40
|
+
images?: string[];
|
|
41
|
+
}>;
|
|
42
|
+
content: string;
|
|
43
|
+
messageCount: number;
|
|
44
|
+
firstSender?: string;
|
|
45
|
+
firstSendTime?: string;
|
|
46
|
+
lastSendTime?: string;
|
|
47
|
+
sender?: string;
|
|
48
|
+
senderName?: string;
|
|
49
|
+
senderOpenDingTalkId?: string;
|
|
50
|
+
images?: string[];
|
|
51
|
+
dingtalk?: {
|
|
52
|
+
conversationId: string;
|
|
53
|
+
openMsgId?: string;
|
|
54
|
+
credRef?: string;
|
|
55
|
+
conversationType?: string;
|
|
56
|
+
conversationTitle?: string;
|
|
57
|
+
robotCode?: string;
|
|
58
|
+
sessionWebhook?: string;
|
|
59
|
+
sessionWebhookExpiredTime?: number;
|
|
60
|
+
atUsers?: Array<{ dingtalkId?: string; staffId?: string }>;
|
|
61
|
+
isInAtList?: boolean;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
EventHandler 里优先读:
|
|
68
|
+
|
|
69
|
+
- `payload.conversationId`:来源群或单聊会话 ID。
|
|
70
|
+
- `payload.content`:本轮聚合后的可读文本,格式为 `发送者: 内容`,多消息用换行拼接。
|
|
71
|
+
- `payload.messages`:需要逐条处理、取 `messageId`、图片或发送时间时读它。
|
|
72
|
+
- `payload.sender` / `payload.senderName` / `payload.senderOpenDingTalkId`:按发送者聚合时的发送者身份;stream 入站通常 `sender` 是 `senderStaffId || senderId`,dws 入站可能只有昵称或 openDingTalkId。
|
|
73
|
+
- `payload.dingtalk`:stream 入站的回复上下文,出站层用于会话内回复;普通 handler 不要依赖它一定存在。
|
|
74
|
+
|
|
75
|
+
## 钉钉消息
|
|
76
|
+
|
|
77
|
+
### `dingtalk_group_message` — 钉钉群消息
|
|
78
|
+
|
|
79
|
+
使用 dws 登录者身份拉取消息,不需要机器人。适合群数量较少、需要监听群内全部消息的场景。
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
# workflow.yml
|
|
83
|
+
event_sources:
|
|
84
|
+
- name: "群消息监听"
|
|
85
|
+
type: dingtalk_group_message
|
|
86
|
+
handler: message_triage_handler
|
|
87
|
+
|
|
88
|
+
# profile/profile.yml
|
|
89
|
+
workflows:
|
|
90
|
+
<workflowId>:
|
|
91
|
+
event_sources:
|
|
92
|
+
群消息监听:
|
|
93
|
+
conversation_ids:
|
|
94
|
+
- "cidXXXXXXXXXXXX=="
|
|
95
|
+
- "cidYYYYYYYYYYYY=="
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
事件源只产出原始 `message_received`,**不做「新对话 vs 追问」判断**——由 EventHandler 决策。
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
### `dingtalk_user_message` — 批量拉取
|
|
102
|
+
|
|
103
|
+
一次 `dws chat message list-all` 分页拉取所有会话后在本地过滤。群数约 200+ 时,用它替代逐群轮询以减少子进程调用;也可用于拉取非群聊消息。
|
|
104
|
+
|
|
105
|
+
它与 `dingtalk_group_message` 只有会话列表语义相同,分页、会话类型和机器人消息过滤等字段必须查阅 [生成的配置字段参考](generated/event-sources.md#dingtalk_user_message)。
|
|
106
|
+
|
|
107
|
+
### `dingtalk_stream` — 企业内机器人
|
|
108
|
+
|
|
109
|
+
必须先在钉钉开放平台创建企业内机器人。群内只有 @机器人时才触发,同时支持单聊。
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
# workflow.yml
|
|
113
|
+
event_sources:
|
|
114
|
+
- name: "机器人监听"
|
|
115
|
+
type: dingtalk_stream
|
|
116
|
+
handler: message_triage_handler
|
|
117
|
+
|
|
118
|
+
# profile/profile.yml
|
|
119
|
+
workflows:
|
|
120
|
+
<workflowId>:
|
|
121
|
+
event_sources:
|
|
122
|
+
机器人监听:
|
|
123
|
+
robot_creds: "${env.DINGTALK_ROBOTS}"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### 轮询 vs 推送怎么选
|
|
127
|
+
|
|
128
|
+
| | `dingtalk_group_message` | `dingtalk_stream` |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| 是否需要机器人 | 不需要 | 需要 |
|
|
131
|
+
| 触发条件 | 会话内所有消息 | 必须 @机器人 |
|
|
132
|
+
| 单聊 | 不支持 | 支持 |
|
|
133
|
+
|
|
134
|
+
## 定时触发
|
|
135
|
+
|
|
136
|
+
### `cron_scheduler`
|
|
137
|
+
|
|
138
|
+
用于日报、巡检和周期汇总等不由外部消息触发的流程。
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
# workflow.yml
|
|
142
|
+
event_sources:
|
|
143
|
+
- name: "每日复盘"
|
|
144
|
+
type: cron_scheduler
|
|
145
|
+
handler: daily_review_handler
|
|
146
|
+
|
|
147
|
+
# profile/profile.yml
|
|
148
|
+
workflows:
|
|
149
|
+
<workflowId>:
|
|
150
|
+
event_sources:
|
|
151
|
+
每日复盘:
|
|
152
|
+
daily_at: "09:00"
|
|
153
|
+
event_type: daily_review
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
调度字段的可选形式及多字段时的实际行为,见 [生成的配置字段参考](generated/event-sources.md#cron_scheduler)。
|
|
157
|
+
|
|
158
|
+
## AI 表格记录轮询
|
|
159
|
+
|
|
160
|
+
### `aitable_record`
|
|
161
|
+
|
|
162
|
+
用于监听其他表或其他 workflow tracker 的记录变化。
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
# workflow.yml
|
|
166
|
+
event_sources:
|
|
167
|
+
- name: "待沉淀记录监听"
|
|
168
|
+
type: aitable_record
|
|
169
|
+
handler: kb_persist_handler
|
|
170
|
+
|
|
171
|
+
# profile/profile.yml
|
|
172
|
+
workflows:
|
|
173
|
+
<workflowId>:
|
|
174
|
+
event_sources:
|
|
175
|
+
待沉淀记录监听:
|
|
176
|
+
base_id: "bXXXXXXXX"
|
|
177
|
+
table_id: "tblXXXXXXXX"
|
|
178
|
+
watch_mode: both
|
|
179
|
+
filter:
|
|
180
|
+
field: "知识沉淀"
|
|
181
|
+
equals: "待沉淀"
|
|
182
|
+
poll_interval_ms: 300000
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**必须带 `filter` 或 `filters`。** 无 filter 时每轮全表扫描,数据量涨到几万行后 dws 可能超时,链路会静默停摆且不报错。`filter` 与 `filters` 的字段结构、优先级及字段名规则见 [生成的配置字段参考](generated/event-sources.md#aitable_record)。
|
|
186
|
+
|
|
187
|
+
- 监听**当前 workflow 的 tracker 表** → 不要生成 event source,tracker 自身轮询即可。
|
|
188
|
+
- 监听**另一个 workflow 的 tracker 表** → 使用 `source_table` 引用。精确结构见 [生成的配置字段参考](generated/event-sources.md#aitable_record)。
|
|
189
|
+
|
|
190
|
+
### 筛选示例
|
|
191
|
+
|
|
192
|
+
以下示例展示常见写法;字段结构与组合语义以生成参考为准。
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
# 单条件
|
|
196
|
+
filter:
|
|
197
|
+
field: "知识沉淀"
|
|
198
|
+
equals: "待沉淀"
|
|
199
|
+
|
|
200
|
+
# 命中任一值
|
|
201
|
+
filter:
|
|
202
|
+
field: "状态"
|
|
203
|
+
equals: ["待处理", "处理中"]
|
|
204
|
+
|
|
205
|
+
# 多条件
|
|
206
|
+
filter:
|
|
207
|
+
- field: "状态"
|
|
208
|
+
equals: "待处理"
|
|
209
|
+
- field: "优先级"
|
|
210
|
+
equals: ["P0-紧急", "P1-高"]
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
filters:
|
|
215
|
+
operator: and
|
|
216
|
+
operands:
|
|
217
|
+
- operator: any_of
|
|
218
|
+
operands: ["状态", ["待处理", "处理中"]]
|
|
219
|
+
- operator: neq
|
|
220
|
+
operands: ["负责人", ""]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Config Surface
|
|
224
|
+
|
|
225
|
+
`config` 放驱动层配置(监听哪个群/表、触发时机),`handler_config` 放 handler 业务配置(昵称、复盘表引用)。详见 `config-surface.md`。
|
|
226
|
+
|
|
227
|
+
## record_policy
|
|
228
|
+
|
|
229
|
+
`eager`(默认)每事件建行,`on_demand` 配合 recipe 返回 `tracker: false` 实现透传。详见 `workflow-yml.md §record_policy`。
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# 字段类型清单
|
|
2
|
+
|
|
3
|
+
`tracker.field_definitions` 中声明业务字段时可用的类型。本清单与代码里的 `VALID_FIELD_TYPES` 保持一致;写了清单外的类型会被静默降级为 `text`(同时告警),所以**不要猜**。
|
|
4
|
+
|
|
5
|
+
## 完整类型表
|
|
6
|
+
|
|
7
|
+
| 框架类型 | AI 表格字段类型 | 可写 options | 可写 formatter | 说明 |
|
|
8
|
+
| --- | --- | --- | --- | --- |
|
|
9
|
+
| `text` | 文本 | 否 | 否 | 默认兜底类型;未知类型也会降级为此 |
|
|
10
|
+
| `number` | 数字 | 否 | 否 | 纯数值,不含货币/百分号格式 |
|
|
11
|
+
| `currency` | 金额 | 否 | 否 | 带货币格式的数值 |
|
|
12
|
+
| `progress` | 进度 | 否 | 否 | 0–100 的进度条 |
|
|
13
|
+
| `rating` | 评分 | 否 | 否 | 星级评分 |
|
|
14
|
+
| `date` | 日期 | 否 | 是 | 含「日期」「时间」语义的字段;可用 `formatter` 指定格式,如 `YYYY-MM-DD HH:mm:ss` |
|
|
15
|
+
| `singleSelect` | 单选 | 是 | 否 | 有限互斥选项 |
|
|
16
|
+
| `multipleSelect` | 多选 | 是 | 否 | 有限多选项 |
|
|
17
|
+
| `user` | 人员 | 否 | 否 | 成员/负责人;需要发 IM 时声明为此类型 |
|
|
18
|
+
| `group` | 群组 | 否 | 否 | 用户组 |
|
|
19
|
+
| `url` | 链接 | 否 | 否 | URL |
|
|
20
|
+
| `richText` | 富文本 | 否 | 否 | 富文本内容 |
|
|
21
|
+
| `attachment` | 附件 | 否 | 否 | 文件/图片附件 |
|
|
22
|
+
| `checkbox` | 复选框 | 否 | 否 | 布尔值 |
|
|
23
|
+
| `creator` | 创建人 | 否 | 否 | **系统只读字段**,不在 `field_definitions` 中声明 |
|
|
24
|
+
| `lastModifier` | 最后修改人 | 否 | 否 | **系统只读字段**,不在 `field_definitions` 中声明 |
|
|
25
|
+
| `createdTime` | 创建时间 | 否 | 否 | **系统只读字段**,不在 `field_definitions` 中声明 |
|
|
26
|
+
| `lastModifiedTime` | 最后修改时间 | 否 | 否 | **系统只读字段**,不在 `field_definitions` 中声明 |
|
|
27
|
+
|
|
28
|
+
## 用法示例
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
tracker:
|
|
32
|
+
field_definitions:
|
|
33
|
+
- name: "问题描述"
|
|
34
|
+
type: text
|
|
35
|
+
- name: "问题类型"
|
|
36
|
+
type: singleSelect
|
|
37
|
+
options: ["功能异常", "使用咨询", "数据问题", "权限问题", "其他"]
|
|
38
|
+
- name: "紧急程度"
|
|
39
|
+
type: singleSelect
|
|
40
|
+
options: ["P0-紧急", "P1-高", "P2-中", "P3-低"]
|
|
41
|
+
- name: "上报时间"
|
|
42
|
+
type: date
|
|
43
|
+
formatter: "YYYY-MM-DD HH:mm:ss"
|
|
44
|
+
- name: "处理人"
|
|
45
|
+
type: user
|
|
46
|
+
- name: "涉及金额"
|
|
47
|
+
type: currency
|
|
48
|
+
- name: "是否加急"
|
|
49
|
+
type: checkbox
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 硬约束
|
|
53
|
+
|
|
54
|
+
1. **系统骨架字段无需声明**:`标题` / `状态` / `当前步骤` / `处理人` / `事件日志` 由 setup 自动创建,不要写进 `field_definitions`。
|
|
55
|
+
2. **系统只读字段不要声明**:`creator` / `lastModifier` / `createdTime` / `lastModifiedTime` 是系统字段,不写入、不出现在 `FormDesigner.visible_fields`。
|
|
56
|
+
3. **状态字段用 `singleSelect`**:若业务需要记录状态,其 `options` 必须与 `tracker.states` 的值**完全一致**。
|
|
57
|
+
4. **`options` 只对 `singleSelect` / `multipleSelect` 有效**:其他类型写 `options` 会被忽略。
|
|
58
|
+
5. **`formatter` 只对 `date` 有效**:且只在**新建字段**时透传给 AI 表格;已有字段不会回填格式。
|
|
59
|
+
6. **`formula` / `lookup` 不在 `field_definitions` 能力范围内**:需要这类字段时,通过 AI 表格原生能力或 automation 处理,不要写进 `field_definitions`。
|
|
60
|
+
7. **未知类型降级为 `text`**:框架不会抛错,但会告警;设计阶段应避免依赖降级。
|
|
61
|
+
|
|
62
|
+
## 选择指南
|
|
63
|
+
|
|
64
|
+
- 自由文本 → `text`
|
|
65
|
+
- 有限互斥选项 → `singleSelect`
|
|
66
|
+
- 有限多选项 → `multipleSelect`
|
|
67
|
+
- 日期/时间 → `date`
|
|
68
|
+
- 数值计算 → `number`
|
|
69
|
+
- 金额 → `currency`
|
|
70
|
+
- 成员/负责人且要发 IM → `user`
|
|
71
|
+
- 布尔开关 → `checkbox`
|
|
72
|
+
- 文件/图片 → `attachment`
|
|
73
|
+
- URL → `url`
|
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
|
|
13
13
|
## dingtalk_group_message
|
|
14
14
|
|
|
15
|
-
钉钉群消息入站事件源(dws 逐群轮询)。conversation_ids 接受内联群 ID 列表或 ${env.*}/${config.*}/${globalVar.*}
|
|
15
|
+
钉钉群消息入站事件源(dws 逐群轮询)。conversation_ids 接受内联群 ID 列表或 ${env.*}/${config.*}/${globalVar.*} 整值引用;多个会话并发拉取,适合群数较少、需要逐群拉取的场景。产出 message_received 事件。
|
|
16
16
|
|
|
17
|
-
- `conversation_ids?` ((string | { groupId: string } | null)[] | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 (string | { groupId: string })[]
|
|
17
|
+
- `conversation_ids?` ((string | { groupId: string } | null)[] | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 (string | { groupId: string })[]:缺省会在 setup 提示必填,空数组表示默认停用,非空则启用;也可用 ${env.*}、${config.*} 或 ${globalVar.*} 整值引用,引用结果必须是同一数组结构
|
|
18
18
|
- `source_adapter?` ("dws_pull") — 消息拉取适配器,当前仅支持 dws_pull
|
|
19
19
|
- `dws_bin?` (string) — dws 可执行文件路径,缺省为 dws
|
|
20
|
-
- `watch_senders?` (string[]) — 只监听这些发送者 ID
|
|
20
|
+
- `watch_senders?` (string[]) — 只监听这些发送者 ID 或昵称;缺省或空数组均监听所有人
|
|
21
21
|
- `ignore_senders?` (string[]) — 忽略这些发送者 ID 或昵称,用于防止机器人消息回流
|
|
22
22
|
- `fetch_limit?` (number) — 每个会话每轮拉取的消息数上限
|
|
23
|
-
- `aggregate_by_sender?` (boolean) —
|
|
23
|
+
- `aggregate_by_sender?` (boolean) — 是否按发送者聚合本轮消息;缺省 true,启用后每个发送者产出一个独立事件
|
|
24
24
|
- `image_mode?` ("download" | "url" | "off") — 图片处理模式:download 下载到本地,url 保留链接,off 不处理
|
|
25
25
|
- `media_dir?` (string) — 图片本地化目录,相对配置目录解析
|
|
26
26
|
- `download_timeout_ms?` (number) — 单张图片下载超时毫秒数
|
|
@@ -29,29 +29,28 @@
|
|
|
29
29
|
|
|
30
30
|
## dingtalk_user_message
|
|
31
31
|
|
|
32
|
-
钉钉消息入站事件源(dws list-all 批量轮询)。一次分页拉取所有会话后按 conversation_ids 和 conversation_types 本地过滤,适合约 200
|
|
32
|
+
钉钉消息入站事件源(dws list-all 批量轮询)。一次分页拉取所有会话后按 conversation_ids 和 conversation_types 本地过滤,适合约 200 个以上群;仅与 dingtalk_group_message 共享 conversation_ids 语义,其余配置见字段表。无效或不可见的 conversation_ids 不报错,只会静默无事件。产出 message_received 事件。
|
|
33
33
|
|
|
34
|
-
- `conversation_ids?` ((string | { groupId: string } | null)[] | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 (string | { groupId: string })[]
|
|
34
|
+
- `conversation_ids?` ((string | { groupId: string } | null)[] | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 (string | { groupId: string })[]:缺省会在 setup 提示必填,空数组表示默认停用,非空则启用;也可用 ${env.*}、${config.*} 或 ${globalVar.*} 整值引用,引用结果必须是同一数组结构
|
|
35
35
|
- `exclude_at_bot?` (boolean) — 是否排除 @机器人的消息,缺省 true
|
|
36
36
|
- `conversation_types?` (("group" | "1to1")[]) — 监听的会话类型:group 群聊、1to1 单聊
|
|
37
37
|
- `dws_bin?` (string) — dws 可执行文件路径,缺省为 dws
|
|
38
38
|
- `page_size?` (number) — 每页拉取数量,缺省 50
|
|
39
39
|
- `max_pages?` (number) — 最大分页次数,缺省 20
|
|
40
|
-
- `aggregate_by_sender?` (boolean) —
|
|
40
|
+
- `aggregate_by_sender?` (boolean) — 是否按发送者聚合本轮消息;缺省 true,启用后每个发送者产出一个独立事件
|
|
41
41
|
- `image_mode?` ("download" | "url" | "off") — 图片处理模式:download 下载到本地,url 保留链接,off 不处理
|
|
42
42
|
- `media_dir?` (string) — 图片本地化目录,相对当前进程工作目录解析
|
|
43
43
|
- `poll_interval_ms?` (number) — 消息轮询间隔,单位毫秒
|
|
44
44
|
|
|
45
45
|
## dingtalk_stream
|
|
46
46
|
|
|
47
|
-
钉钉企业机器人 Stream 入站事件源(WebSocket 推送,群内 @
|
|
47
|
+
钉钉企业机器人 Stream 入站事件源(WebSocket 推送,群内 @ 机器人触发)。单机器人可用 client_id/client_secret 或 *_env;动态多机器人使用 robot_creds 凭据映射,配置多少个 credRef 就监听多少个机器人。robot_creds 可内联,也可通过 ${env.*}/${config.*}/${globalVar.*} 整值引用活读。产出 message_received 事件。
|
|
48
48
|
|
|
49
49
|
- `client_id?` (string) — 单机器人模式的应用 Client ID;敏感值建议改用 client_id_env 指定环境变量名
|
|
50
50
|
- `client_secret?` (string) — 单机器人模式的应用 Client Secret;敏感值建议改用 client_secret_env 指定环境变量名
|
|
51
51
|
- `client_id_env?` (string) — 保存单机器人 Client ID 的环境变量名,如 DINGTALK_CLIENT_ID;这里填写变量名,不填写 ${env.*} 引用
|
|
52
52
|
- `client_secret_env?` (string) — 保存单机器人 Client Secret 的环境变量名,如 DINGTALK_CLIENT_SECRET;这里填写变量名,不填写 ${env.*} 引用
|
|
53
|
-
- `
|
|
54
|
-
- `robot_creds?` (Record<string, { client_id: string; client_secret: string }> | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 Record<credRef, { client_id: string; client_secret: string }>;也可用 ${env.*}、${config.*} 或 ${globalVar.*} 整值引用,引用结果必须是同一映射结构
|
|
53
|
+
- `robot_creds?` (Record<string, { client_id: string; client_secret: string }> | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 Record<credRef, { client_id: string; client_secret: string }>;也可用 ${env.*}、${config.*} 或 ${globalVar.*} 整值引用,引用结果必须是同一映射结构;配置多少个 credRef 就监听多少个机器人
|
|
55
54
|
- `endpoint?` (string) — 专有钉钉 Stream 端点预留值;当前 SDK 尚未消费
|
|
56
55
|
- `conversation_types?` (("group" | "1to1")[]) — 监听的会话类型,缺省仅群聊
|
|
57
56
|
- `image_mode?` ("download" | "url" | "off") — 图片处理模式:download 下载到本地,url 保留链接,off 不处理
|
|
@@ -62,7 +61,7 @@
|
|
|
62
61
|
钉钉个人 IM 事件入站事件源(dws event 长连接推送,无需轮询)。以当前 dws 登录用户身份实时接收目标群的全部消息(含未 @ 的消息);监听形态由 kind 决定(缺省 group=每群一个监听子进程,all-group=单进程监听全部群后按名单本地过滤,内存与群数解耦);conversation_ids 接受内联群 ID 列表或 ${env.*}/${config.*}/${globalVar.*} 整值引用。产出 message_received 事件。
|
|
63
62
|
|
|
64
63
|
- `kind?` ("group" | "all-group") — 监听目标形态,缺省 group。group=每个群一个监听子进程,conversation_ids 即订阅目标。all-group=单进程监听账号全部群,conversation_ids 退化为客户端白名单(名单为空时丢弃全部事件,不是放行全部),内存与群数解耦;代价是会收到无关群的消息并在本地丢弃。at-me/sender/all-direct 预留扩展位
|
|
65
|
-
- `conversation_ids?` ((string | { groupId: string } | null)[] | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 (string | { groupId: string })[]
|
|
64
|
+
- `conversation_ids?` ((string | { groupId: string } | null)[] | "${env.*}" | "${config.*}" | "${globalVar.*}") — 内联值为 (string | { groupId: string })[]:缺省会在 setup 提示必填,空数组表示默认停用,非空则启用;也可用 ${env.*}、${config.*} 或 ${globalVar.*} 整值引用,引用结果必须是同一数组结构
|
|
66
65
|
- `dws_bin?` (string) — dws 可执行文件路径,缺省为 dws
|
|
67
66
|
- `ignore_senders?` (string[]) — 忽略这些发送者(openDingTalkId 或展示名),用于防止机器人消息回流
|
|
68
67
|
- `image_mode?` ("download" | "url" | "off") — 图片处理模式:download 下载到本地,url 保留链接,off 不处理
|
|
@@ -71,12 +70,12 @@
|
|
|
71
70
|
|
|
72
71
|
## cron_scheduler
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
业务定时事件源。至少使用 cron、daily_at 或 interval_ms 之一指定调度;多项同时存在时按 daily_at、cron、interval_ms 的顺序取值。event_type 决定事件名,payload 可携带任意静态键值。精确墙钟调度可配 IANA timezone。
|
|
75
74
|
|
|
76
|
-
- `cron?` (string) — 标准五段 cron 表达式,或 every 5m / every 2h
|
|
77
|
-
- `daily_at?` (string) — 每日触发时间,格式 HH:mm
|
|
75
|
+
- `cron?` (string) — 标准五段 cron 表达式,或 every 5m / every 2h 形式的固定间隔;至少配置 cron、daily_at、interval_ms 之一,若同时存在则优先级低于 daily_at、高于 interval_ms
|
|
76
|
+
- `daily_at?` (string) — 每日触发时间,格式 HH:mm;至少配置 cron、daily_at、interval_ms 之一,若同时存在时优先使用
|
|
78
77
|
- `timezone?` (string) — IANA 时区,如 Asia/Shanghai;缺省使用系统时区
|
|
79
|
-
- `interval_ms?` (number) —
|
|
78
|
+
- `interval_ms?` (number) — 固定触发间隔,单位毫秒;至少配置 cron、daily_at、interval_ms 之一,若同时存在时优先级最低
|
|
80
79
|
- `event_type?` (string) — 产出的事件类型,缺省 scheduled_trigger
|
|
81
80
|
- `payload?` (Record<string, unknown>) — 随事件发送的任意键值对象 Record<string, unknown>;旧 metadata 写法仅为兼容,不推荐新配置使用
|
|
82
81
|
- `fire_immediately?` (boolean) — 是否在启动后立即触发一次,缺省 false
|
|
@@ -90,8 +89,8 @@ AI 表格记录轮询事件源。表来源二选一:直接填写 base_id + tab
|
|
|
90
89
|
- `source_table?` ({ mode: "reference"; ref: string }) — 引用其他 workflow 的 tracker 表,结构为 { mode: "reference"; ref: "workflow:<workflowId>.tracker" }
|
|
91
90
|
- `dws_bin?` (string) — dws 可执行文件路径,缺省为 dws
|
|
92
91
|
- `watch_mode?` ("created" | "updated" | "both") — 监听新增、更新或两者,缺省 created
|
|
93
|
-
- `filters?` ({ operator: "and" | "or"; operands: { operator: string; operands: unknown[] }[] }) —
|
|
94
|
-
- `filter?` ({ field: string; equals: string | number | boolean | (string | number | boolean)[] } | ({ field: string; equals: string | number | boolean | (string | number | boolean)[] })[]) — 首选的等值筛选 DSL:{ field: string; equals: string | number | boolean | (string | number | boolean)[] },或该对象的数组;数组条件按 AND
|
|
92
|
+
- `filters?` ({ operator: "and" | "or"; operands: { operator: string; operands: unknown[] }[] }) — 仅在简写 filter 无法表达时使用的 dws 扁平过滤树:{ operator: and | or, operands: { operator, operands }[] };与 filter 同时存在时以 filters 为准
|
|
93
|
+
- `filter?` ({ field: string; equals: string | number | boolean | (string | number | boolean)[] } | ({ field: string; equals: string | number | boolean | (string | number | boolean)[] })[]) — 首选的等值筛选 DSL:{ field: string; equals: string | number | boolean | (string | number | boolean)[] },或该对象的数组;数组条件按 AND 组合。field 使用字段名时必须与 field_definitions.name 完全一致,才能翻译为 fieldId 快路径;拼错会退回慢路径并可能超时
|
|
95
94
|
- `fetch_limit?` (number) — 每页查询记录数上限
|
|
96
95
|
- `max_pages?` (number) — 单轮最大分页次数
|
|
97
96
|
- `resolve_field_names?` (boolean) — 是否把 fieldId 转成字段名,缺省 true
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
5. `input_mapping` / `output_mapping` 都是 `Record<string,string>`:key 是 recipe 内变量名,value 是 AI 表格字段名。
|
|
12
12
|
6. `edges` 必须形成 `__start__ → … → __end__` 的完整闭链,不允许孤立 step。
|
|
13
13
|
7. 视图与 `input_mapping` / `output_mapping` 引用的字段,必须来自 `tracker.field_definitions` 或系统骨架字段(标题 / 状态 / 当前步骤 / 处理人 / 事件日志),不允许凭空出现。
|
|
14
|
-
8. `tracker` 段必须齐全:`states`(active ≥2 且显式 `review`)、`fields`(state / title / current_step / event_log)、`field_definitions`(所有业务字段的类型)→ `multi-workflow.md
|
|
14
|
+
8. `tracker` 段必须齐全:`states`(active ≥2 且显式 `review`)、`fields`(state / title / current_step / event_log)、`field_definitions`(所有业务字段的类型)→ `multi-workflow.md`、[`field-types.md`](./field-types.md)。缺 `states` 或 `fields` 时整段 tracker 被静默忽略,该 workflow 回落到全局 tracker 与别人共表。`field_definitions` 写在 `tracker` 内,根级已废弃。
|
|
15
15
|
9. recipe 文件以 `.recipe.ts` 结尾,默认导出 `RecipeDefinition`;EventHandler 文件以 `_handler.recipe.ts` 结尾,默认导出 `EventHandlerRecipeDefinition` → `recipe-contract.md`、`event-handlers.md`。
|
|
16
16
|
10. 所有 TypeScript 文件使用 ESM 模块格式。
|
|
17
17
|
|
|
@@ -32,3 +32,6 @@
|
|
|
32
32
|
→ 现象:实例**启动即报错**。见 `profile-and-roles.md`。
|
|
33
33
|
6. **绝不**在需求不明确时静默猜测——在产物注释里写出假设,并在汇报里列为「需确认项」。
|
|
34
34
|
→ 现象:用户不知道你替他做了哪些决定,问题在上线后才暴露。
|
|
35
|
+
7. **绝不**让 event handler 的 `meta.id` 与 workflow.yml 里 `event_sources[i].handler`
|
|
36
|
+
去掉 `_handler` 后缀的部分不一致;handler 引用名必须带 `_handler` 后缀,指向 step recipe 时不带。
|
|
37
|
+
→ 现象:typo 不在启动时报错,第一次事件到达时才报「handler not found」。见 `event-handlers.md`。
|
|
@@ -59,17 +59,23 @@ tracker:
|
|
|
59
59
|
`aitable_record` 事件源专用形式(保留 `mode`/`ref` 结构):
|
|
60
60
|
|
|
61
61
|
```yaml
|
|
62
|
+
# workflow.yml
|
|
62
63
|
event_sources:
|
|
63
64
|
- name: "待沉淀记录监听"
|
|
64
65
|
type: aitable_record
|
|
65
66
|
handler: kb_persist_handler
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
|
|
68
|
+
# profile/profile.yml
|
|
69
|
+
workflows:
|
|
70
|
+
<workflowId>:
|
|
71
|
+
event_sources:
|
|
72
|
+
待沉淀记录监听:
|
|
73
|
+
source_table:
|
|
74
|
+
mode: reference
|
|
75
|
+
ref: "workflow:group-qa.tracker"
|
|
76
|
+
filter:
|
|
77
|
+
field: "知识沉淀"
|
|
78
|
+
equals: "待沉淀"
|
|
73
79
|
```
|
|
74
80
|
|
|
75
81
|
### 通用 `{ ref }` 引用
|
|
@@ -67,13 +67,13 @@ workflows:
|
|
|
67
67
|
group-qa: # 必须是真实存在的 workflow id
|
|
68
68
|
toolkit_config:
|
|
69
69
|
messaging:
|
|
70
|
-
|
|
70
|
+
robot_creds: "${env.DINGTALK_ROBOTS}"
|
|
71
|
+
webhook_creds: "${env.DINGTALK_WEBHOOK?}"
|
|
71
72
|
file:
|
|
72
73
|
base_dir: "${roles.support.knowledge_scope.kb_collections[0]}"
|
|
73
74
|
event_sources:
|
|
74
75
|
群消息监听: # 必须匹配 workflow.yml 里的事件源 name
|
|
75
|
-
config:
|
|
76
|
-
conversation_ids: "${globalVar.group_pull_list}"
|
|
76
|
+
conversation_ids: "${globalVar.group_pull_list}" # 直接合入 event_sources[].config,不要套 config:
|
|
77
77
|
handler_config:
|
|
78
78
|
admins: "${globalVar.admins}"
|
|
79
79
|
steps:
|
|
@@ -106,6 +106,12 @@ workflows:
|
|
|
106
106
|
<只写用户确实要调的项>: <value> # 键直接合入 step.config,不要套 config:
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
profile 里的事件源与步骤都是“锚点覆盖”:
|
|
110
|
+
|
|
111
|
+
- `workflows.<wf>.event_sources.<name>` 下,除 `handler_config` 外的键直接合入 `event_sources[].config`,**不要**套 `config:`。
|
|
112
|
+
- `workflows.<wf>.event_sources.<name>.handler_config` 才合入该事件源的 handler 配置。
|
|
113
|
+
- `workflows.<wf>.steps.<stepId>` 下的键直接合入 `steps[].config`,**不要**套 `config:`。
|
|
114
|
+
|
|
109
115
|
没有东西要配的段(`toolkit_config`、`event_sources`、`guardrails`、`knowledge_scope`…)**整段省略**,不要留空占位。空段没有意义,只增加读者和后续修改的负担。
|
|
110
116
|
|
|
111
117
|
### `${roles.*}` 内部引用
|