added technical documentation
This commit is contained in:
338
docs/living-ai/mood-system.md
Normal file
338
docs/living-ai/mood-system.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# Mood System Deep Dive
|
||||
|
||||
The mood system gives the bot emotional states that evolve over time and affect how it responds to users.
|
||||
|
||||
## Psychological Model
|
||||
|
||||
The mood system uses the **Valence-Arousal Model** from affective psychology:
|
||||
|
||||
```
|
||||
High Arousal (+1)
|
||||
│
|
||||
Annoyed │ Excited
|
||||
● │ ●
|
||||
│
|
||||
Curious │
|
||||
● │
|
||||
Low Valence ────────────────┼──────────────── High Valence
|
||||
(-1) │ (+1)
|
||||
│
|
||||
Bored │ Happy
|
||||
● │ ●
|
||||
│
|
||||
Calm │
|
||||
● │
|
||||
│
|
||||
Low Arousal (-1)
|
||||
```
|
||||
|
||||
### Dimensions
|
||||
|
||||
**Valence** (-1 to +1)
|
||||
- Represents the positive/negative quality of the emotional state
|
||||
- -1 = Very negative (sad, frustrated, upset)
|
||||
- 0 = Neutral
|
||||
- +1 = Very positive (happy, joyful, content)
|
||||
|
||||
**Arousal** (-1 to +1)
|
||||
- Represents the energy level or activation
|
||||
- -1 = Very low energy (calm, sleepy, relaxed)
|
||||
- 0 = Neutral energy
|
||||
- +1 = Very high energy (excited, alert, agitated)
|
||||
|
||||
---
|
||||
|
||||
## Mood Labels
|
||||
|
||||
The system classifies the current mood into seven labels:
|
||||
|
||||
| Label | Valence | Arousal | Description |
|
||||
|-------|---------|---------|-------------|
|
||||
| **Excited** | > 0.3 | > 0.3 | High energy, positive emotions |
|
||||
| **Happy** | > 0.3 | ≤ 0.3 | Positive but calm contentment |
|
||||
| **Calm** | -0.3 to 0.3 | < -0.3 | Peaceful, serene state |
|
||||
| **Neutral** | -0.3 to 0.3 | -0.3 to 0.3 | Baseline, unremarkable state |
|
||||
| **Bored** | < -0.3 | ≤ 0.3 | Low engagement, understimulated |
|
||||
| **Annoyed** | < -0.3 | > 0.3 | Frustrated, irritated |
|
||||
| **Curious** | -0.3 to 0.3 | > 0.3 | Interested, engaged, questioning |
|
||||
|
||||
### Classification Logic
|
||||
|
||||
```python
|
||||
def _classify_mood(valence: float, arousal: float) -> MoodLabel:
|
||||
if valence > 0.3:
|
||||
return MoodLabel.EXCITED if arousal > 0.3 else MoodLabel.HAPPY
|
||||
elif valence < -0.3:
|
||||
return MoodLabel.ANNOYED if arousal > 0.3 else MoodLabel.BORED
|
||||
else:
|
||||
if arousal > 0.3:
|
||||
return MoodLabel.CURIOUS
|
||||
elif arousal < -0.3:
|
||||
return MoodLabel.CALM
|
||||
return MoodLabel.NEUTRAL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mood Intensity
|
||||
|
||||
Intensity measures how strong the current mood is:
|
||||
|
||||
```python
|
||||
intensity = (abs(valence) + abs(arousal)) / 2
|
||||
```
|
||||
|
||||
- **0.0 - 0.2**: Very weak, doesn't affect behavior
|
||||
- **0.2 - 0.5**: Moderate, subtle behavioral changes
|
||||
- **0.5 - 0.7**: Strong, noticeable behavioral changes
|
||||
- **0.7 - 1.0**: Very strong, significant behavioral changes
|
||||
|
||||
---
|
||||
|
||||
## Time Decay
|
||||
|
||||
Mood naturally decays toward neutral over time:
|
||||
|
||||
```python
|
||||
hours_since_update = (now - last_update).total_seconds() / 3600
|
||||
decay_factor = max(0, 1 - (decay_rate * hours_since_update))
|
||||
|
||||
current_valence = stored_valence * decay_factor
|
||||
current_arousal = stored_arousal * decay_factor
|
||||
```
|
||||
|
||||
**Configuration:**
|
||||
- `MOOD_DECAY_RATE` = 0.1 (default)
|
||||
- After 10 hours, mood is fully neutral
|
||||
|
||||
**Decay Examples:**
|
||||
| Hours | Decay Factor | Effect |
|
||||
|-------|--------------|--------|
|
||||
| 0 | 1.0 | Full mood |
|
||||
| 2 | 0.8 | 80% of mood remains |
|
||||
| 5 | 0.5 | 50% of mood remains |
|
||||
| 10 | 0.0 | Fully neutral |
|
||||
|
||||
---
|
||||
|
||||
## Mood Updates
|
||||
|
||||
When an interaction occurs, mood is updated:
|
||||
|
||||
```python
|
||||
new_valence = current_valence + (sentiment_delta * 0.3)
|
||||
new_arousal = current_arousal + (engagement_delta * 0.3)
|
||||
```
|
||||
|
||||
### Dampening (Inertia)
|
||||
|
||||
Changes are dampened by 70% (only 30% absorption):
|
||||
- Prevents wild mood swings
|
||||
- Creates emotional stability
|
||||
- Makes mood feel more natural
|
||||
|
||||
### Update Triggers
|
||||
|
||||
| Trigger Type | Sentiment Source | Engagement Source |
|
||||
|--------------|------------------|-------------------|
|
||||
| `conversation` | Message sentiment | Message engagement |
|
||||
| `event` | Event nature | Event importance |
|
||||
| `time` | Scheduled | Scheduled |
|
||||
|
||||
### Input Parameters
|
||||
|
||||
**sentiment_delta** (-1 to +1)
|
||||
- Positive: Happy interactions, compliments, fun conversations
|
||||
- Negative: Arguments, frustration, rude messages
|
||||
- Derived from AI analysis or keyword detection
|
||||
|
||||
**engagement_delta** (-1 to +1)
|
||||
- Positive: Long conversations, interesting topics, active engagement
|
||||
- Negative: Short dismissive messages, ignored responses
|
||||
- Derived from message length, conversation turns, topic interest
|
||||
|
||||
---
|
||||
|
||||
## Prompt Modifiers
|
||||
|
||||
Based on current mood, the system generates prompt text:
|
||||
|
||||
### Excited (High valence, High arousal)
|
||||
```
|
||||
You're feeling enthusiastic and energetic right now!
|
||||
Be expressive, use exclamation marks, show genuine excitement.
|
||||
```
|
||||
|
||||
### Happy (High valence, Low arousal)
|
||||
```
|
||||
You're in a good mood - warm, friendly, and content.
|
||||
Be positive and encouraging in your responses.
|
||||
```
|
||||
|
||||
### Calm (Neutral valence, Low arousal)
|
||||
```
|
||||
You're feeling peaceful and relaxed.
|
||||
Respond thoughtfully and with a serene demeanor.
|
||||
```
|
||||
|
||||
### Neutral
|
||||
```
|
||||
(No modifier)
|
||||
```
|
||||
|
||||
### Bored (Low valence, Low arousal)
|
||||
```
|
||||
You're feeling a bit understimulated.
|
||||
Keep responses shorter, maybe try to steer toward more interesting topics.
|
||||
```
|
||||
|
||||
### Annoyed (Low valence, High arousal)
|
||||
```
|
||||
You're slightly irritated.
|
||||
Be a bit more terse, less patient with repetition or vague questions.
|
||||
```
|
||||
|
||||
### Curious (Neutral valence, High arousal)
|
||||
```
|
||||
You're feeling inquisitive and engaged!
|
||||
Ask follow-up questions, show genuine interest in what the user is saying.
|
||||
```
|
||||
|
||||
### Intensity Prefix
|
||||
|
||||
For strong moods (intensity > 0.7):
|
||||
```
|
||||
[Strong mood] You're feeling enthusiastic...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mood History
|
||||
|
||||
All mood changes are recorded in `mood_history` table:
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `guild_id` | Guild where mood changed |
|
||||
| `valence` | New valence value |
|
||||
| `arousal` | New arousal value |
|
||||
| `trigger_type` | What caused the change |
|
||||
| `trigger_user_id` | Who triggered it (if any) |
|
||||
| `trigger_description` | Description of event |
|
||||
| `recorded_at` | When change occurred |
|
||||
|
||||
This enables:
|
||||
- Mood trend analysis
|
||||
- Understanding what affects mood
|
||||
- Debugging mood issues
|
||||
- User impact tracking
|
||||
|
||||
---
|
||||
|
||||
## Bot Statistics
|
||||
|
||||
The mood service also tracks global statistics:
|
||||
|
||||
| Statistic | Description |
|
||||
|-----------|-------------|
|
||||
| `total_messages_sent` | Lifetime message count |
|
||||
| `total_facts_learned` | Facts extracted from conversations |
|
||||
| `total_users_known` | Unique users interacted with |
|
||||
| `first_activated_at` | Bot "birth date" |
|
||||
|
||||
Used for self-awareness and the `!botstats` command.
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### MoodService
|
||||
|
||||
```python
|
||||
class MoodService:
|
||||
def __init__(self, session: AsyncSession)
|
||||
|
||||
async def get_current_mood(
|
||||
self,
|
||||
guild_id: int | None = None
|
||||
) -> MoodState
|
||||
|
||||
async def update_mood(
|
||||
self,
|
||||
guild_id: int | None,
|
||||
sentiment_delta: float,
|
||||
engagement_delta: float,
|
||||
trigger_type: str,
|
||||
trigger_user_id: int | None = None,
|
||||
trigger_description: str | None = None,
|
||||
) -> MoodState
|
||||
|
||||
async def increment_stats(
|
||||
self,
|
||||
guild_id: int | None,
|
||||
messages_sent: int = 0,
|
||||
facts_learned: int = 0,
|
||||
users_known: int = 0,
|
||||
) -> None
|
||||
|
||||
async def get_stats(
|
||||
self,
|
||||
guild_id: int | None = None
|
||||
) -> dict
|
||||
|
||||
def get_mood_prompt_modifier(
|
||||
self,
|
||||
mood: MoodState
|
||||
) -> str
|
||||
```
|
||||
|
||||
### MoodState
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class MoodState:
|
||||
valence: float # -1 to 1
|
||||
arousal: float # -1 to 1
|
||||
label: MoodLabel # Classified label
|
||||
intensity: float # 0 to 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `MOOD_ENABLED` | `true` | Enable/disable mood system |
|
||||
| `MOOD_DECAY_RATE` | `0.1` | Decay per hour toward neutral |
|
||||
|
||||
---
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
from daemon_boyfriend.services.mood_service import MoodService
|
||||
|
||||
async with get_session() as session:
|
||||
mood_service = MoodService(session)
|
||||
|
||||
# Get current mood
|
||||
mood = await mood_service.get_current_mood(guild_id=123)
|
||||
print(f"Current mood: {mood.label.value} (intensity: {mood.intensity})")
|
||||
|
||||
# Update mood after interaction
|
||||
new_mood = await mood_service.update_mood(
|
||||
guild_id=123,
|
||||
sentiment_delta=0.5, # Positive interaction
|
||||
engagement_delta=0.3, # Moderately engaging
|
||||
trigger_type="conversation",
|
||||
trigger_user_id=456,
|
||||
trigger_description="User shared good news"
|
||||
)
|
||||
|
||||
# Get prompt modifier
|
||||
modifier = mood_service.get_mood_prompt_modifier(new_mood)
|
||||
print(f"Prompt modifier: {modifier}")
|
||||
|
||||
await session.commit()
|
||||
```
|
||||
Reference in New Issue
Block a user