thread-master/backend/internal/library/placement/context_prompt.go

152 lines
4.5 KiB
Go
Raw Permalink Normal View History

2026-06-26 08:37:04 +00:00
package placement
import (
"strings"
branddomain "haixun-backend/internal/model/brand/domain/usecase"
topicdomain "haixun-backend/internal/model/placement_topic/domain/usecase"
)
// PlacementTopicContext bundles brand, product, and user-entered topic fields for prompts.
type PlacementTopicContext struct {
BrandDisplayName string
TopicName string
SeedQuery string
Brief string
Goals string
TargetAudience string
ProductContext string
ProductBrief string
ProductLabel string
}
func PlacementTopicContextFromTopic(topic *topicdomain.TopicSummary, brand *branddomain.BrandSummary, productBrief string) PlacementTopicContext {
if topic == nil {
return PlacementTopicContextFromBrand(brand, productBrief)
}
merged := brandSummaryForTopic(topic, brand)
return PlacementTopicContextFromBrand(merged, productBrief)
}
func brandSummaryForTopic(topic *topicdomain.TopicSummary, brand *branddomain.BrandSummary) *branddomain.BrandSummary {
if brand == nil {
return nil
}
out := *brand
out.TopicName = strings.TrimSpace(topic.TopicName)
out.SeedQuery = strings.TrimSpace(topic.SeedQuery)
out.Brief = strings.TrimSpace(topic.Brief)
out.ProductID = strings.TrimSpace(topic.ProductID)
out.ResearchMap = topic.ResearchMap
return &out
}
func PlacementTopicContextFromBrand(brand *branddomain.BrandSummary, productBrief string) PlacementTopicContext {
if brand == nil {
return PlacementTopicContext{}
}
ctx := PlacementTopicContext{
BrandDisplayName: strings.TrimSpace(brand.DisplayName),
TopicName: strings.TrimSpace(brand.TopicName),
SeedQuery: strings.TrimSpace(brand.SeedQuery),
Brief: strings.TrimSpace(brand.Brief),
Goals: strings.TrimSpace(brand.Goals),
TargetAudience: strings.TrimSpace(brand.TargetAudience),
ProductContext: strings.TrimSpace(brand.ProductContext),
ProductBrief: strings.TrimSpace(productBrief),
ProductLabel: productLabelFromBrand(brand),
}
if ctx.ProductBrief == "" {
ctx.ProductBrief = strings.TrimSpace(brand.ProductBrief)
}
return ctx
}
func productLabelFromBrand(brand *branddomain.BrandSummary) string {
if brand == nil {
return ""
}
productID := strings.TrimSpace(brand.ProductID)
if productID == "" {
return ""
}
for _, item := range brand.Products {
if item.ID == productID {
return strings.TrimSpace(item.Label)
}
}
return ""
}
func (c PlacementTopicContext) ToResearchMapInput() ResearchMapInput {
return ResearchMapInput{
BrandDisplayName: c.BrandDisplayName,
TopicName: c.TopicName,
SeedQuery: c.SeedQuery,
Brief: c.Brief,
Goals: c.Goals,
TargetAudience: c.TargetAudience,
ProductContext: c.ProductContext,
ProductBrief: c.ProductBrief,
ProductLabel: c.ProductLabel,
}
}
func (c PlacementTopicContext) WritePromptBlock(b *strings.Builder) {
writePromptSection(b, "品牌", c.BrandDisplayName)
writePromptSection(b, "置入產品", c.ProductDisplayName())
writePromptSection(b, "主題名稱", c.TopicName)
writePromptSection(b, "種子關鍵字", c.SeedQuery)
writePromptSection(b, "主題目標", c.Brief)
writePromptSection(b, "置入目標/備註", c.Goals)
writePromptSection(b, "已知受眾描述", c.TargetAudience)
productDetail := FormatProductContextForPrompt(c.ProductContext)
if productDetail == "" && c.ProductBrief != "" {
productDetail = c.ProductBrief
}
if productDetail != "" {
b.WriteString("【產品詳情】\n")
b.WriteString(productDetail)
b.WriteString("\n")
}
}
func (c PlacementTopicContext) ProductDisplayName() string {
fields := ParseProductContext(c.ProductContext)
if fields.Product != "" {
return fields.Product
}
return c.ProductLabel
}
func writePromptSection(b *strings.Builder, label, value string) {
value = strings.TrimSpace(value)
if value == "" {
return
}
b.WriteString("【")
b.WriteString(label)
b.WriteString("】")
b.WriteString(value)
b.WriteString("\n")
}
// PromptLines returns optional labeled lines for knowledge graph template vars.
func (c PlacementTopicContext) PromptLines() map[string]string {
productName := c.ProductDisplayName()
return map[string]string{
"brand_line": optionalPromptLine("品牌", c.BrandDisplayName),
"topic_line": optionalPromptLine("主題名稱", c.TopicName),
"product_line": optionalPromptLine("置入產品", productName),
"goals_line": optionalPromptLine("置入目標", c.Goals),
}
}
func optionalPromptLine(label, value string) string {
value = strings.TrimSpace(value)
if value == "" {
return ""
}
return label + "" + value + "\n"
}