From 14a7921445eba3731ecec567939093e3fd45076e Mon Sep 17 00:00:00 2001 From: liangxu Date: Thu, 30 Apr 2026 01:28:59 +0800 Subject: [PATCH] feat(prompts): expand keyword guidance to long-tail search queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- deploy/prompts.yml | 27 ++++++++++++----- server/configs/prompts.yml | 27 ++++++++++++----- server/internal/tenant/app/template_assist.go | 2 +- .../tenant/app/template_assist_test.go | 30 +++++++++++++++++++ server/internal/tenant/prompts/loader_test.go | 21 +++++++++++++ 5 files changed, 92 insertions(+), 15 deletions(-) diff --git a/deploy/prompts.yml b/deploy/prompts.yml index 80cb512..a8bee3e 100644 --- a/deploy/prompts.yml +++ b/deploy/prompts.yml @@ -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 @@ -264,15 +274,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: | diff --git a/server/configs/prompts.yml b/server/configs/prompts.yml index 0da95a8..b871899 100644 --- a/server/configs/prompts.yml +++ b/server/configs/prompts.yml @@ -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: | diff --git a/server/internal/tenant/app/template_assist.go b/server/internal/tenant/app/template_assist.go index 22a2440..c8ff59d 100644 --- a/server/internal/tenant/app/template_assist.go +++ b/server/internal/tenant/app/template_assist.go @@ -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 } diff --git a/server/internal/tenant/app/template_assist_test.go b/server/internal/tenant/app/template_assist_test.go index fa19b57..afd4c8c 100644 --- a/server/internal/tenant/app/template_assist_test.go +++ b/server/internal/tenant/app/template_assist_test.go @@ -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]) + } +} diff --git a/server/internal/tenant/prompts/loader_test.go b/server/internal/tenant/prompts/loader_test.go index 71e784d..aa52a1b 100644 --- a/server/internal/tenant/prompts/loader_test.go +++ b/server/internal/tenant/prompts/loader_test.go @@ -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()