XiaoBai1221 commited on
Commit
0267618
·
1 Parent(s): 4c9d246

language_upgrade

Browse files
Files changed (2) hide show
  1. core/pipeline.py +59 -162
  2. services/ai_service.py +25 -86
core/pipeline.py CHANGED
@@ -47,158 +47,110 @@ class ChatPipeline:
47
  self._feature_timeout = feature_timeout
48
  self._ai_timeout = ai_timeout
49
  self._model = model
50
-
51
- # 語言名稱映射
52
- self._language_names = {
53
- "zh": "繁體中文",
54
- "en": "English",
55
- "ja": "日本語",
56
- "ko": "한국어",
57
- "id": "Bahasa Indonesia",
58
- "vi": "Tiếng Việt",
59
- }
60
-
61
- def _detect_language(self, text: str) -> str:
62
  """
63
- 單的語言檢測(基於字符範圍)
64
-
65
  Args:
66
- text: 輸入文字
67
-
68
  Returns:
69
- 語言代碼(zh, en, ja, ko, id, vi)
70
  """
71
  if not text:
72
- return "zh"
73
-
74
- # 各語言字符數量
75
- korean_count = 0
76
- japanese_count = 0
77
- chinese_count = 0
78
- latin_count = 0
79
- vietnamese_count = 0
80
-
81
- vietnamese_chars = set("àáảãạăằắẳẵặâầấẩẫậèéẻẽẹêềếểễệìíỉĩịòóỏõọôồốổỗộơờớởỡợùúủũụưừứửữựỳýỷỹỵđ")
82
-
83
- for char in text:
84
- code = ord(char)
85
- # 韓文
86
- if 0xAC00 <= code <= 0xD7AF or 0x1100 <= code <= 0x11FF:
87
- korean_count += 1
88
- # 日文假名
89
- elif 0x3040 <= code <= 0x309F or 0x30A0 <= code <= 0x30FF:
90
- japanese_count += 1
91
- # 中文
92
- elif 0x4E00 <= code <= 0x9FFF:
93
- chinese_count += 1
94
- # 拉丁字母
95
- elif 0x0041 <= code <= 0x007A:
96
- latin_count += 1
97
- # 越南文特殊字符
98
- if char.lower() in vietnamese_chars:
99
- vietnamese_count += 1
100
-
101
- # 判斷主要語言
102
- if korean_count > 0:
103
- return "ko"
104
- if japanese_count > chinese_count and japanese_count > 0:
105
- return "ja"
106
- if vietnamese_count > 0:
107
- return "vi"
108
- if chinese_count > latin_count and chinese_count > 0:
109
- return "zh"
110
- if latin_count > 0:
111
- # 可能是英文或印尼文,預設英文
112
- return "en"
113
-
114
- return "zh"
115
 
116
- async def _translate_tool_data(self, tool_data: Dict[str, Any], target_language: str) -> Dict[str, Any]:
 
 
 
117
  """
118
- 翻譯工具卡片中的文字欄位
119
-
120
  Args:
121
  tool_data: 工具資料字典
122
- target_language: 目標語言代碼
123
-
124
  Returns:
125
  翻譯後的工具資料
126
  """
127
- if not tool_data or target_language == "zh":
128
  return tool_data
129
-
130
  try:
131
  import copy
132
  translated_data = copy.deepcopy(tool_data)
133
-
134
- # 需要翻譯的欄位名稱(天氣、新聞等工具的顯示欄位)
135
  translatable_keys = {
136
- "description", "main", "name", "title", "summary",
137
  "content", "message", "text", "label", "status"
138
  }
139
-
140
- # 收集需要翻譯的文字欄位
141
  texts_to_translate = []
142
- text_paths = [] # 記錄路徑以便回填
143
-
144
  def collect_texts(obj, path="", parent_key=""):
145
  """遞迴收集需要翻譯的文字"""
146
  if isinstance(obj, dict):
147
  for key, value in obj.items():
148
  new_path = f"{path}.{key}" if path else key
