XiaoBai1221 commited on
Commit
dd6ff83
·
1 Parent(s): 6c78660

emotion_update

Browse files
core/pipeline.py CHANGED
@@ -211,7 +211,12 @@ class ChatPipeline:
211
  language: Optional[str] = None,
212
  ) -> PipelineResult:
213
  if not user_message or not user_message.strip():
214
- return PipelineResult(text="我沒有收到您的消息,請重新輸入。", is_fallback=True, reason="empty")
 
 
 
 
 
215
 
216
  # language 參數保留以向後兼容,但不使用(GPT 自動判斷語言)
217
 
@@ -223,33 +228,36 @@ class ChatPipeline:
223
  return detect_res
224
  has_feature, intent_data = detect_res
225
 
226
- # 提取情緒雙軌制:音頻情緒優先,文字情緒輔助
227
  text_emotion = intent_data.get("emotion", "neutral") if intent_data else "neutral"
 
228
 
229
- # DEBUG: 顯示 audio_emotion 的完整內容
230
- logger.info(f"🐛 [DEBUG] audio_emotion = {audio_emotion}")
231
- logger.info(f"🐛 [DEBUG] text_emotion = {text_emotion}")
232
 
233
- # 【優化】情緒融合邏輯 - 降低音頻置信度門檻到 0.35
234
  emotion_confidence = 0.5 # 預設置信度
235
  if audio_emotion and audio_emotion.get("success"):
236
  audio_emotion_label = audio_emotion.get("emotion", "neutral")
237
  audio_confidence = audio_emotion.get("confidence", 0.0)
238
 
239
- # 【優化】降低門檻到 0.35敏感地偵測情緒
240
- if audio_confidence >= 0.35:
241
  emotion_value = audio_emotion_label
242
  emotion_confidence = audio_confidence
243
- logger.info(f"🎭 使用音頻情緒: {emotion_value} (置信度: {audio_confidence:.4f})")
244
- logger.info(f"📝 文字情緒: {text_emotion} (輔助)")
245
  else:
246
  emotion_value = text_emotion
247
  emotion_confidence = 0.5 # 文字情緒預設置信度
248
- logger.info(f"📝 使用文字情緒: {emotion_value} (音頻置信度過低: {audio_confidence:.4f})")
249
  else:
250
  emotion_value = text_emotion
251
  emotion_confidence = 0.5 # 文字情緒預設置信度
252
- logger.info(f"📝 使用文字情緒: {emotion_value} (無音頻情緒)")
 
 
 
253
 
254
  # 1) 檢查是否在關懷模式
255
  if user_id and EmotionCareManager.is_in_care_mode(user_id, chat_id):
@@ -279,7 +287,12 @@ class ChatPipeline:
279
  return ai_res
280
  text = str(ai_res or "").strip()
281
  if not text:
282
- return PipelineResult(text="我在這裡陪你,隨時可以聊聊。", is_fallback=True, reason="ai-care-empty")
 
 
 
 
 
283
  return PipelineResult(text=text, is_fallback=False, meta={"care_mode": True, "emotion": care_emotion})
284
 
285
  # 2) 檢查是否需要進入關懷模式(傳遞置信度,用於連續性判斷)
@@ -332,7 +345,12 @@ class ChatPipeline:
332
  tool_name = feat_res.get('tool_name')
333
  tool_data = feat_res.get('tool_data')
334
  if not text:
335
- return PipelineResult(text="抱歉,功能處理沒有產出結果。", is_fallback=True, reason="feature-empty")
 
 
 
 
 
336
 
337
  # 簡化翻譯:非中文用戶 → 翻譯工具卡片
338
  is_chinese = self._is_chinese_message(user_message)
@@ -365,7 +383,12 @@ class ChatPipeline:
365
  # 正常字串
366
  text = str(feat_res or "").strip()
367
  if not text:
368
- return PipelineResult(text="抱歉,功能處理沒有產出結果。", is_fallback=True, reason="feature-empty")
 
 
 
 
 
369
 
370
  # 不再翻譯工具回應,讓 GPT 自己處理並用對應語言描述
371
 
@@ -394,7 +417,12 @@ class ChatPipeline:
394
  return ai_res
395
  text = str(ai_res or "").strip()
396
  if not text:
