@aigne/doc-smith 0.9.8-alpha.10 → 0.9.8-alpha.11

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.
Files changed (68) hide show
  1. package/agents/generate-images/generate-image.yaml +75 -0
  2. package/agents/generate-images/generate-summary.mjs +213 -0
  3. package/agents/generate-images/index.yaml +39 -0
  4. package/agents/generate-images/prepare-generation.mjs +283 -0
  5. package/agents/generate-images/prepare-image-generation.mjs +130 -0
  6. package/agents/generate-images/prompts/system.md +101 -0
  7. package/agents/generate-images/prompts/user.md +85 -0
  8. package/agents/generate-images/save-image-result.mjs +246 -0
  9. package/agents/generate-images/scan-image-slots.mjs +245 -0
  10. package/agents/localize/index.yaml +8 -4
  11. package/agents/localize/{load-glossary.mjs → translate-documents/load-glossary.mjs} +1 -1
  12. package/agents/localize/{prepare-translation.mjs → translate-documents/prepare-translation.mjs} +34 -31
  13. package/agents/localize/{save-translation.mjs → translate-documents/save-translation.mjs} +27 -5
  14. package/agents/localize/translate-documents/translate-document-to-language.mjs +207 -0
  15. package/agents/localize/{translate-document.yaml → translate-documents/translate-document.yaml} +1 -1
  16. package/agents/localize/translate-documents/translate-to-languages.yaml +10 -0
  17. package/agents/localize/translate-images/check-image-translation.mjs +222 -0
  18. package/agents/localize/translate-images/detect-text/detect-and-update-shared.mjs +147 -0
  19. package/agents/localize/translate-images/detect-text/detect-image-text.yaml +44 -0
  20. package/agents/localize/translate-images/detect-text/detect-images-text.yaml +21 -0
  21. package/agents/localize/translate-images/detect-text/prompts/detect-image-text-system.md +43 -0
  22. package/agents/localize/translate-images/detect-text/prompts/detect-image-text-user.md +14 -0
  23. package/agents/localize/translate-images/detect-text/save-text-detection.mjs +105 -0
  24. package/agents/localize/translate-images/prepare-image-input.mjs +124 -0
  25. package/agents/localize/translate-images/save-image-translation.mjs +171 -0
  26. package/agents/localize/translate-images/scan-doc-images.mjs +164 -0
  27. package/agents/localize/translate-images/translate-doc-images.yaml +24 -0
  28. package/agents/localize/translate-images/translate-image.yaml +73 -0
  29. package/agents/publish/check.mjs +79 -0
  30. package/agents/publish/index.yaml +12 -4
  31. package/agents/publish/publish-docs.mjs +25 -32
  32. package/agents/publish/translate-meta.mjs +169 -0
  33. package/agents/{utils/save-document.mjs → save-document/index.mjs} +5 -1
  34. package/agents/{document-checker → structure-checker}/validate-structure.mjs +1 -3
  35. package/agents/update-image/analyze-feedback.yaml +37 -0
  36. package/agents/update-image/index.yaml +78 -0
  37. package/agents/update-image/load-existing-image.mjs +208 -0
  38. package/agents/update-image/prompts/analyze-feedback-system.md +43 -0
  39. package/agents/update-image/prompts/analyze-feedback-user.md +15 -0
  40. package/aigne.yaml +5 -2
  41. package/doc-smith/SKILL.md +17 -3
  42. package/doc-smith/references/document-content-guide.md +70 -5
  43. package/doc-smith/references/workspace-initialization.md +11 -17
  44. package/doc-smith.yaml +6 -4
  45. package/feature-design/afs-save-file.md +537 -0
  46. package/feature-design/image-generation.md +566 -0
  47. package/package.json +1 -1
  48. package/utils/agent-constants.mjs +12 -0
  49. package/utils/config.mjs +26 -0
  50. package/utils/constants.mjs +0 -6
  51. package/utils/docs-converter.mjs +98 -2
  52. package/utils/docs.mjs +0 -29
  53. package/utils/document-paths.mjs +1 -4
  54. package/utils/files.mjs +2 -2
  55. package/utils/image-slots.mjs +53 -0
  56. package/utils/image-utils.mjs +114 -0
  57. package/utils/project.mjs +19 -0
  58. package/agents/localize/prepare-doc-content.mjs +0 -138
  59. package/agents/localize/translate-to-languages.yaml +0 -11
  60. package/agents/publish/check-docs.mjs +0 -42
  61. package/agents/publish/init-config.mjs +0 -123
  62. /package/agents/bash-executor/{bash-executor.mjs → index.mjs} +0 -0
  63. /package/agents/{document-checker/content-checker.mjs → content-checker/index.mjs} +0 -0
  64. /package/agents/{document-checker → content-checker}/validate-content.mjs +0 -0
  65. /package/{prompts/translate → agents/localize/prompts}/glossary.md +0 -0
  66. /package/{prompts/translate → agents/localize/prompts}/translate-document.md +0 -0
  67. /package/agents/localize/{generate-summary.mjs → translate-documents/generate-summary.mjs} +0 -0
  68. /package/agents/{document-checker/structure-checker.mjs → structure-checker/index.mjs} +0 -0
