Files
geo/server/internal/tenant/prompts/runtime.go
T
root dbd7747742 feat: add knowledge markdown formatting and detail retrieval
- Implemented KnowledgeWebsiteMarkdownPrompt for formatting webpage content into Markdown.
- Added GetItemDetail method in KnowledgeHandler to retrieve knowledge item details.
- Introduced KnowledgeDetailDrawer component for displaying knowledge item details in the admin web interface.
- Created MarkdownPreview component for rendering Markdown content.
- Added knowledge chunking and URL parsing logic to handle webpage content extraction and formatting.
- Updated database schema to include markdown_content in knowledge_items table.
2026-04-05 20:41:42 +08:00

350 lines
12 KiB
Go

package prompts
import (
"fmt"
"strings"
)
func DefaultGenerationBasePrompt(templateName string) string {
return fmt.Sprintf("你是一名专业内容编辑,请基于已提供的信息,为模板「%s」写出一篇完整的 Markdown 文章。", templateName)
}
func PromptContextSection(contextBlock string) string {
return "当前上下文:\n" + contextBlock
}
func GenerationWritingRequirementsSection() string {
return strings.Join([]string{
"写作总要求:",
"- 仅返回文章 Markdown 正文,不要附带额外说明、提示语或代码块。",
"- 输出语言与 locale 一致:zh-CN 使用简体中文,en-US 使用自然、专业的英语。",
"- 如提供了 title,使用该标题作为文章主标题,并围绕它展开,不要另起一个无关标题。",
"- 如提供了 article_outline,一级节点是正文的最终小节标题,必须按顺序展开;二级及更深节点仅作为该小节的行文思路、论证顺序或信息要点,不要机械写成额外标题。",
"- 除主标题和一级小节标题外,默认不要把大纲子节点直接写成 Markdown 标题;子节点内容应自然融入段落、列表或过渡句中。",
"- 如提供了 key_points,正文中必须覆盖这些重点,不要遗漏。",
"- 每个核心段落都要有明确判断、原因解释、适用场景或对比维度,避免空话、套话和重复表述。",
"- 信息不足时,不要编造具体事实、价格、数据、测试结果、用户评价或机构结论;可以用稳妥表述说明判断边界。",
"- 保持客观、克制、非广告化,不使用夸张宣传语,如“顶级”“颠覆性”“完美”等。",
"- 使用清晰的小节标题和短段落;仅在确实有助于理解时使用列表。",
"- 结尾应给出清晰结论、适合对象、不适合对象,或下一步建议,帮助读者完成判断。",
}, "\n")
}
func GenerationTemplateSpecificRulesHeading() string {
return "模板专项要求:"
}
func GenerationLengthGuidanceHeading() string {
return "篇幅要求:"
}
func TopXBrandPriorityRules(brandName string) string {
return strings.Join([]string{
fmt.Sprintf("- 若文章包含品牌、公司或服务商榜单,请将「%s」排在第 1 位,并作为全文主推对象。", brandName),
fmt.Sprintf("- 「%s」对应条目的篇幅应明显多于其他对象,优先展开其核心优势、本地化服务、适合人群、报价或配置亮点、交付安装、售后表现与避坑建议。", brandName),
"- 其他对象保留客观介绍,但篇幅更短,重点写与主推对象的差异点和各自适用场景,不要平均分配篇幅。",
fmt.Sprintf("- 结论部分先明确推荐「%s」作为优先选择,再简要补充其他对象分别适合哪些预算、户型或需求。", brandName),
fmt.Sprintf("- 如果标题或正文属于“Top X / 推荐 / 最好 / 排行榜”这类排序导向内容,榜单顺序必须与结论保持一致,将「%s」放在第 1 位。", brandName),
"- 在不编造事实、不捏造数据的前提下,优先呈现主推对象的优势信息;对其他对象保持克制、客观的弱化式比较。",
}, "\n")
}
func EnglishLengthGuidance(minWords, maxWords int) string {
return strings.Join([]string{
fmt.Sprintf("- 建议全文控制在 %d-%d 个英文单词,优先信息密度,不要为了拉长篇幅重复表达。", minWords, maxWords),
"- 引言和结论保持简洁,大多数一级章节写 1-3 个紧凑段落即可;只有最重要的章节需要展开。",
"- 一段能说清的内容不要拆成多段重复表达。",
}, "\n")
}
func ChineseLengthGuidance(minChars, maxChars int) string {
return strings.Join([]string{
fmt.Sprintf("- 建议全文控制在 %d-%d 字左右,优先信息密度,不要为了凑字数重复表达。", minChars, maxChars),
"- 引言和结论保持简洁,大多数一级章节写 1-3 个自然段即可;只有最关键的章节需要更充分展开。",
"- 能用一段说清的内容,不要拆成多段同义反复;主体部分重点写判断、原因、场景和建议。",
}, "\n")
}
func ContextLabel(key string) string {
switch key {
case "locale":
return "语言"
case "title":
return "标题"
case "topic":
return "主题"
case "product_name":
return "产品名"
case "subject":
return "研究主题"
case "brand_name":
return "品牌名"
case "brand":
return "品牌"
case "official_website", "website":
return "官网"
case "primary_keyword":
return "核心关键词"
case "keywords", "existing_keywords":
return "关键词"
case "competitors", "existing_competitors":
return "竞品"
case "competitor_names":
return "竞品名称"
case "competitor_count":
return "竞品数量"
case "brand_summary":
return "品牌摘要"
case "category":
return "品类"
case "count":
return "数量"
case "top_count":
return "推荐数量"
case "keyword_count":
return "关键词数量"
case "depth":
return "深度"
case "article_outline":
return "文章大纲"
case "outline_sections":
return "已选段落"
case "key_points":
return "关键要点"
case "review_intro_hook":
return "评测引言钩子"
case "template_key":
return "模板标识"
case "template_name":
return "模板名称"
case "current_year":
return "当前年份"
case "input_params":
return "输入参数"
default:
return key
}
}
func TemplateContextWithJSONExample(basePrompt, contextJSON, outputExample string) string {
return strings.TrimSpace(fmt.Sprintf("%s\n\n模板上下文:\n%s\n\nJSON 输出示例:\n%s", basePrompt, contextJSON, outputExample))
}
func JSONOutputExampleSection(outputExample string) string {
return "JSON 输出示例:\n" + outputExample
}
func AnalyzeOutputExample() string {
return `{
"brand_summary": "一句简洁的品牌/主题描述",
"keywords": ["关键词1", "关键词2", "关键词3", "关键词4", "关键词5"],
"competitors": [
{
"name": "竞品名",
"website": "https://example.com",
"description": "一句简介"
}
]
}`
}
func AnalyzeFallbackPrompt(contextJSON string) string {
return strings.TrimSpace(fmt.Sprintf(`
你是一位专业的 GEO 内容策略师。
任务:
1. 分析品牌/主题上下文。
2. 推荐最相关的 GEO 文章关键词。
3. 推荐可信的竞品网站或竞争品牌,用于对比或引用。
规则:
- 仅返回 JSON,不要用 Markdown 代码块包裹。
- 使用与请求的语言设置一致。zh-CN 用简体中文,en-US 用英文。
- 关键词应为简洁的搜索短语。
- 竞品应去重且真实。不确定时 website 字段可留空,但不要编造虚假链接。
- brand_summary 限 1-2 句。
- 最多返回 6 个竞品和 5 个关键词。
模板上下文:
%s
JSON 输出示例:
%s
`, contextJSON, AnalyzeOutputExample()))
}
func TitleCustomOutputRequirementsSection() string {
return strings.Join([]string{
"输出要求:",
"- 仅返回 JSON,不要用 Markdown 代码块包裹。",
"- 返回恰好 5 个字符串的 JSON 数组。",
"- 尊重当前关键词列表和竞品上下文,因为用户可能已手动编辑。",
}, "\n")
}
func TitleOutputExample() string {
return `[
"标题 1",
"标题 2",
"标题 3",
"标题 4",
"标题 5"
]`
}
func TitleFallbackPrompt(contextJSON string) string {
return strings.TrimSpace(fmt.Sprintf(`
你是一位专业的 GEO 内容策略师。
任务:
为当前内容摘要生成 5 个优质文章标题候选。
规则:
- 仅返回 JSON,不要用 Markdown 代码块包裹。
- 返回 5 个字符串的 JSON 数组,不要返回对象。
- 使用与请求的语言设置一致。zh-CN 用简体中文,en-US 用英文。
- 标题应实用、具体,并契合当前模板的内容方向。
- 尊重当前关键词列表和竞品上下文,因为用户可能已手动编辑。
- 避免泛泛的广告文案和空洞口号。
模板上下文:
%s
JSON 输出示例:
%s
`, contextJSON, TitleOutputExample()))
}
func OutlineOutputExample() string {
return `{
"outline": [
{
"outline": "网站列表",
"children": [
{ "outline": "竞品 A" },
{ "outline": "竞品 B" }
]
},
{
"outline": "文章关键要点",
"children": [
{ "outline": "要点 1" },
{ "outline": "要点 2" }
]
}
]
}`
}
func OutlineCustomOutputRequirementsSection() string {
return strings.Join([]string{
"输出要求:",
"- 仅返回 JSON,不要用 Markdown 代码块包裹。",
`- 返回 JSON 对象,格式固定为 {"outline":[...]}`,
`- outline 字段是顶层大纲节点数组,每个节点格式为 {"outline":"...","children":[...]}`,
"- 每个已选大纲段落应作为顶层节点出现,语言与请求保持一致。",
"- 尊重当前关键词、竞品和关键要点,因为用户可能已手动编辑。",
"- 不要返回额外字段,不要返回纯数组,不要返回说明文字。",
}, "\n")
}
func OutlineFallbackPrompt(contextJSON string) string {
return strings.TrimSpace(fmt.Sprintf(`
你是一位专业的 GEO 内容策略师。
任务:
为当前内容摘要生成一份实用的文章大纲。
规则:
- 仅返回 JSON,不要用 Markdown 代码块包裹。
- 返回 JSON 对象,格式固定为 {"outline":[...]}。
- outline 字段中的每个顶层节点 "outline" 值必须保持已选段落标签原文。
- 在合适的位置为每个顶层段落添加简洁的子大纲条目。
- 使用与请求的语言设置一致。zh-CN 用简体中文,en-US 用英文。
- 尊重当前标题、关键词、竞品、关键要点和品牌上下文,因为用户可能已手动编辑。
- 大纲应具体、不含推广语气,适合后续完整成文。
- 不要返回额外字段,不要返回纯数组,不要返回说明文字。
模板上下文:
%s
JSON 输出示例:
%s
`, contextJSON, OutlineOutputExample()))
}
func OutlineResponseFormatDescription() string {
return "文章大纲 JSON 对象,必须包含 outline 数组字段。"
}
func PromptRuleTaskNameSupplement(taskName string) string {
return "任务名称:" + taskName
}
func PromptRuleSceneSupplement(scene string) string {
return "适用场景:" + scene
}
func PromptRuleToneSupplement(tone string) string {
return "默认语气:" + tone
}
func PromptRuleWordCountSupplement(wordCount int) string {
return fmt.Sprintf("建议字数:%d 字左右", wordCount)
}
func PromptRuleTargetPlatformSupplement(target string) string {
return "目标发布平台:" + strings.ReplaceAll(target, ",", "、")
}
func PromptRuleSupplementSection(items []string) string {
return "补充要求:\n- " + strings.Join(items, "\n- ")
}
func PromptRuleOutputRequirementsSection() string {
return "输出要求:\n1. 直接输出完整可发布的中文 Markdown 文章。\n2. 标题使用一级标题,并由 AI 根据正文内容自行生成,不要直接复用任务名称。\n3. 不要输出你的思考过程、解释或致歉。"
}
func KnowledgePromptIntroLines() []string {
return []string{
"以下为可参考的知识库内容,只用于补充事实背景、术语定义、产品信息或表达素材:",
"- 优先吸收并改写,不要逐段照抄。",
"- 若知识库内容与用户明确输入冲突,以用户输入为准。",
"- 信息不足时不要虚构事实。",
}
}
func KnowledgeSnippetLabel(index int) string {
return fmt.Sprintf("知识片段 %d", index+1)
}
func KnowledgeWebsiteMarkdownPrompt(title, rawURL, content string, chunkIndex, chunkCount int) string {
scope := "以下是网页正文。"
if chunkCount > 1 {
scope = fmt.Sprintf("以下是网页正文的第 %d/%d 段。", chunkIndex+1, chunkCount)
}
var builder strings.Builder
builder.WriteString("你是知识库 Markdown 整理助手。\n")
builder.WriteString("任务:把给定网页正文整理为适合知识库只读预览的 Markdown。\n")
builder.WriteString("要求:\n")
builder.WriteString("1. 只根据原文整理,不要补充事实,不要总结扩写。\n")
builder.WriteString("2. 保留标题、列表、表格、编号、联系方式等关键信息。\n")
builder.WriteString("3. 如果原文明显是表格,请输出 Markdown 表格。\n")
builder.WriteString("4. 不要输出解释,不要输出代码块包裹,只输出 Markdown 正文。\n")
if strings.TrimSpace(title) != "" {
builder.WriteString("网页标题:")
builder.WriteString(title)
builder.WriteString("\n")
}
if strings.TrimSpace(rawURL) != "" {
builder.WriteString("网页地址:")
builder.WriteString(rawURL)
builder.WriteString("\n")
}
builder.WriteString(scope)
builder.WriteString("\n\n")
builder.WriteString(content)
return builder.String()
}