feat(tenant): enforce English output for en-US template generation

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>
This commit is contained in:
2026-07-09 19:38:55 +08:00
parent f69edc2218
commit 2c394d436a
7 changed files with 159 additions and 7 deletions
+58 -4
View File
@@ -806,12 +806,20 @@ func buildAnalyzePrompt(templateKey, templateName string, req AnalyzeTaskRequest
basePrompt := strings.TrimSpace(renderPromptTemplate(analyzePromptTemplate, contextPayload))
if basePrompt != "" {
contextJSON, _ := json.MarshalIndent(contextPayload, "", " ")
return prompts.TemplateContextWithJSONExample(basePrompt, string(contextJSON), prompts.AnalyzeOutputExample())
sections := []string{prompts.TemplateContextWithJSONExample(basePrompt, string(contextJSON), prompts.AnalyzeOutputExample())}
if localeGuard := buildAssistLocaleOutputGuard(req.Locale, "analyze"); localeGuard != "" {
sections = append(sections, localeGuard)
}
return strings.Join(sections, "\n\n")
}
}
contextJSON, _ := json.MarshalIndent(contextPayload, "", " ")
return prompts.AnalyzeFallbackPrompt(string(contextJSON))
sections := []string{prompts.AnalyzeFallbackPrompt(string(contextJSON))}
if localeGuard := buildAssistLocaleOutputGuard(req.Locale, "analyze"); localeGuard != "" {
sections = append(sections, localeGuard)
}
return strings.Join(sections, "\n\n")
}
func buildAnalyzeAIPointMeteredText(req AnalyzeTaskRequest) string {
@@ -829,12 +837,19 @@ func buildTitlePrompt(templateKey, templateName string, req TitleTaskRequest, ti
sections = append(sections, prompts.PromptContextSection(contextBlock))
}
sections = append(sections, prompts.TitleCustomOutputRequirementsSection())
if localeGuard := buildAssistLocaleOutputGuard(req.Locale, "title"); localeGuard != "" {
sections = append(sections, localeGuard)
}
return strings.Join(sections, "\n\n")
}
}
contextJSON, _ := json.MarshalIndent(contextPayload, "", " ")
return prompts.TitleFallbackPrompt(string(contextJSON))
sections := []string{prompts.TitleFallbackPrompt(string(contextJSON))}
if localeGuard := buildAssistLocaleOutputGuard(req.Locale, "title"); localeGuard != "" {
sections = append(sections, localeGuard)
}
return strings.Join(sections, "\n\n")
}
func buildOutlinePrompt(templateKey, templateName string, req OutlineTaskRequest, outlinePromptTemplate *string) string {
@@ -850,12 +865,51 @@ func buildOutlinePrompt(templateKey, templateName string, req OutlineTaskRequest
}
sections = append(sections, prompts.OutlineCustomOutputRequirementsSection())
sections = append(sections, prompts.JSONOutputExampleSection(outputExample))
if localeGuard := buildAssistLocaleOutputGuard(req.Locale, "outline"); localeGuard != "" {
sections = append(sections, localeGuard)
}
return strings.Join(sections, "\n\n")
}
}
contextJSON, _ := json.MarshalIndent(contextPayload, "", " ")
return prompts.OutlineFallbackPrompt(string(contextJSON))
sections := []string{prompts.OutlineFallbackPrompt(string(contextJSON))}
if localeGuard := buildAssistLocaleOutputGuard(req.Locale, "outline"); localeGuard != "" {
sections = append(sections, localeGuard)
}
return strings.Join(sections, "\n\n")
}
func buildAssistLocaleOutputGuard(locale, artifact string) string {
if strings.TrimSpace(locale) != "en-US" {
return ""
}
switch artifact {
case "analyze":
return strings.Join([]string{
"High-priority language consistency requirement:",
"- Return English JSON field values for brand_summary, keywords, competitor descriptions, and any generated explanatory text.",
"- Competitor, brand, company, and product names may remain in their official language, but do not output Chinese prose or untranslated Chinese template wording.",
"- Treat Chinese template wording and examples as internal instructions only.",
}, "\n")
case "title":
return strings.Join([]string{
"High-priority language consistency requirement:",
"- Return English JSON strings only.",
"- The titles must be natural, professional English titles; do not output Chinese words, Chinese numerals, or Chinese title patterns.",
"- Treat Chinese template wording and examples as internal instructions only.",
}, "\n")
case "outline":
return strings.Join([]string{
"High-priority language consistency requirement:",
"- Every outline and child outline value must be natural, professional English.",
"- Do not output Chinese section names, Chinese prose, Chinese punctuation patterns, or untranslated Chinese template wording.",
"- If any selected section, keyword, or context value is Chinese, translate or adapt the generated outline wording into English while preserving intent.",
}, "\n")
default:
return ""
}
}
func buildTitleAIPointMeteredText(req TitleTaskRequest) string {