thread-master/internal/library/style8d/prompt.go

105 lines
2.9 KiB
Go
Raw Permalink 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 style8d
import (
"encoding/json"
"strings"
)
var dimensionLabels = map[string]string{
"d1Tone": "D1 語氣人格",
"d2Structure": "D2 結構模板",
"d3Interaction": "D3 互動方式",
"d4Topics": "D4 主題分布",
"d5Rhythm": "D5 發文節奏",
"d6Visual": "D6 視覺語法",
"d7Conversion": "D7 轉換方式",
"d8Risk": "D8 風險紅線",
}
var dimensionOrder = []string{
"d1Tone", "d2Structure", "d3Interaction", "d4Topics",
"d5Rhythm", "d6Visual", "d7Conversion", "d8Risk",
}
// ParseStoredProfile decodes the persona.style_profile JSON blob.
func ParseStoredProfile(raw string) (*StoredProfile, bool) {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil, false
}
var profile StoredProfile
if err := json.Unmarshal([]byte(raw), &profile); err != nil {
return nil, false
}
if len(profile.Analysis) == 0 && strings.TrimSpace(profile.PersonaDraft) == "" {
return nil, false
}
return &profile, true
}
// HasReady8D returns true when 8D analysis exists and can drive copy generation.
func HasReady8D(personaText, styleProfileJSON string) bool {
if profile, ok := ParseStoredProfile(styleProfileJSON); ok {
if strings.TrimSpace(profile.PersonaDraft) != "" {
return true
}
for _, key := range dimensionOrder {
if summary := strings.TrimSpace(profile.Analysis[key].Summary); summary != "" {
return true
}
}
}
return strings.TrimSpace(personaText) != "" && strings.TrimSpace(styleProfileJSON) != ""
}
// BuildStyle8DPromptBlock formats D1D8 summaries for LLM prompts.
func BuildStyle8DPromptBlock(styleProfileJSON string) string {
profile, ok := ParseStoredProfile(styleProfileJSON)
if !ok {
return ""
}
lines := make([]string, 0, len(dimensionOrder))
for _, key := range dimensionOrder {
summary := strings.TrimSpace(profile.Analysis[key].Summary)
if summary == "" {
continue
}
label := dimensionLabels[key]
if label == "" {
label = key
}
lines = append(lines, label+""+summary)
}
if len(lines) == 0 {
return ""
}
var b strings.Builder
b.WriteString("【8D 風格策略】\n產文必須遵守\n")
b.WriteString(strings.Join(lines, "\n"))
return b.String()
}
// ResolvePersonaBlock picks the best persona voice block for copy generation.
// Priority: explicit persona field → 8D personaDraft → brief fallback; always append 8D strategy when present.
func ResolvePersonaBlock(personaText, styleProfileJSON, brief string) string {
var parts []string
voice := strings.TrimSpace(personaText)
if voice == "" {
if profile, ok := ParseStoredProfile(styleProfileJSON); ok {
voice = strings.TrimSpace(profile.PersonaDraft)
}
}
if voice == "" {
voice = strings.TrimSpace(brief)
}
if voice != "" {
parts = append(parts, "【人設語氣】\n"+voice)
}
if block := BuildStyle8DPromptBlock(styleProfileJSON); block != "" {
parts = append(parts, block)
}
return strings.TrimSpace(strings.Join(parts, "\n\n"))
}