Emotion Classifier: GPT-5-nano Emotional Intelligence¶
Last Updated: January 9, 2026
Overview¶
Sage uses a GPT-5-nano based emotion classifier to detect user emotional states beyond simple keyword matching. This enables more empathetic, contextually appropriate responses.
Key improvement over keyword matching: - Detects sarcasm: "oh great, another meeting" → frustration, not joy - Understands context: short terse replies after long messages → possible frustration - Measures intensity: "a bit stressed" vs "I'm completely overwhelmed" - Identifies vulnerability for extra supportive responses
How It Works¶
Architecture¶
User Message
↓
┌─────────────────────────────────────────────────┐
│ PARALLEL EXECUTION (zero latency impact) │
├─────────────────────────────────────────────────┤
│ Context Loading │ Emotion Classifier │
│ - Memory retrieval │ - GPT-5-nano call │
│ - Relationship state │ - ~80ms latency │
│ - User traits │ │
│ (~200-300ms) │ │
└─────────────────────────┴───────────────────────┘
↓
Response Generation (with emotion context)
The emotion classifier runs in parallel with context loading, adding zero additional latency to the response pipeline.
Classification Output¶
{
"primary_emotion": "anxiety", # joy|sadness|anxiety|anger|frustration|neutral
"intensity": 7, # 1-10 scale
"is_vulnerable": True, # User sharing something sensitive
"suggested_tone": "calm" # warm|calm|celebratory|supportive|playful
}
Emotion Categories¶
| Emotion | Description | Example Triggers |
|---|---|---|
| joy | Happy, excited, celebratory | "I got the job!", "omg yes!!" |
| sadness | Sad, disappointed, grieving | "didn't get it...", "feeling down" |
| anxiety | Worried, stressed, overwhelmed | "so much to do", "nervous about tomorrow" |
| anger | Angry, annoyed, irritated | "this is ridiculous", "so frustrating" |
| frustration | Stuck, struggling, exasperated | "nothing's working", "ugh again??" |
| neutral | Informational, casual | "what time is it?", "ok sounds good" |
Intensity Scale¶
| Range | Level | Example |
|---|---|---|
| 1-3 | Mild/subtle | "a bit tired" |
| 4-6 | Moderate | "pretty stressed about this" |
| 7-10 | Strong/intense | "I'm completely overwhelmed" |
Suggested Response Tones¶
| Tone | When Used | Guidance |
|---|---|---|
| warm | Default, neutral messages | Be your usual friendly, casual self |
| calm | User is anxious | Be soothing and reassuring |
| celebratory | User is happy | Match their energy and celebrate |
| supportive | User is sad/vulnerable | Be caring and validating |
| playful | User is joking/light | Match the banter and have fun |
Vulnerability Detection¶
When is_vulnerable: true, the user is sharing something sensitive:
- Personal struggles or fears
- Asking for emotional support
- Sharing difficult news
- Opening up about problems
Response guidance: Be extra supportive and caring. Don't minimize their feelings.
Sarcasm Detection¶
The classifier is specifically prompted to detect sarcasm:
| Message | Naive Interpretation | Correct Classification |
|---|---|---|
| "oh great, another meeting" | joy (keyword: "great") | frustration |
| "just what I needed" (after bad news) | gratitude | sarcasm/frustration |
| "wonderful, truly wonderful" (after setback) | joy | frustration |
Integration with Response Generation¶
When emotion is detected, it's added to the system prompt:
# EMOTIONAL CONTEXT (from current message)
Detected emotion: anxiety (intensity: 7/10)
Suggested tone: calm - User seems anxious - be soothing and reassuring
⚠️ User seems VULNERABLE - be extra supportive and caring
Calibrate your response to match this emotional state.
Only included when: - Emotion is not neutral, OR - Intensity > 6, OR - User is vulnerable
Neutral/low-intensity messages don't get emotion context (to avoid over-processing).
Performance¶
| Metric | Value |
|---|---|
| Model | GPT-5-nano |
| Latency | ~80ms |
| Temperature | 0.3 (for consistency) |
| Max tokens | 150 |
| Parallel execution | Yes (with context loading) |
File Locations¶
| Component | Location |
|---|---|
| Classifier | app/orchestrator/emotion_classifier.py |
| Integration | app/orchestrator/message_handler.py (lines 632-650) |
| Prompt injection | app/orchestrator/response_generator.py (lines 245-275) |
Example Scenarios¶
Scenario 1: Stressed User¶
User: "ugh I have SO much to do and my boss just added another project"
Classification:
Sage responds with calm, supportive tone instead of default playful.
Scenario 2: Excited User¶
User: "I GOT THE JOB!!!"
Classification:
{
"primary_emotion": "joy",
"intensity": 9,
"is_vulnerable": False,
"suggested_tone": "celebratory"
}
Sage matches energy: "YESSS omg congrats!!!"
Scenario 3: Vulnerable User¶
User: "I don't know what to do... my mom's in the hospital"
Classification:
{
"primary_emotion": "sadness",
"intensity": 8,
"is_vulnerable": True,
"suggested_tone": "supportive"
}
Sage responds with extra care and support, no jokes or casual banter.
Error Handling¶
If emotion classification fails (API error, timeout), the system:
1. Logs a warning (non-critical)
2. Continues with None emotion context
3. Response generates normally without emotion guidance
This ensures emotion detection never blocks the main conversation flow.