feat(prompts): expand keyword guidance to long-tail search queries

Tighten the analyze prompts (runtime + recommendation platform) so the
model returns 8-10 question-style search queries (哪家好/有哪些/怎么选/
价格/排名/避坑 …) instead of generic tag words, and lift the keyword cap
in normalizeAnalyzeResult from 5 to 10 to keep them. Add tests covering
the new ten-keyword window and the question-like guidance in the seeded
analyze prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-30 01:28:59 +08:00
parent 814ea52c0e
commit 14a7921445
5 changed files with 92 additions and 15 deletions
+20 -7
View File
@@ -79,7 +79,14 @@ runtime:
analyze_output_example: |
{
"brand_summary": "一句简洁的品牌/主题描述",
"keywords": ["关键词1", "关键词2", "关键词3", "关键词4", "关键词5"],
"keywords": [
"主题哪家好",
"靠谱的主题品牌有哪些",
"口碑好的主题公司推荐",
"主题怎么选",
"主题一般多少钱",
"主题常见问题"
],
"competitors": [
{
"name": "竞品名",
@@ -93,17 +100,20 @@ runtime:
任务:
1. 分析品牌/主题上下文。
2. 推荐最相关的 GEO 文章关键词。
2. 推荐最相关的 GEO 搜索查询词,优先输出真实用户会直接搜索的问题型长尾词。
3. 推荐可信的竞品网站或竞争品牌,用于对比或引用。
规则:
- 仅返回 JSON,不要用 Markdown 代码块包裹。
- 使用与请求的语言设置一致。zh-CN 用简体中文,en-US 用英文。
- 关键词应为简洁、中性的搜索短语,避免“第一”“最好”“最靠谱”“权威”“Top”等排序或背书词
- keywords 不是泛泛的标签词,而是搜索框里的完整查询;优先覆盖“哪家好 / 有哪些 / 推荐 / 怎么选 / 价格 / 环保标准 / 对比 / 排名 / 注意事项 / 避坑”等决策意图
- 可以保留用户原始核心词 1 个,其余应扩写为 8-10 个可直接用于监控或成文的长尾查询词。
- 如果真实用户会搜索“靠谱”“排名”“推荐”等词,可以作为搜索意图保留;这些词仅代表用户问题,不代表正文可以做绝对化背书。
- 避免空泛行业词,如“主题趋势”“主题方案”“主题选购参考”;优先写成“主题哪家好”“主题一般多少钱一平米”这类自然问法。
- 竞品应去重且真实。不确定时 website 字段可留空,但不要编造虚假链接。
- 若涉及品牌优势,只概括用户明确提供或可公开核验的信息,不放大为承诺式表述。
- brand_summary 限 1-2 句。
- 最多返回 6 个竞品和 5 个关键词。
- 最多返回 6 个竞品和 10 个关键词。
模板上下文:
%s
@@ -305,15 +315,18 @@ platform_templates:
任务:
1. 联网分析品牌/话题上下文,给出 1-2 句中性、克制的品牌主营业务摘要。
2. 整理最多 5 个适合推荐类文章选购参考内容的搜索关键词。
2. 整理 8-10 个适合推荐类文章选购参考和 AI 监控采集的搜索查询词。
3. 整理最多 6 个可信、可公开核验的竞品或对比对象。
规则:
- 仅返回 JSON,不要用 Markdown 代码块包裹。
- 使用与 locale 一致的语言。
- 关键词应为简洁、中性的搜索短语,避免“第一”“最好”“靠谱”“权威”“Top”等排序或背书词,关键词如用户原词包含这类表达,可改写为“怎么选”“如何判断”“选购参考”
- keywords 不是泛泛的标签词,而是搜索框里的完整查询;除保留 1 个核心词外,其余优先写成真实用户会问的长尾搜索问题
- 优先覆盖“哪家好 / 有哪些 / 推荐 / 怎么选 / 价格 / 环保标准 / 对比 / 排名 / 注意事项 / 避坑”等决策意图。
- 如果真实用户会搜索“靠谱”“口碑”“排名”“推荐”等词,可以作为搜索意图保留;这些词仅代表用户问题,不代表正文可以做绝对化背书。
- 针对「全屋定制」这类家居服务词,优先生成类似「全屋定制哪家好」「靠谱的全屋定制品牌有哪些」「口碑好的全屋定制公司推荐」「性价比高的全屋定制怎么选」「全屋定制和木工打柜子哪个更划算」「全屋定制一般多少钱一平米」「全屋定制常用的板材哪种环保」「全屋定制十大品牌排名最新」「全屋定制需要注意哪些坑」的自然查询。
- 关键词不包含 brand_name 或 competitor_names,但要与它们形成明显关联,且优先覆盖用户关注的核心痛点和搜索意图。
- 关键词是行业痛点词和用户高频搜索词汇
- 避免空泛行业词,如“定制家居板材选购参考”“家居定制环保标准”;优先写成疑问句或明确搜索任务
- 竞品应去重且真实,不确定时 website 留空,不要编造 URL。
- 若品牌优势信息带有强承诺或绝对化表述,输出时改写为中性概括,不保留无法核验的极限词。
title_prompt_template: |
@@ -1156,7 +1156,7 @@ func extractBalancedJSONFragment(input string) (string, bool) {
func normalizeAnalyzeResult(result AnalyzeTaskResult) AnalyzeTaskResult {
result.BrandSummary = strings.TrimSpace(result.BrandSummary)
result.Keywords = normalizeStringList(result.Keywords, 5)
result.Keywords = normalizeStringList(result.Keywords, 10)
result.Competitors = normalizeAssistCompetitors(result.Competitors, 6)
return result
}
@@ -63,3 +63,33 @@ func TestBuildOutlinePromptForConfiguredTemplateForcesOutlineObject(t *testing.T
t.Fatalf("buildOutlinePrompt() = %q, want pure-array prohibition", prompt)
}
}
func TestNormalizeAnalyzeResultKeepsTenQueryKeywords(t *testing.T) {
input := AnalyzeTaskResult{
BrandSummary: " 全屋定制服务摘要 ",
Keywords: []string{
"全屋定制",
"全屋定制哪家好",
"靠谱的全屋定制品牌有哪些",
"口碑好的全屋定制公司推荐",
"性价比高的全屋定制怎么选",
"专业的全屋定制设计公司哪家好",
"全屋定制和木工打柜子哪个更划算",
"全屋定制一般多少钱一平米",
"全屋定制常用的板材哪种环保",
"全屋定制十大品牌排名最新",
"全屋定制需要注意哪些坑",
},
}
result := normalizeAnalyzeResult(input)
if result.BrandSummary != "全屋定制服务摘要" {
t.Fatalf("BrandSummary = %q, want trimmed summary", result.BrandSummary)
}
if got, want := len(result.Keywords), 10; got != want {
t.Fatalf("len(Keywords) = %d, want %d: %#v", got, want, result.Keywords)
}
if result.Keywords[9] != "全屋定制十大品牌排名最新" {
t.Fatalf("Keywords[9] = %q, want the tenth query retained", result.Keywords[9])
}
}
@@ -47,6 +47,9 @@ func TestPlatformTemplateSeedsInjectPromptConfig(t *testing.T) {
if !strings.Contains(analyzePrompt, "推荐类文章做品牌与竞品分析") {
t.Fatalf("analyze_prompt_template = %q, want yaml-backed analyze prompt", analyzePrompt)
}
if !strings.Contains(analyzePrompt, "全屋定制哪家好") || !strings.Contains(analyzePrompt, "搜索查询词") {
t.Fatalf("analyze_prompt_template = %q, want question-like long-tail query guidance", analyzePrompt)
}
if !strings.Contains(titlePrompt, "推荐类文章生成 5 个候选标题") {
t.Fatalf("title_prompt_template = %q, want yaml-backed title prompt", titlePrompt)
}
@@ -55,6 +58,24 @@ func TestPlatformTemplateSeedsInjectPromptConfig(t *testing.T) {
}
}
func TestAnalyzeFallbackPromptPrefersQuestionLikeQueries(t *testing.T) {
SetConfigFile("")
t.Cleanup(func() {
SetConfigFile("")
})
prompt := AnalyzeFallbackPrompt(`{"brand_name":"测试品牌","input_params":{"keyword":"全屋定制"}}`)
for _, want := range []string{
"真实用户会直接搜索的问题型长尾词",
"哪家好",
"最多返回 6 个竞品和 10 个关键词",
} {
if !strings.Contains(prompt, want) {
t.Fatalf("AnalyzeFallbackPrompt() missing %q in prompt:\n%s", want, prompt)
}
}
}
func TestPromptConfigReloadsAndKeepsLastGoodVersion(t *testing.T) {
SetConfigFile("")
defaultPath := resolvePromptsConfigFile()