This commit is contained in:
949036910@qq.com
2026-05-31 23:22:11 +08:00
parent 620b022af0
commit da8bccf3c1

View File

@@ -8,17 +8,14 @@ const desktopConfig = require("./desktop_config.js");
const KEYWORD_GROUPS = ["重点词/成语词", "描述词", "行动词", "情感词"]; const KEYWORD_GROUPS = ["重点词/成语词", "描述词", "行动词", "情感词"];
const DEFAULT_TITLE_TAG_PROMPT = `你是短视频标题与标签助手。分析以下文案内容,生成: const ELECTRON_DEFAULT_TITLE_TAG_PROMPT =
1. 一个最吸引人的标题不超过30字若需多个建议可写在 title 字段内用换行分隔) "你是短视频标题与标签助手。分析文案内容 {{content}}生成5个吸引眼球的标题建议、10个相关标签、4个分组关键词每组重点词/成语词、描述词、行动词、情感词每个分组只要0-2个词不要多。";
2. 8-10 个相关话题标签(带 # 号)
3. 四个分组关键词,每组 0-2 个词:重点词/成语词、描述词、行动词、情感词
文案内容: const JSON_OUTPUT_SUFFIX = `
{{content}}
请只返回 JSON不要 markdown格式如下 请只返回 JSON不要 markdown格式如下
{ {
"title": "标题", "title": "最推荐标题(多个标题建议可用换行分隔)",
"tags": ["#标签1", "#标签2"], "tags": ["#标签1", "#标签2"],
"keywords": { "keywords": {
"重点词/成语词": ["词1"], "重点词/成语词": ["词1"],
@@ -28,12 +25,28 @@ const DEFAULT_TITLE_TAG_PROMPT = `你是短视频标题与标签助手。分析
} }
}`; }`;
function logInfo(tag, payload) {
if (payload === undefined) {
console.info(tag);
return;
}
if (typeof payload === "string") {
console.info(`${tag} - ${payload}`);
return;
}
console.info(`${tag} - ${JSON.stringify(payload)}`);
}
function buildPrompt(template, content) { function buildPrompt(template, content) {
let prompt = String(template || "").trim(); let prompt = String(template || "").trim();
if (!prompt) prompt = DEFAULT_TITLE_TAG_PROMPT; if (!prompt) prompt = ELECTRON_DEFAULT_TITLE_TAG_PROMPT;
return prompt prompt = prompt
.replace(/\{\{content\}\}/g, content) .replace(/\{\{content\}\}/g, content)
.replace(/\{content\}/g, content); .replace(/\{content\}/g, content);
if (!/json|JSON/.test(prompt)) {
prompt += JSON_OUTPUT_SUFFIX;
}
return prompt;
} }
function cleanJsonText(raw) { function cleanJsonText(raw) {
@@ -76,6 +89,16 @@ function normalizeKeywordGroup(val) {
return ""; return "";
} }
function normalizeTitle(val) {
if (Array.isArray(val)) {
return val
.map((t) => String(t || "").trim())
.filter(Boolean)
.join("\n");
}
return String(val || "").trim();
}
function parseTitleTagsContent(content) { function parseTitleTagsContent(content) {
let title = ""; let title = "";
let tags = ""; let tags = "";
@@ -83,7 +106,8 @@ function parseTitleTagsContent(content) {
try { try {
const parsed = JSON.parse(cleanJsonText(content)); const parsed = JSON.parse(cleanJsonText(content));
title = String(parsed.title || "").trim(); logInfo("ipAgent.generateTitleTags.cleanedContent", cleanJsonText(content));
title = normalizeTitle(parsed.title || parsed.titles);
tags = normalizeTags(parsed.tags); tags = normalizeTags(parsed.tags);
const kw = parsed.keywords; const kw = parsed.keywords;
if (kw && typeof kw === "object" && !Array.isArray(kw)) { if (kw && typeof kw === "object" && !Array.isArray(kw)) {
@@ -95,7 +119,12 @@ function parseTitleTagsContent(content) {
} else if (typeof kw === "string") { } else if (typeof kw === "string") {
keywords["重点词/成语词"] = normalizeKeywordGroup(kw); keywords["重点词/成语词"] = normalizeKeywordGroup(kw);
} }
} catch { logInfo("ipAgent.generateTitleTags.parsed", { title, tags, keywords });
} catch (err) {
logInfo("ipAgent.generateTitleTags.jsonParseFailed", {
error: err instanceof Error ? err.message : String(err),
content,
});
const lines = String(content).split("\n"); const lines = String(content).split("\n");
for (const line of lines) { for (const line of lines) {
if (/标题|Title/i.test(line)) { if (/标题|Title/i.test(line)) {
@@ -121,6 +150,16 @@ function parseTitleTagsContent(content) {
return { title, tags, keywords }; return { title, tags, keywords };
} }
function validateLlmConfig(modelConfig) {
if (!modelConfig.apiKey) {
return { ok: false, error: "缺少配置 LLM_API_KEYdesktop_configs 表)" };
}
if (!modelConfig.apiUrl) {
return { ok: false, error: "缺少配置 LLM_BASE_URL或 LLM_API_URL" };
}
return { ok: true };
}
globalThis.__nodejsMain = async function main(params) { globalThis.__nodejsMain = async function main(params) {
const p = params || {}; const p = params || {};
const content = String(p.content || p.script || "").trim(); const content = String(p.content || p.script || "").trim();
@@ -129,12 +168,39 @@ globalThis.__nodejsMain = async function main(params) {
} }
const modelConfig = desktopConfig.buildModelConfig(); const modelConfig = desktopConfig.buildModelConfig();
const check = desktopConfig.validateModelConfig(modelConfig); const check = validateLlmConfig(modelConfig);
if (!check.ok) { if (!check.ok) {
return { success: false, error: check.error }; return { success: false, error: check.error };
} }
const prompt = buildPrompt(p.titleTagPrompt, content); const prompt = buildPrompt(p.titleTagPrompt, content);
const requestPayload = {
model: modelConfig.apiModelId,
messages: [{ role: "user", content: prompt }],
};
logInfo("ipAgent.generateTitleTags", {
scriptLength: content.length,
modelConfig: {
providerId: modelConfig.providerId,
modelId: modelConfig.apiModelId,
apiUrl: modelConfig.apiUrl,
apiHost: "",
apiKey: modelConfig.apiKey,
type: modelConfig.type,
},
});
logInfo("ipAgent.generateTitleTags.REQUEST", {
apiUrl: modelConfig.apiUrl,
model: modelConfig.apiModelId,
promptLength: prompt.length,
hasApiKey: Boolean(modelConfig.apiKey),
});
logInfo("ipAgent.generateTitleTags.REQUEST_FULL_PROMPT", prompt);
logInfo(
"ipAgent.generateTitleTags.REQUEST_FULL_PAYLOAD",
JSON.stringify(requestPayload, null, 2),
);
globalThis.__native?.emitProgress?.("正在生成标题、标签和关键词…"); globalThis.__native?.emitProgress?.("正在生成标题、标签和关键词…");
@@ -142,24 +208,35 @@ globalThis.__nodejsMain = async function main(params) {
apiUrl: modelConfig.apiUrl, apiUrl: modelConfig.apiUrl,
apiKey: modelConfig.apiKey, apiKey: modelConfig.apiKey,
model: modelConfig.apiModelId, model: modelConfig.apiModelId,
messages: [{ role: "user", content: prompt }], messages: requestPayload.messages,
temperature: typeof p.temperature === "number" ? p.temperature : 0.7, temperature: typeof p.temperature === "number" ? p.temperature : 0.7,
max_tokens: p.max_tokens || 2000, max_tokens: p.max_tokens || 2000,
}); });
if (!chat.success || !chat.content) { if (!chat.success || !chat.content) {
logInfo("ipAgent.generateTitleTags.api.error", chat.error || "模型返回为空");
return { success: false, error: chat.error || "模型返回为空" }; return { success: false, error: chat.error || "模型返回为空" };
} }
logInfo("ipAgent.generateTitleTags.RESPONSE_CONTENT", chat.content);
logInfo("ipAgent.generateTitleTags.rawContent", { content: chat.content });
const parsed = parseTitleTagsContent(chat.content); const parsed = parseTitleTagsContent(chat.content);
if (!parsed) { if (!parsed) {
logInfo("ipAgent.generateTitleTags.emptyResult", { content: chat.content });
return { return {
success: false, success: false,
error: "解析结果为空,请检查模型返回格式", error: "解析结果为空,AI返回内容: " + chat.content,
rawContent: chat.content, rawContent: chat.content,
}; };
} }
logInfo("ipAgent.generateTitleTags.success", {
title: parsed.title,
tags: parsed.tags,
keywords: parsed.keywords,
});
return { return {
success: true, success: true,
title: parsed.title, title: parsed.title,