foliko 1.0.67 → 1.0.69
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/.claude/settings.local.json +148 -145
- package/examples/basic.js +110 -110
- package/examples/mcp-example.js +53 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-mcp.js +79 -79
- package/examples/test-reload.js +61 -61
- package/news-20260329-1774794949179.html +39 -0
- package/news-20260329-1774794970785.html +39 -0
- package/news-20260329-1774797491928.html +39 -0
- package/package.json +1 -1
- package/plugins/ambient-agent-plugin.js +333 -22
- package/plugins/email.js +106 -20
- package/plugins/feishu-plugin.js +12 -2
- package/plugins/file-system-plugin.js +76 -30
- package/plugins/python-executor-plugin.js +41 -8
- package/plugins/scheduler-plugin.js +14 -11
- package/plugins/telegram-plugin.js +51 -23
- package/plugins/web-plugin.js +8 -6
- package/plugins/weixin-plugin.js +12 -2
- package/skills/ambient-agent/SKILL.md +84 -14
- package/skills/workflow-guide/SKILL.md +214 -2
- package/skills/workflow-troubleshooting/DEBUGGING.md +182 -0
- package/skills/workflow-troubleshooting/SKILL.md +314 -0
- package/src/capabilities/workflow-engine.js +367 -22
- package/src/core/agent-chat.js +106 -14
- package/src/core/framework.js +81 -1
- package/src/executors/executor-base.js +58 -58
- package/test-server.js +0 -25
- package/test.txt +0 -3
package/plugins/weixin-plugin.js
CHANGED
|
@@ -424,8 +424,18 @@ class WeixinPlugin extends Plugin {
|
|
|
424
424
|
|
|
425
425
|
// 确定用户ID
|
|
426
426
|
let userId = null
|
|
427
|
-
|
|
428
|
-
|
|
427
|
+
let effectiveSessionId = sessionId
|
|
428
|
+
|
|
429
|
+
// 如果没有 sessionId,尝试从执行上下文获取
|
|
430
|
+
if (!effectiveSessionId) {
|
|
431
|
+
const ctx = this._framework.getExecutionContext()
|
|
432
|
+
if (ctx?.sessionId) {
|
|
433
|
+
effectiveSessionId = ctx.sessionId
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (effectiveSessionId && effectiveSessionId.startsWith('weixin_')) {
|
|
438
|
+
userId = effectiveSessionId.replace('weixin_', '')
|
|
429
439
|
} else if (this._sessionPlugin) {
|
|
430
440
|
// 获取最近的 weixin 会话
|
|
431
441
|
const sessions = this._sessionPlugin.listSessions()
|
|
@@ -144,7 +144,7 @@ think → think
|
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
这意味着:
|
|
147
|
-
-
|
|
147
|
+
- 变量引用语法一致:`${variableName}`(Ambient Agent 使用 `${}`)
|
|
148
148
|
- sessionId 传递机制一致
|
|
149
149
|
- 错误处理方式一致
|
|
150
150
|
|
|
@@ -162,6 +162,66 @@ context.variables.loopIndex // 循环索引(如果被 loop 执行)
|
|
|
162
162
|
context.input // 工作流/目标输入参数
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
## 变量引用语法
|
|
166
|
+
|
|
167
|
+
Ambient Agent 使用 `${}` 语法在 action 参数中引用变量:
|
|
168
|
+
|
|
169
|
+
### 1. 事件数据引用 `${event.xxx}`
|
|
170
|
+
|
|
171
|
+
当 goal 监听事件时,事件数据可通过 `${event.xxx}` 直接引用:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
{
|
|
175
|
+
id: 'auto-reply',
|
|
176
|
+
type: 'tool',
|
|
177
|
+
name: 'email_auto_reply',
|
|
178
|
+
args: {
|
|
179
|
+
to: '${event.from}',
|
|
180
|
+
subject: '${event.subject}',
|
|
181
|
+
body: '${event.text}',
|
|
182
|
+
from: ''
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**注意**:`email:received` 事件的数据结构:
|
|
188
|
+
- `event.from` - 发件人地址
|
|
189
|
+
- `event.subject` - 邮件主题
|
|
190
|
+
- `event.text` - 邮件正文(纯文本)
|
|
191
|
+
- `event.body` - 邮件正文(可能含 HTML)
|
|
192
|
+
- `event.messageId` - 邮件消息 ID
|
|
193
|
+
|
|
194
|
+
### 2. 多步骤引用 `${actionId.output.field}`
|
|
195
|
+
|
|
196
|
+
当有多个顺序执行的 action 时,后续 action 可以引用前一个 action 的输出:
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
actions: [
|
|
200
|
+
{
|
|
201
|
+
id: 'read-email',
|
|
202
|
+
type: 'tool',
|
|
203
|
+
name: 'email_read',
|
|
204
|
+
args: { limit: 1, unreadOnly: true }
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: 'process',
|
|
208
|
+
type: 'tool',
|
|
209
|
+
name: 'some_processor',
|
|
210
|
+
args: {
|
|
211
|
+
// 引用 read-email 输出的 emails 数组的第一个元素的 from 字段
|
|
212
|
+
sender: '${read-email.output.emails[0].from}'
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 3. 变量解析规则
|
|
219
|
+
|
|
220
|
+
`${}` 语法支持嵌套属性访问:
|
|
221
|
+
- `${event.from}` - 直接字段
|
|
222
|
+
- `${actionId.output.field}` - 嵌套字段
|
|
223
|
+
- `${event.data.xxx}` - data 对象内的字段(兼容性别名)
|
|
224
|
+
|
|
165
225
|
## 创建目标的最佳实践
|
|
166
226
|
|
|
167
227
|
1. **明确目标标题** - 让用户清楚知道目标意图
|
|
@@ -172,13 +232,13 @@ context.input // 工作流/目标输入参数
|
|
|
172
232
|
|
|
173
233
|
## 示例:创建邮件自动回复目标
|
|
174
234
|
|
|
175
|
-
**重要**:`email:received`
|
|
176
|
-
- `
|
|
177
|
-
- `
|
|
178
|
-
- `
|
|
179
|
-
- `
|
|
235
|
+
**重要**:`email:received` 事件的数据结构:
|
|
236
|
+
- `event.from` - 发件人地址
|
|
237
|
+
- `event.subject` - 邮件主题
|
|
238
|
+
- `event.text` - 邮件正文(纯文本)
|
|
239
|
+
- `event.messageId` - 邮件消息 ID
|
|
180
240
|
|
|
181
|
-
使用
|
|
241
|
+
使用 `${}` 语法从事件中提取参数:
|
|
182
242
|
|
|
183
243
|
```javascript
|
|
184
244
|
await ambient_goals({
|
|
@@ -192,9 +252,18 @@ await ambient_goals({
|
|
|
192
252
|
type: 'tool',
|
|
193
253
|
name: 'email_auto_reply',
|
|
194
254
|
args: {
|
|
195
|
-
to: '{
|
|
196
|
-
subject: '{
|
|
197
|
-
body: '{
|
|
255
|
+
to: '${event.from}',
|
|
256
|
+
subject: '${event.subject}',
|
|
257
|
+
body: '${event.text}',
|
|
258
|
+
from: ''
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'mark-read',
|
|
263
|
+
type: 'tool',
|
|
264
|
+
name: 'email_mark_read',
|
|
265
|
+
args: {
|
|
266
|
+
messageId: '${event.messageId}'
|
|
198
267
|
}
|
|
199
268
|
}
|
|
200
269
|
],
|
|
@@ -211,14 +280,15 @@ await ambient_goals({
|
|
|
211
280
|
|
|
212
281
|
// ✅ 正确 - 从事件中提取参数
|
|
213
282
|
{ type: 'tool', name: 'email_auto_reply', args: {
|
|
214
|
-
to: '{
|
|
215
|
-
subject: '{
|
|
216
|
-
body: '{
|
|
283
|
+
to: '${event.from}',
|
|
284
|
+
subject: '${event.subject}',
|
|
285
|
+
body: '${event.text}',
|
|
286
|
+
from: ''
|
|
217
287
|
}
|
|
218
288
|
}
|
|
219
289
|
```
|
|
220
290
|
|
|
221
|
-
**注意**:`email_auto_reply` 需要 `to`、`subject`、`body` 三个必需参数,必须从
|
|
291
|
+
**注意**:`email_auto_reply` 需要 `to`、`subject`、`body` 三个必需参数,必须从 `${event.xxx}` 提取。由于 `email:received` 事件已携带邮件数据,不需要额外的 `email_read` 步骤来读取邮件。
|
|
222
292
|
|
|
223
293
|
## 生命周期
|
|
224
294
|
|
|
@@ -70,6 +70,23 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
70
70
|
|
|
71
71
|
## 工作流步骤类型
|
|
72
72
|
|
|
73
|
+
### 支持的步骤类型
|
|
74
|
+
|
|
75
|
+
| 类型 | 说明 | 自动推断 |
|
|
76
|
+
|------|------|---------|
|
|
77
|
+
| `tool` | 工具调用 | 有 `tool` 字段 |
|
|
78
|
+
| `script` | JavaScript 脚本 | 有 `code` 或 `script` 字段 |
|
|
79
|
+
| `condition` | 条件分支 | 有 `branches` 字段 |
|
|
80
|
+
| `switch` | 开关分支 | 有 `value` + `branches` |
|
|
81
|
+
| `try` | 异常捕获 | 有 `try` 或 `catch` 字段 |
|
|
82
|
+
| `parallel` | 并行执行 | `"parallel": true` 或 `"mode": "parallel"` |
|
|
83
|
+
| `sequential` | 顺序执行 | 有 `steps` 字段 |
|
|
84
|
+
| `loop` | 循环执行 | 有 `steps` + `loopVariable`/`maxIterations` |
|
|
85
|
+
| `delay` | 延时等待 | 有 `delayMs` 字段 |
|
|
86
|
+
| `workflow` | 嵌套工作流 | 有 `workflow` 字段 |
|
|
87
|
+
| `message` | 发送消息 | 有 `message` 字段 |
|
|
88
|
+
| `think` | 主动思考 | 有 `topic` 字段 |
|
|
89
|
+
|
|
73
90
|
### 1. script - 脚本步骤
|
|
74
91
|
|
|
75
92
|
执行 JavaScript 脚本,**必须用 return 返回值**:
|
|
@@ -219,7 +236,90 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
219
236
|
}
|
|
220
237
|
```
|
|
221
238
|
|
|
222
|
-
### 5.
|
|
239
|
+
### 5. switch - 开关分支
|
|
240
|
+
|
|
241
|
+
根据值匹配执行对应分支(类似编程语言的 switch-case):
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"type": "switch",
|
|
246
|
+
"value": "{{status}}",
|
|
247
|
+
"branches": [
|
|
248
|
+
{ "case": "success", "steps": [...] },
|
|
249
|
+
{ "case": "error", "steps": [...] },
|
|
250
|
+
{ "case": "pending", "steps": [...] }
|
|
251
|
+
],
|
|
252
|
+
"default": { "steps": [...] }
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
- `value`: 要匹配的值,支持 `{{variable}}` 引用
|
|
257
|
+
- `branches`: 分支数组,匹配第一个 `case` 等于 `value` 的分支
|
|
258
|
+
- `default`: 默认分支(可选)
|
|
259
|
+
|
|
260
|
+
### 6. try - 异常捕获
|
|
261
|
+
|
|
262
|
+
尝试执行,失败时执行 catch 分支:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"type": "try",
|
|
267
|
+
"name": "尝试执行",
|
|
268
|
+
"try": {
|
|
269
|
+
"steps": [
|
|
270
|
+
{ "type": "tool", "tool": "可能失败的工具", "args": {...} }
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
"catch": {
|
|
274
|
+
"steps": [
|
|
275
|
+
{ "type": "tool", "tool": "notification_send", "args": {"message": "执行失败: {{lastError}}"} }
|
|
276
|
+
]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
- `try`: 尝试执行的步骤
|
|
282
|
+
- `catch`: 捕获异常后执行的步骤
|
|
283
|
+
|
|
284
|
+
### 7. parallel - 并行执行
|
|
285
|
+
|
|
286
|
+
多个步骤同时并行执行:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"type": "parallel",
|
|
291
|
+
"name": "并行获取数据",
|
|
292
|
+
"steps": [
|
|
293
|
+
{ "type": "tool", "tool": "fetch", "args": {"url": "https://api1.com"}, "output": "data1" },
|
|
294
|
+
{ "type": "tool", "tool": "fetch", "args": {"url": "https://api2.com"}, "output": "data2" },
|
|
295
|
+
{ "type": "tool", "tool": "fetch", "args": {"url": "https://api3.com"}, "output": "data3" }
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
返回数组形式的执行结果。
|
|
301
|
+
|
|
302
|
+
### 8. workflow - 嵌套工作流
|
|
303
|
+
|
|
304
|
+
在一个工作流中调用另一个工作流:
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"type": "workflow",
|
|
309
|
+
"name": "调用子工作流",
|
|
310
|
+
"workflow": {
|
|
311
|
+
"name": "子工作流名称",
|
|
312
|
+
"steps": [...]
|
|
313
|
+
},
|
|
314
|
+
"input": {
|
|
315
|
+
"param1": "{{parentVar}}"
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
子工作流的输出会合并到父工作流的上下文变量中。
|
|
321
|
+
|
|
322
|
+
### 9. delay - 延时步骤
|
|
223
323
|
|
|
224
324
|
等待指定毫秒数:
|
|
225
325
|
|
|
@@ -231,7 +331,7 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
231
331
|
}
|
|
232
332
|
```
|
|
233
333
|
|
|
234
|
-
###
|
|
334
|
+
### 10. sequential - 顺序步骤
|
|
235
335
|
|
|
236
336
|
将多个步骤组合为顺序执行(可嵌套使用):
|
|
237
337
|
|
|
@@ -338,6 +438,91 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
338
438
|
}
|
|
339
439
|
```
|
|
340
440
|
|
|
441
|
+
### 示例:并行获取多个数据源
|
|
442
|
+
|
|
443
|
+
```json
|
|
444
|
+
{
|
|
445
|
+
"name": "multi-source-fetch",
|
|
446
|
+
"description": "并行获取多个数据源",
|
|
447
|
+
"steps": [
|
|
448
|
+
{
|
|
449
|
+
"type": "parallel",
|
|
450
|
+
"name": "并行获取数据",
|
|
451
|
+
"steps": [
|
|
452
|
+
{ "type": "tool", "tool": "fetch", "args": {"url": "https://api.baidu.com"}, "output": "baiduData" },
|
|
453
|
+
{ "type": "tool", "tool": "fetch", "args": {"url": "https://api.bbc.com"}, "output": "bbcData" },
|
|
454
|
+
{ "type": "tool", "tool": "fetch", "args": {"url": "https://api.reddit.com"}, "output": "redditData" }
|
|
455
|
+
]
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"type": "tool",
|
|
459
|
+
"tool": "notification_send",
|
|
460
|
+
"args": {
|
|
461
|
+
"message": "数据获取完成: 百度 {{result[0].length}} 字节, BBC {{result[1].length}} 字节"
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
]
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### 示例:try-catch 异常处理
|
|
469
|
+
|
|
470
|
+
```json
|
|
471
|
+
{
|
|
472
|
+
"name": "safe-execute",
|
|
473
|
+
"description": "带异常处理的工作流",
|
|
474
|
+
"steps": [
|
|
475
|
+
{
|
|
476
|
+
"type": "try",
|
|
477
|
+
"name": "尝试执行",
|
|
478
|
+
"try": {
|
|
479
|
+
"steps": [
|
|
480
|
+
{ "type": "tool", "tool": "fetch", "args": {"url": "https://可能失败的api.com"} }
|
|
481
|
+
]
|
|
482
|
+
},
|
|
483
|
+
"catch": {
|
|
484
|
+
"steps": [
|
|
485
|
+
{ "type": "script", "outputVariable": "errorMsg", "script": "return 'API 调用失败,使用备用数据';" }
|
|
486
|
+
]
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"type": "tool",
|
|
491
|
+
"tool": "notification_send",
|
|
492
|
+
"args": {
|
|
493
|
+
"message": "结果: {{result.result || result.error}}"
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### 示例:switch 分支处理
|
|
501
|
+
|
|
502
|
+
```json
|
|
503
|
+
{
|
|
504
|
+
"name": "status-handler",
|
|
505
|
+
"description": "根据状态分发处理",
|
|
506
|
+
"steps": [
|
|
507
|
+
{
|
|
508
|
+
"type": "script",
|
|
509
|
+
"outputVariable": "status",
|
|
510
|
+
"script": "return 'success';"
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
"type": "switch",
|
|
514
|
+
"value": "{{status}}",
|
|
515
|
+
"branches": [
|
|
516
|
+
{ "case": "success", "steps": [{ "type": "tool", "tool": "notification_send", "args": {"message": "操作成功"} }] },
|
|
517
|
+
{ "case": "error", "steps": [{ "type": "tool", "tool": "notification_send", "args": {"message": "操作失败"} }] },
|
|
518
|
+
{ "case": "pending", "steps": [{ "type": "tool", "tool": "notification_send", "args": {"message": "操作进行中"} }] }
|
|
519
|
+
],
|
|
520
|
+
"default": { "steps": [{ "type": "tool", "tool": "notification_send", "args": {"message": "未知状态"} }] }
|
|
521
|
+
}
|
|
522
|
+
]
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
341
526
|
## 注意事项
|
|
342
527
|
|
|
343
528
|
1. **优先使用 `{{result}}` 引用**:上一步结果直接用 `{{result.field}}` 提取,比 script 更简洁
|
|
@@ -349,6 +534,10 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
349
534
|
7. 循环变量 `i` 从 0 开始计数
|
|
350
535
|
8. 条件分支的 `condition` 是 JavaScript 表达式字符串
|
|
351
536
|
9. **自动 JSON 解析**:`{{result.body}}` 如果是 JSON 字符串会自动解析
|
|
537
|
+
10. **类型自动推断**:如果步骤没有指定 `type`,会根据字段自动推断类型
|
|
538
|
+
11. **`${stepId.output}` 引用**:可引用指定 id 步骤的输出(兼容旧格式)
|
|
539
|
+
12. **`input` 字段**:可传递上一步输出给工具(兼容旧格式)
|
|
540
|
+
13. **try-catch 异常处理**:使用 try 包裹可能失败的步骤,catch 捕获错误
|
|
352
541
|
|
|
353
542
|
## 最佳实践
|
|
354
543
|
|
|
@@ -359,6 +548,9 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
359
548
|
5. 复杂的业务逻辑优先使用脚本步骤
|
|
360
549
|
6. **script 必须 return**:`script` 是函数体,必须用 return 返回值
|
|
361
550
|
7. **JSON 中 script 单行写**:用分号分隔多个语句,不要换行
|
|
551
|
+
8. **并行获取独立数据**:使用 `parallel` 同时获取多个数据源,节省时间
|
|
552
|
+
9. **使用 try-catch**:对可能失败的步骤使用异常捕获,避免整个工作流中断
|
|
553
|
+
10. **嵌套工作流**:将复杂工作流拆分为子工作流,提高复用性和可维护性
|
|
362
554
|
|
|
363
555
|
**错误示例**:
|
|
364
556
|
```json
|
|
@@ -377,6 +569,16 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
377
569
|
{
|
|
378
570
|
"script": "return 1; // 这是注释"
|
|
379
571
|
}
|
|
572
|
+
|
|
573
|
+
// ❌ 错误 - switch 缺少 default 兜底
|
|
574
|
+
{
|
|
575
|
+
"type": "switch",
|
|
576
|
+
"value": "{{status}}",
|
|
577
|
+
"branches": [
|
|
578
|
+
{ "case": "success", "steps": [...] }
|
|
579
|
+
]
|
|
580
|
+
// 没有 default 分支,匹配不到会静默忽略
|
|
581
|
+
}
|
|
380
582
|
```
|
|
381
583
|
|
|
382
584
|
**正确示例**:
|
|
@@ -386,4 +588,14 @@ allowed-tools: execute_workflow,reloadWorkflows
|
|
|
386
588
|
|
|
387
589
|
// ✅ 正确 - 复杂逻辑拆成多步
|
|
388
590
|
{ "type": "script", "script": "context.variables.a=10; context.variables.b=20; return context.variables.a+context.variables.b;" }
|
|
591
|
+
|
|
592
|
+
// ✅ 正确 - switch 有 default 兜底
|
|
593
|
+
{
|
|
594
|
+
"type": "switch",
|
|
595
|
+
"value": "{{status}}",
|
|
596
|
+
"branches": [
|
|
597
|
+
{ "case": "success", "steps": [...] }
|
|
598
|
+
],
|
|
599
|
+
"default": { "steps": [{ "type": "tool", "tool": "notification_send", "args": {"message": "未知状态"} }] }
|
|
600
|
+
}
|
|
389
601
|
```
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# 工作流开发调试指南
|
|
2
|
+
|
|
3
|
+
## 快速测试工作流
|
|
4
|
+
|
|
5
|
+
### 1. 使用 execute_workflow 直接测试
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
execute_workflow({
|
|
9
|
+
workflow: {
|
|
10
|
+
name: "test-workflow",
|
|
11
|
+
description: "测试工作流",
|
|
12
|
+
steps: [
|
|
13
|
+
{
|
|
14
|
+
id: "step1",
|
|
15
|
+
type: "tool",
|
|
16
|
+
tool: "python-execute",
|
|
17
|
+
args: {
|
|
18
|
+
code: "print('Hello from Python!')"
|
|
19
|
+
},
|
|
20
|
+
outputVariable: "result"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. JSON 格式注意事项
|
|
28
|
+
|
|
29
|
+
- ✅ 使用双引号包裹字符串
|
|
30
|
+
- ✅ 确保 JSON 语法正确
|
|
31
|
+
- ❌ 避免在 JSON 中使用未转义的特殊字符
|
|
32
|
+
- ❌ 不要在 code 中混用单引号和双引号
|
|
33
|
+
|
|
34
|
+
### 3. 常见错误
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Unexpected identifier 'xxx'
|
|
38
|
+
```
|
|
39
|
+
- 原因:JSON 格式错误,可能是引号或逗号问题
|
|
40
|
+
- 解决:检查 JSON 语法
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Unexpected end of JSON input
|
|
44
|
+
```
|
|
45
|
+
- 原因:模板字符串 `${}` 在 JSON 中导致解析错误
|
|
46
|
+
- 解决:不要在 JSON 中使用模板变量,改用纯 Python 代码
|
|
47
|
+
|
|
48
|
+
## 工作流重载问题
|
|
49
|
+
|
|
50
|
+
### 问题
|
|
51
|
+
添加新工作流后,`reloadWorkflows` 不会重新加载同名工作流。
|
|
52
|
+
|
|
53
|
+
### 解决
|
|
54
|
+
```bash
|
|
55
|
+
# 方法1:使用不同的文件名
|
|
56
|
+
news-dashboard-v3.json # 不会被 v2 覆盖
|
|
57
|
+
|
|
58
|
+
# 方法2:重启插件
|
|
59
|
+
reload_plugins() # 重载所有插件
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Python 执行最佳实践
|
|
63
|
+
|
|
64
|
+
### 1. 错误处理
|
|
65
|
+
```python
|
|
66
|
+
try:
|
|
67
|
+
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
|
|
68
|
+
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
69
|
+
data = resp.read().decode('utf-8')
|
|
70
|
+
print('SUCCESS')
|
|
71
|
+
except Exception as e:
|
|
72
|
+
print(f'ERROR: {e}')
|
|
73
|
+
# fallback 逻辑
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 2. 输出格式
|
|
77
|
+
```python
|
|
78
|
+
# 简单输出
|
|
79
|
+
print('Status: OK')
|
|
80
|
+
print(f'Count: {len(items)}')
|
|
81
|
+
|
|
82
|
+
# JSON 输出
|
|
83
|
+
import json
|
|
84
|
+
print('---JSON_START---')
|
|
85
|
+
print(json.dumps(data, ensure_ascii=False))
|
|
86
|
+
print('---JSON_END---')
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. 超时设置
|
|
90
|
+
```python
|
|
91
|
+
with urllib.request.urlopen(req, timeout=30) as resp: # 30秒超时
|
|
92
|
+
...
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 工作流步骤类型
|
|
96
|
+
|
|
97
|
+
| 类型 | 用途 | 示例 |
|
|
98
|
+
|------|------|------|
|
|
99
|
+
| `tool` | 调用工具 | `python-execute`, `shell`, `get_time` |
|
|
100
|
+
| `script` | 执行 JavaScript | 数据处理、变量操作 |
|
|
101
|
+
| `condition` | 条件分支 | if/else 逻辑 |
|
|
102
|
+
| `loop` | 循环执行 | 遍历列表 |
|
|
103
|
+
| `delay` | 延时等待 | 等待指定时间 |
|
|
104
|
+
| `message` | 消息通知 | 发送通知 |
|
|
105
|
+
| `think` | LLM 思考 | 反思/分析 |
|
|
106
|
+
|
|
107
|
+
## 工具输出变量
|
|
108
|
+
|
|
109
|
+
### 正确用法
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"id": "step1",
|
|
113
|
+
"type": "tool",
|
|
114
|
+
"tool": "get_time",
|
|
115
|
+
"outputVariable": "current_time"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 访问输出
|
|
120
|
+
```javascript
|
|
121
|
+
// 在 script 步骤中
|
|
122
|
+
context.input.current_time
|
|
123
|
+
|
|
124
|
+
// 在下一个 tool 步骤中
|
|
125
|
+
${step1.current_time}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 调试技巧
|
|
129
|
+
|
|
130
|
+
### 1. 打印中间变量
|
|
131
|
+
```python
|
|
132
|
+
print('Debug - data:', data)
|
|
133
|
+
print('Debug - length:', len(data))
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 2. 保存到文件
|
|
137
|
+
```python
|
|
138
|
+
with open('debug.log', 'w') as f:
|
|
139
|
+
f.write(str(data))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 3. 逐步执行
|
|
143
|
+
```javascript
|
|
144
|
+
{
|
|
145
|
+
"steps": [
|
|
146
|
+
{ "id": "step1", ... }, // 先测试 step1
|
|
147
|
+
{ "id": "step2", ... } // 确认 step1 成功后添加
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 性能优化
|
|
153
|
+
|
|
154
|
+
### 1. 合并步骤
|
|
155
|
+
```javascript
|
|
156
|
+
// ❌ 多个小步骤
|
|
157
|
+
{ "id": "step1", "tool": "python-execute", "code": "a = 1" }
|
|
158
|
+
{ "id": "step2", "tool": "python-execute", "code": "b = 2" }
|
|
159
|
+
|
|
160
|
+
// ✅ 单个大步骤
|
|
161
|
+
{ "id": "step1", "tool": "python-execute", "code": "a = 1; b = 2; print(a + b)" }
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 2. 减少变量传递
|
|
165
|
+
```javascript
|
|
166
|
+
// ❌ 多步骤传递
|
|
167
|
+
{ "outputVariable": "data1" }
|
|
168
|
+
{ "outputVariable": "data2" }
|
|
169
|
+
|
|
170
|
+
// ✅ 单步骤完成
|
|
171
|
+
{ "outputVariable": "final_result" }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 3. 超时设置
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"args": {
|
|
178
|
+
"code": "...",
|
|
179
|
+
"timeout": 60000 // 60秒超时
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|