@lark-apaas/coding-steering 0.1.13-alpha.0 → 0.1.13-alpha.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-steering",
3
- "version": "0.1.13-alpha.0",
3
+ "version": "0.1.13-alpha.1",
4
4
  "description": "Stack-specific steering content for miaoda-coding templates",
5
5
  "type": "module",
6
6
  "files": [
@@ -29,7 +29,7 @@ lark-cli apps --help 2>&1 | grep -q '+plugin-install' && echo "READY" || echo "M
29
29
  |------|------|
30
30
  | 插件包管理(安装/卸载/查看) | 由 lark-apps skill 引导,参考其 plugin reference 文档 |
31
31
  | 创建实例 | 设计 paramsSchema/formValue → 自查规则 → 写入配置文件(见 Create 链路) |
32
- | 获取调用投影 | `npx @lark-apaas/miaoda-cli plugin list --id <id>`(见调用代码生成) |
32
+ | 获取插件调用代码编写依据 | 按「插件调用代码编写依据」章流程获取 |
33
33
  | Client 侧非流式调用 | `capabilityClient.load(id).call(actionKey, input)` |
34
34
  | Client 侧流式调用 | `capabilityClient.load(id).callStream(actionKey, input)` |
35
35
  | Server 侧调用(仅兜底) | `capabilityService.load(id).call(actionKey, input)` |
@@ -234,6 +234,7 @@ const structured = await capabilityClient
234
234
  ```
235
235
 
236
236
  **关键区分**:
237
+
237
238
  - formValue 的 **key** = Plugin form.schema 的字段名(如 `prompt`、`content`、`fileUrl`)
238
239
  - formValue 的 **value** 中通过 `{{input.xxx}}` 引用 paramsSchema 定义的变量
239
240
  - 变量名(paramsSchema)与 form 字段名(form.schema)分属不同层,通常名称不同
@@ -243,21 +244,25 @@ const structured = await capabilityClient
243
244
  #### 支持的参数类型(仅 4 种)
244
245
 
245
246
  **文本**:
247
+
246
248
  ```json
247
249
  { "type": "string", "description": "文本参数描述" }
248
250
  ```
249
251
 
250
252
  **数组**:
253
+
251
254
  ```json
252
255
  { "type": "array", "description": "描述", "items": { "type": "string", "description": "元素描述" } }
253
256
  ```
254
257
 
255
258
  **图片**:
259
+
256
260
  ```json
257
261
  { "type": "array", "format": "plugin-image-url", "description": "描述", "items": { "type": "string" } }
258
262
  ```
259
263
 
260
264
  **文件**:
265
+
261
266
  ```json
262
267
  { "type": "array", "format": "plugin-file-url", "description": "描述", "items": { "type": "string" } }
263
268
  ```
@@ -341,16 +346,19 @@ const structured = await capabilityClient
341
346
  #### 各场景 Prompt 模板
342
347
 
343
348
  **文本生成类**:
349
+
344
350
  ```json
345
351
  "prompt": "你是一位资深的[平台名]内容创作专家。\n\n请根据以下关键词生成一篇文案:\n关键词:{{input.keywords}}\n\n内容要求:\n1. 标题(15-25字)\n2. 正文(300-500字)\n3. 结尾设置互动问题"
346
352
  ```
347
353
 
348
354
  **图片理解类**:
355
+
349
356
  ```json
350
357
  "prompt": "你是一位专业的图像分析专家。\n\n请对提供的图片进行深度分析:\n1. 基础信息:图片类型、主体内容\n2. 细节描述:颜色、构图、关键元素\n3. 语义理解:含义、情感、用途\n\n{{input.additional_requirements}}"
351
358
  ```
352
359
 
353
360
  **文生图类**:
361
+
354
362
  ```json
355
363
  "prompt": "请生成一张高质量图片:\n\n主题内容:{{input.subject}}\n\n画面要求:风格、构图、光线、色调\n质量要求:画面清晰、主体突出"
356
364
  ```
@@ -393,23 +401,22 @@ const structured = await capabilityClient
393
401
  5. **自查规则** — 写入前逐条对照 §一致性铁律 检查:变量引用是否对称、类型是否允许、是否使用了禁止的模板语法、数组是否有 items。发现违规立即修正,超过 3 次仍未通过则上报用户
394
402
  6. **写入实例配置** — 将以下结构写入 `server/capabilities/<id>.json`(或步骤 3 确定的路径):
395
403
 
396
- ```json
397
- {
398
- "id": "<id>",
399
- "pluginKey": "<pluginKey>",
400
- "pluginVersion": "<version>",
401
- "name": "<实例名称>",
402
- "description": "<实例描述>",
403
- "paramsSchema": { ... },
404
- "formValue": { ... },
405
- "createdAt": <unix毫秒>,
406
- "updatedAt": <unix毫秒>
407
- }
408
- ```
409
-
410
- 7. **获取调用投影**`npx @lark-apaas/miaoda-cli plugin list --id <id>`
411
- 合并 manifest actions 与实例配置,输出含 `actions[]` 的完整投影
412
- 8. **编写调用代码** — 基于投影 + `references/plugin-coding-guide.md`。**禁止凭记忆猜测**
404
+ ```json
405
+ {
406
+ "id": "<id>",
407
+ "pluginKey": "<pluginKey>",
408
+ "pluginVersion": "<version>",
409
+ "name": "<实例名称>",
410
+ "description": "<实例描述>",
411
+ "paramsSchema": { ... },
412
+ "formValue": { ... },
413
+ "createdAt": <unix毫秒>,
414
+ "updatedAt": <unix毫秒>
415
+ }
416
+ ```
417
+
418
+ 7. **获取插件调用代码编写依据**必须按「插件调用代码编写依据」章的获取流程执行(含多级 Fallback)。未成功获取 禁止进入步骤 8
419
+ 8. **编写调用代码** 基于步骤 7 的结果,**禁止凭记忆猜测**
413
420
 
414
421
  大 JSON 场景先写临时文件再读取,避免命令行转义问题。
415
422
 
@@ -420,8 +427,8 @@ const structured = await capabilityClient
420
427
  1. `cat server/capabilities/<id>.json` → 查看当前实例配置
421
428
  2. 读 manifest + 设计修改方案(改 name / formValue / paramsSchema)
422
429
  3. 改后逐条对照 §一致性铁律 自查,修正后写回配置文件,更新 `updatedAt`
423
- 4. paramsSchema 变化 → `grep -rn "load('${id}')" <project-path>/` 扫描代码引用并更新
424
- 5. 重新获取调用投影:`npx @lark-apaas/miaoda-cli plugin list --id <id>`
430
+ 4. **获取插件调用代码编写依据** 按「插件调用代码编写依据」章的获取流程执行
431
+ 5. paramsSchema 变化 基于步骤 4 的结果扫描代码引用并更新
425
432
 
426
433
  ### Delete 链路
427
434
 
@@ -438,7 +445,7 @@ const structured = await capabilityClient
438
445
  | 所有实例概览 | `ls server/capabilities/` |
439
446
  | 单个实例完整配置 | `cat server/capabilities/<id>.json` |
440
447
  | 插件的 actions/schema | `cat node_modules/<pluginKey>/manifest.json` |
441
- | 获取调用投影 | `npx @lark-apaas/miaoda-cli plugin list --id <id>` |
448
+ | 获取插件调用代码编写依据 | 按「插件调用代码编写依据」章流程获取 |
442
449
 
443
450
  ### 常见违规及修正
444
451
 
@@ -452,9 +459,9 @@ const structured = await capabilityClient
452
459
 
453
460
  ---
454
461
 
455
- ## 调用代码生成
462
+ ## 插件调用代码编写依据
456
463
 
457
- ### 调用前获取权威依据(每次编写/修改调用代码前必做)
464
+ ### 获取调用依据(每次编写/修改调用代码前必须执行)
458
465
 
459
466
  **首次尝试**:
460
467
 
@@ -491,6 +498,7 @@ npx @lark-apaas/miaoda-cli plugin list --id <instance_id>
491
498
  | 全栈应用(NestJS + React) | Client 侧(首选)或 Server 侧 |
492
499
 
493
500
  **Server 侧仅在以下场景使用**:
501
+
494
502
  1. 涉及敏感凭证(token/secret 不能暴露给前端)
495
503
  2. 多步骤强事务编排
496
504
  3. 触发器/定时任务(无前端上下文)
@@ -501,6 +509,7 @@ npx @lark-apaas/miaoda-cli plugin list --id <instance_id>
501
509
  ### 持久化决策
502
510
 
503
511
  以下任一条件成立时,插件结果**必须**保存到数据库:
512
+
504
513
  1. 结果会在其他页面展示
505
514
  2. 结果供后续功能消费
506
515
  3. 用户再次访问时需要看到结果
@@ -513,7 +522,6 @@ npx @lark-apaas/miaoda-cli plugin list --id <instance_id>
513
522
 
514
523
  ---
515
524
 
516
-
517
525
  | 错误类型 | 含义 | 应对策略 |
518
526
  |----------|------|---------|
519
527
  | `InputValidationError` | 入参不符合 schema | 修复参数后重试 |
@@ -522,6 +530,7 @@ npx @lark-apaas/miaoda-cli plugin list --id <instance_id>
522
530
  | `OutputValidationError` | 返回值不符合 schema | 记录异常返回 + 使用默认值 |
523
531
 
524
532
  **规则**:
533
+
525
534
  1. **禁止静默吞异常**:每个 `catch` 块必须向用户展示错误或触发补偿
526
535
  2. **异步操作必须有终态**:DB 中维护状态(pending → success / failed)
527
536
  3. **插件失败必须有补偿**:至少记录到待处理列表或提示用户重试
@@ -565,7 +574,7 @@ npx @lark-apaas/miaoda-cli plugin list --id <instance_id>
565
574
  1. **写入实例配置前必须自查** — 逐条对照 §一致性铁律 检查参数定义与引用的对称性、类型合法性、模板语法限制。自查通过后再写入配置文件。
566
575
  2. **先装包再建实例** — 创建插件实例前必须确保插件包已安装。
567
576
  3. **校验失败走重试** — max 3 次,3 次仍失败上报用户。
568
- 4. **写代码前获取调用投影**`npx @lark-apaas/miaoda-cli plugin list --id <id>` 或手动合并 manifest + capability JSON。禁止凭记忆猜测 actionKey / inputSchema / outputMode。
577
+ 4. **写代码前获取插件调用代码编写依据**必须按「插件调用代码编写依据」章流程获取。禁止凭记忆猜测 actionKey / inputSchema / outputMode。
569
578
  5. **禁止用 `npm install` 安装插件包** — 插件包和 npm 包是两套独立机制。
570
579
  6. **禁止 Mock** — 必须走真实插件实例调用链路。
571
580
  7. **formValue 禁止 Handlebars 控制语法** — 仅允许 `{{input.xxx}}`。
@@ -47,7 +47,9 @@
47
47
  ### Client 侧调用方式(默认首选)
48
48
 
49
49
  #### 1. 调用前获取权威依据
50
+
50
51
  在为某个插件实例生成调用代码前,必须先通过 `get_plugin_ai_json` 工具获取该插件实例的运行时投影(plugin_Instance.ai.json),并以其中信息为准:
52
+
51
53
  - `actions[].key`:调用时要传的 `actionKey`
52
54
  - `actions[].inputSchema / outputSchema`:入参/出参结构
53
55
  - `actions[].outputMode`:`unary | stream`(决定调用与结果处理方式)
@@ -150,7 +152,7 @@ function readFirstStringField(
150
152
 
151
153
  **核心原则**:在插件设计阶段按「原子化拆解」拆分,避免单插件返回多字段 JSON。
152
154
 
153
- ##### 推荐:多插件并行流式
155
+ ##### 推荐:多插件并行流式
154
156
 
155
157
  适用于需求涉及多种输出(标题、正文、图片等),各输出相对独立。
156
158
 
@@ -312,6 +314,7 @@ this.somePluginInstanceSideEffect(input).catch(error => {
312
314
  | 任意(兜底场景) | Server 侧 | `capabilityService.load(id).call(actionKey, input)` |
313
315
 
314
316
  **选择原则**:
317
+
315
318
  - 不涉及持久化时,优先在 Client 侧直接调用
316
319
  - `outputMode = stream` 时,Client 侧使用 `callStream` 做渐进式渲染
317
320
  - 涉及持久化、触发器、敏感凭证、事务编排等场景时,使用 Server 侧
@@ -1,316 +0,0 @@
1
- ---
2
- name: plugin-guide
3
- description: AI 新版插件集成规范与 Plugin 链路指南。支持AI智能生文、AI智能生图、AI图片理解、AI文档解析、AI结构化提取、AI图片处理等。Use when 需要:(1) 创建或管理 PluginInstance 插件实例,(2) 调用 capabilityClient 生成插件调用代码,(3) 编写 capabilityClient 调用代码。触发词:插件, plugin, AI生文, AI生图, 图片理解, 文档解析, 结构化提取, capabilityClient, pluginInstance
4
- compatibility: "requires: lark-cli with plugin commands (apps +plugin-install, +plugin-uninstall, +plugin-list). If not met, tell user: lark-cli 当前版本不支持插件能力,请升级到最新版本"
5
- when_to_use: "lark-cli apps +plugin-* commands are available. If not, tell user: lark-cli 当前版本不支持插件能力,请升级到最新版本"
6
- steering: true
7
- steering-topic: plugin_guide
8
- match-template-name: design-stack
9
- ---
10
-
11
- # Plugin 集成指南
12
-
13
- AI 新版插件集成规范与 Plugin 链路指南。支持的能力包括:AI智能生文、AI智能生图、AI图片理解、AI文档解析(PDF/DOC/PPTX等)、AI结构化提取(文本→JSON、图片→JSON)、AI内容摘要、AI翻译、AI语音合成与识别、AI图片处理(抠图/换背景/风格转换)等。本文档介绍插件实例配置、调用投影的获取方式,以及 `capabilityClient` 调用入口。
14
-
15
- ## ⚠️ 前置门禁
16
-
17
- 本 skill 依赖 lark-cli 的插件管理能力。开始前先确认当前环境是否可用:
18
-
19
- ```bash
20
- lark-cli apps --help 2>&1 | grep -q '+plugin-install' && echo "READY" || echo "MISSING"
21
- ```
22
-
23
- - **READY** → 继续
24
- - **MISSING** → 当前 lark-cli 版本不支持插件命令,请更新到最新版后重试:
25
-
26
- ## ⚠️ 铁律:禁止 Mock 插件调用
27
-
28
- **当 AGENTS.md 包含「插件规划」时,所有规划的插件必须创建实例并通过 `capabilityClient` 真实调用。**
29
-
30
- | 禁止 | 正确 |
31
- |------|------|
32
- | ❌ `setTimeout` + 硬编码数据模拟 AI 返回 | ✅ `capabilityClient.load(id).call(actionKey, input)` |
33
- | ❌ `picsum.photos` / 随机图片替代 AI 生图 | ✅ `capabilityClient.load(id).call('textToImage', {...})` |
34
- | ❌ 注释掉 `capabilityClient` import 用 Mock 函数 | ✅ 真实 import 并调用 |
35
- | ❌ AGENTS.md 规划了插件但 `server/capabilities/` 为空 | ✅ 每个规划的插件都有对应 JSON 文件 |
36
-
37
- > **与 Mock 展示数据的区别**:design_agent prompt 中的 "Mock 数据仅用于无附件的 prototype" 指的是表格示例行、卡片占位文本等**展示数据**。AI 生文/生图/翻译/结构化等**插件能力不属于 Mock 数据范围**,必须真实调用。
38
-
39
- ## Quick Reference
40
-
41
- | 操作 | 方式 |
42
- |------|------|
43
- | 插件包管理(安装/卸载/查看) | 由 lark-apps skill 引导,参考其 plugin reference 文档 |
44
- | 创建实例 | 设计 paramsSchema/formValue → 自查规则 → 写入配置文件(见 Create 链路) |
45
- | 获取调用投影 | `npx @lark-apaas/miaoda-cli plugin list --id <id>`(见调用代码生成) |
46
- | 非流式调用 | `capabilityClient.load(id).call(actionKey, input)` |
47
- | 流式调用 | `capabilityClient.load(id).callStream(actionKey, input)` |
48
- | capabilityClient 导入 | `import { capabilityClient } from '@lark-apaas/client-toolkit'` |
49
- | 插件实例配置位置 | `server/capabilities/<instance_id>.json`(默认纯前端应用 / appType=3)。见「配置目录」段 |
50
-
51
- > **capabilityClient 导入警告**:`capabilityClient` 是从 `@lark-apaas/client-toolkit` 直接导入的独立对象,**不是**从 `getDataloom()` 上获取的。错误写法:`const dataloom = await getDataloom(); (dataloom as any).capability` — 这样写不会工作。正确且唯一的方式:`import { capabilityClient } from '@lark-apaas/client-toolkit'`。
52
-
53
- ## Plugin 代码编写指南
54
-
55
- 以下场景**必须**先读取 `references/plugin-coding-guide.md` 再编写代码:
56
-
57
- - 编写 `capabilityClient` 调用代码时(含导入路径、调用方式)
58
- - 处理流式输出(`outputMode = stream`),包括多插件并行流式、单插件 JSON 流式解析
59
- - 需要根据 `outputMode` 选择 `call()` 或 `callStream()` 时
60
-
61
- ## Plugin 链式调用(Plugin Chain)
62
-
63
- 很多业务场景需要多个插件串联完成,**禁止用正则/字符串解析替代 AI 插件做结构化提取**。
64
-
65
- ### 插件能力分类
66
-
67
- | 类别 | 插件 | 输入→输出 |
68
- |------|------|----------|
69
- | **内容提取** | `ai-doc-parser` | 文档(PDF/DOC/PPTX/XLSX/CSV等9种)→纯文本 |
70
- | **内容提取** | `ai-speech-to-text` | 音频→纯文本 |
71
- | **内容提取** | `ai-image-understanding` | 图片→文本描述(流式,适合理解/问答) |
72
- | **结构化提取** | `ai-text-to-json` | 文本→结构化 JSON(最多20字段) |
73
- | **结构化提取** | `ai-image-to-json` | 图片→结构化 JSON(最多20字段,**单步直达**) |
74
- | **结构化提取** | `ai-categorization` | 文本→分类标签 |
75
- | **内容生成** | `ai-text-generate` | 提示词→文本(流式) |
76
- | **内容生成** | `ai-text-to-image` | 文字描述→图片 |
77
- | **内容生成** | `ai-text-summary` | 长文本→摘要(流式) |
78
- | **内容生成** | `ai-translate` | 文本→翻译(12种语言,流式) |
79
- | **内容生成** | `ai-search-summary` | 搜索词→网页摘要(流式) |
80
- | **内容生成** | `ai-speech-synthesis` | 文本→语音(44+音色) |
81
- | **图片处理** | `ai-image-matting` | 图片→抠图/去背景/去水印 |
82
- | **图片处理** | `ai-background-replace` | 主体图+背景→合成图 |
83
- | **图片处理** | `ai-image-to-image` | 参考图(1-5张)+描述→编辑/风格转换 |
84
- | **图片处理** | `ai-image-compare` | 两张图→对比分析(流式) |
85
-
86
- ### 决策树:选择单步还是链式
87
-
88
- ```
89
- 输入是什么?
90
- ├── 文档文件 → 必须先用 ai-doc-parser 提取文本,再根据目标选择下游插件:
91
- │ ├── 需要结构化数据 → ai-doc-parser → ai-text-to-json (2步链)
92
- │ ├── 需要摘要 → ai-doc-parser → ai-text-summary
93
- │ ├── 需要翻译 → ai-doc-parser → ai-translate
94
- │ ├── 需要分类 → ai-doc-parser → ai-categorization
95
- │ └── 仅需原文 → ai-doc-parser(单步)
96
- ├── 图片 → ⚠️ 注意选择正确的插件:
97
- │ ├── 提取结构化数据(发票/名片/证件等)→ ai-image-to-json(⭐ 单步直达!)
98
- │ ├── 理解内容后提取结构化数据 → ai-image-understanding → ai-text-to-json(2步链)
99
- │ ├── 抠图后换背景 → ai-image-matting → ai-background-replace(2步链)
100
- │ └── 理解/问答/编辑/对比 → 对应单插件即可
101
- ├── 音频
102
- │ ├── 需要结构化数据 → ai-speech-to-text → ai-text-to-json(2步链)
103
- │ └── 仅需文字 → ai-speech-to-text(单步)
104
- └── 纯文本
105
- ├── 需要结构化数据 → ai-text-to-json(⭐ 单步直达!)
106
- └── 摘要/翻译/分类/生成 → 对应单插件即可
107
- ```
108
-
109
- ### 常见 Plugin Chain 组合
110
-
111
- | 链路 | 插件组合 | 场景举例 |
112
- |------|---------|---------|
113
- | 文档→结构化数据 | `ai-doc-parser` → `ai-text-to-json` | 简历PDF→员工档案、合同→结构化条款 |
114
- | 文档→摘要 | `ai-doc-parser` → `ai-text-summary` | 研报PDF→摘要、长文档→概要 |
115
- | 文档→翻译 | `ai-doc-parser` → `ai-translate` | 英文论文→中文翻译 |
116
- | 文档→分类 | `ai-doc-parser` → `ai-categorization` | 工单文档→类型标签 |
117
- | 图片→结构化数据 | `ai-image-to-json`(**单步**) | 发票→金额/日期、名片→联系人 |
118
- | 图片理解→结构化 | `ai-image-understanding` → `ai-text-to-json` | 复杂图表→数据、截图→字段 |
119
- | 音频→结构化数据 | `ai-speech-to-text` → `ai-text-to-json` | 会议录音→待办事项 |
120
- | 音频→翻译 | `ai-speech-to-text` → `ai-translate` | 外语录音→中文 |
121
- | 抠图→换背景 | `ai-image-matting` → `ai-background-replace` | 商品图→电商主图 |
122
-
123
- ### Plugin Chain 调用模式
124
-
125
- ```typescript
126
- // 示例:文档 → 结构化数据(2步链)
127
- // Step 1: 内容提取(注意:fileUrl 通常为数组类型,必须按 inputSchema 传入)
128
- const rawResult = await capabilityClient
129
- .load('doc_parser_instance')
130
- .call('parseDocToMarkdown', { fileUrl: [docUrl] });
131
-
132
- // Step 2: AI 结构化提取
133
- const structured = await capabilityClient
134
- .load('text_to_json_instance')
135
- .call('textToJson', { text: rawResult.content });
136
-
137
- // Step 3: 使用结果(填入表单 / 写入数据库等)
138
- ```
139
-
140
- > **Client 侧提示**:`capabilityClient` 支持直接传 File/Blob 对象作为文件参数,无需先上传到 dataloom 获取 URL:
141
- > ```typescript
142
- > // Client 侧:直接传 File 对象,SDK 自动处理上传
143
- > const rawResult = await capabilityClient
144
- > .load('doc_parser_instance')
145
- > .call('parseDocToMarkdown', { fileUrl: [file] }); // file 为 File/Blob 对象
146
- > ```
147
- > ⚠️ `capabilityClient` 支持此能力,SDK 自动处理文件上传。
148
-
149
- ### 创建结构化提取 PluginInstance 的关键要求
150
-
151
- 创建 `ai-text-to-json` 或 `ai-image-to-json` 类型的 PluginInstance 时:
152
- 1. **必须一次性定义所有需要提取的字段**(参考数据库 schema / 表单定义 / UI 设计),宁多勿漏
153
- 2. 字段类型仅支持 String / Number / Boolean,最多 20 个字段
154
- 3. 先获取调用投影确认上游插件的 `outputSchema`,确保输入格式正确
155
- 4. 图片→结构化数据场景,优先使用 `ai-image-to-json`(单步),避免不必要的链式调用
156
-
157
- ## 核心概念
158
-
159
- - **插件包(Plugin Package)**:npm 格式的功能包,安装到 `node_modules/`,含 `manifest.json` 描述 actions 和 form.schema。
160
- - **插件实例(Plugin Instance / Capability)**:基于插件包创建的业务配置,存储在 `server/capabilities/{id}.json`(默认纯前端应用,路径规则见「配置目录」),定义 `paramsSchema`(业务入参)和 `formValue`(表单映射,通过 `{{input.xxx}}` 引用 paramsSchema 参数)。
161
- - **变量映射**:`调用方传值 → paramsSchema 定义变量 → formValue 消费变量 {{input.xxx}} → Plugin form.schema 接收`。
162
-
163
- ### 插件包 ≠ npm 包(必读)
164
-
165
- | | 插件包 | npm 依赖 |
166
- |------|------|------|
167
- | 写入字段 | `package.json` → **`actionPlugins`** | `package.json` → `dependencies` / `devDependencies` |
168
- | 用途 | 妙搭平台 AI 能力 | 项目依赖库 |
169
- | **禁止** | ❌ 不能用 `npm install` 装插件包 | ❌ 不能用 `npm install` 管理插件包 |
170
-
171
- 两套机制完全独立。混淆会导致运行时找不到插件。
172
-
173
- ### 插件实例配置目录
174
-
175
- 插件实例配置文件(`<instance_id>.json`)存放在 capabilities 目录下。按以下优先级确定路径:
176
-
177
- 1. 环境变量 `MIAODA_CAPABILITIES_DIR` → 直接使用
178
- 2. 环境变量 `MIAODA_APP_TYPE` 或 `.env.local` 中的 `MIAODA_APP_TYPE`:
179
- - `6` → `shared/capabilities/`(纯前端应用 appType=6)
180
- - 其他 → `server/capabilities/`(Design 应用 appType=3)
181
- 3. 以上都未设置 → 默认 `server/capabilities/`
182
-
183
- 目录不存在时 `mkdir -p` 创建。
184
-
185
- ### 插件实例配置结构
186
-
187
- 写入 `server/capabilities/<id>.json` 的 JSON 结构:
188
-
189
- ```json
190
- {
191
- "id": "<语义化ID>",
192
- "pluginKey": "<pluginKey>",
193
- "pluginVersion": "<version>",
194
- "name": "<实例名称>",
195
- "description": "<实例描述>",
196
- "paramsSchema": { ... },
197
- "formValue": { ... },
198
- "createdAt": <unix毫秒>,
199
- "updatedAt": <unix毫秒>
200
- }
201
- ```
202
-
203
- **paramsSchema 支持 4 种参数类型**:
204
-
205
- 1. **文本**:`{ "type": "string", "description": "..." }`
206
- 2. **数组**:`{ "type": "array", "description": "...", "items": { "type": "string" } }`
207
- 3. **图片**:`{ "type": "array", "format": "plugin-image-url", "description": "...", "items": { "type": "string" } }`
208
- 4. **文件**:`{ "type": "array", "format": "plugin-file-url", "description": "...", "items": { "type": "string" } }`
209
-
210
- 只允许 string 和 array 两种 type。array 必须有 items。format 只允许 `plugin-image-url` 或 `plugin-file-url`。
211
-
212
- > **注意**:`format: "plugin-image-url"` 和 `format: "plugin-file-url"` 的字段支持直接传入 File/Blob 对象,`capabilityClient` SDK 会自动处理上传。
213
-
214
- ---
215
-
216
- ## CRUD 链路
217
-
218
- ### Create 链路
219
-
220
- 创建一个**插件实例**,即能力目录下的 `<id>.json` 配置文件。
221
-
222
- 1. **安装插件包** — 由 lark-apps skill 引导完成
223
- 2. **读 manifest** — `cat node_modules/<pluginKey>/manifest.json`
224
- 3. **确定配置目录** — 按上方「插件实例配置目录」规则确定,不存在则 `mkdir -p` 创建
225
- 4. **设计实例配置** — 确定 id / name / paramsSchema / formValue
226
- 5. **自查规则** — 写入前逐条检查:变量引用对称、类型合法、禁止模板控制语法、array 有 items。违规修正,超过 3 次上报用户
227
- 6. **写入配置文件** — 按上方 JSON 结构写入
228
- 7. **获取调用投影** — `npx @lark-apaas/miaoda-cli plugin list --id <id>`
229
- 8. **编写调用代码** — 基于投影。**禁止凭记忆猜测**
230
-
231
- ### Update 链路
232
-
233
- 1. `cat server/capabilities/<id>.json` → 查看当前配置
234
- 2. 读 manifest + 设计修改方案。不修改 id / pluginKey / pluginVersion / createdAt
235
- 3. 改后自查规则,修正后写回,更新 `updatedAt`
236
- 4. paramsSchema 变化 → `grep -rn "load('${id}')"` 扫描代码引用并更新
237
- 5. 重新获取调用投影:`npx @lark-apaas/miaoda-cli plugin list --id <id>`
238
-
239
- ### Delete 链路
240
-
241
- 1. `cat server/capabilities/<id>.json` → 确认实例存在
242
- 2. `grep -rn "load('${id}')"` → 扫描代码引用,有则先清理
243
- 3. `rm server/capabilities/<id>.json`
244
-
245
- ---
246
-
247
- ## 调用投影
248
-
249
- ### 获取调用投影(每次编写/修改调用代码前必做)
250
-
251
- ```bash
252
- npx @lark-apaas/miaoda-cli plugin list --id <instance_id>
253
- ```
254
-
255
- 输出含 `actions[].key`、`inputSchema`、`outputSchema`、`outputMode`,是编写调用代码的唯一依据。
256
-
257
- ### 充血 Fallback 链
258
-
259
- `npx @lark-apaas/miaoda-cli plugin list --id <id>` → 失败则 `npx fullstack-cli capability list --id <id>` → 失败则 `node .agents/skills/plugin-guide/scripts/plugin-hydrate.js <pluginKey> <instanceId>` → 失败则手动合并 manifest + capability JSON。
260
-
261
- ### 无 Node.js 环境
262
-
263
- 1. 告知用户未安装 Node.js
264
- 2. 手动合并 manifest + capability JSON
265
- 3. 如果插件有 `{dynamic: true}`:读插件源码推导逻辑 → 基于推导编写代码 → 运行正确则成立,报错则上报用户
266
-
267
- ### 调用代码
268
-
269
- 根据 `actions[].outputMode` 选择调用方式:
270
- - `unary` → `capabilityClient.load(id).call(actionKey, input)`
271
- - `stream` → `capabilityClient.load(id).callStream(actionKey, input)`
272
-
273
- **必读**:`references/plugin-coding-guide.md`
274
-
275
- ### 代码放置
276
-
277
- 调用代码放在 `client/` 目录下的组件/hooks 中,由用户交互触发。
278
-
279
- ---
280
-
281
-
282
- ## 业务语义映射约定
283
-
284
- 用户会用「AI 生文」「AI 生图」「AI 摘要」等**业务语言**描述需求;这些关键词必须被识别为**待使用或待创建的 PluginInstance**。
285
-
286
- 开发流程:
287
- 1. 收到需求后,先 `ls server/capabilities/` 看是否有可复用的实例
288
- 2. 若有候选但不确定是否满足,获取调用投影查看其 actions/schema/outputMode 再决策
289
- 3. 若无,按 Create 链路新建实例
290
- 4. 生成 `capabilityClient` 调用代码
291
-
292
- ## 插件调用错误处理规范
293
-
294
- | 错误类型 | 应对策略 |
295
- |----------|---------|
296
- | `InputValidationError` | 修复参数后重试 |
297
- | `RateLimitError` | 指数退避重试(1s/2s/4s),最多 3 次 |
298
- | `ExecutionError` | 记录日志 + 降级方案 + 通知用户 |
299
- | 网络超时 | 重试 + 超时后降级 |
300
-
301
- **规则**: (1) 禁止静默吞异常,每个 catch 必须向用户展示错误或触发补偿;(2) 异步调用必须有终态(success/failed),前端不能永远 loading;(3) 通知类插件失败必须有补偿机制。
302
-
303
- ## 缓存与幂等性
304
-
305
- - AI 类插件**没有请求级缓存**。同样输入返回相似结果是 LLM 低 temperature 下的正常行为。
306
- - **禁止**通过修改业务参数注入 UUID 来"绕缓存",会污染 AI 输入导致输出包含垃圾文本。
307
- - 需要不同结果时:调整 temperature、修改 prompt 要求、或使用不同业务上下文。
308
-
309
- ## 插件参数来源规范
310
-
311
- | 类型 | 来源 | 示例 |
312
- |------|------|------|
313
- | 业务数据 | DB 查询或前端传入 | 候选人姓名、简历内容 |
314
- | 运行时配置 | 配置/环境变量/平台 API | 接收人 user_id、阈值 |
315
-
316
- **禁止**硬编码运行时配置值(user_id、邮箱等),必须从配置表/平台 API/环境变量获取。
@@ -1,126 +0,0 @@
1
- ## PluginInstance 代码编写指南
2
-
3
- ### 核心原则
4
-
5
- **严禁** import { capabilityClient } from '@lark-apaas/client-capability'。
6
- **唯一指定**的导入方式是 import { capabilityClient } from '@lark-apaas/client-toolkit';
7
-
8
- | 场景 | 调用方式 |
9
- |------|---------|
10
- | 非流式调用 | `capabilityClient.load(id).call()` |
11
- | 流式输出场景 | `capabilityClient.load(id).callStream()` |
12
-
13
- ---
14
-
15
- ### 调用方式
16
-
17
- #### 1. 调用前获取权威依据
18
- 在为某个插件实例生成调用代码前,必须先通过 `get_plugin_ai_json` 工具获取该插件实例的运行时投影(plugin_Instance.ai.json),并以其中信息为准:
19
- - `actions[].key`:调用时要传的 `actionKey`
20
- - `actions[].inputSchema / outputSchema`:入参/出参结构
21
- - `actions[].outputMode`:`unary | stream`(决定调用与结果处理方式)
22
-
23
- #### call / callStream 函数签名
24
-
25
- ```typescript
26
- .call(actionKey: string, input: object) // 非流式,返回 Promise<output>
27
- .callStream(actionKey: string, input: object) // 流式,返回 AsyncIterable<chunk>
28
- ```
29
-
30
- - **第一个参数 `actionKey`**:必须是字符串,值来自 `get_plugin_ai_json` 返回的 `actions[].key`(如 `'sendFeishuMessage'`、`'textGenerate'`)
31
- - **第二个参数 `input`**:必须是对象,结构符合 `actions[].inputSchema`
32
-
33
- ```typescript
34
- // ❌ 错误:把参数 JSON.stringify 后当作 actionKey
35
- plugin.call(JSON.stringify({ meeting_title: '...' }));
36
- // ❌ 错误:漏掉 actionKey,直接传参数对象
37
- plugin.call({ meeting_title: '...' });
38
-
39
- // ✅ 正确:第一个参数是 actionKey 字符串,第二个参数是 input 对象
40
- plugin.call('send_feishu_message', { meeting_title: '...' });
41
- ```
42
-
43
- #### 2. 非流式调用(outputMode = "unary")
44
-
45
- ```typescript
46
- import { capabilityClient } from '@lark-apaas/client-toolkit';
47
- import { logger } from "@lark-apaas/client-toolkit/logger";
48
-
49
- const result = await capabilityClient
50
- .load('create_feishu_group')
51
- .call('createGroup', {
52
- group_name: '项目讨论群',
53
- members: ['user_001', 'user_002'],
54
- });
55
-
56
- logger.info(result);
57
- ```
58
-
59
- #### 3. 流式调用(outputMode = "stream")
60
-
61
- ##### 场景判断与推荐方案
62
-
63
- | 场景 | 特征 | 推荐度 |
64
- |-----|------|-------|
65
- | **多插件并行流式** | 多个插件各返回单一输出,并行调用 | **优先推荐** |
66
- | **单插件 JSON 流式解析** | 单插件返回结构化 JSON,需边接收边解析 | ⚠️ 仅在必要时 |
67
-
68
- **核心原则**:在插件设计阶段按「原子化拆解」拆分,避免单插件返回多字段 JSON。
69
-
70
- ##### 推荐:多插件并行流式
71
-
72
- 适用于需求涉及多种输出(标题、正文、图片等),各输出相对独立。
73
-
74
- ```tsx
75
- import { logger } from "@lark-apaas/client-toolkit/logger";
76
-
77
- function MultiPluginStreamExample() {
78
- const [title, setTitle] = useState('');
79
- const [content, setContent] = useState('');
80
- const [coverUrl, setCoverUrl] = useState('');
81
-
82
- const handleGenerate = async (keywords: string) => {
83
- // 1. 封面图(非流式,异步不阻塞)
84
- capabilityClient
85
- .load('cover_generator')
86
- .call<{ images: string[] }>('textToImage', { keywords })
87
- .then(res => res?.images?.[0] && setCoverUrl(res.images[0]))
88
- .catch(err => logger.warn('封面生成失败', err));
89
-
90
- // 2. 标题(非流式)
91
- capabilityClient
92
- .load('title_generator')
93
- .call<{ content: string }>('textGenerate', { keywords })
94
- .then(res => res?.content && setTitle(res.content));
95
-
96
- // 3. 正文(流式,边生成边展示)
97
- const contentStream = capabilityClient
98
- .load('content_generator')
99
- .callStream<{ content: string }>('textGenerate', { keywords });
100
-
101
- // 🎯 流式内容直接拼接,无需复杂解析
102
- for await (const chunk of contentStream) {
103
- setContent(prev => prev + (chunk.content || ''));
104
- }
105
- };
106
-
107
- return (/* 各字段独立渲染 */);
108
- }
109
- ```
110
-
111
- **优点**:代码简洁、各插件独立、某个失败不影响其他。
112
-
113
- ##### ⚠️ 兜底:单插件 JSON 流式解析
114
-
115
- 当无法拆分为多插件时,需处理不完整 JSON 的逐字符到达,需实现 `parseStreamingStringField` 和 `extractJsonObject` 工具函数。**强烈建议在插件设计阶段拆分为多插件并行流式调用,避免此场景。**
116
-
117
- ### outputMode 与调用方式
118
-
119
- 先通过 `get_plugin_ai_json(pluginInstanceId)` 获取 `actions[].outputMode`:
120
-
121
- | outputMode | 调用方式 |
122
- |------------|---------|
123
- | `unary` | `capabilityClient.load(id).call(actionKey, input)` |
124
- | `stream` | `capabilityClient.load(id).callStream(actionKey, input)` |
125
-
126
- ---