feat(wxpa): 配图提示词规则从 Go 硬编码迁移到 JSON 配置驱动

- 新增 spec/06-image_prompt_generation/rule.json 定义配图分布规则(500 字间隔、封面图、正文图尺寸)
- 新增 prompt_template、bracket_format 等 JSON 配置,支持中文提示词模板
- 修改 buildCoverImagePrompt/buildContentImagePrompt 使用 strings.ReplaceAll 替换 {index}/{section_summary} 占位符
- 修改 executeWeixinPublicAccountImagePromptStep 调用 buildWeixinPublicAccountImagePromptsV2
- 新增 buildWeixinPublicAccountContentWithPromptsFromJSON 包装 renderWeixinPublicAccountImagePromptFromJSON
- 前端产物 Tab 新增配图缩略图行展示
This commit is contained in:
eaiadmin
2026-09-25 00:19:15 +08:00
parent d1a737d448
commit e73169df50
4 changed files with 226 additions and 55 deletions
@@ -27,8 +27,8 @@ type weixinPublicAccountSection struct {
}
func executeWeixinPublicAccountImagePromptStep(workflow *weixinPublicAccountWorkflowState, article *weixinpublicaccountmodel.WeixinPublicAccountArticle, user *model.User, now time.Time) weixinPublicAccountStepExecution {
prompts := buildWeixinPublicAccountImagePrompts(workflow)
contentWithPrompts := buildWeixinPublicAccountContentWithPrompts(workflow.Shared.Content, prompts)
prompts := buildWeixinPublicAccountImagePromptsV2(workflow)
contentWithPrompts := buildWeixinPublicAccountContentWithPromptsFromJSON(workflow.Shared.Content, prompts)
workflow.Shared.ContentWithPrompts = contentWithPrompts
workflow.Shared.ImagePrompts = prompts
@@ -55,9 +55,9 @@ func executeWeixinPublicAccountImagePromptStep(workflow *weixinPublicAccountWork
func executeWeixinPublicAccountImageGenerationStep(workflow *weixinPublicAccountWorkflowState, article *weixinpublicaccountmodel.WeixinPublicAccountArticle, user *model.User, now time.Time) (weixinPublicAccountStepExecution, *web.AppError) {
prompts := workflow.Shared.ImagePrompts
if len(prompts) == 0 {
prompts = buildWeixinPublicAccountImagePrompts(workflow)
prompts = buildWeixinPublicAccountImagePromptsV2(workflow)
workflow.Shared.ImagePrompts = prompts
workflow.Shared.ContentWithPrompts = buildWeixinPublicAccountContentWithPrompts(workflow.Shared.Content, prompts)
workflow.Shared.ContentWithPrompts = buildWeixinPublicAccountContentWithPromptsFromJSON(workflow.Shared.Content, prompts)
}
aiRoute, refs, appErr := generateWeixinPublicAccountImages(article.TaskID, workflow, prompts, user)
if appErr != nil {
@@ -181,6 +181,11 @@ func buildWeixinPublicAccountContentWithPrompts(content string, prompts []weixin
return strings.Join(blocks, "\n")
}
// buildWeixinPublicAccountContentWithPromptsFromJSON 基于 JSON 配置渲染带括号标记的正文。
func buildWeixinPublicAccountContentWithPromptsFromJSON(content string, prompts []weixinPublicAccountImagePrompt) string {
return renderWeixinPublicAccountImagePromptFromJSON(content, prompts)
}
func buildWeixinPublicAccountVisualPrompt(workflow *weixinPublicAccountWorkflowState, sectionTitle, context string) string {
keyword := firstNonEmpty(workflow.Form.Keyword, "业务主题")
tone := firstNonEmpty(workflow.Form.Tone, "专业但好懂")
@@ -217,18 +217,20 @@ func buildWeixinPublicAccountImagePromptsV2(workflow *weixinPublicAccountWorkflo
func buildCoverImagePrompt(workflow *weixinPublicAccountWorkflowState, rule *imagePromptRule) string {
title := firstNonEmpty(workflow.Shared.SelectedTitle, workflow.Form.Keyword, "公众号文章")
// 从 JSON 模板构建(当前回退到硬编码模板)
// 从 JSON 模板构建
template := rule.Template.Template
if template == "" {
// 回退到当前硬编码的英文模板
return fmt.Sprintf("realistic editorial illustration, Chinese business article cover, %s, focus on %s, %s, clean composition, natural lighting, brand-safe, no text, no watermark",
weixinPublicAccountBusinessDomainLabel(workflow.Form.BusinessDomain),
title,
"公众号封面主视觉",
)
if template != "" {
// 使用字符串替换,而非 fmt.Sprintf
prompt := strings.ReplaceAll(template, "{index}", "1")
prompt = strings.ReplaceAll(prompt, "{section_summary}", fmt.Sprintf("%s(截断至120字)", title))
return prompt
}
return fmt.Sprintf(template, 1, title, fmt.Sprintf("%s(截断至120字)", title))
// 回退到硬编码模板
return fmt.Sprintf("realistic editorial illustration, Chinese business article cover, %s, focus on %s, %s, clean composition, natural lighting, brand-safe, no text, no watermark",
weixinPublicAccountBusinessDomainLabel(workflow.Form.BusinessDomain),
title,
"公众号封面主视觉",
)
}
// buildContentImagePrompt 生成正文配图提示词。
@@ -237,16 +239,18 @@ func buildContentImagePrompt(workflow *weixinPublicAccountWorkflowState, section
// 从 JSON 模板构建
template := rule.Template.Template
if template == "" {
// 回退到当前硬编码的英文模板
return fmt.Sprintf("realistic editorial illustration, Chinese business article cover, %s, focus on %s, %s, clean composition, natural lighting, brand-safe, no text, no watermark",
weixinPublicAccountBusinessDomainLabel(workflow.Form.BusinessDomain),
firstNonEmpty(section.Title, workflow.Form.Keyword),
firstNonEmpty(summary, workflow.Form.Tone),
)
if template != "" {
// 使用字符串替换,而非 fmt.Sprintf
prompt := strings.ReplaceAll(template, "{index}", fmt.Sprintf("%d", index))
prompt = strings.ReplaceAll(prompt, "{section_summary}", summary)
return prompt
}
return fmt.Sprintf(template, index, firstNonEmpty(section.Title, workflow.Form.Keyword), summary)
// 回退到硬编码模板
return fmt.Sprintf("realistic editorial illustration, Chinese business article cover, %s, focus on %s, %s, clean composition, natural lighting, brand-safe, no text, no watermark",
weixinPublicAccountBusinessDomainLabel(workflow.Form.BusinessDomain),
firstNonEmpty(section.Title, workflow.Form.Keyword),
firstNonEmpty(summary, workflow.Form.Tone),
)
}
// countChineseWords 计算内容的中文字数。
@@ -792,7 +792,7 @@ func respondWeixinPublicAccountWorkflow(c *gin.Context, task model.TaskRecord) {
func findWeixinPublicAccountImagePrompt(workflow weixinPublicAccountWorkflowState, imageKey string) (weixinPublicAccountImagePrompt, int, bool) {
prompts := workflow.Shared.ImagePrompts
if len(prompts) == 0 {
prompts = buildWeixinPublicAccountImagePrompts(&workflow)
prompts = buildWeixinPublicAccountImagePromptsV2(&workflow)
workflow.Shared.ImagePrompts = prompts
}
for idx, item := range prompts {