package formula import ( "context" "encoding/json" "fmt" "strings" domai "haixun-backend/internal/model/ai/domain/usecase" cfdom "haixun-backend/internal/model/content_formula/domain/usecase" ) type GenerateInput struct { Topic string Brief string PersonaBlock string ResearchNotes string Formula cfdom.FormulaSummary } type GeneratedDraft struct { Text string `json:"text"` Hook string `json:"hook"` Angle string `json:"angle"` Rationale string `json:"rationale"` StructureNotes string `json:"structure_notes"` } func BuildGenerateSystemPrompt() string { return strings.TrimSpace(`你是 Threads 文案寫手。依「爆款公式」與「人設」為指定主題寫一篇新貼文。 規則: - 用台灣繁體中文,符合人設語氣。 - 套用公式結構,但內容必須圍繞使用者主題,不可抄襲範例貼文。 - 只回傳 JSON:text, hook, angle, rationale, structure_notes(繁體中文)。`) } func BuildGenerateUserPrompt(in GenerateInput) string { var b strings.Builder b.WriteString("【主題】\n") b.WriteString(strings.TrimSpace(in.Topic)) if brief := strings.TrimSpace(in.Brief); brief != "" { b.WriteString("\n補充:") b.WriteString(brief) } b.WriteString("\n\n【人設】\n") b.WriteString(strings.TrimSpace(in.PersonaBlock)) b.WriteString("\n\n【公式】\n") b.WriteString(strings.TrimSpace(in.Formula.Formula)) if tpl := strings.TrimSpace(in.Formula.PostTemplate); tpl != "" { b.WriteString("\n\n【發文模板】\n") b.WriteString(tpl) } if hook := strings.TrimSpace(in.Formula.HookPattern); hook != "" { b.WriteString("\n\n【Hook 模式】\n") b.WriteString(hook) } if structure := strings.TrimSpace(in.Formula.Structure); structure != "" { b.WriteString("\n\n【結構節奏】\n") b.WriteString(structure) } if len(in.Formula.ReplicationTips) > 0 { b.WriteString("\n\n【複製技巧】\n") b.WriteString(strings.Join(in.Formula.ReplicationTips, "\n")) } if len(in.Formula.Avoid) > 0 { b.WriteString("\n\n【避免】\n") b.WriteString(strings.Join(in.Formula.Avoid, "\n")) } if notes := strings.TrimSpace(in.ResearchNotes); notes != "" { b.WriteString("\n\n【周邊知識參考】\n") b.WriteString(notes) } if src := strings.TrimSpace(in.Formula.SourcePostText); src != "" { b.WriteString("\n\n【參考原文(僅學結構,勿抄內容)】\n") b.WriteString(src) } b.WriteString("\n\n請產出一篇可直接發布的 Threads 貼文 JSON。") return b.String() } func GenerateDraft(ctx context.Context, ai domai.UseCase, req domai.GenerateRequest, in GenerateInput) (*GeneratedDraft, error) { temp := 0.75 req.System = BuildGenerateSystemPrompt() req.Messages = []domai.Message{{Role: "user", Content: BuildGenerateUserPrompt(in)}} req.Temperature = &temp out, err := ai.GenerateText(ctx, req) if err != nil { return nil, err } return ParseGeneratedDraft(out.Text) } func ParseGeneratedDraft(raw string) (*GeneratedDraft, error) { raw = strings.TrimSpace(raw) start := strings.Index(raw, "{") end := strings.LastIndex(raw, "}") if start < 0 || end <= start { return nil, fmt.Errorf("generate output missing json object") } var draft GeneratedDraft if err := json.Unmarshal([]byte(raw[start:end+1]), &draft); err != nil { return nil, fmt.Errorf("parse generated draft: %w", err) } draft.Text = strings.TrimSpace(draft.Text) if draft.Text == "" { return nil, fmt.Errorf("generated draft missing text") } return &draft, nil }