XiaoBai1221 commited on
Commit
3fe95cf
·
1 Parent(s): d751b82
core/intent_detector.py CHANGED
@@ -198,14 +198,19 @@ class IntentDetector:
198
  - 位置查詢:「我在哪」「where am I」使用 reverse_geocode
199
  - YouBike 查詢:YouBike/Ubike/微笑單車 使用 tdx_youbike
200
 
201
- 【情緒判斷】
202
- 根據用戶消息的語氣判斷情緒:
203
- - neutral: 平靜、中性
204
- - happy: 開心、興奮
205
- - sad: 難過、沮喪
206
- - angry: 生氣、煩躁
207
- - fear: 恐懼、擔心
208
- - surprise: 驚訝、意外"""
 
 
 
 
 
209
 
210
  def _parse_function_calling_response(
211
  self,
@@ -255,13 +260,36 @@ class IntentDetector:
255
  return False, {"emotion": emotion}
256
 
257
  def _extract_emotion_from_response(self, response: Dict[str, Any]) -> str:
258
- """從回應中提取情緒"""
259
- # 嘗試從 content 中提取
 
 
 
 
260
  content = response.get("content", "")
261
- if content:
262
- for emotion in self.EMOTIONS:
263
- if emotion in content.lower():
264
- return emotion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
  return "neutral"
267
 
 
198
  - 位置查詢:「我在哪」「where am I」使用 reverse_geocode
199
  - YouBike 查詢:YouBike/Ubike/微笑單車 使用 tdx_youbike
200
 
201
+ 【情緒判斷 - 重要】
202
+ 根據用戶消息的語氣判斷情緒,並在回應開頭以 [EMOTION:xxx] 格式輸出:
203
+ - [EMOTION:neutral] - 平靜、中性、一般詢問
204
+ - [EMOTION:happy] - 開心、興奮、正面情緒(如:我很快樂、太棒了、好開心)
205
+ - [EMOTION:sad] - 難過、沮喪、失落
206
+ - [EMOTION:angry] - 生氣、煩躁、憤怒
207
+ - [EMOTION:fear] - 恐懼、擔心、焦慮
208
+ - [EMOTION:surprise] - 驚訝、意外
209
+
210
+ 範例:
211
+ - 用戶說「我很快樂」→ 回應開頭必須是 [EMOTION:happy]
212
+ - 用戶說「今天天氣如何」→ 回應開頭必須是 [EMOTION:neutral]
213
+ - 用戶說「我好難過」→ 回應開頭必須是 [EMOTION:sad]"""
214
 
