111
This commit is contained in:
@@ -8,17 +8,14 @@ const desktopConfig = require("./desktop_config.js");
|
||||
|
||||
const KEYWORD_GROUPS = ["重点词/成语词", "描述词", "行动词", "情感词"];
|
||||
|
||||
const DEFAULT_TITLE_TAG_PROMPT = `你是短视频标题与标签助手。分析以下文案内容,生成:
|
||||
1. 一个最吸引人的标题(不超过30字,若需多个建议可写在 title 字段内用换行分隔)
|
||||
2. 8-10 个相关话题标签(带 # 号)
|
||||
3. 四个分组关键词,每组 0-2 个词:重点词/成语词、描述词、行动词、情感词
|
||||
const ELECTRON_DEFAULT_TITLE_TAG_PROMPT =
|
||||
"你是短视频标题与标签助手。分析文案内容 {{content}},生成5个吸引眼球的标题建议、10个相关标签、4个分组关键词(每组:重点词/成语词、描述词、行动词、情感词,每个分组只要0-2个词不要多)。";
|
||||
|
||||
文案内容:
|
||||
{{content}}
|
||||
const JSON_OUTPUT_SUFFIX = `
|
||||
|
||||
请只返回 JSON,不要 markdown,格式如下:
|
||||
{
|
||||
"title": "标题",
|
||||
"title": "最推荐标题(多个标题建议可用换行分隔)",
|
||||
"tags": ["#标签1", "#标签2"],
|
||||
"keywords": {
|
||||
"重点词/成语词": ["词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) {
|
||||
let prompt = String(template || "").trim();
|
||||
if (!prompt) prompt = DEFAULT_TITLE_TAG_PROMPT;
|
||||
return prompt
|
||||
if (!prompt) prompt = ELECTRON_DEFAULT_TITLE_TAG_PROMPT;
|
||||
prompt = prompt
|
||||
.replace(/\{\{content\}\}/g, content)
|
||||
.replace(/\{content\}/g, content);
|
||||
if (!/json|JSON/.test(prompt)) {
|
||||
prompt += JSON_OUTPUT_SUFFIX;
|
||||
}
|
||||
return prompt;
|
||||
}
|
||||
|
||||
function cleanJsonText(raw) {
|
||||
@@ -76,6 +89,16 @@ function normalizeKeywordGroup(val) {
|
||||
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) {
|
||||
let title = "";
|
||||
let tags = "";
|
||||
@@ -83,7 +106,8 @@ function parseTitleTagsContent(content) {
|
||||
|
||||
try {
|
||||
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);
|
||||
const kw = parsed.keywords;
|
||||
if (kw && typeof kw === "object" && !Array.isArray(kw)) {
|
||||
@@ -95,7 +119,12 @@ function parseTitleTagsContent(content) {
|
||||
} else if (typeof kw === "string") {
|
||||
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");
|
||||
for (const line of lines) {
|
||||
if (/标题|Title/i.test(line)) {
|
||||
@@ -121,6 +150,16 @@ function parseTitleTagsContent(content) {
|
||||
return { title, tags, keywords };
|
||||
}
|
||||
|
||||
function validateLlmConfig(modelConfig) {
|
||||
if (!modelConfig.apiKey) {
|
||||
return { ok: false, error: "缺少配置 LLM_API_KEY(desktop_configs 表)" };
|
||||
}
|
||||
if (!modelConfig.apiUrl) {
|
||||
return { ok: false, error: "缺少配置 LLM_BASE_URL(或 LLM_API_URL)" };
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
globalThis.__nodejsMain = async function main(params) {
|
||||
const p = params || {};
|
||||
const content = String(p.content || p.script || "").trim();
|
||||
@@ -129,12 +168,39 @@ globalThis.__nodejsMain = async function main(params) {
|
||||
}
|
||||
|
||||
const modelConfig = desktopConfig.buildModelConfig();
|
||||
const check = desktopConfig.validateModelConfig(modelConfig);
|
||||
const check = validateLlmConfig(modelConfig);
|
||||
if (!check.ok) {
|
||||
return { success: false, error: check.error };
|
||||
}
|
||||
|
||||
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?.("正在生成标题、标签和关键词…");
|
||||
|
||||
@@ -142,24 +208,35 @@ globalThis.__nodejsMain = async function main(params) {
|
||||
apiUrl: modelConfig.apiUrl,
|
||||
apiKey: modelConfig.apiKey,
|
||||
model: modelConfig.apiModelId,
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
messages: requestPayload.messages,
|
||||
temperature: typeof p.temperature === "number" ? p.temperature : 0.7,
|
||||
max_tokens: p.max_tokens || 2000,
|
||||
});
|
||||
|
||||
if (!chat.success || !chat.content) {
|
||||
logInfo("ipAgent.generateTitleTags.api.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);
|
||||
if (!parsed) {
|
||||
logInfo("ipAgent.generateTitleTags.emptyResult", { content: chat.content });
|
||||
return {
|
||||
success: false,
|
||||
error: "解析结果为空,请检查模型返回格式",
|
||||
error: "解析结果为空,AI返回内容: " + chat.content,
|
||||
rawContent: chat.content,
|
||||
};
|
||||
}
|
||||
|
||||
logInfo("ipAgent.generateTitleTags.success", {
|
||||
title: parsed.title,
|
||||
tags: parsed.tags,
|
||||
keywords: parsed.keywords,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
title: parsed.title,
|
||||
|
||||
Reference in New Issue
Block a user