package persona import ( "log" "sync" "time" ) // MoodTransition records a change from one mood to another. type MoodTransition struct { From string `json:"from"` To string `json:"to"` Reason string `json:"reason"` Timestamp time.Time `json:"timestamp"` } // EmotionState is the current emotional state of the persona. type EmotionState struct { CurrentMood string `json:"current_mood"` Intensity float64 `json:"intensity"` // 0.0 - 1.0 DominantSentiment string `json:"dominant_sentiment"` SentimentCounts map[string]int `json:"sentiment_counts"` MoodHistory []MoodTransition `json:"mood_history"` LastUpdated time.Time `json:"last_updated"` } // EmotionTracker manages emotional state for a single user. // Tracks mood, intensity, sentiment accumulation, and triggers transitions. type EmotionTracker struct { mu sync.Mutex state EmotionState moodConfig []MoodConfig // from YAML mood_system positiveThreshold int // sentiment count to trigger positive transition negativeThreshold int // sentiment count to trigger negative transition maxHistory int // max mood history entries } // NewEmotionTracker creates a new tracker from YAML mood config. func NewEmotionTracker(moodSystem []MoodConfig) *EmotionTracker { return &EmotionTracker{ state: EmotionState{ CurrentMood: "thoughtful", Intensity: 0.3, DominantSentiment: "neutral", SentimentCounts: map[string]int{"positive": 0, "neutral": 0, "negative": 0}, MoodHistory: make([]MoodTransition, 0, 20), LastUpdated: time.Now(), }, moodConfig: moodSystem, positiveThreshold: 3, negativeThreshold: 3, maxHistory: 20, } } // RecordSentiment records a user sentiment and potentially triggers mood transitions. func (t *EmotionTracker) RecordSentiment(sentiment string) { t.mu.Lock() defer t.mu.Unlock() t.state.SentimentCounts[sentiment]++ t.state.LastUpdated = time.Now() total := t.state.SentimentCounts["positive"] + t.state.SentimentCounts["neutral"] + t.state.SentimentCounts["negative"] if total > 0 { posRatio := float64(t.state.SentimentCounts["positive"]) / float64(total) negRatio := float64(t.state.SentimentCounts["negative"]) / float64(total) switch { case posRatio > 0.5: t.state.DominantSentiment = "positive" case negRatio > 0.5: t.state.DominantSentiment = "negative" default: t.state.DominantSentiment = "neutral" } } posCount := t.state.SentimentCounts["positive"] negCount := t.state.SentimentCounts["negative"] if posCount >= t.positiveThreshold && t.state.CurrentMood != "happy" && t.state.CurrentMood != "playful" { if t.state.Intensity > 0.6 { t.applyMoodTransition("playful", "积极情绪积累") } else { t.applyMoodTransition("happy", "积极情绪积累") } t.state.SentimentCounts["positive"] = 0 } if negCount >= t.negativeThreshold && t.state.CurrentMood != "worried" { t.applyMoodTransition("worried", "消极情绪积累") t.state.SentimentCounts["negative"] = 0 } } // UpdateMood explicitly changes mood for significant events. func (t *EmotionTracker) UpdateMood(trigger string) { t.mu.Lock() defer t.mu.Unlock() switch trigger { case "user_returned": t.applyMoodTransition("happy", "开拓者回来了") case "long_silence": if t.state.CurrentMood != "thoughtful" && t.state.CurrentMood != "nostalgic" { t.applyMoodTransition("thoughtful", "长时间没有交流") } case "deep_conversation": t.applyMoodTransition("thoughtful", "深度对话后") case "nostalgic_trigger": t.applyMoodTransition("nostalgic", "触及回忆") } } // GetCurrentMood returns the current mood, its YAML expression, and intensity. func (t *EmotionTracker) GetCurrentMood() (mood string, expression string, intensity float64) { t.mu.Lock() defer t.mu.Unlock() mood = t.state.CurrentMood intensity = t.state.Intensity for _, mc := range t.moodConfig { if mc.Mood == mood { expression = mc.Expression break } } return } // Decay reduces intensity over time, drifting toward "thoughtful" baseline. func (t *EmotionTracker) Decay() { t.mu.Lock() defer t.mu.Unlock() hoursSinceUpdate := time.Since(t.state.LastUpdated).Hours() decayAmount := hoursSinceUpdate * 0.1 t.state.Intensity -= decayAmount if t.state.Intensity < 0.1 { t.state.Intensity = 0.1 } if t.state.Intensity < 0.2 && t.state.CurrentMood != "thoughtful" { t.applyMoodTransition("thoughtful", "情绪自然消退") } } // applyMoodTransition internal mood change with hysteresis. func (t *EmotionTracker) applyMoodTransition(newMood, reason string) { if t.state.CurrentMood == newMood { return } oldMood := t.state.CurrentMood t.state.CurrentMood = newMood t.state.Intensity = 0.5 + t.state.Intensity*0.3 if t.state.Intensity > 1.0 { t.state.Intensity = 1.0 } transition := MoodTransition{ From: oldMood, To: newMood, Reason: reason, Timestamp: time.Now(), } t.state.MoodHistory = append(t.state.MoodHistory, transition) if len(t.state.MoodHistory) > t.maxHistory { t.state.MoodHistory = t.state.MoodHistory[1:] } log.Printf("[情感] 心情转变: %s -> %s (原因: %s, 强度: %.2f)", oldMood, newMood, reason, t.state.Intensity) } // GetState returns a copy of the current emotion state. func (t *EmotionTracker) GetState() EmotionState { t.mu.Lock() defer t.mu.Unlock() return t.state }