@@ -0,0 +1,75 @@
1
+ type: image
2
+ name: generateSlotDiagramImage
3
+ image_model:
4
+ model: google/gemini-3-pro-image-preview
5
+ imageConfig:
6
+ imageSize:
7
+ $get: size
8
+ aspectRatio:
9
+ $get: aspectRatio
10
+
11
+ instructions:
12
+ - role: system
13
+ url: ./prompts/system.md
14
+ - role: user
15
+ url: ./prompts/user.md
16
+
17
+ # Support image-to-image generation by passing existing image via input_file_key
18
+ # If existingImage is provided (array of mediaFile objects), it will be passed to the model
19
+ input_file_key: existingImage
20
+
21
+ input_schema:
22
+ type: object
23
+ properties:
24
+ documentContent:
25
+ type: string
26
+ description: The full document content for context
27
+ desc:
28
+ type: string
29
+ description: The AFS slot description that describes what should be illustrated
30
+ locale:
31
+ type: string
32
+ description: Language for diagram labels
33
+ default: zh
34
+ size:
35
+ type: string
36
+ description: Size of the generated image
37
+ default: "2K"
38
+ aspectRatio:
39
+ type: string
40
+ description: Aspect ratio of the generated image
41
+ enum: ["1:1", "5:4", "4:3", "3:2", "16:9", "21:9"]
42
+ default: "4:3"
43
+ existingImage:
44
+ type: array
45
+ nullable: true
46
+ description: Array of mediaFile objects for existing diagram image (for image-to-image generation)
47
+ items:
48
+ type: object
49
+ properties:
50
+ type:
51
+ type: string
52
+ description: File type, should be "local"
53
+ path:
54
+ type: string
55
+ description: Absolute path to the image file
56
+ filename:
57
+ type: string
58
+ description: Image filename
59
+ mimeType:
60
+ type: string
61
+ description: MIME type of the image (e.g., "image/png", "image/jpeg")
62
+ useImageToImage:
63
+ type: boolean
64
+ description: Whether to use image-to-image generation mode
65
+ default: false
66
+ feedback:
67
+ type: string
68
+ description: User feedback for image editing (used in image-to-image mode to guide modifications)
69
+ default: ""
70
+ required:
71
+ - documentContent
72
+ - desc
73
+ - aspectRatio
74
+
75
+ include_input_in_output: true
@@ -0,0 +1,213 @@
1
+ /**
2
+ * 生成图片生成任务的最终总结报告
3
+ * @param {Object} input - 输入参数
4
+ * @param {string} input.locale - 主语言
5
+ * @param {Array} input.generationTasks - 生成任务列表(来自 prepare-generation)
6
+ * @param {Array} input.processAllSlots - 执行结果列表(来自 team 的 iterate)
7
+ * @param {number} input.newTasks - 新增任务数量
8
+ * @param {number} input.updateTasks - 更新任务数量
9
+ * @param {number} input.skippedTasks - 跳过的任务数量
10
+ * @returns {Object} - 包含格式化消息和统计数据的对象
11
+ */
12
+ export default function generateSummary(input) {
13
+ const { locale, generationTasks, processAllSlots, newTasks, updateTasks, skippedTasks } = input;
14
+
15
+ // 如果没有任务
16
+ if (!generationTasks || generationTasks.length === 0) {
17
+ return {
18
+ message: `⏭️ 没有需要生成的图片 slot`,
19
+ summary: {
20
+ locale,
21
+ totalTasks: 0,
22
+ newImages: 0,
23
+ updatedImages: 0,
24
+ skippedImages: skippedTasks || 0,
25
+ successTasks: 0,
26
+ failedTasks: 0,
27
+ generatedImages: [],
28
+ },
29
+ };
30
+ }
31
+
32
+ // 统计成功和失败的任务
33
+ const results = processAllSlots || [];
34
+ const successTasks = results.filter((r) => r && r.success);
35
+ const failedTasks = results.filter((r) => r && !r.success);
36
+
37
+ const successCount = successTasks.length;
38
+ const failedCount = failedTasks.length;
39
+
40
+ // 生成成功的图片路径列表(最多显示10个)
41
+ const successPaths = successTasks.map((r) => r.imagePath).filter(Boolean);
42
+ const displayPaths =
43
+ successPaths.length > 10
44
+ ? [...successPaths.slice(0, 10), `... 还有 ${successPaths.length - 10} 个图片`]
45
+ : successPaths;
46
+
47
+ // 生成失败的任务列表
48
+ const failedKeys = failedTasks.map((r) => ({
49
+ key: r.key,
50
+ error: r.message || r.error || "未知错误",
51
+ }));
52
+
53
+ // 生成格式化的消息
54
+ let message = `
55
+ ✅ 图片生成任务已完成
56
+
57
+ 📊 **生成统计**:
58
+ - 主语言:${locale}
59
+ - 总任务数:${generationTasks.length}
60
+ - 新增图片:${newTasks || 0}
61
+ - 更新图片:${updateTasks || 0}
62
+ - 跳过图片:${skippedTasks || 0}
63
+ - 成功:${successCount}
64
+ - 失败:${failedCount}
65
+ `;
66
+
67
+ if (successPaths.length > 0) {
68
+ message += `
69
+ 📷 **生成的图片**:
70
+ ${displayPaths.map((path) => ` - ${path}`).join("\n")}
71
+ `;
72
+ }
73
+
74
+ if (failedKeys.length > 0) {
75
+ message += `
76
+ ❌ **失败的任务**:
77
+ ${failedKeys.map((f) => ` - ${f.key}: ${f.error}`).join("\n")}
78
+ `;
79
+ }
80
+
81
+ message += `
82
+ 💡 **提示**:
83
+ - 图片已保存到 assets/{key}/images/${locale}.png
84
+ - 元信息已保存到 assets/{key}/.meta.yaml
85
+ `;
86
+
87
+ return {
88
+ message: message.trim(),
89
+ summary: {
90
+ locale,
91
+ totalTasks: generationTasks.length,
92
+ newImages: newTasks || 0,
93
+ updatedImages: updateTasks || 0,
94
+ skippedImages: skippedTasks || 0,
95
+ successTasks: successCount,
96
+ failedTasks: failedCount,
97
+ generatedImages: successPaths,
98
+ failedKeys,
99
+ },
100
+ };
101
+ }
102
+
103
+ // 添加描述信息
104
+ generateSummary.description =
105
+ "生成图片生成任务的最终总结报告。" +
106
+ "汇总生成统计数据(主语言、新增/更新/跳过数量、成功/失败任务等),生成易读的格式化消息。" +
107
+ "列出生成的图片路径和失败的任务信息。";
108
+
109
+ // 定义输入 schema
110
+ generateSummary.input_schema = {
111
+ type: "object",
112
+ properties: {
113
+ locale: {
114
+ type: "string",
115
+ description: "主语言代码",
116
+ },
117
+ generationTasks: {
118
+ type: "array",
119
+ description: "生成任务列表",
120
+ items: {
121
+ type: "object",
122
+ },
123
+ },
124
+ processAllSlots: {
125
+ type: "array",
126
+ description: "执行结果列表",
127
+ items: {
128
+ type: "object",
129
+ properties: {
130
+ success: { type: "boolean" },
131
+ key: { type: "string" },
132
+ imagePath: { type: "string" },
133
+ message: { type: "string" },
134
+ error: { type: "string" },
135
+ },
136
+ },
137
+ },
138
+ newTasks: {
139
+ type: "number",
140
+ description: "新增任务数量",
141
+ },
142
+ updateTasks: {
143
+ type: "number",
144
+ description: "更新任务数量",
145
+ },
146
+ skippedTasks: {
147
+ type: "number",
148
+ description: "跳过的任务数量",
149
+ },
150
+ },
151
+ };
152
+
153
+ // 定义输出 schema
154
+ generateSummary.output_schema = {
155
+ type: "object",
156
+ required: ["message", "summary"],
157
+ properties: {
158
+ message: {
159
+ type: "string",
160
+ description: "格式化的总结消息,包含生成统计和提示信息",
161
+ },
162
+ summary: {
163
+ type: "object",
164
+ description: "结构化的统计数据",
165
+ properties: {
166
+ locale: {
167
+ type: "string",
168
+ description: "主语言代码",
169
+ },
170
+ totalTasks: {
171
+ type: "number",
172
+ description: "总任务数",
173
+ },
174
+ newImages: {
175
+ type: "number",
176
+ description: "新增图片数量",
177
+ },
178
+ updatedImages: {
179
+ type: "number",
180
+ description: "更新图片数量",
181
+ },
182
+ skippedImages: {
183
+ type: "number",
184
+ description: "跳过图片数量",
185
+ },
186
+ successTasks: {
187
+ type: "number",
188
+ description: "成功任务数量",
189
+ },
190
+ failedTasks: {
191
+ type: "number",
192
+ description: "失败任务数量",
193
+ },
194
+ generatedImages: {
195
+ type: "array",
196
+ items: { type: "string" },
197
+ description: "生成的图片路径列表",
198
+ },
199
+ failedKeys: {
200
+ type: "array",
201
+ items: {
202
+ type: "object",
203
+ properties: {
204
+ key: { type: "string" },
205
+ error: { type: "string" },
206
+ },
207
+ },
208
+ description: "失败的任务列表",
209
+ },
210
+ },
211
+ },
212
+ },
213
+ };
@@ -0,0 +1,39 @@
1
+ type: team
2
+ name: generateImages
3
+ alias:
4
+ - gen-images
5
+ description: |
6
+ 扫描主语言文档中的 AFS image slot 并批量生成主语言图片。支持以下功能:
7
+ - 扫描所有文档中的 slot 并生成图片
8
+ - 扫描指定文档中的 slot 并生成图片
9
+ - 支持强制重新生成已有图片
10
+
11
+ skills:
12
+ - url: ./scan-image-slots.mjs # 扫描主语言文档中的 slot
13
+ - url: ./prepare-generation.mjs # 准备生图任务(去重、检查已有图片)
14
+ - type: team
15
+ name: processAllSlots # 批量生成图片
16
+ skills:
17
+ - url: ./prepare-image-generation.mjs
18
+ - url: ./generate-image.yaml
19
+ - url: ./save-image-result.mjs
20
+ iterate_on: generationTasks
21
+ concurrency: 1 # 并发生成
22
+ - url: ./generate-summary.mjs # 生成总结报告
23
+
24
+ input_schema:
25
+ type: object
26
+ properties:
27
+ docs:
28
+ type: array
29
+ items:
30
+ type: string
31
+ description: |
32
+ 要处理的文档路径列表(可选),必须是文档结构 yaml 中的 path 字段。如果不传或为空数组,则处理所有文档。
33
+ force:
34
+ type: boolean
35
+ default: false
36
+ description: |
37
+ 是否强制重新生成已有图片(默认只生成缺失的图片)
38
+
39
+ mode: sequential
@@ -0,0 +1,283 @@
1
+ import { readFile, access, stat } from "node:fs/promises";
2
+ import { constants } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { parse as yamlParse } from "yaml";
5
+ import { PATHS } from "../../utils/agent-constants.mjs";
6
+
7
+ /**
8
+ * 检查图片目录是否存在
9
+ * @param {string} key - 图片 key
10
+ * @returns {Promise<boolean>}
11
+ */
12
+ async function imageDirectoryExists(key) {
13
+ const dirPath = join(PATHS.ASSETS_DIR, key);
14
+ try {
15
+ const stats = await stat(dirPath);
16
+ return stats.isDirectory();
17
+ } catch (_error) {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * 读取图片的 meta 信息
24
+ * @param {string} key - 图片 key
25
+ * @returns {Promise<Object|null>} - meta 信息或 null(文件不存在)
26
+ */
27
+ async function readImageMeta(key) {
28
+ const metaPath = join(PATHS.ASSETS_DIR, key, ".meta.yaml");
29
+
30
+ try {
31
+ await access(metaPath, constants.F_OK | constants.R_OK);
32
+ const content = await readFile(metaPath, "utf8");
33
+ return yamlParse(content);
34
+ } catch (_error) {
35
+ return null;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * 检查图片文件是否存在
41
+ * @param {string} key - 图片 key
42
+ * @param {string} locale - 语言代码
43
+ * @returns {Promise<string|null>} - 图片路径或 null(不存在)
44
+ */
45
+ async function getExistingImagePath(key, locale) {
46
+ const imagePath = join(PATHS.ASSETS_DIR, key, "images", `${locale}.png`);
47
+
48
+ try {
49
+ await access(imagePath, constants.F_OK | constants.R_OK);
50
+ return imagePath;
51
+ } catch (_error) {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 判断文档 hash 是否发生变化
58
+ * @param {Array} currentDocs - 当前文档列表
59
+ * @param {Array} metaDocs - meta 中记录的文档列表
60
+ * @returns {boolean} - 是否有变化
61
+ */
62
+ function hasDocumentChanges(currentDocs, metaDocs) {
63
+ if (!metaDocs || metaDocs.length === 0) {
64
+ return true;
65
+ }
66
+
67
+ // 将 meta 文档转换为 map(path -> hash)
68
+ const metaHashMap = new Map(metaDocs.map((doc) => [doc.path, doc.hash]));
69
+
70
+ // 检查每个当前文档的 hash
71
+ for (const doc of currentDocs) {
72
+ const metaHash = metaHashMap.get(doc.path);
73
+ if (!metaHash || metaHash !== doc.hash) {
74
+ return true; // hash 不同或新文档
75
+ }
76
+ }
77
+
78
+ return false;
79
+ }
80
+
81
+ /**
82
+ * 准备生图任务
83
+ * @param {Object} input - 输入参数
84
+ * @param {string} input.locale - 主语言
85
+ * @param {Array} input.slots - 扫描到的 slot 列表
86
+ * @param {boolean} input.force - 是否强制重新生成
87
+ * @returns {Promise<Object>} - 任务列表
88
+ */
89
+ export default async function prepareGeneration(input) {
90
+ try {
91
+ const { locale, slots, force = false } = input;
92
+
93
+ if (!locale) {
94
+ throw new Error("缺少主语言参数, 请检查 doc-smith 工作目录是否已初始化,且文件已生成!");
95
+ }
96
+
97
+ if (!slots || slots.length === 0) {
98
+ return {
99
+ success: true,
100
+ locale,
101
+ generationTasks: [],
102
+ message: "没有找到需要生成的图片 slot",
103
+ };
104
+ }
105
+
106
+ const generationTasks = [];
107
+ let skippedCount = 0;
108
+
109
+ // 检查每个 slot
110
+ for (const slot of slots) {
111
+ const { key, id, desc, documents } = slot;
112
+
113
+ // 检查图片目录是否存在
114
+ const dirExists = await imageDirectoryExists(key);
115
+
116
+ let needsGeneration = false;
117
+ let isUpdate = false;
118
+ let existingImagePath = null;
119
+
120
+ if (force) {
121
+ // 强制重新生成
122
+ needsGeneration = true;
123
+ isUpdate = dirExists;
124
+ if (isUpdate) {
125
+ existingImagePath = await getExistingImagePath(key, locale);
126
+ }
127
+ } else if (!dirExists) {
128
+ // 图片目录不存在,需要生成
129
+ needsGeneration = true;
130
+ isUpdate = false;
131
+ } else {
132
+ // 图片目录存在,检查 hash 是否变化
133
+ const meta = await readImageMeta(key);
134
+
135
+ if (!meta) {
136
+ // meta 文件不存在,重新生成
137
+ needsGeneration = true;
138
+ isUpdate = false;
139
+ } else {
140
+ const hasChanges = hasDocumentChanges(documents, meta.documents);
141
+
142
+ if (hasChanges) {
143
+ // 文档有变化,更新图片
144
+ needsGeneration = true;
145
+ isUpdate = true;
146
+ existingImagePath = await getExistingImagePath(key, locale);
147
+ } else {
148
+ // 没有变化,跳过
149
+ needsGeneration = false;
150
+ skippedCount++;
151
+ }
152
+ }
153
+ }
154
+
155
+ if (needsGeneration) {
156
+ generationTasks.push({
157
+ key,
158
+ id,
159
+ desc,
160
+ documents,
161
+ isUpdate,
162
+ existingImagePath,
163
+ });
164
+ }
165
+ }
166
+
167
+ return {
168
+ success: true,
169
+ locale,
170
+ generationTasks,
171
+ totalSlots: slots.length,
172
+ newTasks: generationTasks.filter((t) => !t.isUpdate).length,
173
+ updateTasks: generationTasks.filter((t) => t.isUpdate).length,
174
+ skippedTasks: skippedCount,
175
+ message: `准备生成 ${generationTasks.length} 个图片(新增 ${generationTasks.filter((t) => !t.isUpdate).length},更新 ${generationTasks.filter((t) => t.isUpdate).length},跳过 ${skippedCount})`,
176
+ };
177
+ } catch (error) {
178
+ throw new Error(`准备生图任务时发生错误: ${error.message}`);
179
+ }
180
+ }
181
+
182
+ // 添加描述信息
183
+ prepareGeneration.description =
184
+ "检查已有图片目录和 meta 信息,对比文档 hash," + "判断哪些图片需要生成或更新,生成任务列表。";
185
+
186
+ // 定义输入 schema
187
+ prepareGeneration.input_schema = {
188
+ type: "object",
189
+ properties: {
190
+ locale: {
191
+ type: "string",
192
+ description: "主语言代码",
193
+ },
194
+ slots: {
195
+ type: "array",
196
+ description: "扫描到的 slot 列表",
197
+ items: {
198
+ type: "object",
199
+ properties: {
200
+ key: { type: "string" },
201
+ id: { type: "string" },
202
+ desc: { type: "string" },
203
+ documents: {
204
+ type: "array",
205
+ items: {
206
+ type: "object",
207
+ properties: {
208
+ path: { type: "string" },
209
+ hash: { type: "string" },
210
+ content: { type: "string" },
211
+ },
212
+ },
213
+ },
214
+ },
215
+ },
216
+ },
217
+ force: {
218
+ type: "boolean",
219
+ description: "是否强制重新生成所有图片",
220
+ default: false,
221
+ },
222
+ },
223
+ required: ["locale", "slots"],
224
+ };
225
+
226
+ // 定义输出 schema
227
+ prepareGeneration.output_schema = {
228
+ type: "object",
229
+ required: ["success"],
230
+ properties: {
231
+ success: {
232
+ type: "boolean",
233
+ description: "操作是否成功",
234
+ },
235
+ locale: {
236
+ type: "string",
237
+ description: "主语言代码",
238
+ },
239
+ generationTasks: {
240
+ type: "array",
241
+ description: "需要生成的任务列表",
242
+ items: {
243
+ type: "object",
244
+ properties: {
245
+ key: { type: "string" },
246
+ id: { type: "string" },
247
+ desc: { type: "string" },
248
+ documents: { type: "array" },
249
+ isUpdate: { type: "boolean" },
250
+ existingImagePath: { type: "string", nullable: true },
251
+ },
252
+ },
253
+ },
254
+ totalSlots: {
255
+ type: "number",
256
+ description: "总 slot 数量",
257
+ },
258
+ newTasks: {
259
+ type: "number",
260
+ description: "新增任务数量",
261
+ },
262
+ updateTasks: {
263
+ type: "number",
264
+ description: "更新任务数量",
265
+ },
266
+ skippedTasks: {
267
+ type: "number",
268
+ description: "跳过的任务数量",
269
+ },
270
+ message: {
271
+ type: "string",
272
+ description: "操作结果描述",
273
+ },
274
+ error: {
275
+ type: "string",
276
+ description: "错误代码(失败时存在)",
277
+ },
278
+ suggestion: {
279
+ type: "string",
280
+ description: "建议操作(失败时存在)",
281
+ },
282
+ },
283
+ };