thread-master/backend/internal/library/formula/generate_from_formula.go

110 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 文案寫手。依「爆款公式」與「人設」為指定主題寫一篇新貼文。
規則:
- 用台灣繁體中文,符合人設語氣。
- 套用公式結構,但內容必須圍繞使用者主題,不可抄襲範例貼文。
- 只回傳 JSONtext, 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
}