2c394d436a
When the article locale is en-US, Chinese prompt templates could leak Chinese wording into generated titles, outlines, and article bodies. Add high-priority language-consistency guards so the model treats the Chinese template text as internal instructions and emits English only. - Append per-artifact locale guards (analyze/title/outline) in template assist prompts and an article-body guard in the generation prompt, only when locale is en-US - Reinforce locale consistency in the title/outline runtime prompts and drop retained images from the rewrite prompt - Cover the new guards with unit tests Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
154 lines
5.5 KiB
Go
154 lines
5.5 KiB
Go
package app
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDecodeOutlineResultAcceptsWrappedObject(t *testing.T) {
|
|
raw := `{"outline":[{"outline":"网站列表","children":[{"outline":"竞品 A"}]}]}`
|
|
|
|
result, err := decodeOutlineResult(raw)
|
|
if err != nil {
|
|
t.Fatalf("decodeOutlineResult() error = %v", err)
|
|
}
|
|
if len(result) != 1 || result[0].Outline != "网站列表" {
|
|
t.Fatalf("decodeOutlineResult() = %#v, want outline payload", result)
|
|
}
|
|
if len(result[0].Children) != 1 || result[0].Children[0].Outline != "竞品 A" {
|
|
t.Fatalf("decodeOutlineResult() children = %#v, want child outline", result[0].Children)
|
|
}
|
|
}
|
|
|
|
func TestDecodeOutlineResultExtractsArrayFromNarrativeText(t *testing.T) {
|
|
raw := "以下是整理后的结果:\n```json\n[\n {\"outline\":\"文章关键要点\",\"children\":[{\"outline\":\"要点 1\"}]}\n]\n```\n请直接使用。"
|
|
|
|
result, err := decodeOutlineResult(raw)
|
|
if err != nil {
|
|
t.Fatalf("decodeOutlineResult() error = %v", err)
|
|
}
|
|
if len(result) != 1 || result[0].Outline != "文章关键要点" {
|
|
t.Fatalf("decodeOutlineResult() = %#v, want extracted array payload", result)
|
|
}
|
|
}
|
|
|
|
func TestDecodeOutlineResultExtractsInnerArrayFromMalformedWrapper(t *testing.T) {
|
|
raw := "结果如下:{\n[\n {\"outline\":\"常见问题\"}\n]\n}"
|
|
|
|
result, err := decodeOutlineResult(raw)
|
|
if err != nil {
|
|
t.Fatalf("decodeOutlineResult() error = %v", err)
|
|
}
|
|
if len(result) != 1 || result[0].Outline != "常见问题" {
|
|
t.Fatalf("decodeOutlineResult() = %#v, want inner array payload", result)
|
|
}
|
|
}
|
|
|
|
func TestBuildOutlinePromptForConfiguredTemplateForcesOutlineObject(t *testing.T) {
|
|
template := "你正在为文章生成大纲。\n标题: {{title}}\n已选段落: {{outline_sections}}"
|
|
|
|
prompt := buildOutlinePrompt("recommendation_list", "推荐类模板", OutlineTaskRequest{
|
|
Locale: "zh-CN",
|
|
Title: "测试标题",
|
|
OutlineSections: []string{"引言", "文章关键要点"},
|
|
}, &template)
|
|
|
|
if !strings.Contains(prompt, `{"outline":[...]}`) {
|
|
t.Fatalf("buildOutlinePrompt() = %q, want fixed outline object instruction", prompt)
|
|
}
|
|
if !strings.Contains(prompt, "JSON 输出示例") {
|
|
t.Fatalf("buildOutlinePrompt() = %q, want JSON example", prompt)
|
|
}
|
|
if !strings.Contains(prompt, "不要返回纯数组") {
|
|
t.Fatalf("buildOutlinePrompt() = %q, want pure-array prohibition", prompt)
|
|
}
|
|
}
|
|
|
|
func TestBuildAnalyzePromptEnglishLocaleAddsHardLanguageGuard(t *testing.T) {
|
|
template := "你正在做品牌分析。\n语言: {{locale}}\n品牌名: {{brand_name}}"
|
|
|
|
prompt := buildAnalyzePrompt("top_x_article", "Top X 推荐文章", AnalyzeTaskRequest{
|
|
Locale: "en-US",
|
|
BrandName: "Rankly",
|
|
}, &template)
|
|
|
|
for _, expected := range []string{
|
|
"High-priority language consistency requirement:",
|
|
"Return English JSON field values for brand_summary, keywords, competitor descriptions",
|
|
"Competitor, brand, company, and product names may remain in their official language",
|
|
} {
|
|
if !strings.Contains(prompt, expected) {
|
|
t.Fatalf("buildAnalyzePrompt() = %q, want English language guard %q", prompt, expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildTitlePromptEnglishLocaleAddsHardLanguageGuard(t *testing.T) {
|
|
template := "你正在为文章生成标题。\n语言: {{locale}}\n优化关键词: {{primary_keyword}}"
|
|
|
|
prompt := buildTitlePrompt("top_x_article", "Top X 推荐文章", TitleTaskRequest{
|
|
Locale: "en-US",
|
|
BrandQuestion: "whole-home customization",
|
|
}, &template)
|
|
|
|
for _, expected := range []string{
|
|
"High-priority language consistency requirement:",
|
|
"Return English JSON strings only.",
|
|
"The titles must be natural, professional English titles",
|
|
} {
|
|
if !strings.Contains(prompt, expected) {
|
|
t.Fatalf("buildTitlePrompt() = %q, want English language guard %q", prompt, expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildOutlinePromptEnglishLocaleAddsHardLanguageGuard(t *testing.T) {
|
|
template := "你正在为文章生成大纲。\n语言: {{locale}}\n标题: {{title}}\n已选段落: {{outline_sections}}"
|
|
|
|
prompt := buildOutlinePrompt("top_x_article", "Top X 推荐文章", OutlineTaskRequest{
|
|
Locale: "en-US",
|
|
Title: "How to Choose Whole-home Customization",
|
|
OutlineSections: []string{"Introduction", "Key Points"},
|
|
}, &template)
|
|
|
|
for _, expected := range []string{
|
|
"High-priority language consistency requirement:",
|
|
"Every outline and child outline value must be natural, professional English.",
|
|
"Do not output Chinese section names",
|
|
} {
|
|
if !strings.Contains(prompt, expected) {
|
|
t.Fatalf("buildOutlinePrompt() = %q, want English language guard %q", prompt, expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
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])
|
|
}
|
|
}
|