149
- # 跳過技術欄位
150
- if key in ("id", "url", "link", "lat", "lon", "timestamp", "code", "icon", "base", "cod"):
151
  continue
152
  collect_texts(value, new_path, key)
153
  elif isinstance(obj, list):
154
  for i, item in enumerate(obj):
155
  collect_texts(item, f"{path}[{i}]", parent_key)
156
  elif isinstance(obj, str) and len(obj) > 1:
157
- # 翻譯條件
158
- # 1. 欄位名稱在可翻譯列表中
159
- # 2. 或字串包含中文
160
- # 3. 或字串是純英文描述(非數字、非代碼)
161
  should_translate = (
162
  parent_key.lower() in translatable_keys or
163
- any('\u4e00' <= c <= '\u9fff' for c in obj) or
164
- (obj.isalpha() or ' ' in obj) and len(obj) > 2
165
  )
166
  if should_translate:
167
  texts_to_translate.append(obj)
168
  text_paths.append(path)
169
-
170
  collect_texts(translated_data)
171
-
172
  if not texts_to_translate:
173
  return tool_data
174
-
175
- # 批量翻譯
176
  import services.ai_service as ai_service
177
- lang_name = self._language_names.get(target_language, target_language)
178
-
179
  combined_text = "\n---\n".join(texts_to_translate)
180
  messages = [
181
  {
182
  "role": "system",
183
- "content": f"將以下內容翻譯成 {lang_name}保持格式和表情符號。每段用 '---' 分隔,輸出也用 '---' 分隔。只輸出翻譯結果。"
184
  },
185
  {"role": "user", "content": combined_text}
186
  ]
187
-
188
  translated = await ai_service.generate_response_async(
189
  messages=messages,
190
  model="gpt-5-nano",
191
  reasoning_effort="minimal",
192
- max_tokens=800, # 工具卡片翻譯:實際輸出限制 800 tokens
193
  )
194
-
195
  if translated:
196
  translated_parts = translated.strip().split("---")
197
  translated_parts = [p.strip() for p in translated_parts if p.strip()]
198
-
199
  # 回填翻譯結果
200
  def set_value(obj, path, value):
201
- """根據路徑設置值"""
202
  parts = path.replace("]", "").replace("[", ".").split(".")
203
  for part in parts[:-1]:
204
  if part.isdigit():
@@ -210,67 +162,20 @@ class ChatPipeline:
210
  obj[int(last)] = value
211
  else:
212
  obj[last] = value
213
-
214
  for i, path in enumerate(text_paths):
215
  if i < len(translated_parts):
216
  try:
217
  set_value(translated_data, path, translated_parts[i])
218
  except Exception:
219
  pass
220
-
221
  logger.info(f"🌐 工具卡片已翻譯: {len(texts_to_translate)} 個欄位")
222
  return translated_data
223
-
224
- except Exception as e:
225
- logger.warning(f"⚠️ 工具卡片翻譯失敗: {e}")
226
- return tool_data
227
 
228
- async def _translate_tool_response(self, text: str, target_language: str) -> str:
229
- """
230
- 翻譯工具回應到目標語言
231
-
232
- Args:
233
- text: 原始文字(中文)
234
- target_language: 目標語言代碼(en, ja, ko, id, vi)
235
-
236
- Returns:
237
- 翻譯後的文字
238
- """
239
- if not text or target_language == "zh":
240
- return text
241
-
242
- try:
243
- import services.ai_service as ai_service
244
-
245
- lang_name = self._language_names.get(target_language, target_language)
246
-
247
- messages = [
248
- {
249
- "role": "system",
250
- "content": f"你是一個翻譯助手。將以下內容翻譯成 {lang_name},保持格式、表情符號和數字不變。只輸出翻譯結果,不要加任何解釋。"
251
- },
252
- {
253
- "role": "user",
254
- "content": text
255
- }
256
- ]
257
-
258
- translated = await ai_service.generate_response_async(
259
- messages=messages,
260
- model="gpt-5-nano",
261
- reasoning_effort="minimal",
262
- max_tokens=500, # 工具回應翻譯:實際輸出限制 500 tokens
263
- )
264
-
265
- if translated and translated.strip():
266
- logger.info(f"🌐 工具回應已翻譯: {target_language}")
267
- return translated.strip()
268
-
269
- return text
270
-
271
  except Exception as e:
