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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user