215
  def _parse_function_calling_response(
216
  self,
 
260
  return False, {"emotion": emotion}
261
 
262
  def _extract_emotion_from_response(self, response: Dict[str, Any]) -> str:
263
+ """從回應中提取情緒
264
+
265
+ 優先使用 [EMOTION:xxx] 格式提取,降級使用關鍵字匹配
266
+ """
267
+ import re
268
+
269
  content = response.get("content", "")
270
+ if not content:
271
+ return "neutral"
272
+
273
+ # 優先:使用正則表達式提取 [EMOTION:xxx] 格式
274
+ emotion_match = re.search(r'\[EMOTION:(\w+)\]', content, re.IGNORECASE)
275
+ if emotion_match:
276
+ extracted = emotion_match.group(1).lower()
277
+ if extracted in self.EMOTIONS:
278
+ logger.info(f"🎭 從格式化標籤提取情緒: {extracted}")
279
+ return extracted
280
+
281
+ # 降級:使用關鍵字匹配(但需要更精確的匹配)
282
+ content_lower = content.lower()
283
+ for emotion in self.EMOTIONS:
284
+ # 使用單詞邊界匹配,避免誤判(如 "not angry" 被判為 angry)
285
+ pattern = rf'\b{emotion}\b'
286
+ if re.search(pattern, content_lower):
287
+ # 檢查是否有否定詞在前面
288
+ negation_pattern = rf'(not|no|isn\'t|aren\'t|wasn\'t|weren\'t|don\'t|doesn\'t|didn\'t|never|neither)\s+{emotion}'
289
+ if re.search(negation_pattern, content_lower):
290
+ continue # 跳過被否定的情緒
291
+ logger.info(f"🎭 從關鍵字提取情緒: {emotion}")
292
+ return emotion
293
 
294
  return "neutral"
295
 
services/realtime_stt_service.py CHANGED
@@ -45,27 +45,22 @@ class RealtimeSTTService:
45
  self._receive_task: Optional[asyncio.Task] = None
46
  self.current_language: str = "zh"
47
 
48
- def _build_language_prompt(self) -> str:
49
  """
50
- 建立語言提示,引導 Whisper 優先識別支援的 5 種語言
51
 
52
- Whisper 的 prompt 參數可以包含:
53
- - 多語言範例文字
54
- - 引導模型識別特定語言
 
 
55
 
56
  Returns:
57
- 語言提示字串
58
  """
59
- # 使用多語言範例引導 Whisper(每種語言的常見詞彙)
60
- prompt_samples = [
61
- "你好", # 中文
62
- "Hello", # 英文
63
- "Halo", # 印尼文
64
- "こんにちは", # 日文
65
- "Xin chào" # 越南文
66
- ]
67
-
68
- return ", ".join(prompt_samples)
69
 
70
  def _validate_language(self, language: str) -> Optional[str]:
71
  """
@@ -146,19 +141,22 @@ class RealtimeSTTService:
146
  self.is_connected = True
147
  logger.info("✅ 已連接到 OpenAI Realtime API")
148
 
149
- # 建立語言提示(引導 Whisper 優先識別支援的 5 種語言)
150
- language_prompt = self._build_language_prompt()
151
-
152
  # 發送 session 配置(正確格式:需要 session 物件包裹)
 
 
 
 
 
 
 
 
 
 
153
  session_config = {
154
  "type": "transcription_session.update",
155
  "session": {
156
  "input_audio_format": "pcm16",
157
- "input_audio_transcription": {
158
- "model": model,
159
- "prompt": language_prompt # 使用語言提示引導識別
160
- # 不指定 language,讓 Whisper 自動檢測(但透過 prompt 引導)
161
- },
162
  "turn_detection": {
163
  "type": "server_vad",
164
  "threshold": 0.5,
@@ -170,10 +168,6 @@ class RealtimeSTTService:
170
  }
171
  }
172
  }
173
-
174
- # 如果指定了語言,則加入配置
175
- if validated_language:
176
- session_config["session"]["input_audio_transcription"]["language"] = validated_language
177
 
178
  await self.ws.send(json.dumps(session_config))
179
  logger.info("📤 已發送 session 配置(含語言引導提示)")
 
45
  self._receive_task: Optional[asyncio.Task] = None
46
  self.current_language: str = "zh"
47
 
48
+ def _build_language_prompt(self, language: Optional[str] = None) -> Optional[str]:
49
  """
50
+ 建立語言提示
51
 
52
+ 注意:不使用具體詞彙(如「你好」「Hello」),避免 Whisper 在靜音或
53
+ 低音量時產生幻覺,將 prompt 中的文字當作轉錄結果輸出。
54
+
55
+ Args:
56
+ language: 語言代碼(zh/en/id/ja/vi)或 None(自動檢測)
57
 
58
  Returns:
59
+ 語言提示字串,或 None(不使用 prompt)
60
  """
61
+ # 不使用 prompt,完全依賴 language 參數和音頻內容
62
+ # 這樣可以避免 Whisper 幻覺出 prompt 中的文字
63
+ return None
 
 
 
 
 
 
 
64
 
65
  def _validate_language(self, language: str) -> Optional[str]:
66
  """
 
141
  self.is_connected = True
142
  logger.info("✅ 已連接到 OpenAI Realtime API")
143
 
 
 
 
144
  # 發送 session 配置(正確格式:需要 session 物件包裹)
145
+ # 不使用 prompt 參數,避免 Whisper 幻覺
146
+ transcription_config = {
147
+ "model": model,
148
+ }
149
+
150
+ # 如果指定了語言,加入 language 參數
151
+ if validated_language:
152
+ transcription_config["language"] = validated_language
153
+ logger.info(f"🌐 Whisper 語言設定: {validated_language}")
154
+
155
  session_config = {
156
  "type": "transcription_session.update",
157
  "session": {
158
  "input_audio_format": "pcm16",
159
+ "input_audio_transcription": transcription_config,
 
 
 
 
160
  "turn_detection": {
161
  "type": "server_vad",
162
  "threshold": 0.5,
 
168
  }
169
  }
170
  }
 
 
 
 
171
 
172
  await self.ws.send(json.dumps(session_config))
173
  logger.info("📤 已發送 session 配置(含語言引導提示)")