272
- logger.warning(f"⚠️ 翻譯失敗,使用原: {e}")
273
- return text
274
 
275
  async def _with_timeout(self, coro: Awaitable[Any], timeout: float, reason: str) -> Any:
276
  try:
@@ -302,10 +207,7 @@ class ChatPipeline:
302
  if not user_message or not user_message.strip():
303
  return PipelineResult(text="我沒有收到您的消息,請重新輸入。", is_fallback=True, reason="empty")
304
 
305
- # 自動檢測語言(如果沒有傳入
306
- if not language:
307
- language = self._detect_language(user_message)
308
- logger.info(f"🌐 自動檢測語言: {language}")
309
 
310
  # 0) 先進行意圖偵測以提取情緒(需要在關懷模式檢查前執行)
311
  detect_res = await self._with_timeout(
@@ -415,14 +317,11 @@ class ChatPipeline:
415
  tool_data = feat_res.get('tool_data')
416
  if not text:
417
  return PipelineResult(text="抱歉,功能處理沒有產出結果。", is_fallback=True, reason="feature-empty")
418
-
419
- # 如果語言不是中文翻譯工具回應和工具卡片
420
- if language and language != "zh":
421
- text = await self._translate_tool_response(text, language)
422
- # 翻譯工具卡片中的文字欄位
423
- if tool_data:
424
- tool_data = await self._translate_tool_data(tool_data, language)
425
-
426
  # 返回帶有工具元數據的結果(包含情緒)
427
  meta_dict = {}
428
  if tool_name:
@@ -441,11 +340,9 @@ class ChatPipeline:
441
  text = str(feat_res or "").strip()
442
  if not text:
443
  return PipelineResult(text="抱歉,功能處理沒有產出結果。", is_fallback=True, reason="feature-empty")
444
-
445
- # 如果語言是中文,翻譯工具回應
446
- if language and language != "zh":
447
- text = await self._translate_tool_response(text, language)
448
-
449
  return PipelineResult(
450
  text=text,
451
  is_fallback=False,
 
47
  self._feature_timeout = feature_timeout
48
  self._ai_timeout = ai_timeout
49
  self._model = model
50
+
51
+ def _is_chinese_message(self, text: str) -> bool:
 
 
 
 
 
 
 
 
 
 
52
  """
53
+ 語言判斷:檢測訊息是否為中文
54
+
55
  Args:
56
+ text: 用戶訊息
57
+
58
  Returns:
59
+ True 如果訊息主要是中文,False 如果是其他語言
60
  """
61
  if not text:
62
+ return True # 預設為中文
63
+
64
+ # 計算中文字符比例
65
+ chinese_chars = sum(1 for c in text if '\u4e00' <= c <= '\u9fff')
66
+ total_chars = len(text.replace(' ', '').replace('\n', ''))
67
+
68
+ if total_chars == 0:
69
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ # 如果中文字符超過 30%,視為中文訊息
72
+ return chinese_chars > total_chars * 0.3
73
+
74
+ async def _translate_tool_data(self, tool_data: Dict[str, Any], user_message: str) -> Dict[str, Any]:
75
  """
76
+ 簡化版工具卡片翻譯:讓 GPT 自動判斷目標語言
77
+
78
  Args:
79
  tool_data: 工具資料字典
80
+ user_message: 用戶原始訊息(用於推斷目標語言
81
+
82
  Returns:
83
  翻譯後的工具資料
84
  """
85
+ if not tool_data:
86
  return tool_data
87
+
88
  try:
89
  import copy
90
  translated_data = copy.deepcopy(tool_data)
91
+
92
+ # 需要翻譯的欄位(天氣、新聞等工具的顯示欄位)
93
  translatable_keys = {
94
+ "description", "main", "name", "title", "summary",
95
  "content", "message", "text", "label", "status"
96
  }
97
+
98
+ # 收集需要翻譯的文字
99
  texts_to_translate = []
100
+ text_paths = []
101
+
102
  def collect_texts(obj, path="", parent_key=""):
103
  """遞迴收集需要翻譯的文字"""
104
  if isinstance(obj, dict):
105
  for key, value in obj.items():
106
  new_path = f"{path}.{key}" if path else key
107
+ # 跳過技術欄位
108
+ if key in ("id", "url", "link", "lat", "lon", "timestamp", "code", "icon"):
109
  continue
110
  collect_texts(value, new_path, key)
111
  elif isinstance(obj, list):
112
  for i, item in enumerate(obj):
113
  collect_texts(item, f"{path}[{i}]", parent_key)
114
  elif isinstance(obj, str) and len(obj) > 1:
115
+ # 需要翻譯條件
 
 
 
116
  should_translate = (
117
  parent_key.lower() in translatable_keys or
118
+ any('\u4e00' <= c <= '\u9fff' for c in obj) # 包含中文
 
119
  )
120
  if should_translate:
121
  texts_to_translate.append(obj)
122
  text_paths.append(path)
123
+
124
  collect_texts(translated_data)
125
+
126
  if not texts_to_translate:
127
  return tool_data
128
+
129
+ # 批量翻譯(讓 GPT 自動判斷目標語言)
130
  import services.ai_service as ai_service
131
+
 
132
  combined_text = "\n---\n".join(texts_to_translate)
133
  messages = [
134
  {
135
  "role": "system",
136
+ "content": f"將以下內容翻譯成與用戶訊息「{user_message}」相同的語言。保持格式和表情符號。每段用 '---' 分隔,輸出也用 '---' 分隔。只輸出翻譯結果,不要加解釋。"
137
  },
138
  {"role": "user", "content": combined_text}
139
  ]
140
+
141
  translated = await ai_service.generate_response_async(
142
  messages=messages,
143
  model="gpt-5-nano",
144
  reasoning_effort="minimal",
145
+ max_tokens=800,
146
  )
147
+
148
  if translated:
149
  translated_parts = translated.strip().split("---")
150
  translated_parts = [p.strip() for p in translated_parts if p.strip()]
151
+
152
  # 回填翻譯結果
153
  def set_value(obj, path, value):
 
154
  parts = path.replace("]", "").replace("[", ".").split(".")
155
  for part in parts[:-1]:
156
  if part.isdigit():
 
162
  obj[int(last)] = value
163
  else:
164
  obj[last] = value
165
+
166
  for i, path in enumerate(text_paths):
167
  if i < len(translated_parts):
168
  try:
169
  set_value(translated_data, path, translated_parts[i])
170
  except Exception:
171
  pass
172
+
173
  logger.info(f"🌐 工具卡片已翻譯: {len(texts_to_translate)} 個欄位")
174
  return translated_data
 
 
 
 
175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  except Exception as e:
177
+ logger.warning(f"⚠️ 工具卡片翻譯失敗,使用原始數據: {e}")
178
+ return tool_data
179
 
180
  async def _with_timeout(self, coro: Awaitable[Any], timeout: float, reason: str) -> Any:
181
  try:
 
207
  if not user_message or not user_message.strip():
208
  return PipelineResult(text="我沒有收到您的消息,請重新輸入。", is_fallback=True, reason="empty")
209
 
210
+ # language 參數保留以向後兼容,但不使用(GPT 自動判斷語言)
 
 
 
211
 
212
  # 0) 先進行意圖偵測以提取情緒(需要在關懷模式檢查前執行)
213
  detect_res = await self._with_timeout(
 
317
  tool_data = feat_res.get('tool_data')
318
  if not text:
319
  return PipelineResult(text="抱歉,功能處理沒有產出結果。", is_fallback=True, reason="feature-empty")
320
+
321
+ # 簡化翻譯:非中文用戶 → 翻譯工具卡片
322
+ if not self._is_chinese_message(user_message) and tool_data:
323
+ tool_data = await self._translate_tool_data(tool_data, user_message)
324
+
 
 
 
325
  # 返回帶有工具元數據的結果(包含情緒)
326
  meta_dict = {}
327
  if tool_name:
 
340
  text = str(feat_res or "").strip()
341
  if not text:
342
  return PipelineResult(text="抱歉,功能處理沒有產出結果。", is_fallback=True, reason="feature-empty")
343
+
344
+ # 不翻譯工具回應,讓 GPT 自己處理並用對應語言描述
345
+
 
 
346
  return PipelineResult(
347
  text=text,
348
  is_fallback=False,
services/ai_service.py CHANGED
@@ -17,35 +17,22 @@ from core.ai_client import get_openai_client
17
  # 超時設定(秒)
18
  OPENAI_TIMEOUT = settings.OPENAI_TIMEOUT
19
 
20
- # 語言指令
21
- LANGUAGE_INSTRUCTIONS = {
22
- "zh": "請使用繁體中文回覆",
23
- "en": "Please respond in English",
24
- "id": "Silakan balas dalam Bahasa Indonesia",
25
- "ja": "日本語で返信してください",
26
- "vi": "Vui lòng trả lời bằng tiếng Việt",
27
- "auto": "請使用與用戶相同的語言回覆"
28
- }
29
-
30
- # 情緒關懷模式 System Prompt(新增)
31
  CARE_MODE_SYSTEM_PROMPT = """你是 BloomWare 的情緒關懷助手「小花」,由銘傳大學人工智慧應用學系槓上開發團隊打造。你不是 GPT,也不要自稱 GPT;你的任務是在情緒低落時傾聽、陪伴。
32
 
33
  【回應原則】
34
- 1. 第一句必須貼近用戶訊息中的核心事件或感受,必要時引用對方用詞,讓對方感受到被理解
35
- 2. 第二句提供溫柔的陪伴或追問,邀請對方分享需要或下一步;若用戶提出明確請求(如想聽笑話),可在保持關懷語氣下予以回應或確認
36
- 3. 句式要自然口語並隨內容調整字詞,避免反覆使用同一套罐頭話術
37
 
38
  【長度限制】
39
- - 回覆最多 2 句話、總字數不超過 60 字
40
 
41
  【嚴格禁止】
42
- - 提供指示性建議、醫療/心理診斷或引導用戶求助的教科書式說法
43
- - 連續重複完全相同的句型,例如一再出現「我在這裡陪你」而沒有結合具體情境
44
 
45
- 範例
46
- 用戶:「我好難過」 → 你:「聽見你說自己好難過,心裡一定很不好受。想聊聊剛剛發生了什麼嗎?」
47
- 用戶:「我很生氣」 → 你:「這件事讓你超級生氣,情緒一定卡著。要不要跟我說說最困擾你的地方?」
48
- 用戶:「講笑話給我聽」 → 你:「你想聽點輕鬆的,我當然可以陪你。想先聽小笑話還是先聊聊怎麼了?」"""
49
 
50
  # 取得 OpenAI 客戶端(使用統一管理)
51
  def _get_client():
@@ -81,7 +68,7 @@ def _build_base_system_prompt(
81
  use_care_mode: bool,
82
  care_emotion: Optional[str],
83
  user_name: Optional[str],
84
- language: Optional[str] = None,
85
  ) -> str:
86
  if use_care_mode:
87
  base_prompt = CARE_MODE_SYSTEM_PROMPT.strip()
@@ -93,10 +80,8 @@ def _build_base_system_prompt(
93
  "你不是 GPT,也不要自稱 GPT。"
94
  "你是一個友善、有禮、幽默且能夠提供幫助的AI助手。"
95
  )
96
-
97
- # 加入語言指令(明確指定輸出語言)
98
- language_instruction = LANGUAGE_INSTRUCTIONS.get(language or "auto", LANGUAGE_INSTRUCTIONS["auto"])
99
- base_prompt = f"{base_prompt}\n\n【重要】{language_instruction},保持簡潔清晰的表達。"
100
 
101
  if user_name:
102
  base_prompt = f"用戶名稱:{user_name}\n\n{base_prompt}"
@@ -750,36 +735,13 @@ async def _generate_response_with_chat_db(
750
  try:
751
  if messages:
752
  if not any(msg.get("role") == "system" for msg in messages):
753
- # 根據是否為關懷模式選擇 System Prompt(新增)
754
- if use_care_mode:
755
- emotion_text = f"(用戶情緒:{care_emotion})" if care_emotion else ""
756
- system_prompt = f"{CARE_MODE_SYSTEM_PROMPT}\n\n{emotion_text}"
757
- logger.info(f"💙 使用關懷模式 System Prompt,情緒:{care_emotion}")
758
- else:
759
- # 根據語言參數調整回應語言
760
- language_instruction = {
761
- "zh": "繁體中文",
762
- "en": "English",
763
- "ko": "한국어 (Korean)",
764
- "ja": "日本語 (Japanese)",
765
- "id": "Bahasa Indonesia",
766
- "vi": "Tiếng Việt (Vietnamese)"
767
- }.get(language, "繁體中文")
768
-
769
- system_prompt = (
770
- "你是 BloomWare 的個人化助理 小花,由銘傳大學人工智慧應用學系 槓上開發 團隊開發。"
771
- "你不是 GPT,也不要自稱 GPT。"
772
- "你是一個友善、有禮、幽默且能夠提供幫助的AI助手。\n\n"
773
- f"【重要】語言使用規範:\n"
774
- f"- 回覆用戶時:必須使用 {language_instruction},保持簡潔清晰的表達\n"
775
- "- 調用工具時:所有參數必須使用英文(城市名、國家名、貨幣代碼等)\n"
776
- "- 範例:用戶問「台北天氣」→ 調用工具時參數用 {\"city\": \"Taipei\"},回覆時用對應語言描述"
777
- )
778
-
779
- # 在系統提示前加上用戶名稱
780
- if user_name:
781
- system_prompt = f"用戶名稱:{user_name}\n\n{system_prompt}"
782
-
783
  messages.insert(0, {"role": "system", "content": system_prompt})
784
  ai_response = await generate_response_async(
785
  messages,
@@ -970,36 +932,13 @@ async def _generate_response_with_global_history(
970
  try:
971
  if messages:
972
  if not any(msg.get("role") == "system" for msg in messages):
973
- # 根據是否為關懷模式選擇 System Prompt(新增)
974
- if use_care_mode:
975
- emotion_text = f"(用戶情緒:{care_emotion})" if care_emotion else ""
976
- system_prompt = f"{CARE_MODE_SYSTEM_PROMPT}\n\n{emotion_text}"
977
- logger.info(f"💙 使用關懷模式 System Prompt(全局歷史),情緒:{care_emotion}")
978
- else:
979
- # 根據語言參數調整回應語言
980
- language_instruction = {
981
- "zh": "繁體中文",
982
- "en": "English",
983
- "ko": "한국어 (Korean)",
984
- "ja": "日本語 (Japanese)",
985
- "id": "Bahasa Indonesia",
986
- "vi": "Tiếng Việt (Vietnamese)"
987
- }.get(language, "繁體中文")
988
-
989
- system_prompt = (
990
- "你是 BloomWare 的個人化助理 小花,由銘傳大學人工智慧應用學系 槓上開發 團隊開發。"
991
- "你不是 GPT,也不要自稱 GPT。"
992
- "你是一個友善、有禮、幽默且能夠提供幫助的AI助手。\n\n"
993
- f"【重要】語言使用規範:\n"
994
- f"- 回覆用戶時:必須使用 {language_instruction},保持簡潔清晰的表達\n"
995
- "- 調用工具時:所有參數必須使用英文(城市名、國家名、貨幣代碼等)\n"
996
- "- 範例:用戶問「台北天氣」→ 調用工具時參數用 {\"city\": \"Taipei\"},回覆時用對應語言描述"
997
- )
998
-
999
- # 在系統提示前加上用戶名稱
1000
- if user_name:
1001
- system_prompt = f"用戶名稱:{user_name}\n\n{system_prompt}"
1002
-
1003
  messages.insert(0, {"role": "system", "content": system_prompt})
1004
  user_messages = [msg for msg in messages if msg.get("role") == "user"]
1005
  if user_messages and user_id not in conversation_history:
 
17
  # 超時設定(秒)
18
  OPENAI_TIMEOUT = settings.OPENAI_TIMEOUT
19
 
20
+ # 情緒關懷式 System Prompt
 
 
 
 
 
 
 
 
 
 
21
  CARE_MODE_SYSTEM_PROMPT = """你是 BloomWare 的情緒關懷助手「小花」,由銘傳大學人工智慧應用學系槓上開發團隊打造。你不是 GPT,也不要自稱 GPT;你的任務是在情緒低落時傾聽、陪伴。
22
 
23
  【回應原則】
24
+ 1. 第一句必須貼近用戶訊息中的核心事件或感受,必要時引用對方用詞,讓對方感受到被理解
25
+ 2. 第二句提供溫柔的陪伴或追問,邀請對方分享需要或下一步;若用戶提出明確請求(如想聽笑話),可在保持關懷語氣下予以回應或確認
26
+ 3. 句式要自然口語並隨內容調整字詞,避免反覆使用同一套罐頭話術
27
 
28
  【長度限制】
29
+ - 回覆最多 2 句話、總字數不超過 60 字
30
 
31
  【嚴格禁止】
32
+ - 提供指示性建議、醫療/心理診斷或引導用戶求助的教科書式說法
33
+ - 連續重複完全相同的句型,例如一再出現「我在這裡陪你」而沒有結合具體情境
34
 
35
+ 重要請用與用戶相同的語言回應,匹配他們的語言風格和情感語調。"""
 
 
 
36
 
37
  # 取得 OpenAI 客戶端(使用統一管理)
38
  def _get_client():
 
68
  use_care_mode: bool,
69
  care_emotion: Optional[str],
70
  user_name: Optional[str],
71
+ language: Optional[str] = None, # 保留參數以兼容現有調用,但不使用
72
  ) -> str:
73
  if use_care_mode:
74
  base_prompt = CARE_MODE_SYSTEM_PROMPT.strip()
 
80
  "你不是 GPT,也不要自稱 GPT。"
81
  "你是一個友善、有禮、幽默且能夠提供幫助的AI助手。"
82
  )
83
+ # 簡化語言指令 - 讓 GPT 自動判斷用戶語言
84
+ base_prompt = f"{base_prompt}\n\n【重要】請用與用戶相同的語言回應,保持簡潔清晰的表達。"
 
 
85
 
86
  if user_name:
87
  base_prompt = f"用戶名稱:{user_name}\n\n{base_prompt}"
 
735
  try:
736
  if messages:
737
  if not any(msg.get("role") == "system" for msg in messages):
738
+ # 使用統一的 System Prompt 構建函數
739
+ system_prompt = _build_base_system_prompt(
740
+ use_care_mode=use_care_mode,
741
+ care_emotion=care_emotion,
742
+ user_name=user_name,
743
+ language=language # 參數保留但不使用,GPT 自動判斷語言
744
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
745
  messages.insert(0, {"role": "system", "content": system_prompt})
746
  ai_response = await generate_response_async(
747
  messages,
 
932
  try:
933
  if messages:
934
  if not any(msg.get("role") == "system" for msg in messages):
935
+ # 使用統一的 System Prompt 構建函數
936
+ system_prompt = _build_base_system_prompt(
937
+ use_care_mode=use_care_mode,
938
+ care_emotion=care_emotion,
939
+ user_name=user_name,
940
+ language=language # 參數保留但不使用,GPT 自動判斷語言
941
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
942
  messages.insert(0, {"role": "system", "content": system_prompt})
943
  user_messages = [msg for msg in messages if msg.get("role") == "user"]
944
  if user_messages and user_id not in conversation_history: