@dai_ming/plugin-deliverables 1.0.22 → 1.1.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/INSTALL.md +12 -200
- package/README.md +22 -227
- package/agents-rules/deliverables.md +30 -26
- package/index.js +248 -2
- package/openclaw-plugin.json +2 -14
- package/openclaw.plugin.json +1 -1
- package/package.json +5 -8
- package/skills/deliverables/SKILL.md +7 -5
- package/mcp-servers/deliverables.js +0 -515
- package/test/index.test.js +0 -114
- package/test/mcp-server.test.js +0 -60
package/index.js
CHANGED
|
@@ -53,6 +53,7 @@ const TEXT_EXTENSIONS = new Set([
|
|
|
53
53
|
const IMAGE_EXTENSIONS = new Set([".apng", ".avif", ".bmp", ".gif", ".ico", ".jpeg", ".jpg", ".png", ".svg", ".webp"]);
|
|
54
54
|
const VIDEO_EXTENSIONS = new Set([".avi", ".m4v", ".mkv", ".mov", ".mp4", ".mpeg", ".mpg", ".webm"]);
|
|
55
55
|
const PPT_EXTENSIONS = new Set([".ppt", ".pptx"]);
|
|
56
|
+
const PDF_EXTENSIONS = new Set([".pdf"]);
|
|
56
57
|
const ARCHIVE_EXTENSIONS = new Set([".7z", ".gz", ".rar", ".tar", ".tgz", ".zip"]);
|
|
57
58
|
const IGNORED_PATH_PARTS = new Set([
|
|
58
59
|
".git",
|
|
@@ -76,15 +77,77 @@ const RUNTIME_DELIVERABLES_GUIDANCE = [
|
|
|
76
77
|
"## Deliverables Runtime Guard (HARD CONSTRAINT)",
|
|
77
78
|
"",
|
|
78
79
|
"- These rules apply to the main agent and all subagents.",
|
|
79
|
-
"- When the user asks for a document, article, report, HTML page, Markdown file, PPT, image, archive, game, or any other file deliverable, you MUST create it under the current workspace `output/` directory and upload it with `deliverables__upload_deliverable`.",
|
|
80
|
+
"- When the user asks for a document, article, report, PDF, HTML page, Markdown file, PPT, image, archive, game, or any other file deliverable, you MUST create it under the current workspace `output/` directory and upload it with `deliverables__upload_deliverable`.",
|
|
80
81
|
"- For binary deliverables such as PDF, PPT, images, video, or zip files, pass `file_path` to `deliverables__upload_deliverable` after writing the file under `output/`; do not paste command output or partial base64 into `content_base64`.",
|
|
82
|
+
"- If the user asks for PDF, upload the generated `.pdf` as `type: \"pdf\"`; do not substitute HTML or Markdown unless PDF generation truly failed and you clearly say so.",
|
|
81
83
|
"- Every single-file deliverable must use a file name with an explicit extension. If the user did not specify a document/text format, default to Markdown and use a `.md` file name.",
|
|
82
84
|
"- Do not use direct message attachments to deliver generated files. This includes the message tool attachment fields such as `media`, `path`, `filePath`, `buffer`, `attachment`, or similar file-carrying fields.",
|
|
83
85
|
"- Do not emit `MEDIA:` file references for user-facing deliverables. Deliverable links must come from the deliverables upload tool instead.",
|
|
84
86
|
"- If the deliverables upload tool returns an error, retry with a valid `file_path` or explain the upload failure; never invent or rewrite OSS/download URLs.",
|
|
85
|
-
"- After a successful upload, reply with a substantive content summary before the links. For documents/articles/reports,
|
|
87
|
+
"- After a successful upload, reply with a substantive content summary before the links. For documents/articles/reports/PDFs, include 1 short intro plus 3-5 concise bullets covering the actual sections, highlights, or findings; never respond with only a generic one-sentence upload notice.",
|
|
86
88
|
].join("\n");
|
|
87
89
|
|
|
90
|
+
const UPLOAD_DELIVERABLE_TOOL = {
|
|
91
|
+
name: "deliverables__upload_deliverable",
|
|
92
|
+
description: [
|
|
93
|
+
"将 AI 生成的内容或 output/ 下的文件上传为交付物,返回可分享的下载/预览链接。",
|
|
94
|
+
"二进制文件优先传 file_path;文本单文件可传 content_text;多文件静态站点/游戏传 files。",
|
|
95
|
+
"返回的 reply_markdown 可直接回显给用户。",
|
|
96
|
+
].join(" "),
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: "object",
|
|
99
|
+
properties: {
|
|
100
|
+
resource_id: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "当前聊天框/会话的唯一 ID(必填)。",
|
|
103
|
+
},
|
|
104
|
+
group_id: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "当前会话所属的群聊 ID(可选)。",
|
|
107
|
+
},
|
|
108
|
+
user_id: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "请求交付物的用户 ID(可选)。",
|
|
111
|
+
},
|
|
112
|
+
type: {
|
|
113
|
+
type: "string",
|
|
114
|
+
enum: ["article", "game", "zip", "image", "video", "ppt", "pdf", "link"],
|
|
115
|
+
description: "交付物类型。若使用 file_path 且未准确判断,可由插件按文件扩展名兜底。",
|
|
116
|
+
},
|
|
117
|
+
file_name: {
|
|
118
|
+
type: "string",
|
|
119
|
+
description: "用户可见文件名。单文件必须带扩展名;未指定文档格式时默认 .md。",
|
|
120
|
+
},
|
|
121
|
+
file_path: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "output/ 下已生成文件或目录的路径。二进制交付物优先使用此字段。",
|
|
124
|
+
},
|
|
125
|
+
content_text: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "文本内容(Markdown、HTML 等)。",
|
|
128
|
+
},
|
|
129
|
+
content_base64: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "Base64 编码的二进制内容。",
|
|
132
|
+
},
|
|
133
|
+
files: {
|
|
134
|
+
type: "array",
|
|
135
|
+
description: "多文件列表。每项包含 name 以及 content_text 或 content_base64。",
|
|
136
|
+
items: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
name: { type: "string", description: "相对路径,例如 index.html 或 assets/main.js" },
|
|
140
|
+
content_text: { type: "string", description: "文本内容" },
|
|
141
|
+
content_base64: { type: "string", description: "Base64 二进制内容" },
|
|
142
|
+
},
|
|
143
|
+
required: ["name"],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
required: ["resource_id", "type", "file_name"],
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
88
151
|
function isString(value) {
|
|
89
152
|
return typeof value === "string";
|
|
90
153
|
}
|
|
@@ -154,6 +217,9 @@ function deliverableTypeForPath(filePath, isDirectory) {
|
|
|
154
217
|
if (PPT_EXTENSIONS.has(ext)) {
|
|
155
218
|
return "ppt";
|
|
156
219
|
}
|
|
220
|
+
if (PDF_EXTENSIONS.has(ext)) {
|
|
221
|
+
return "pdf";
|
|
222
|
+
}
|
|
157
223
|
if (ARCHIVE_EXTENSIONS.has(ext)) {
|
|
158
224
|
return "zip";
|
|
159
225
|
}
|
|
@@ -565,6 +631,174 @@ function httpJSONRequest(method, requestPath, body) {
|
|
|
565
631
|
});
|
|
566
632
|
}
|
|
567
633
|
|
|
634
|
+
function looksLikeHTMLContent(contentText) {
|
|
635
|
+
const text = trimString(contentText).toLowerCase();
|
|
636
|
+
if (!text) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
if (text.indexOf("<!doctype html") === 0 || text.indexOf("<html") === 0) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
return (
|
|
643
|
+
text.indexOf("<head") >= 0 ||
|
|
644
|
+
text.indexOf("<body") >= 0 ||
|
|
645
|
+
text.indexOf("<main") >= 0 ||
|
|
646
|
+
text.indexOf("<section") >= 0 ||
|
|
647
|
+
text.indexOf("<article") >= 0 ||
|
|
648
|
+
text.indexOf("<style") >= 0 ||
|
|
649
|
+
text.indexOf("<script") >= 0
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function defaultExtensionForDeliverable(args) {
|
|
654
|
+
const deliverableType = trimString(args && args.type);
|
|
655
|
+
switch (deliverableType) {
|
|
656
|
+
case "article":
|
|
657
|
+
case "game":
|
|
658
|
+
return looksLikeHTMLContent(args && args.content_text) ? ".html" : ".md";
|
|
659
|
+
case "zip":
|
|
660
|
+
return ".zip";
|
|
661
|
+
case "ppt":
|
|
662
|
+
return ".pptx";
|
|
663
|
+
case "pdf":
|
|
664
|
+
return ".pdf";
|
|
665
|
+
case "image":
|
|
666
|
+
return ".png";
|
|
667
|
+
case "video":
|
|
668
|
+
return ".mp4";
|
|
669
|
+
default:
|
|
670
|
+
return "";
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function normalizeDeliverableFileName(args, fallbackName) {
|
|
675
|
+
let fileName = trimString(args && args.file_name) || trimString(fallbackName) || "file";
|
|
676
|
+
if (trimString(args && args.type) === "link") {
|
|
677
|
+
return fileName;
|
|
678
|
+
}
|
|
679
|
+
if (Array.isArray(args && args.files) && args.files.length > 0) {
|
|
680
|
+
return fileName;
|
|
681
|
+
}
|
|
682
|
+
if (trimString(args && args.file_path)) {
|
|
683
|
+
return fileName;
|
|
684
|
+
}
|
|
685
|
+
if (hasFileExtension(fileName)) {
|
|
686
|
+
return fileName;
|
|
687
|
+
}
|
|
688
|
+
const ext = defaultExtensionForDeliverable(args);
|
|
689
|
+
return ext ? fileName + ext : fileName;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function buildReplyMarkdown(opts) {
|
|
693
|
+
const previewURL = opts.previewURL || "";
|
|
694
|
+
const downloadURL = opts.downloadURL || "";
|
|
695
|
+
const deliverableType = opts.type || "";
|
|
696
|
+
const isDirectory = !!opts.isDirectory;
|
|
697
|
+
const lines = [];
|
|
698
|
+
if (previewURL) {
|
|
699
|
+
lines.push(`预览链接:[点击预览](${previewURL})`);
|
|
700
|
+
}
|
|
701
|
+
if (downloadURL) {
|
|
702
|
+
if (isDirectory || deliverableType === "game") {
|
|
703
|
+
lines.push(`文件列表:[查看目录](${downloadURL})`);
|
|
704
|
+
} else {
|
|
705
|
+
lines.push(`下载链接:[点击下载](${downloadURL})`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
if (lines.length === 0) {
|
|
709
|
+
return "交付物已上传成功。";
|
|
710
|
+
}
|
|
711
|
+
return lines.join("\n");
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function normalizeNativeToolFiles(files) {
|
|
715
|
+
if (!Array.isArray(files)) {
|
|
716
|
+
return [];
|
|
717
|
+
}
|
|
718
|
+
return files.map((file) => ({
|
|
719
|
+
name: file.name,
|
|
720
|
+
contentText: file.contentText || file.content_text || "",
|
|
721
|
+
contentBase64: file.contentBase64 || file.content_base64 || "",
|
|
722
|
+
}));
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function buildNativeUploadRequestBody(args) {
|
|
726
|
+
const rawFilePath = trimString(args && args.file_path);
|
|
727
|
+
let fileCandidate = null;
|
|
728
|
+
if (rawFilePath) {
|
|
729
|
+
fileCandidate = resolveFileReference({ raw: rawFilePath, value: rawFilePath, kind: "tool" });
|
|
730
|
+
if (!fileCandidate) {
|
|
731
|
+
throw new Error(`file_path is not readable or not under an OpenClaw workspace: ${rawFilePath}`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const body = {
|
|
736
|
+
resourceId: args.resource_id,
|
|
737
|
+
groupId: args.group_id,
|
|
738
|
+
userId: args.user_id,
|
|
739
|
+
release: deriveReleaseName(),
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
if (fileCandidate) {
|
|
743
|
+
const stat = safeStat(fileCandidate.path);
|
|
744
|
+
const isDirectory = !!(stat && stat.isDirectory());
|
|
745
|
+
body.type = trimString(args.type) || deliverableTypeForPath(fileCandidate.path, isDirectory);
|
|
746
|
+
body.fileName = normalizeDeliverableFileName(args, fileCandidate.fileName);
|
|
747
|
+
if (isDirectory) {
|
|
748
|
+
const files = collectDirectoryFiles(fileCandidate.path);
|
|
749
|
+
if (files.length === 0) {
|
|
750
|
+
throw new Error(`directory has no uploadable files: ${fileCandidate.fileName}`);
|
|
751
|
+
}
|
|
752
|
+
body.files = files;
|
|
753
|
+
} else if (isTextLikeFile(fileCandidate.path)) {
|
|
754
|
+
body.contentText = fs.readFileSync(fileCandidate.path, "utf8");
|
|
755
|
+
} else {
|
|
756
|
+
body.contentBase64 = fs.readFileSync(fileCandidate.path).toString("base64");
|
|
757
|
+
}
|
|
758
|
+
return { body, isDirectory };
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
body.type = trimString(args && args.type);
|
|
762
|
+
body.fileName = normalizeDeliverableFileName(args, "");
|
|
763
|
+
const files = normalizeNativeToolFiles(args && args.files);
|
|
764
|
+
if (files.length > 0) {
|
|
765
|
+
body.files = files;
|
|
766
|
+
return { body, isDirectory: true };
|
|
767
|
+
}
|
|
768
|
+
body.contentText = (args && (args.content_text || args.contentText)) || "";
|
|
769
|
+
body.contentBase64 = (args && (args.content_base64 || args.contentBase64)) || "";
|
|
770
|
+
return { body, isDirectory: false };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
async function uploadDeliverable(args) {
|
|
774
|
+
const { body, isDirectory } = buildNativeUploadRequestBody(args || {});
|
|
775
|
+
const response = await httpJSONRequest("POST", "/openclaw-gateway/be/deliverables", body);
|
|
776
|
+
const data = response.data || response;
|
|
777
|
+
const previewURL = data.previewUrl || `${gatewayPublicURL()}/openclaw-gateway/preview/${data.uuid}`;
|
|
778
|
+
const downloadURL = data.downloadUrl || previewURL;
|
|
779
|
+
const replyMarkdown = buildReplyMarkdown({
|
|
780
|
+
previewURL,
|
|
781
|
+
downloadURL,
|
|
782
|
+
type: body.type,
|
|
783
|
+
isDirectory,
|
|
784
|
+
});
|
|
785
|
+
return {
|
|
786
|
+
uuid: data.uuid,
|
|
787
|
+
backend: data.backend || "",
|
|
788
|
+
download_url: downloadURL,
|
|
789
|
+
preview_url: previewURL,
|
|
790
|
+
expire_at: data.expireAt,
|
|
791
|
+
reply_markdown: replyMarkdown,
|
|
792
|
+
message: replyMarkdown,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function nativeToolResult(result) {
|
|
797
|
+
return {
|
|
798
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
568
802
|
function uploadCacheKey(candidate, resourceId) {
|
|
569
803
|
return `${resourceId || ""}:${candidate.path}:${candidate.mtimeMs || 0}:${candidate.size || 0}`;
|
|
570
804
|
}
|
|
@@ -1505,6 +1739,18 @@ const plugin = {
|
|
|
1505
1739
|
register(api) {
|
|
1506
1740
|
installPalzFetchPatch(api);
|
|
1507
1741
|
|
|
1742
|
+
if (api && typeof api.registerTool === "function") {
|
|
1743
|
+
api.registerTool({
|
|
1744
|
+
name: UPLOAD_DELIVERABLE_TOOL.name,
|
|
1745
|
+
description: UPLOAD_DELIVERABLE_TOOL.description,
|
|
1746
|
+
parameters: UPLOAD_DELIVERABLE_TOOL.inputSchema,
|
|
1747
|
+
async execute(_callId, params) {
|
|
1748
|
+
cacheUploadSummary(params);
|
|
1749
|
+
return nativeToolResult(await uploadDeliverable(params || {}));
|
|
1750
|
+
},
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1508
1754
|
api.on("before_prompt_build", async () => ({
|
|
1509
1755
|
prependSystemContext: RUNTIME_DELIVERABLES_GUIDANCE,
|
|
1510
1756
|
}));
|
package/openclaw-plugin.json
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plugin-deliverables",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"npm_package": "@dai_ming/plugin-deliverables",
|
|
5
|
-
"description": "Deliverables plugin:
|
|
6
|
-
"mcp_servers": {
|
|
7
|
-
"deliverables": {
|
|
8
|
-
"script": "mcp-servers/deliverables.js",
|
|
9
|
-
"command": "node",
|
|
10
|
-
"env": {
|
|
11
|
-
"CLAW_GATEWAY_URL": "${CLAW_GATEWAY_URL}",
|
|
12
|
-
"CLAW_GATEWAY_PUBLIC_URL": "${CLAW_GATEWAY_PUBLIC_URL}",
|
|
13
|
-
"CLAW_GATEWAY_API_KEY": "${CLAW_GATEWAY_API_KEY}",
|
|
14
|
-
"OPENCLAW_GATEWAY_API_KEY": "${OPENCLAW_GATEWAY_API_KEY}"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
},
|
|
5
|
+
"description": "Deliverables plugin: native upload tool + skill + AGENTS rules for AI-generated file uploads",
|
|
18
6
|
"skills": {
|
|
19
7
|
"deliverables": "skills/deliverables/SKILL.md"
|
|
20
8
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "plugin-deliverables",
|
|
3
3
|
"name": "Deliverables",
|
|
4
4
|
"description": "Deliverables runtime guard for upload-first file delivery with Palz split-send diagnostics.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.1.1",
|
|
6
6
|
"skills": ["./skills"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dai_ming/plugin-deliverables",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "OpenClaw deliverables plugin — upload AI-generated files to OSS and return shareable preview/download links",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "OpenClaw deliverables native plugin — upload AI-generated files to OSS and return shareable preview/download links",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openclaw",
|
|
7
7
|
"plugin",
|
|
8
|
-
"deliverables"
|
|
9
|
-
"mcp"
|
|
8
|
+
"deliverables"
|
|
10
9
|
],
|
|
11
10
|
"license": "MIT",
|
|
12
11
|
"main": "index.js",
|
|
13
12
|
"scripts": {
|
|
14
|
-
"test": "node test/index.test.js
|
|
13
|
+
"test": "node test/index.test.js"
|
|
15
14
|
},
|
|
16
15
|
"files": [
|
|
17
16
|
"INSTALL.md",
|
|
18
17
|
"index.js",
|
|
19
18
|
"openclaw.plugin.json",
|
|
20
19
|
"openclaw-plugin.json",
|
|
21
|
-
"mcp-servers/",
|
|
22
20
|
"skills/",
|
|
23
|
-
"agents-rules/"
|
|
24
|
-
"test/"
|
|
21
|
+
"agents-rules/"
|
|
25
22
|
],
|
|
26
23
|
"openclaw": {
|
|
27
24
|
"extensions": [
|
|
@@ -5,7 +5,7 @@ description: 上传AI生成的文件到交付物系统,返回可分享的预
|
|
|
5
5
|
|
|
6
6
|
# 文件交付规则(强制)
|
|
7
7
|
|
|
8
|
-
当你生成了文件内容(文章、文档、介绍、攻略、HTML页面、Markdown、游戏、图片等),**必须**调用交付物上传工具,不得把文件内容直接粘贴在消息里。
|
|
8
|
+
当你生成了文件内容(文章、文档、介绍、攻略、PDF、HTML页面、Markdown、游戏、图片等),**必须**调用交付物上传工具,不得把文件内容直接粘贴在消息里。
|
|
9
9
|
|
|
10
10
|
## 工具名说明(强制)
|
|
11
11
|
|
|
@@ -31,7 +31,7 @@ description: 上传AI生成的文件到交付物系统,返回可分享的预
|
|
|
31
31
|
| `resource_id` | 消息元数据中的 `resource_id` 字段 | `user_xxx_lobster_yyy` |
|
|
32
32
|
| `group_id` | 消息元数据中的 `group_id` 或 `conversation_id` | `group_abc123` |
|
|
33
33
|
| `user_id` | 消息元数据中的 `sender_id` 或 `owner_id` | `cbb0fab9...` |
|
|
34
|
-
| `type` | 根据内容选择:`article`/`game`/`image`/`video`/`ppt`/`zip`/`link` | `article` |
|
|
34
|
+
| `type` | 根据内容选择:`article`/`game`/`image`/`video`/`ppt`/`pdf`/`zip`/`link` | `article` |
|
|
35
35
|
| `file_name` | 有意义的文件名,单文件必须含扩展名;若用户未指定文档格式,默认用 `.md` | `report-2026.html` |
|
|
36
36
|
| `content_text` | 文件的完整文本内容(HTML/Markdown等) | `<html>...</html>` |
|
|
37
37
|
| `file_path` | PDF/PPT/图片/zip 等二进制文件的本地路径,推荐使用,避免手工复制 base64 | `output/sample.pdf` |
|
|
@@ -42,6 +42,7 @@ description: 上传AI生成的文件到交付物系统,返回可分享的预
|
|
|
42
42
|
|
|
43
43
|
- PDF、PPT、图片、视频、zip 等二进制文件必须优先传 `file_path`,不要把 `base64` 命令输出复制到 `content_base64`。
|
|
44
44
|
- `file_path` 指向你已经写入 `output/` 的文件,例如 `output/sample.pdf`;上传工具会读取文件并自动编码。
|
|
45
|
+
- 用户要求 PDF 时,必须上传生成好的 `.pdf` 文件,`type` 使用 `pdf`;不要只返回 HTML/Markdown 作为替代格式。
|
|
45
46
|
- 如果上传工具返回错误,必须修正参数后重试,或明确告诉用户上传失败;禁止编造 OSS、下载或预览链接。
|
|
46
47
|
|
|
47
48
|
## 多文件(游戏)
|
|
@@ -64,9 +65,10 @@ files: [
|
|
|
64
65
|
## 上传成功后
|
|
65
66
|
|
|
66
67
|
优先规则:
|
|
67
|
-
- 最终消息必须先用你自己的话写 1-2
|
|
68
|
-
-
|
|
69
|
-
-
|
|
68
|
+
- 最终消息必须先用你自己的话写 1-2 句简短介绍,说明你生成了什么内容。
|
|
69
|
+
- 对文章、文档、报告、PDF 等内容型交付物,介绍后必须补充 3-5 条内容要点,概括实际章节、重点信息或结论;不要只写一句“已上传/已生成”。
|
|
70
|
+
- 介绍和要点必须基于实际产物内容,不要使用固定模板,例如:`交付物已上传成功,可直接在线预览或下载。`
|
|
71
|
+
- 如果工具结果里有 `reply_markdown`,把它原样放在内容要点后面,不要改写其中的链接。
|
|
70
72
|
|
|
71
73
|
否则只发给用户:
|
|
72
74
|
- 预览链接(`preview_url`,必须用 Markdown 链接格式)
|