397
- return PipelineResult(text="抱歉,我暫時沒有合適的回應。可以換個說法再試試嗎?", is_fallback=True, reason="ai-empty")
 
 
 
 
 
398
 
399
  # 一般聊天也包含情緒資訊
400
  meta_dict = {
 
211
  language: Optional[str] = None,
212
  ) -> PipelineResult:
213
  if not user_message or not user_message.strip():
214
+ return PipelineResult(
215
+ text="我沒有收到您的消息,請重新輸入。",
216
+ is_fallback=True,
217
+ reason="empty",
218
+ meta={"emotion": "neutral", "care_mode": False}
219
+ )
220
 
221
  # language 參數保留以向後兼容,但不使用(GPT 自動判斷語言)
222
 
 
228
  return detect_res
229
  has_feature, intent_data = detect_res
230
 
231
+ # 情緒融合】雙軌制:音頻情緒優先,文字情緒輔助
232
  text_emotion = intent_data.get("emotion", "neutral") if intent_data else "neutral"
233
+ logger.info(f"🎭 [情緒流向-1] 文字情緒: {text_emotion}")
234
 
235
+ # 檢查音頻情緒
236
+ if audio_emotion:
237
+ logger.info(f"🎭 [情緒流向-2] 音頻情緒資料: success={audio_emotion.get('success')}, emotion={audio_emotion.get('emotion')}, confidence={audio_emotion.get('confidence')}")
238
 
239
+ # 情緒融合邏輯
240
  emotion_confidence = 0.5 # 預設置信度
241
  if audio_emotion and audio_emotion.get("success"):
242
  audio_emotion_label = audio_emotion.get("emotion", "neutral")
243
  audio_confidence = audio_emotion.get("confidence", 0.0)
244
 
245
+ # 【優化】提高門檻到 0.7避免誤判(太敏感會導致錯誤情緒
246
+ if audio_confidence >= 0.7:
247
  emotion_value = audio_emotion_label
248
  emotion_confidence = audio_confidence
249
+ logger.info(f"🎭 [情緒流向-3] ✅ 採用音頻情緒: {emotion_value} (置信度: {audio_confidence:.4f})")
 
250
  else:
251
  emotion_value = text_emotion
252
  emotion_confidence = 0.5 # 文字情緒預設置信度
253
+ logger.info(f"🎭 [情緒流向-3] ⬇️ 音頻置信度過低 ({audio_confidence:.4f}),改用文字情緒: {emotion_value}")
254
  else:
255
  emotion_value = text_emotion
256
  emotion_confidence = 0.5 # 文字情緒預設置信度
257
+ logger.info(f"🎭 [情緒流向-3] 📝 無音頻情緒,使用文字情緒: {emotion_value}")
258
+
259
+ # 【關鍵】記錄最終情緒
260
+ logger.info(f"🎭 [情緒流向-最終] emotion={emotion_value}, confidence={emotion_confidence:.2f}")
261
 
262
  # 1) 檢查是否在關懷模式
263
  if user_id and EmotionCareManager.is_in_care_mode(user_id, chat_id):
 
287
  return ai_res
288
  text = str(ai_res or "").strip()
289
  if not text:
290
+ return PipelineResult(
291
+ text="我在這裡陪你,隨時可以聊聊。",
292
+ is_fallback=True,
293
+ reason="ai-care-empty",
294
+ meta={"care_mode": True, "emotion": care_emotion or "sad"}
295
+ )
296
  return PipelineResult(text=text, is_fallback=False, meta={"care_mode": True, "emotion": care_emotion})
297
 
298
  # 2) 檢查是否需要進入關懷模式(傳遞置信度,用於連續性判斷)
 
345
  tool_name = feat_res.get('tool_name')
346
  tool_data = feat_res.get('tool_data')
347
  if not text:
348
+ return PipelineResult(
349
+ text="抱歉,功能處理沒有產出結果。",
350
+ is_fallback=True,
351
+ reason="feature-empty",
352
+ meta={"emotion": emotion_value, "care_mode": False}
353
+ )
354
 
355
  # 簡化翻譯:非中文用戶 → 翻譯工具卡片
356
  is_chinese = self._is_chinese_message(user_message)
 
383
  # 正常字串
384
  text = str(feat_res or "").strip()
385
  if not text:
386
+ return PipelineResult(
387
+ text="抱歉,功能處理沒有產出結果。",
388
+ is_fallback=True,
389
+ reason="feature-empty",
390
+ meta={"emotion": emotion_value, "care_mode": False}
391
+ )
392
 
