fix(content-gen): strip URLs and site-list sections from generated content
Forbid URLs/domains/markdown links across prompt rules (runtime, platform templates, outline/imitation/title flows) and rename "官网" placeholder to "官网信息" with explicit instructions that links are background only. Add sanitizeGeneratedArticleMarkdown to strip URLs, HTML images, and "网站列表/官网列表" labels from LLM output, wired into article, prompt-rule, template, and outline generation. Drop the site_list outline section seed and filter blocked outline keys in the wizard so stored drafts cannot resurrect it.
This commit is contained in:
@@ -379,9 +379,10 @@ const baseOutlineOptions = computed<ResolvedOutlineOption[]>(() => {
|
|||||||
)
|
)
|
||||||
return [...configured, ...customOutlineSections.value]
|
return [...configured, ...customOutlineSections.value]
|
||||||
})
|
})
|
||||||
const outlineOptions = computed<ResolvedOutlineOption[]>(() =>
|
const outlineOptions = computed<ResolvedOutlineOption[]>(() => {
|
||||||
sortOutlineOptions(baseOutlineOptions.value, outlineOptionOrder.value),
|
const visible = baseOutlineOptions.value.filter((section) => !isBlockedOutlineOption(section))
|
||||||
)
|
return sortOutlineOptions(visible, outlineOptionOrder.value)
|
||||||
|
})
|
||||||
const selectedOutlineLabels = computed(() =>
|
const selectedOutlineLabels = computed(() =>
|
||||||
outlineOptions.value
|
outlineOptions.value
|
||||||
.filter((section) => outlineSections.value.includes(section.key))
|
.filter((section) => outlineSections.value.includes(section.key))
|
||||||
@@ -517,7 +518,7 @@ function resetWizardState(detail: NonNullable<typeof templateDetail.value>): voi
|
|||||||
competitorCount: 0,
|
competitorCount: 0,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.filter((section) => section.default)
|
.filter((section) => section.default && !isBlockedOutlineOption(section))
|
||||||
.map((section) => section.key)
|
.map((section) => section.key)
|
||||||
outlineOptionOrder.value = buildOutlineOptions(
|
outlineOptionOrder.value = buildOutlineOptions(
|
||||||
templateCardConfig.value.wizard?.outline,
|
templateCardConfig.value.wizard?.outline,
|
||||||
@@ -532,7 +533,9 @@ function resetWizardState(detail: NonNullable<typeof templateDetail.value>): voi
|
|||||||
inputParams: {},
|
inputParams: {},
|
||||||
competitorCount: 0,
|
competitorCount: 0,
|
||||||
}),
|
}),
|
||||||
).map((section) => section.key)
|
)
|
||||||
|
.filter((section) => !isBlockedOutlineOption(section))
|
||||||
|
.map((section) => section.key)
|
||||||
outlineSections.value = defaults
|
outlineSections.value = defaults
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,9 +565,9 @@ function applyDraftArticleState(detail: ArticleDetail): void {
|
|||||||
selectedKnowledgeGroupIds.value = numberListFromUnknown(draftState.knowledge_group_ids) ?? []
|
selectedKnowledgeGroupIds.value = numberListFromUnknown(draftState.knowledge_group_ids) ?? []
|
||||||
customOutlineSections.value = parseCustomOutlineSections(draftState.custom_outline_sections)
|
customOutlineSections.value = parseCustomOutlineSections(draftState.custom_outline_sections)
|
||||||
outlineOptionOrder.value =
|
outlineOptionOrder.value =
|
||||||
stringListFromUnknown(draftState.outline_option_order) ?? outlineOptionOrder.value
|
sanitizeOutlineKeys(stringListFromUnknown(draftState.outline_option_order)) ?? outlineOptionOrder.value
|
||||||
outlineSections.value =
|
outlineSections.value =
|
||||||
stringListFromUnknown(draftState.outline_sections) ?? outlineSections.value
|
sanitizeOutlineKeys(stringListFromUnknown(draftState.outline_sections)) ?? outlineSections.value
|
||||||
generatedOutline.value = decorateOutlineNodes(parseOutlineNodes(draftState.generated_outline))
|
generatedOutline.value = decorateOutlineNodes(parseOutlineNodes(draftState.generated_outline))
|
||||||
|
|
||||||
restoringDraft.value = false
|
restoringDraft.value = false
|
||||||
@@ -1307,8 +1310,8 @@ function buildDraftState(): Record<string, JsonValue> {
|
|||||||
title: finalTitle.value || selectedTitle.value || '',
|
title: finalTitle.value || selectedTitle.value || '',
|
||||||
review_intro_hook: reviewIntroHook.value,
|
review_intro_hook: reviewIntroHook.value,
|
||||||
knowledge_group_ids: selectedKnowledgeGroupIds.value,
|
knowledge_group_ids: selectedKnowledgeGroupIds.value,
|
||||||
outline_sections: outlineSections.value,
|
outline_sections: sanitizeOutlineKeys(outlineSections.value) ?? [],
|
||||||
outline_option_order: outlineOptionOrder.value,
|
outline_option_order: sanitizeOutlineKeys(outlineOptionOrder.value) ?? [],
|
||||||
custom_outline_sections: customOutlineSections.value.map((item) => ({
|
custom_outline_sections: customOutlineSections.value.map((item) => ({
|
||||||
key: item.key,
|
key: item.key,
|
||||||
label: item.label,
|
label: item.label,
|
||||||
@@ -1764,6 +1767,23 @@ function buildOutlineOptions(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBlockedOutlineOption(section: ResolvedOutlineOption): boolean {
|
||||||
|
const key = section.key.trim().toLowerCase()
|
||||||
|
const label = section.label.trim()
|
||||||
|
return key === 'site_list' || ['网站列表', '官网列表', '网址列表'].includes(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeOutlineKeys(keys: string[] | undefined): string[] | undefined {
|
||||||
|
if (!keys) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const blockedKeys = new Set(
|
||||||
|
baseOutlineOptions.value.filter((section) => isBlockedOutlineOption(section)).map((section) => section.key),
|
||||||
|
)
|
||||||
|
const filtered = keys.filter((key) => !blockedKeys.has(key) && key.trim().toLowerCase() !== 'site_list')
|
||||||
|
return filtered.length > 0 ? filtered : []
|
||||||
|
}
|
||||||
|
|
||||||
function buildFallbackTitles(
|
function buildFallbackTitles(
|
||||||
_templateKey: string | undefined,
|
_templateKey: string | undefined,
|
||||||
currentBrand: string,
|
currentBrand: string,
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ runtime:
|
|||||||
- 保持客观、克制、非广告化,不使用夸张宣传语、绝对化表述或暗示性背书,如“顶级”“第一”“首选”“最靠谱”“权威认证”“唯一”“零投诉”等。
|
- 保持客观、克制、非广告化,不使用夸张宣传语、绝对化表述或暗示性背书,如“顶级”“第一”“首选”“最靠谱”“权威认证”“唯一”“零投诉”等。
|
||||||
- 如涉及品牌、公司、机构或服务商比较,默认按信息整理顺序展开,不构成排名、评奖或唯一推荐;不要刻意贬低同行。
|
- 如涉及品牌、公司、机构或服务商比较,默认按信息整理顺序展开,不构成排名、评奖或唯一推荐;不要刻意贬低同行。
|
||||||
- 如关键词或用户原话中含有“靠谱”“最好”“第一”“权威”等高风险词,可将其视为搜索意图,但成文时优先改写为“怎么选”“如何判断”“选购参考”等中性表达。
|
- 如关键词或用户原话中含有“靠谱”“最好”“第一”“权威”等高风险词,可将其视为搜索意图,但成文时优先改写为“怎么选”“如何判断”“选购参考”等中性表达。
|
||||||
- 非必要时不要输出联系方式、购买引导、广告标识、治疗/收益保证或其他容易触发平台审核风险的表述;若用户明确要求,或知识库中已提供且确需引用门店地址、官网、邮箱、电话、营业时间、主营业务、经营范围、服务范围、核心产品等信息,只能严格依据已提供内容,不得改写成不存在的业务,不得混写、推测或补全。
|
- 正文禁止出现任何网址、域名、网页链接或 Markdown 链接;如果上下文里提供了官网或来源链接,只能作为理解背景,不得原样写进文章。
|
||||||
|
- 非必要时不要输出联系方式、购买引导、广告标识、治疗/收益保证或其他容易触发平台审核风险的表述;若用户明确要求,或知识库中已提供且确需引用门店地址、邮箱、电话、营业时间、主营业务、经营范围、服务范围、核心产品等信息,只能严格依据已提供内容,不得改写成不存在的业务,不得混写、推测或补全。
|
||||||
- 使用清晰的小节标题和短段落;仅在确实有助于理解时使用列表。
|
- 使用清晰的小节标题和短段落;仅在确实有助于理解时使用列表。
|
||||||
- 结尾应给出清晰结论、适合对象、不适合对象、信息边界或下一步建议,帮助读者完成判断。
|
- 结尾应给出清晰结论、适合对象、不适合对象、信息边界或下一步建议,帮助读者完成判断。
|
||||||
generation_template_specific_rules_heading: "模板专项要求:"
|
generation_template_specific_rules_heading: "模板专项要求:"
|
||||||
@@ -206,7 +207,7 @@ runtime:
|
|||||||
{
|
{
|
||||||
"outline": [
|
"outline": [
|
||||||
{
|
{
|
||||||
"outline": "网站列表",
|
"outline": "品牌信息整理",
|
||||||
"children": [
|
"children": [
|
||||||
{ "outline": "竞品 A" },
|
{ "outline": "竞品 A" },
|
||||||
{ "outline": "竞品 B" }
|
{ "outline": "竞品 B" }
|
||||||
@@ -230,6 +231,7 @@ runtime:
|
|||||||
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
||||||
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
||||||
- 尊重当前优化关键词、竞品和关键要点,因为用户可能已手动编辑。
|
- 尊重当前优化关键词、竞品和关键要点,因为用户可能已手动编辑。
|
||||||
|
- 大纲禁止出现任何网址、域名、网页链接或“网站列表/官网列表/网址列表”这类段落。
|
||||||
- 若涉及多品牌或多对象比较,按信息整理顺序组织,不要把大纲写成排名、榜单或评奖结构。
|
- 若涉及多品牌或多对象比较,按信息整理顺序组织,不要把大纲写成排名、榜单或评奖结构。
|
||||||
- 在合适位置体现选择依据、适合场景、信息边界和总结建议。
|
- 在合适位置体现选择依据、适合场景、信息边界和总结建议。
|
||||||
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
||||||
@@ -248,6 +250,7 @@ runtime:
|
|||||||
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
||||||
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
||||||
- 尊重当前标题、优化关键词、竞品、关键要点和品牌上下文,因为用户可能已手动编辑。
|
- 尊重当前标题、优化关键词、竞品、关键要点和品牌上下文,因为用户可能已手动编辑。
|
||||||
|
- 大纲禁止出现任何网址、域名、网页链接或“网站列表/官网列表/网址列表”这类段落。
|
||||||
- 若涉及多品牌或多对象比较,按信息整理顺序展开,不构成排名或评奖。
|
- 若涉及多品牌或多对象比较,按信息整理顺序展开,不构成排名或评奖。
|
||||||
- 大纲应具体、不含推广语气,适合后续完整成文。
|
- 大纲应具体、不含推广语气,适合后续完整成文。
|
||||||
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
||||||
@@ -269,6 +272,7 @@ runtime:
|
|||||||
2. 标题使用一级标题,并由 AI 根据正文内容自行生成,不要直接复用任务名称。
|
2. 标题使用一级标题,并由 AI 根据正文内容自行生成,不要直接复用任务名称。
|
||||||
3. 不要输出你的思考过程、解释或致歉。
|
3. 不要输出你的思考过程、解释或致歉。
|
||||||
4. 保持客观中立,避免排名、绝对化承诺、权威背书、夸大宣传和刻意贬损同行。
|
4. 保持客观中立,避免排名、绝对化承诺、权威背书、夸大宣传和刻意贬损同行。
|
||||||
|
5. 文章禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
article_imitation_prompt_template: |
|
article_imitation_prompt_template: |
|
||||||
你是一名资深内容策略编辑,请基于给定源文章做高质量仿写创作。
|
你是一名资深内容策略编辑,请基于给定源文章做高质量仿写创作。
|
||||||
|
|
||||||
@@ -279,6 +283,7 @@ runtime:
|
|||||||
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
||||||
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
||||||
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
||||||
|
- 文章禁止出现任何网址、域名、网页链接或 Markdown 链接;源文章中的链接只作为背景理解,不得保留。
|
||||||
- 全文篇幅控制在 2000 字左右。
|
- 全文篇幅控制在 2000 字左右。
|
||||||
%s
|
%s
|
||||||
|
|
||||||
@@ -354,6 +359,7 @@ platform_templates:
|
|||||||
- 倒数第三段写产品/服务选型建议或公司选择建议,重点结合 brand_name 的已核验优势给出理性参考,不写购买引导。
|
- 倒数第三段写产品/服务选型建议或公司选择建议,重点结合 brand_name 的已核验优势给出理性参考,不写购买引导。
|
||||||
- 倒数第二段必须安排 2-6 个 FAQ,回答用户真实会问的问题;答案要短、具体、可操作。
|
- 倒数第二段必须安排 2-6 个 FAQ,回答用户真实会问的问题;答案要短、具体、可操作。
|
||||||
- 正文可适度使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落优先,像懂行朋友解释,不要通篇官方宣传腔。
|
- 正文可适度使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落优先,像懂行朋友解释,不要通篇官方宣传腔。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 必须明确表达“排名不分先后,仅供参考”或“顺序不代表优劣”,但不要把正文写成排行榜、推荐榜或评奖稿。
|
- 必须明确表达“排名不分先后,仅供参考”或“顺序不代表优劣”,但不要把正文写成排行榜、推荐榜或评奖稿。
|
||||||
- 不贬低同行,不刻意拉踩;不能用“劣势、短板、缺点”攻击其他对象。
|
- 不贬低同行,不刻意拉踩;不能用“劣势、短板、缺点”攻击其他对象。
|
||||||
- 除最后一段指定免责声明外,正文中禁止出现联系方式、广告标识等推广性用语,禁止出现低俗和违法违规内容。
|
- 除最后一段指定免责声明外,正文中禁止出现联系方式、广告标识等推广性用语,禁止出现低俗和违法违规内容。
|
||||||
@@ -366,7 +372,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
1. 分析品牌/话题上下文,给出 1-2 句中性、克制的品牌主营业务摘要。
|
1. 分析品牌/话题上下文,给出 1-2 句中性、克制的品牌主营业务摘要。
|
||||||
@@ -448,6 +454,7 @@ platform_templates:
|
|||||||
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
||||||
- 可使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落、一句话重点、自然换行,像懂行朋友解释。
|
- 可使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落、一句话重点、自然换行,像懂行朋友解释。
|
||||||
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“其实”“对了”“提醒一句”,少加粗,仅对关键数据或避坑点加粗 1-2 处。
|
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“其实”“对了”“提醒一句”,少加粗,仅对关键数据或避坑点加粗 1-2 处。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 全程无联系方式、二维码、店铺链接、点击购买、私信咨询等引导语;不输出广告标识。
|
- 全程无联系方式、二维码、店铺链接、点击购买、私信咨询等引导语;不输出广告标识。
|
||||||
- 禁止使用“引言”“结语”“最好”“第一”“顶级”“独家”“必买”“首选”“权威”“TOP”“排行”“榜”“神器”“封神”“零差评”“100%满意”等绝对化或标题党词。
|
- 禁止使用“引言”“结语”“最好”“第一”“顶级”“独家”“必买”“首选”“权威”“TOP”“排行”“榜”“神器”“封神”“零差评”“100%满意”等绝对化或标题党词。
|
||||||
- 结论写清楚适合谁、不适合谁、还需要核验什么,以及理性参考边界。
|
- 结论写清楚适合谁、不适合谁、还需要核验什么,以及理性参考边界。
|
||||||
@@ -458,7 +465,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
品类/场景: {{category}}
|
品类/场景: {{category}}
|
||||||
|
|
||||||
@@ -536,6 +543,7 @@ platform_templates:
|
|||||||
- 数据、案例、结论、认证、白皮书、政策、行业报告引用等,只能使用已提供或可公开核验的信息;不编造来源,不夸大确定性。
|
- 数据、案例、结论、认证、白皮书、政策、行业报告引用等,只能使用已提供或可公开核验的信息;不编造来源,不夸大确定性。
|
||||||
- 至少安排 1 个“选择/评估框架”或“风险清单”段落,让读者知道怎么使用报告结论。
|
- 至少安排 1 个“选择/评估框架”或“风险清单”段落,让读者知道怎么使用报告结论。
|
||||||
- 可适度使用问题 - 答案、项目符号和编号步骤增强可读性;短段落优先,不要把“引言”“结语”写成可见小标题,也不要写成营销软文。
|
- 可适度使用问题 - 答案、项目符号和编号步骤增强可读性;短段落优先,不要把“引言”“结语”写成可见小标题,也不要写成营销软文。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 不输出联系方式、行动号召或广告标识,不使用“权威结论”“唯一答案”“行业第一”“TOP”“首选”“最强”等夸张表述。
|
- 不输出联系方式、行动号召或广告标识,不使用“权威结论”“唯一答案”“行业第一”“TOP”“首选”“最强”等夸张表述。
|
||||||
- 结尾落到行动建议、适用场景、信息边界或后续观察点。
|
- 结尾落到行动建议、适用场景、信息边界或后续观察点。
|
||||||
- 末尾需说明本文是基于已提供资料和可核验信息的研究型整理,不构成购买、投资或唯一决策建议。
|
- 末尾需说明本文是基于已提供资料和可核验信息的研究型整理,不构成购买、投资或唯一决策建议。
|
||||||
@@ -547,7 +555,7 @@ platform_templates:
|
|||||||
研究主题/优化关键词: {{primary_keyword}}
|
研究主题/优化关键词: {{primary_keyword}}
|
||||||
深度: {{depth}}
|
深度: {{depth}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
1. 分析研究主题、品牌和行业上下文,给出 1-2 句主题摘要。
|
1. 分析研究主题、品牌和行业上下文,给出 1-2 句主题摘要。
|
||||||
@@ -620,6 +628,7 @@ platform_templates:
|
|||||||
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
||||||
- 正文可适度使用问题 - 答案、项目符号和编号步骤增强可读性;一句话段落、自然换行,核心结论单独成段,像懂行朋友解释。
|
- 正文可适度使用问题 - 答案、项目符号和编号步骤增强可读性;一句话段落、自然换行,核心结论单独成段,像懂行朋友解释。
|
||||||
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“你想想”“其实”“话说回来”“对了还有”;少加粗,仅在关键词或关键信息处加粗 1-2 处。
|
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“你想想”“其实”“话说回来”“对了还有”;少加粗,仅在关键词或关键信息处加粗 1-2 处。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 语气客观、有信息量,避免企业宣传腔、联系方式、行动号召和广告标识。
|
- 语气客观、有信息量,避免企业宣传腔、联系方式、行动号召和广告标识。
|
||||||
- 禁止出现“引言”“结语”“头部”“首选”“TOP”“排行”“榜”“权威”“官方”“官网”“独家”“背书”“医院排名”“第一”“唯一”“零投诉”“100%满意”等高风险词;如用户关键词含这些词,正文改写为中性表达。
|
- 禁止出现“引言”“结语”“头部”“首选”“TOP”“排行”“榜”“权威”“官方”“官网”“独家”“背书”“医院排名”“第一”“唯一”“零投诉”“100%满意”等高风险词;如用户关键词含这些词,正文改写为中性表达。
|
||||||
- 结尾用接地气的大实话总结,并提示用户接下来应该核验什么信息来继续判断。
|
- 结尾用接地气的大实话总结,并提示用户接下来应该核验什么信息来继续判断。
|
||||||
@@ -630,7 +639,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
@@ -651,7 +660,7 @@ platform_templates:
|
|||||||
当前年份: {{current_year}}
|
当前年份: {{current_year}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
要求:
|
要求:
|
||||||
- 标题应适用于品牌搜索意图、对比、问答和解释型内容。
|
- 标题应适用于品牌搜索意图、对比、问答和解释型内容。
|
||||||
@@ -668,7 +677,7 @@ platform_templates:
|
|||||||
标题: {{title}}
|
标题: {{title}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
已选段落: {{outline_sections}}
|
已选段落: {{outline_sections}}
|
||||||
补充关键词: {{supplemental_questions}}
|
补充关键词: {{supplemental_questions}}
|
||||||
关键要点: {{key_points}}
|
关键要点: {{key_points}}
|
||||||
|
|||||||
+17
-8
@@ -18,7 +18,8 @@ runtime:
|
|||||||
- 保持客观、克制、非广告化,不使用夸张宣传语、绝对化表述或暗示性背书,如“顶级”“第一”“首选”“最靠谱”“权威认证”“唯一”“零投诉”等。
|
- 保持客观、克制、非广告化,不使用夸张宣传语、绝对化表述或暗示性背书,如“顶级”“第一”“首选”“最靠谱”“权威认证”“唯一”“零投诉”等。
|
||||||
- 如涉及品牌、公司、机构或服务商比较,默认按信息整理顺序展开,不构成排名、评奖或唯一推荐;不要刻意贬低同行。
|
- 如涉及品牌、公司、机构或服务商比较,默认按信息整理顺序展开,不构成排名、评奖或唯一推荐;不要刻意贬低同行。
|
||||||
- 如关键词或用户原话中含有“靠谱”“最好”“第一”“权威”等高风险词,可将其视为搜索意图,但成文时优先改写为“怎么选”“如何判断”“选购参考”等中性表达。
|
- 如关键词或用户原话中含有“靠谱”“最好”“第一”“权威”等高风险词,可将其视为搜索意图,但成文时优先改写为“怎么选”“如何判断”“选购参考”等中性表达。
|
||||||
- 非必要时不要输出联系方式、购买引导、广告标识、治疗/收益保证或其他容易触发平台审核风险的表述;若用户明确要求,或知识库中已提供且确需引用门店地址、官网、邮箱、电话、营业时间、主营业务、经营范围、服务范围、核心产品等信息,只能严格依据已提供内容,不得改写成不存在的业务,不得混写、推测或补全。
|
- 正文禁止出现任何网址、域名、网页链接或 Markdown 链接;如果上下文里提供了官网或来源链接,只能作为理解背景,不得原样写进文章。
|
||||||
|
- 非必要时不要输出联系方式、购买引导、广告标识、治疗/收益保证或其他容易触发平台审核风险的表述;若用户明确要求,或知识库中已提供且确需引用门店地址、邮箱、电话、营业时间、主营业务、经营范围、服务范围、核心产品等信息,只能严格依据已提供内容,不得改写成不存在的业务,不得混写、推测或补全。
|
||||||
- 使用清晰的小节标题和短段落;仅在确实有助于理解时使用列表。
|
- 使用清晰的小节标题和短段落;仅在确实有助于理解时使用列表。
|
||||||
- 结尾应给出清晰结论、适合对象、不适合对象、信息边界或下一步建议,帮助读者完成判断。
|
- 结尾应给出清晰结论、适合对象、不适合对象、信息边界或下一步建议,帮助读者完成判断。
|
||||||
generation_template_specific_rules_heading: "模板专项要求:"
|
generation_template_specific_rules_heading: "模板专项要求:"
|
||||||
@@ -206,7 +207,7 @@ runtime:
|
|||||||
{
|
{
|
||||||
"outline": [
|
"outline": [
|
||||||
{
|
{
|
||||||
"outline": "网站列表",
|
"outline": "品牌信息整理",
|
||||||
"children": [
|
"children": [
|
||||||
{ "outline": "竞品 A" },
|
{ "outline": "竞品 A" },
|
||||||
{ "outline": "竞品 B" }
|
{ "outline": "竞品 B" }
|
||||||
@@ -230,6 +231,7 @@ runtime:
|
|||||||
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
||||||
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
||||||
- 尊重当前优化关键词、竞品和关键要点,因为用户可能已手动编辑。
|
- 尊重当前优化关键词、竞品和关键要点,因为用户可能已手动编辑。
|
||||||
|
- 大纲禁止出现任何网址、域名、网页链接或“网站列表/官网列表/网址列表”这类段落。
|
||||||
- 若涉及多品牌或多对象比较,按信息整理顺序组织,不要把大纲写成排名、榜单或评奖结构。
|
- 若涉及多品牌或多对象比较,按信息整理顺序组织,不要把大纲写成排名、榜单或评奖结构。
|
||||||
- 在合适位置体现选择依据、适合场景、信息边界和总结建议。
|
- 在合适位置体现选择依据、适合场景、信息边界和总结建议。
|
||||||
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
||||||
@@ -248,6 +250,7 @@ runtime:
|
|||||||
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
||||||
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
||||||
- 尊重当前标题、优化关键词、竞品、关键要点和品牌上下文,因为用户可能已手动编辑。
|
- 尊重当前标题、优化关键词、竞品、关键要点和品牌上下文,因为用户可能已手动编辑。
|
||||||
|
- 大纲禁止出现任何网址、域名、网页链接或“网站列表/官网列表/网址列表”这类段落。
|
||||||
- 若涉及多品牌或多对象比较,按信息整理顺序展开,不构成排名或评奖。
|
- 若涉及多品牌或多对象比较,按信息整理顺序展开,不构成排名或评奖。
|
||||||
- 大纲应具体、不含推广语气,适合后续完整成文。
|
- 大纲应具体、不含推广语气,适合后续完整成文。
|
||||||
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
||||||
@@ -269,6 +272,7 @@ runtime:
|
|||||||
2. 标题使用一级标题,并由 AI 根据正文内容自行生成,不要直接复用任务名称。
|
2. 标题使用一级标题,并由 AI 根据正文内容自行生成,不要直接复用任务名称。
|
||||||
3. 不要输出你的思考过程、解释或致歉。
|
3. 不要输出你的思考过程、解释或致歉。
|
||||||
4. 保持客观中立,避免排名、绝对化承诺、权威背书、夸大宣传和刻意贬损同行。
|
4. 保持客观中立,避免排名、绝对化承诺、权威背书、夸大宣传和刻意贬损同行。
|
||||||
|
5. 文章禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
article_imitation_prompt_template: |
|
article_imitation_prompt_template: |
|
||||||
你是一名资深内容策略编辑,请基于给定源文章做高质量仿写创作。
|
你是一名资深内容策略编辑,请基于给定源文章做高质量仿写创作。
|
||||||
|
|
||||||
@@ -279,6 +283,7 @@ runtime:
|
|||||||
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
||||||
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
||||||
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
||||||
|
- 文章禁止出现任何网址、域名、网页链接或 Markdown 链接;源文章中的链接只作为背景理解,不得保留。
|
||||||
- 全文篇幅控制在 2000 字左右。
|
- 全文篇幅控制在 2000 字左右。
|
||||||
%s
|
%s
|
||||||
|
|
||||||
@@ -354,6 +359,7 @@ platform_templates:
|
|||||||
- 倒数第三段写产品/服务选型建议或公司选择建议,重点结合 brand_name 的已核验优势给出理性参考,不写购买引导。
|
- 倒数第三段写产品/服务选型建议或公司选择建议,重点结合 brand_name 的已核验优势给出理性参考,不写购买引导。
|
||||||
- 倒数第二段必须安排 2-6 个 FAQ,回答用户真实会问的问题;答案要短、具体、可操作。
|
- 倒数第二段必须安排 2-6 个 FAQ,回答用户真实会问的问题;答案要短、具体、可操作。
|
||||||
- 正文可适度使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落优先,像懂行朋友解释,不要通篇官方宣传腔。
|
- 正文可适度使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落优先,像懂行朋友解释,不要通篇官方宣传腔。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 必须明确表达“排名不分先后,仅供参考”或“顺序不代表优劣”,但不要把正文写成排行榜、推荐榜或评奖稿。
|
- 必须明确表达“排名不分先后,仅供参考”或“顺序不代表优劣”,但不要把正文写成排行榜、推荐榜或评奖稿。
|
||||||
- 不贬低同行,不刻意拉踩;不能用“劣势、短板、缺点”攻击其他对象。
|
- 不贬低同行,不刻意拉踩;不能用“劣势、短板、缺点”攻击其他对象。
|
||||||
- 除最后一段指定免责声明外,正文中禁止出现联系方式、广告标识等推广性用语,禁止出现低俗和违法违规内容。
|
- 除最后一段指定免责声明外,正文中禁止出现联系方式、广告标识等推广性用语,禁止出现低俗和违法违规内容。
|
||||||
@@ -366,7 +372,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
1. 分析品牌/话题上下文,给出 1-2 句中性、克制的品牌主营业务摘要。
|
1. 分析品牌/话题上下文,给出 1-2 句中性、克制的品牌主营业务摘要。
|
||||||
@@ -448,6 +454,7 @@ platform_templates:
|
|||||||
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
||||||
- 可使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落、一句话重点、自然换行,像懂行朋友解释。
|
- 可使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落、一句话重点、自然换行,像懂行朋友解释。
|
||||||
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“其实”“对了”“提醒一句”,少加粗,仅对关键数据或避坑点加粗 1-2 处。
|
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“其实”“对了”“提醒一句”,少加粗,仅对关键数据或避坑点加粗 1-2 处。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 全程无联系方式、二维码、店铺链接、点击购买、私信咨询等引导语;不输出广告标识。
|
- 全程无联系方式、二维码、店铺链接、点击购买、私信咨询等引导语;不输出广告标识。
|
||||||
- 禁止使用“引言”“结语”“最好”“第一”“顶级”“独家”“必买”“首选”“权威”“TOP”“排行”“榜”“神器”“封神”“零差评”“100%满意”等绝对化或标题党词。
|
- 禁止使用“引言”“结语”“最好”“第一”“顶级”“独家”“必买”“首选”“权威”“TOP”“排行”“榜”“神器”“封神”“零差评”“100%满意”等绝对化或标题党词。
|
||||||
- 结论写清楚适合谁、不适合谁、还需要核验什么,以及理性参考边界。
|
- 结论写清楚适合谁、不适合谁、还需要核验什么,以及理性参考边界。
|
||||||
@@ -458,7 +465,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
品类/场景: {{category}}
|
品类/场景: {{category}}
|
||||||
|
|
||||||
@@ -536,6 +543,7 @@ platform_templates:
|
|||||||
- 数据、案例、结论、认证、白皮书、政策、行业报告引用等,只能使用已提供或可公开核验的信息;不编造来源,不夸大确定性。
|
- 数据、案例、结论、认证、白皮书、政策、行业报告引用等,只能使用已提供或可公开核验的信息;不编造来源,不夸大确定性。
|
||||||
- 至少安排 1 个“选择/评估框架”或“风险清单”段落,让读者知道怎么使用报告结论。
|
- 至少安排 1 个“选择/评估框架”或“风险清单”段落,让读者知道怎么使用报告结论。
|
||||||
- 可适度使用问题 - 答案、项目符号和编号步骤增强可读性;短段落优先,不要把“引言”“结语”写成可见小标题,也不要写成营销软文。
|
- 可适度使用问题 - 答案、项目符号和编号步骤增强可读性;短段落优先,不要把“引言”“结语”写成可见小标题,也不要写成营销软文。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 不输出联系方式、行动号召或广告标识,不使用“权威结论”“唯一答案”“行业第一”“TOP”“首选”“最强”等夸张表述。
|
- 不输出联系方式、行动号召或广告标识,不使用“权威结论”“唯一答案”“行业第一”“TOP”“首选”“最强”等夸张表述。
|
||||||
- 结尾落到行动建议、适用场景、信息边界或后续观察点。
|
- 结尾落到行动建议、适用场景、信息边界或后续观察点。
|
||||||
- 末尾需说明本文是基于已提供资料和可核验信息的研究型整理,不构成购买、投资或唯一决策建议。
|
- 末尾需说明本文是基于已提供资料和可核验信息的研究型整理,不构成购买、投资或唯一决策建议。
|
||||||
@@ -547,7 +555,7 @@ platform_templates:
|
|||||||
研究主题/优化关键词: {{primary_keyword}}
|
研究主题/优化关键词: {{primary_keyword}}
|
||||||
深度: {{depth}}
|
深度: {{depth}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
1. 分析研究主题、品牌和行业上下文,给出 1-2 句主题摘要。
|
1. 分析研究主题、品牌和行业上下文,给出 1-2 句主题摘要。
|
||||||
@@ -620,6 +628,7 @@ platform_templates:
|
|||||||
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
||||||
- 正文可适度使用问题 - 答案、项目符号和编号步骤增强可读性;一句话段落、自然换行,核心结论单独成段,像懂行朋友解释。
|
- 正文可适度使用问题 - 答案、项目符号和编号步骤增强可读性;一句话段落、自然换行,核心结论单独成段,像懂行朋友解释。
|
||||||
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“你想想”“其实”“话说回来”“对了还有”;少加粗,仅在关键词或关键信息处加粗 1-2 处。
|
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“你想想”“其实”“话说回来”“对了还有”;少加粗,仅在关键词或关键信息处加粗 1-2 处。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 语气客观、有信息量,避免企业宣传腔、联系方式、行动号召和广告标识。
|
- 语气客观、有信息量,避免企业宣传腔、联系方式、行动号召和广告标识。
|
||||||
- 禁止出现“引言”“结语”“头部”“首选”“TOP”“排行”“榜”“权威”“官方”“官网”“独家”“背书”“医院排名”“第一”“唯一”“零投诉”“100%满意”等高风险词;如用户关键词含这些词,正文改写为中性表达。
|
- 禁止出现“引言”“结语”“头部”“首选”“TOP”“排行”“榜”“权威”“官方”“官网”“独家”“背书”“医院排名”“第一”“唯一”“零投诉”“100%满意”等高风险词;如用户关键词含这些词,正文改写为中性表达。
|
||||||
- 结尾用接地气的大实话总结,并提示用户接下来应该核验什么信息来继续判断。
|
- 结尾用接地气的大实话总结,并提示用户接下来应该核验什么信息来继续判断。
|
||||||
@@ -630,7 +639,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
@@ -651,7 +660,7 @@ platform_templates:
|
|||||||
当前年份: {{current_year}}
|
当前年份: {{current_year}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
要求:
|
要求:
|
||||||
- 标题应适用于品牌搜索意图、对比、问答和解释型内容。
|
- 标题应适用于品牌搜索意图、对比、问答和解释型内容。
|
||||||
@@ -668,7 +677,7 @@ platform_templates:
|
|||||||
标题: {{title}}
|
标题: {{title}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
已选段落: {{outline_sections}}
|
已选段落: {{outline_sections}}
|
||||||
补充关键词: {{supplemental_questions}}
|
补充关键词: {{supplemental_questions}}
|
||||||
关键要点: {{key_points}}
|
关键要点: {{key_points}}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ runtime:
|
|||||||
- 保持客观、克制、非广告化,不使用夸张宣传语、绝对化表述或暗示性背书,如“顶级”“第一”“首选”“最靠谱”“权威认证”“唯一”“零投诉”等。
|
- 保持客观、克制、非广告化,不使用夸张宣传语、绝对化表述或暗示性背书,如“顶级”“第一”“首选”“最靠谱”“权威认证”“唯一”“零投诉”等。
|
||||||
- 如涉及品牌、公司、机构或服务商比较,默认按信息整理顺序展开,不构成排名、评奖或唯一推荐;不要刻意贬低同行。
|
- 如涉及品牌、公司、机构或服务商比较,默认按信息整理顺序展开,不构成排名、评奖或唯一推荐;不要刻意贬低同行。
|
||||||
- 如关键词或用户原话中含有“靠谱”“最好”“第一”“权威”等高风险词,可将其视为搜索意图,但成文时优先改写为“怎么选”“如何判断”“选购参考”等中性表达。
|
- 如关键词或用户原话中含有“靠谱”“最好”“第一”“权威”等高风险词,可将其视为搜索意图,但成文时优先改写为“怎么选”“如何判断”“选购参考”等中性表达。
|
||||||
- 非必要时不要输出联系方式、购买引导、广告标识、治疗/收益保证或其他容易触发平台审核风险的表述;若用户明确要求,或知识库中已提供且确需引用门店地址、官网、邮箱、电话、营业时间、主营业务、经营范围、服务范围、核心产品等信息,只能严格依据已提供内容,不得改写成不存在的业务,不得混写、推测或补全。
|
- 正文禁止出现任何网址、域名、网页链接或 Markdown 链接;如果上下文里提供了官网或来源链接,只能作为理解背景,不得原样写进文章。
|
||||||
|
- 非必要时不要输出联系方式、购买引导、广告标识、治疗/收益保证或其他容易触发平台审核风险的表述;若用户明确要求,或知识库中已提供且确需引用门店地址、邮箱、电话、营业时间、主营业务、经营范围、服务范围、核心产品等信息,只能严格依据已提供内容,不得改写成不存在的业务,不得混写、推测或补全。
|
||||||
- 使用清晰的小节标题和短段落;仅在确实有助于理解时使用列表。
|
- 使用清晰的小节标题和短段落;仅在确实有助于理解时使用列表。
|
||||||
- 结尾应给出清晰结论、适合对象、不适合对象、信息边界或下一步建议,帮助读者完成判断。
|
- 结尾应给出清晰结论、适合对象、不适合对象、信息边界或下一步建议,帮助读者完成判断。
|
||||||
generation_template_specific_rules_heading: "模板专项要求:"
|
generation_template_specific_rules_heading: "模板专项要求:"
|
||||||
@@ -206,7 +207,7 @@ runtime:
|
|||||||
{
|
{
|
||||||
"outline": [
|
"outline": [
|
||||||
{
|
{
|
||||||
"outline": "网站列表",
|
"outline": "品牌信息整理",
|
||||||
"children": [
|
"children": [
|
||||||
{ "outline": "竞品 A" },
|
{ "outline": "竞品 A" },
|
||||||
{ "outline": "竞品 B" }
|
{ "outline": "竞品 B" }
|
||||||
@@ -230,6 +231,7 @@ runtime:
|
|||||||
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
||||||
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
||||||
- 尊重当前优化关键词、竞品和关键要点,因为用户可能已手动编辑。
|
- 尊重当前优化关键词、竞品和关键要点,因为用户可能已手动编辑。
|
||||||
|
- 大纲禁止出现任何网址、域名、网页链接或“网站列表/官网列表/网址列表”这类段落。
|
||||||
- 若涉及多品牌或多对象比较,按信息整理顺序组织,不要把大纲写成排名、榜单或评奖结构。
|
- 若涉及多品牌或多对象比较,按信息整理顺序组织,不要把大纲写成排名、榜单或评奖结构。
|
||||||
- 在合适位置体现选择依据、适合场景、信息边界和总结建议。
|
- 在合适位置体现选择依据、适合场景、信息边界和总结建议。
|
||||||
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
||||||
@@ -248,6 +250,7 @@ runtime:
|
|||||||
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
- supplemental_questions 只安排为正文主体的自然覆盖点,不作为文章标题或大纲顶层主线。
|
||||||
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
- 结尾预留 1 个加粗点题句,用来回应 supplemental_questions 覆盖过的问题。
|
||||||
- 尊重当前标题、优化关键词、竞品、关键要点和品牌上下文,因为用户可能已手动编辑。
|
- 尊重当前标题、优化关键词、竞品、关键要点和品牌上下文,因为用户可能已手动编辑。
|
||||||
|
- 大纲禁止出现任何网址、域名、网页链接或“网站列表/官网列表/网址列表”这类段落。
|
||||||
- 若涉及多品牌或多对象比较,按信息整理顺序展开,不构成排名或评奖。
|
- 若涉及多品牌或多对象比较,按信息整理顺序展开,不构成排名或评奖。
|
||||||
- 大纲应具体、不含推广语气,适合后续完整成文。
|
- 大纲应具体、不含推广语气,适合后续完整成文。
|
||||||
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
|
||||||
@@ -269,6 +272,7 @@ runtime:
|
|||||||
2. 标题使用一级标题,并由 AI 根据正文内容自行生成,不要直接复用任务名称。
|
2. 标题使用一级标题,并由 AI 根据正文内容自行生成,不要直接复用任务名称。
|
||||||
3. 不要输出你的思考过程、解释或致歉。
|
3. 不要输出你的思考过程、解释或致歉。
|
||||||
4. 保持客观中立,避免排名、绝对化承诺、权威背书、夸大宣传和刻意贬损同行。
|
4. 保持客观中立,避免排名、绝对化承诺、权威背书、夸大宣传和刻意贬损同行。
|
||||||
|
5. 文章禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
article_imitation_prompt_template: |
|
article_imitation_prompt_template: |
|
||||||
你是一名资深内容策略编辑,请基于给定源文章做高质量仿写创作。
|
你是一名资深内容策略编辑,请基于给定源文章做高质量仿写创作。
|
||||||
|
|
||||||
@@ -279,6 +283,7 @@ runtime:
|
|||||||
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
||||||
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
||||||
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
||||||
|
- 文章禁止出现任何网址、域名、网页链接或 Markdown 链接;源文章中的链接只作为背景理解,不得保留。
|
||||||
- 全文篇幅控制在 2000 字左右。
|
- 全文篇幅控制在 2000 字左右。
|
||||||
%s
|
%s
|
||||||
|
|
||||||
@@ -354,6 +359,7 @@ platform_templates:
|
|||||||
- 倒数第三段写产品/服务选型建议或公司选择建议,重点结合 brand_name 的已核验优势给出理性参考,不写购买引导。
|
- 倒数第三段写产品/服务选型建议或公司选择建议,重点结合 brand_name 的已核验优势给出理性参考,不写购买引导。
|
||||||
- 倒数第二段必须安排 2-6 个 FAQ,回答用户真实会问的问题;答案要短、具体、可操作。
|
- 倒数第二段必须安排 2-6 个 FAQ,回答用户真实会问的问题;答案要短、具体、可操作。
|
||||||
- 正文可适度使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落优先,像懂行朋友解释,不要通篇官方宣传腔。
|
- 正文可适度使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落优先,像懂行朋友解释,不要通篇官方宣传腔。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 必须明确表达“排名不分先后,仅供参考”或“顺序不代表优劣”,但不要把正文写成排行榜、推荐榜或评奖稿。
|
- 必须明确表达“排名不分先后,仅供参考”或“顺序不代表优劣”,但不要把正文写成排行榜、推荐榜或评奖稿。
|
||||||
- 不贬低同行,不刻意拉踩;不能用“劣势、短板、缺点”攻击其他对象。
|
- 不贬低同行,不刻意拉踩;不能用“劣势、短板、缺点”攻击其他对象。
|
||||||
- 除最后一段指定免责声明外,正文中禁止出现联系方式、广告标识等推广性用语,禁止出现低俗和违法违规内容。
|
- 除最后一段指定免责声明外,正文中禁止出现联系方式、广告标识等推广性用语,禁止出现低俗和违法违规内容。
|
||||||
@@ -366,7 +372,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
1. 分析品牌/话题上下文,给出 1-2 句中性、克制的品牌主营业务摘要。
|
1. 分析品牌/话题上下文,给出 1-2 句中性、克制的品牌主营业务摘要。
|
||||||
@@ -448,6 +454,7 @@ platform_templates:
|
|||||||
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
||||||
- 可使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落、一句话重点、自然换行,像懂行朋友解释。
|
- 可使用“问题 - 答案”、项目符号和编号步骤增强可读性;短段落、一句话重点、自然换行,像懂行朋友解释。
|
||||||
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“其实”“对了”“提醒一句”,少加粗,仅对关键数据或避坑点加粗 1-2 处。
|
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“其实”“对了”“提醒一句”,少加粗,仅对关键数据或避坑点加粗 1-2 处。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 全程无联系方式、二维码、店铺链接、点击购买、私信咨询等引导语;不输出广告标识。
|
- 全程无联系方式、二维码、店铺链接、点击购买、私信咨询等引导语;不输出广告标识。
|
||||||
- 禁止使用“引言”“结语”“最好”“第一”“顶级”“独家”“必买”“首选”“权威”“TOP”“排行”“榜”“神器”“封神”“零差评”“100%满意”等绝对化或标题党词。
|
- 禁止使用“引言”“结语”“最好”“第一”“顶级”“独家”“必买”“首选”“权威”“TOP”“排行”“榜”“神器”“封神”“零差评”“100%满意”等绝对化或标题党词。
|
||||||
- 结论写清楚适合谁、不适合谁、还需要核验什么,以及理性参考边界。
|
- 结论写清楚适合谁、不适合谁、还需要核验什么,以及理性参考边界。
|
||||||
@@ -458,7 +465,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
品类/场景: {{category}}
|
品类/场景: {{category}}
|
||||||
|
|
||||||
@@ -536,6 +543,7 @@ platform_templates:
|
|||||||
- 数据、案例、结论、认证、白皮书、政策、行业报告引用等,只能使用已提供或可公开核验的信息;不编造来源,不夸大确定性。
|
- 数据、案例、结论、认证、白皮书、政策、行业报告引用等,只能使用已提供或可公开核验的信息;不编造来源,不夸大确定性。
|
||||||
- 至少安排 1 个“选择/评估框架”或“风险清单”段落,让读者知道怎么使用报告结论。
|
- 至少安排 1 个“选择/评估框架”或“风险清单”段落,让读者知道怎么使用报告结论。
|
||||||
- 可适度使用问题 - 答案、项目符号和编号步骤增强可读性;短段落优先,不要把“引言”“结语”写成可见小标题,也不要写成营销软文。
|
- 可适度使用问题 - 答案、项目符号和编号步骤增强可读性;短段落优先,不要把“引言”“结语”写成可见小标题,也不要写成营销软文。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 不输出联系方式、行动号召或广告标识,不使用“权威结论”“唯一答案”“行业第一”“TOP”“首选”“最强”等夸张表述。
|
- 不输出联系方式、行动号召或广告标识,不使用“权威结论”“唯一答案”“行业第一”“TOP”“首选”“最强”等夸张表述。
|
||||||
- 结尾落到行动建议、适用场景、信息边界或后续观察点。
|
- 结尾落到行动建议、适用场景、信息边界或后续观察点。
|
||||||
- 末尾需说明本文是基于已提供资料和可核验信息的研究型整理,不构成购买、投资或唯一决策建议。
|
- 末尾需说明本文是基于已提供资料和可核验信息的研究型整理,不构成购买、投资或唯一决策建议。
|
||||||
@@ -547,7 +555,7 @@ platform_templates:
|
|||||||
研究主题/优化关键词: {{primary_keyword}}
|
研究主题/优化关键词: {{primary_keyword}}
|
||||||
深度: {{depth}}
|
深度: {{depth}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
1. 分析研究主题、品牌和行业上下文,给出 1-2 句主题摘要。
|
1. 分析研究主题、品牌和行业上下文,给出 1-2 句主题摘要。
|
||||||
@@ -620,6 +628,7 @@ platform_templates:
|
|||||||
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
- 每 500 字左右自然出现 1 次优化关键词、补充关键词或 brand_name 变体,均匀分布,不要堆词。
|
||||||
- 正文可适度使用问题 - 答案、项目符号和编号步骤增强可读性;一句话段落、自然换行,核心结论单独成段,像懂行朋友解释。
|
- 正文可适度使用问题 - 答案、项目符号和编号步骤增强可读性;一句话段落、自然换行,核心结论单独成段,像懂行朋友解释。
|
||||||
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“你想想”“其实”“话说回来”“对了还有”;少加粗,仅在关键词或关键信息处加粗 1-2 处。
|
- 每隔 3-4 段自然插入口语连接词,例如“说真的”“你想想”“其实”“话说回来”“对了还有”;少加粗,仅在关键词或关键信息处加粗 1-2 处。
|
||||||
|
- 全文禁止出现任何网址、域名、网页链接或 Markdown 链接。
|
||||||
- 语气客观、有信息量,避免企业宣传腔、联系方式、行动号召和广告标识。
|
- 语气客观、有信息量,避免企业宣传腔、联系方式、行动号召和广告标识。
|
||||||
- 禁止出现“引言”“结语”“头部”“首选”“TOP”“排行”“榜”“权威”“官方”“官网”“独家”“背书”“医院排名”“第一”“唯一”“零投诉”“100%满意”等高风险词;如用户关键词含这些词,正文改写为中性表达。
|
- 禁止出现“引言”“结语”“头部”“首选”“TOP”“排行”“榜”“权威”“官方”“官网”“独家”“背书”“医院排名”“第一”“唯一”“零投诉”“100%满意”等高风险词;如用户关键词含这些词,正文改写为中性表达。
|
||||||
- 结尾用接地气的大实话总结,并提示用户接下来应该核验什么信息来继续判断。
|
- 结尾用接地气的大实话总结,并提示用户接下来应该核验什么信息来继续判断。
|
||||||
@@ -630,7 +639,7 @@ platform_templates:
|
|||||||
模板: {{template_name}}
|
模板: {{template_name}}
|
||||||
语言: {{locale}}
|
语言: {{locale}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
|
|
||||||
任务:
|
任务:
|
||||||
@@ -651,7 +660,7 @@ platform_templates:
|
|||||||
当前年份: {{current_year}}
|
当前年份: {{current_year}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
|
|
||||||
要求:
|
要求:
|
||||||
- 标题应适用于品牌搜索意图、对比、问答和解释型内容。
|
- 标题应适用于品牌搜索意图、对比、问答和解释型内容。
|
||||||
@@ -668,7 +677,7 @@ platform_templates:
|
|||||||
标题: {{title}}
|
标题: {{title}}
|
||||||
品牌名: {{brand_name}}
|
品牌名: {{brand_name}}
|
||||||
优化关键词: {{primary_keyword}}
|
优化关键词: {{primary_keyword}}
|
||||||
官网: {{official_website}}
|
官网信息: 已提供时仅用于理解品牌背景,禁止在标题、大纲或正文中输出网址
|
||||||
已选段落: {{outline_sections}}
|
已选段落: {{outline_sections}}
|
||||||
补充关键词: {{supplemental_questions}}
|
补充关键词: {{supplemental_questions}}
|
||||||
关键要点: {{key_points}}
|
关键要点: {{key_points}}
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ func (s *ArticleImitationService) executeGeneration(ctx context.Context, job art
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content := strings.TrimSpace(result.Content)
|
content := sanitizeGeneratedArticleMarkdown(result.Content)
|
||||||
if content == "" {
|
if content == "" {
|
||||||
s.failGeneration(ctx, job, "llm_generate", fmt.Errorf("模型返回空内容"))
|
s.failGeneration(ctx, job, "llm_generate", fmt.Errorf("模型返回空内容"))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
generatedArticleSchemeURLPattern = `https?://[^\s<>"',。;、?!\]))}]+`
|
||||||
|
generatedArticleDomainPattern = `(?:www\.)?[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*\.(?:com|cn|net|org|io|ai|co|cc|top|xyz|shop|site|online|tech|app|gov|edu|info|biz|me|tv)(?:/[^\s<>"',。;、?!\]))}]*)?`
|
||||||
|
generatedArticleURLPattern = `(?:` + generatedArticleSchemeURLPattern + `|` + generatedArticleDomainPattern + `)`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
generatedArticleMarkdownLinkPattern = regexp.MustCompile(`!?\[([^\]\n]*)\]\(([^)\s]+)\)`)
|
||||||
|
generatedArticleHTMLImageParagraph = regexp.MustCompile(`(?is)<p\b[^>]*>\s*<img\b[^>]*\bsrc\s*=\s*["']?` + generatedArticleURLPattern + `["']?[^>]*>\s*</p>`)
|
||||||
|
generatedArticleHTMLImagePattern = regexp.MustCompile(`(?is)<img\b[^>]*\bsrc\s*=\s*["']?` + generatedArticleURLPattern + `["']?[^>]*>`)
|
||||||
|
generatedArticleURLLabelPattern = regexp.MustCompile(`(?i)(?:官网|官方网站|网站|网址|链接|URL|website|official website)\s*[::]?\s*` + generatedArticleURLPattern)
|
||||||
|
generatedArticleBareURLPattern = regexp.MustCompile(`(?i)\b` + generatedArticleURLPattern)
|
||||||
|
generatedArticleDanglingURLLabelPattern = regexp.MustCompile(`(?i)(?:官网|官方网站|网站|网址|链接|URL|website|official website)\s*[::]?\s*$`)
|
||||||
|
generatedArticleBlankLinePattern = regexp.MustCompile(`\n{3,}`)
|
||||||
|
generatedArticlePunctuationGapPattern = regexp.MustCompile(`\s+([,。;、?!,.;!?])`)
|
||||||
|
generatedArticleEmptyPunctuationPattern = regexp.MustCompile(`[::]\s*([,。;、?!,.;!?])`)
|
||||||
|
generatedArticleRepeatedSpacePattern = regexp.MustCompile(`[ \t]{2,}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func sanitizeGeneratedArticleMarkdown(markdown string) string {
|
||||||
|
text := strings.TrimSpace(markdown)
|
||||||
|
if text == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
text = generatedArticleMarkdownLinkPattern.ReplaceAllStringFunc(text, func(match string) string {
|
||||||
|
parts := generatedArticleMarkdownLinkPattern.FindStringSubmatch(match)
|
||||||
|
if len(parts) != 3 || !looksLikeGeneratedArticleURL(parts[2]) {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(match, "![") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
label := strings.TrimSpace(parts[1])
|
||||||
|
if label == "" || looksLikeGeneratedArticleURL(label) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
})
|
||||||
|
|
||||||
|
text = generatedArticleHTMLImageParagraph.ReplaceAllString(text, "")
|
||||||
|
text = generatedArticleHTMLImagePattern.ReplaceAllString(text, "")
|
||||||
|
text = generatedArticleURLLabelPattern.ReplaceAllString(text, "")
|
||||||
|
text = generatedArticleBareURLPattern.ReplaceAllString(text, "")
|
||||||
|
text = cleanGeneratedArticleURLArtifacts(text)
|
||||||
|
return strings.TrimSpace(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeGeneratedOutlineText(text string) string {
|
||||||
|
text = sanitizeGeneratedArticleMarkdown(text)
|
||||||
|
replacer := strings.NewReplacer(
|
||||||
|
"网站列表", "品牌信息整理",
|
||||||
|
"官网列表", "品牌信息整理",
|
||||||
|
"网址列表", "品牌信息整理",
|
||||||
|
"官方网站", "公开信息",
|
||||||
|
"官方网址", "公开信息",
|
||||||
|
)
|
||||||
|
return strings.TrimSpace(replacer.Replace(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func looksLikeGeneratedArticleURL(text string) bool {
|
||||||
|
text = strings.TrimSpace(text)
|
||||||
|
if text == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return generatedArticleBareURLPattern.MatchString(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanGeneratedArticleURLArtifacts(text string) string {
|
||||||
|
lines := strings.Split(text, "\n")
|
||||||
|
cleaned := make([]string, 0, len(lines))
|
||||||
|
for _, line := range lines {
|
||||||
|
originalLine := line
|
||||||
|
line = strings.TrimRight(line, " \t")
|
||||||
|
line = generatedArticleDanglingURLLabelPattern.ReplaceAllString(line, "")
|
||||||
|
line = strings.ReplaceAll(line, "网站列表", "品牌信息整理")
|
||||||
|
line = strings.ReplaceAll(line, "官网列表", "品牌信息整理")
|
||||||
|
line = strings.ReplaceAll(line, "网址列表", "品牌信息整理")
|
||||||
|
line = generatedArticleEmptyPunctuationPattern.ReplaceAllString(line, "$1")
|
||||||
|
line = generatedArticlePunctuationGapPattern.ReplaceAllString(line, "$1")
|
||||||
|
line = generatedArticleRepeatedSpacePattern.ReplaceAllString(line, " ")
|
||||||
|
if line != originalLine {
|
||||||
|
line = strings.TrimRight(line, " \t::,,;;")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(line) == "" {
|
||||||
|
if strings.TrimSpace(originalLine) == "" {
|
||||||
|
cleaned = append(cleaned, line)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isGeneratedArticleURLOnlyLine(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cleaned = append(cleaned, line)
|
||||||
|
}
|
||||||
|
return generatedArticleBlankLinePattern.ReplaceAllString(strings.Join(cleaned, "\n"), "\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isGeneratedArticleURLOnlyLine(line string) bool {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
trimmed = strings.TrimLeft(trimmed, "#-*+> \t")
|
||||||
|
trimmed = strings.Trim(trimmed, " \t::,,;;。")
|
||||||
|
if trimmed == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch strings.ToLower(trimmed) {
|
||||||
|
case "官网", "官方网站", "官方网址", "网站", "网址", "链接", "url", "website", "official website":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSanitizeGeneratedArticleMarkdownRemovesURLs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
markdown := strings.Join([]string{
|
||||||
|
"# 标题",
|
||||||
|
"",
|
||||||
|
"品牌官网:https://www.defnit.cn,可查询产品参数。",
|
||||||
|
"也可以查看 www.example.com/path 了解更多。",
|
||||||
|
"参考 [官网](https://defnit.cn/contact) 的信息整理。",
|
||||||
|
}, "\n")
|
||||||
|
|
||||||
|
got := sanitizeGeneratedArticleMarkdown(markdown)
|
||||||
|
|
||||||
|
for _, unexpected := range []string{"https://", "www.", "defnit.cn", "example.com", "[官网]("} {
|
||||||
|
if strings.Contains(got, unexpected) {
|
||||||
|
t.Fatalf("sanitizeGeneratedArticleMarkdown() = %q, contains %q", got, unexpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, expected := range []string{"# 标题", "可查询产品参数。", "也可以查看 了解更多。", "参考 官网 的信息整理。"} {
|
||||||
|
if !strings.Contains(got, expected) {
|
||||||
|
t.Fatalf("sanitizeGeneratedArticleMarkdown() = %q, want %q", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizeGeneratedArticleMarkdownRemovesHTMLImageURLs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
markdown := `<p class="article-editor-image"><img src="https://example.com/cover.webp" data-asset-id="88" /></p>`
|
||||||
|
|
||||||
|
got := sanitizeGeneratedArticleMarkdown(markdown)
|
||||||
|
|
||||||
|
if strings.TrimSpace(got) != "" {
|
||||||
|
t.Fatalf("sanitizeGeneratedArticleMarkdown() = %q, want generated image URL removed", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSanitizeGeneratedArticleMarkdownKeepsNormalPunctuation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
markdown := strings.Join([]string{
|
||||||
|
"## 选择建议:",
|
||||||
|
"下面从几个维度看。",
|
||||||
|
}, "\n")
|
||||||
|
|
||||||
|
got := sanitizeGeneratedArticleMarkdown(markdown)
|
||||||
|
|
||||||
|
if got != markdown {
|
||||||
|
t.Fatalf("sanitizeGeneratedArticleMarkdown() = %q, want %q", got, markdown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeOutlineNodesRemovesURLsAndWebsiteListLabel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
nodes := []OutlineNode{
|
||||||
|
{
|
||||||
|
Outline: "网站列表",
|
||||||
|
Children: []OutlineNode{
|
||||||
|
{Outline: "丹福尼门锁官方网站:www.defnit.cn,可查询产品参数"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := normalizeOutlineNodes(nodes, 2)
|
||||||
|
if len(got) != 1 || got[0].Outline != "品牌信息整理" {
|
||||||
|
t.Fatalf("normalizeOutlineNodes() = %#v, want top outline sanitized", got)
|
||||||
|
}
|
||||||
|
if len(got[0].Children) != 1 {
|
||||||
|
t.Fatalf("normalizeOutlineNodes() children = %#v, want one child", got[0].Children)
|
||||||
|
}
|
||||||
|
child := got[0].Children[0].Outline
|
||||||
|
if strings.Contains(child, "www.") || strings.Contains(child, "defnit.cn") || strings.Contains(child, "官方网站") {
|
||||||
|
t.Fatalf("normalizeOutlineNodes() child = %q, still contains website text", child)
|
||||||
|
}
|
||||||
|
if !strings.Contains(child, "丹福尼门锁") || !strings.Contains(child, "可查询产品参数") {
|
||||||
|
t.Fatalf("normalizeOutlineNodes() child = %q, lost useful context", child)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -559,7 +559,7 @@ func (s *PromptRuleGenerationService) executeGeneration(ctx context.Context, job
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content := strings.TrimSpace(result.Content)
|
content := sanitizeGeneratedArticleMarkdown(result.Content)
|
||||||
if content == "" {
|
if content == "" {
|
||||||
s.failGeneration(ctx, job, title, "llm_generate", fmt.Errorf("模型返回空内容"))
|
s.failGeneration(ctx, job, title, "llm_generate", fmt.Errorf("模型返回空内容"))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1222,7 +1222,7 @@ func normalizeOutlineNodes(nodes []OutlineNode, depth int) []OutlineNode {
|
|||||||
|
|
||||||
items := make([]OutlineNode, 0, len(nodes))
|
items := make([]OutlineNode, 0, len(nodes))
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
outline := strings.TrimSpace(node.Outline)
|
outline := sanitizeGeneratedOutlineText(node.Outline)
|
||||||
if outline == "" {
|
if outline == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ func buildPromptContext(params map[string]interface{}) string {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch key {
|
switch key {
|
||||||
case "knowledge_group_ids", "knowledge_groups", "knowledge_context":
|
case "knowledge_group_ids", "knowledge_groups", "knowledge_context", "official_website", "website", "input_params":
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if key == "outline_sections" && hasStructuredOutline(params["article_outline"]) {
|
if key == "outline_sections" && hasStructuredOutline(params["article_outline"]) {
|
||||||
|
|||||||
@@ -520,7 +520,7 @@ func (s *TemplateService) executeGeneration(ctx context.Context, job generationJ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content := strings.TrimSpace(result.Content)
|
content := sanitizeGeneratedArticleMarkdown(result.Content)
|
||||||
if content == "" {
|
if content == "" {
|
||||||
s.failGeneration(ctx, job, title, "llm_generate", fmt.Errorf("模型返回空内容"))
|
s.failGeneration(ctx, job, title, "llm_generate", fmt.Errorf("模型返回空内容"))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ var platformTemplateSeeds = []PlatformTemplateSeed{
|
|||||||
"preview_caption": "预览会根据已选标题和结构实时更新。",
|
"preview_caption": "预览会根据已选标题和结构实时更新。",
|
||||||
"sections": [
|
"sections": [
|
||||||
{ "key": "intro", "label": "引言", "default": true },
|
{ "key": "intro", "label": "引言", "default": true },
|
||||||
{ "key": "site_list", "label": "网站列表", "default": true },
|
|
||||||
{ "key": "key_points", "label": "文章关键要点", "default": true },
|
{ "key": "key_points", "label": "文章关键要点", "default": true },
|
||||||
{ "key": "what_is_question", "label_template": "围绕{{brand_question}}展开", "default": false },
|
{ "key": "what_is_question", "label_template": "围绕{{brand_question}}展开", "default": false },
|
||||||
{ "key": "features", "label": "特点", "default": false },
|
{ "key": "features", "label": "特点", "default": false },
|
||||||
|
|||||||
Reference in New Issue
Block a user