393
  # 不再翻譯工具回應,讓 GPT 自己處理並用對應語言描述
394
 
 
417
  return ai_res
418
  text = str(ai_res or "").strip()
419
  if not text:
420
+ return PipelineResult(
421
+ text="抱歉,我暫時沒有合適的回應。可以換個說法再試試嗎?",
422
+ is_fallback=True,
423
+ reason="ai-empty",
424
+ meta={"emotion": emotion_value, "care_mode": False}
425
+ )
426
 
427
  # 一般聊天也包含情緒資訊
428
  meta_dict = {
features/mcp/agent_bridge.py CHANGED
@@ -511,6 +511,18 @@ class MCPAgentBridge:
511
  has_feature, intent_data, cached_time = self._intent_cache[cache_key]
512
  if time_module.time() - cached_time < self._intent_cache_ttl:
513
  logger.debug(f"💾 意圖快取命中: {message[:50]}...")
 
 
 
 
 
 
 
 
 
 
 
 
514
  return has_feature, intent_data
515
  else:
516
  del self._intent_cache[cache_key]
 
511
  has_feature, intent_data, cached_time = self._intent_cache[cache_key]
512
  if time_module.time() - cached_time < self._intent_cache_ttl:
513
  logger.debug(f"💾 意圖快取命中: {message[:50]}...")
514
+
515
+ # 【關鍵修復】快取命中時,仍需重新偵測情緒(情緒是即時的)
516
+ # 因為同一句話在不同時間說可能帶有不同的情緒強度
517
+ try:
518
+ fresh_emotion = await self._analyze_emotion_from_message(message)
519
+ if fresh_emotion and intent_data:
520
+ intent_data = dict(intent_data) # 複製避免修改原快取
521
+ intent_data['emotion'] = fresh_emotion
522
+ logger.info(f"🎭 快取命中但重新偵測情緒: {fresh_emotion}")
523
+ except Exception as e:
524
+ logger.warning(f"快取命中時情緒分析失敗: {e}")
525
+
526
  return has_feature, intent_data
527
  else:
528
  del self._intent_cache[cache_key]
static/frontend/js/websocket.js CHANGED
@@ -418,10 +418,8 @@ function initializeWebSocket(token) {
418
  break;
419
 
420
  case 'bot_message':
421
-
422
- if (data.emotion && typeof applyEmotion === 'function') {
423
- applyEmotion(data.emotion);
424
- }
425
 
426
  if (data.care_mode && typeof hideToolCards === 'function') {
427
  hideToolCards();
@@ -455,10 +453,7 @@ function initializeWebSocket(token) {
455
  transcript.textContent = data.text;
456
  transcript.className = 'voice-transcript final';
457
  window.realtimeTranscript = '';
458
-
459
- if (data.emotion && typeof applyEmotion === 'function') {
460
- applyEmotion(data.emotion);
461
- }
462
  break;
463
 
464
  case 'realtime_stt_status':
@@ -494,9 +489,8 @@ function initializeWebSocket(token) {
494
  break;
495
 
496
  case 'audio_emotion_detected':
497
- if (data.emotion && data.confidence >= 0.35 && typeof applyEmotion === 'function') {
498
- applyEmotion(data.emotion);
499
- }
500
  break;
501
 
502
  case 'env_ack':
 
418
  break;
419
 
420
  case 'bot_message':
421
+ // 【統一】不在此處套用情緒,只由 emotion_detected 事件控制
422
+ // 保留情緒資訊在 data 中供調試使用
 
 
423
 
424
  if (data.care_mode && typeof hideToolCards === 'function') {
425
  hideToolCards();
 
453
  transcript.textContent = data.text;
454
  transcript.className = 'voice-transcript final';
455
  window.realtimeTranscript = '';
456
+ // 【統一】不在此處套用情緒,只由 emotion_detected 事件控制
 
 
 
457
  break;
458
 
459
  case 'realtime_stt_status':
 
489
  break;
490
 
491
  case 'audio_emotion_detected':
492
+ // 【統一】不在此處套用情緒,只由 emotion_detected 事件控制
493
+ // 後端會融合音頻和文字情緒後統一發送 emotion_detected
 
494
  break;
495
 
496
  case 'env_ack':