Spaces:
Running
Running
Commit
·
b0d8d63
1
Parent(s):
f998449
進一步修復和優化
Browse files- 修復 app.py 中的額外問題
- 優化 services/ai_service.py 的功能
- app.py +64 -64
- services/ai_service.py +13 -3
app.py
CHANGED
|
@@ -832,6 +832,70 @@ async def websocket_endpoint_with_jwt(websocket: WebSocket, token: str = Query(N
|
|
| 832 |
import asyncio as _asyncio
|
| 833 |
_asyncio.create_task(_do_process_and_send())
|
| 834 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 835 |
elif message_type == "chat_focus":
|
| 836 |
try:
|
| 837 |
cid = message_data.get("chat_id")
|
|
@@ -1209,70 +1273,6 @@ async def websocket_endpoint_with_jwt(websocket: WebSocket, token: str = Query(N
|
|
| 1209 |
"timestamp": time.time()
|
| 1210 |
})
|
| 1211 |
|
| 1212 |
-
finally:
|
| 1213 |
-
pass
|
| 1214 |
-
# ===== 環境快照上報 =====
|
| 1215 |
-
if message_type == "env_snapshot":
|
| 1216 |
-
try:
|
| 1217 |
-
lat = float(message_data.get("lat")) if message_data.get("lat") is not None else None
|
| 1218 |
-
lon = float(message_data.get("lon")) if message_data.get("lon") is not None else None
|
| 1219 |
-
acc = message_data.get("accuracy_m")
|
| 1220 |
-
acc = float(acc) if acc is not None else None
|
| 1221 |
-
heading_deg = message_data.get("heading_deg")
|
| 1222 |
-
heading_deg = float(heading_deg) if heading_deg is not None else None
|
| 1223 |
-
tz = message_data.get("tz")
|
| 1224 |
-
locale = message_data.get("locale")
|
| 1225 |
-
device = message_data.get("device")
|
| 1226 |
-
|
| 1227 |
-
# 後端節流:距離<100m且方位差<25度則忽略
|
| 1228 |
-
do_write_snapshot = False
|
| 1229 |
-
last = manager.last_env.get(user_id)
|
| 1230 |
-
if last and lat is not None and lon is not None and last.get("lat") is not None:
|
| 1231 |
-
dist = _haversine_m(last.get("lat",0), last.get("lon",0), lat, lon)
|
| 1232 |
-
deg_diff = abs((heading_deg or 0) - (last.get("heading_deg") or 0))
|
| 1233 |
-
if dist >= 100 or deg_diff >= 25:
|
| 1234 |
-
do_write_snapshot = True
|
| 1235 |
-
else:
|
| 1236 |
-
do_write_snapshot = True
|
| 1237 |
-
|
| 1238 |
-
from geohash2 import encode as gh_encode
|
| 1239 |
-
geohash7 = gh_encode(lat, lon, precision=7) if (lat is not None and lon is not None) else None
|
| 1240 |
-
heading_cardinal = _heading_to_cardinal(heading_deg) if heading_deg is not None else None
|
| 1241 |
-
env_payload = {
|
| 1242 |
-
"lat": lat,
|
| 1243 |
-
"lon": lon,
|
| 1244 |
-
"accuracy_m": acc,
|
| 1245 |
-
"heading_deg": heading_deg,
|
| 1246 |
-
"heading_cardinal": heading_cardinal,
|
| 1247 |
-
"tz": tz,
|
| 1248 |
-
"locale": locale,
|
| 1249 |
-
"device": device,
|
| 1250 |
-
"geohash_7": geohash7,
|
| 1251 |
-
}
|
| 1252 |
-
|
| 1253 |
-
# 更新會話暫存
|
| 1254 |
-
manager.last_env[user_id] = env_payload
|
| 1255 |
-
info = manager.get_client_info(user_id) or {}
|
| 1256 |
-
info['env_context'] = env_payload
|
| 1257 |
-
manager.set_client_info(user_id, info)
|
| 1258 |
-
|
| 1259 |
-
try:
|
| 1260 |
-
await set_user_env_current(user_id, env_payload)
|
| 1261 |
-
except Exception as e:
|
| 1262 |
-
logger.warning(f"寫入環境現況失敗: {e}")
|
| 1263 |
-
|
| 1264 |
-
if do_write_snapshot:
|
| 1265 |
-
try:
|
| 1266 |
-
snap = env_payload.copy()
|
| 1267 |
-
snap['reason'] = 'threshold'
|
| 1268 |
-
await add_user_env_snapshot(user_id, snap)
|
| 1269 |
-
except Exception as e:
|
| 1270 |
-
logger.warning(f"寫入環境快照失敗: {e}")
|
| 1271 |
-
|
| 1272 |
-
await websocket.send_json({"type": "env_ack", "success": True, "geohash_7": geohash7, "heading_cardinal": heading_cardinal})
|
| 1273 |
-
except Exception as e:
|
| 1274 |
-
logger.error(f"處理 env_snapshot 失敗: {e}")
|
| 1275 |
-
await websocket.send_json({"type": "env_ack", "success": False, "error": str(e)})
|
| 1276 |
except json.JSONDecodeError:
|
| 1277 |
await manager.send_message("消息格式錯誤,無法解析", user_id, "error")
|
| 1278 |
except Exception as e:
|
|
|
|
| 832 |
import asyncio as _asyncio
|
| 833 |
_asyncio.create_task(_do_process_and_send())
|
| 834 |
|
| 835 |
+
elif message_type == "env_snapshot":
|
| 836 |
+
try:
|
| 837 |
+
lat = float(message_data.get("lat")) if message_data.get("lat") is not None else None
|
| 838 |
+
lon = float(message_data.get("lon")) if message_data.get("lon") is not None else None
|
| 839 |
+
acc = message_data.get("accuracy_m")
|
| 840 |
+
acc = float(acc) if acc is not None else None
|
| 841 |
+
heading_deg = message_data.get("heading_deg")
|
| 842 |
+
heading_deg = float(heading_deg) if heading_deg is not None else None
|
| 843 |
+
tz = message_data.get("tz")
|
| 844 |
+
locale = message_data.get("locale")
|
| 845 |
+
device = message_data.get("device")
|
| 846 |
+
|
| 847 |
+
# 後端節流:距離<100m且方位差<25度則忽略
|
| 848 |
+
do_write_snapshot = False
|
| 849 |
+
last = manager.last_env.get(user_id)
|
| 850 |
+
if last and lat is not None and lon is not None and last.get("lat") is not None:
|
| 851 |
+
dist = _haversine_m(last.get("lat", 0), last.get("lon", 0), lat, lon)
|
| 852 |
+
deg_diff = abs((heading_deg or 0) - (last.get("heading_deg") or 0))
|
| 853 |
+
if dist >= 100 or deg_diff >= 25:
|
| 854 |
+
do_write_snapshot = True
|
| 855 |
+
else:
|
| 856 |
+
do_write_snapshot = True
|
| 857 |
+
|
| 858 |
+
from geohash2 import encode as gh_encode
|
| 859 |
+
geohash7 = gh_encode(lat, lon, precision=7) if (lat is not None and lon is not None) else None
|
| 860 |
+
heading_cardinal = _heading_to_cardinal(heading_deg) if heading_deg is not None else None
|
| 861 |
+
env_payload = {
|
| 862 |
+
"lat": lat,
|
| 863 |
+
"lon": lon,
|
| 864 |
+
"accuracy_m": acc,
|
| 865 |
+
"heading_deg": heading_deg,
|
| 866 |
+
"heading_cardinal": heading_cardinal,
|
| 867 |
+
"tz": tz,
|
| 868 |
+
"locale": locale,
|
| 869 |
+
"device": device,
|
| 870 |
+
"geohash_7": geohash7,
|
| 871 |
+
}
|
| 872 |
+
|
| 873 |
+
# 更新會話暫存
|
| 874 |
+
manager.last_env[user_id] = env_payload
|
| 875 |
+
info = manager.get_client_info(user_id) or {}
|
| 876 |
+
info["env_context"] = env_payload
|
| 877 |
+
manager.set_client_info(user_id, info)
|
| 878 |
+
|
| 879 |
+
try:
|
| 880 |
+
await set_user_env_current(user_id, env_payload)
|
| 881 |
+
except Exception as e:
|
| 882 |
+
logger.warning(f"寫入環境現況失敗: {e}")
|
| 883 |
+
|
| 884 |
+
if do_write_snapshot:
|
| 885 |
+
try:
|
| 886 |
+
snap = env_payload.copy()
|
| 887 |
+
snap["reason"] = "threshold"
|
| 888 |
+
await add_user_env_snapshot(user_id, snap)
|
| 889 |
+
except Exception as e:
|
| 890 |
+
logger.warning(f"寫入環境快照失敗: {e}")
|
| 891 |
+
|
| 892 |
+
await websocket.send_json(
|
| 893 |
+
{"type": "env_ack", "success": True, "geohash_7": geohash7, "heading_cardinal": heading_cardinal}
|
| 894 |
+
)
|
| 895 |
+
except Exception as e:
|
| 896 |
+
logger.error(f"處理 env_snapshot 失敗: {e}")
|
| 897 |
+
await websocket.send_json({"type": "env_ack", "success": False, "error": str(e)})
|
| 898 |
+
|
| 899 |
elif message_type == "chat_focus":
|
| 900 |
try:
|
| 901 |
cid = message_data.get("chat_id")
|
|
|
|
| 1273 |
"timestamp": time.time()
|
| 1274 |
})
|
| 1275 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1276 |
except json.JSONDecodeError:
|
| 1277 |
await manager.send_message("消息格式錯誤,無法解析", user_id, "error")
|
| 1278 |
except Exception as e:
|
services/ai_service.py
CHANGED
|
@@ -32,7 +32,7 @@ from core.config import settings
|
|
| 32 |
OPENAI_TIMEOUT = settings.OPENAI_TIMEOUT # 關懷模式 reasoning model 需要更長時間
|
| 33 |
|
| 34 |
# 情緒關懷模式 System Prompt(新增)
|
| 35 |
-
CARE_MODE_SYSTEM_PROMPT = """
|
| 36 |
|
| 37 |
**極簡短回應規則(必須嚴格遵守)**:
|
| 38 |
- 最多 1-2 句話(總共不超過 30 字)
|
|
@@ -99,6 +99,8 @@ def _build_base_system_prompt(
|
|
| 99 |
base_prompt = f"用戶情緒:{care_emotion}\n{base_prompt}"
|
| 100 |
else:
|
| 101 |
base_prompt = (
|
|
|
|
|
|
|
| 102 |
"你是一個友善、有禮、幽默且能夠提供幫助的AI助手。"
|
| 103 |
"請使用繁體中文回覆,保持簡潔清晰的表達。"
|
| 104 |
)
|
|
@@ -693,7 +695,11 @@ async def _generate_response_with_chat_db(
|
|
| 693 |
system_prompt = f"{CARE_MODE_SYSTEM_PROMPT}\n\n{emotion_text}"
|
| 694 |
logger.info(f"💙 使用關懷模式 System Prompt,情緒:{care_emotion}")
|
| 695 |
else:
|
| 696 |
-
system_prompt =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 697 |
|
| 698 |
# 在系統提示前加上用戶名稱
|
| 699 |
if user_name:
|
|
@@ -890,7 +896,11 @@ async def _generate_response_with_global_history(
|
|
| 890 |
system_prompt = f"{CARE_MODE_SYSTEM_PROMPT}\n\n{emotion_text}"
|
| 891 |
logger.info(f"💙 使用關懷模式 System Prompt(全局歷史),情緒:{care_emotion}")
|
| 892 |
else:
|
| 893 |
-
system_prompt =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 894 |
|
| 895 |
# 在系統提示前加上用戶名稱
|
| 896 |
if user_name:
|
|
|
|
| 32 |
OPENAI_TIMEOUT = settings.OPENAI_TIMEOUT # 關懷模式 reasoning model 需要更長時間
|
| 33 |
|
| 34 |
# 情緒關懷模式 System Prompt(新增)
|
| 35 |
+
CARE_MODE_SYSTEM_PROMPT = """你是 BloomWare 情緒關懷助手,由銘傳大學人工智慧應用學系 BloomWare 團隊開發。你不是 GPT,也不要自稱 GPT。你是富有同理心的 AI 助手,用戶情緒不佳需要支持。
|
| 36 |
|
| 37 |
**極簡短回應規則(必須嚴格遵守)**:
|
| 38 |
- 最多 1-2 句話(總共不超過 30 字)
|
|
|
|
| 99 |
base_prompt = f"用戶情緒:{care_emotion}\n{base_prompt}"
|
| 100 |
else:
|
| 101 |
base_prompt = (
|
| 102 |
+
"你是 BloomWare 助理,由銘傳大學人工智慧應用學系 BloomWare 團隊開發。"
|
| 103 |
+
"你不是 GPT,也不要自稱 GPT。"
|
| 104 |
"你是一個友善、有禮、幽默且能夠提供幫助的AI助手。"
|
| 105 |
"請使用繁體中文回覆,保持簡潔清晰的表達。"
|
| 106 |
)
|
|
|
|
| 695 |
system_prompt = f"{CARE_MODE_SYSTEM_PROMPT}\n\n{emotion_text}"
|
| 696 |
logger.info(f"💙 使用關懷模式 System Prompt,情緒:{care_emotion}")
|
| 697 |
else:
|
| 698 |
+
system_prompt = (
|
| 699 |
+
"你是 BloomWare 助理,由銘傳大學人工智慧應用學系 BloomWare 團隊開發。"
|
| 700 |
+
"你不是 GPT,也不要自稱 GPT。"
|
| 701 |
+
"你是一個友善、有禮、幽默且能夠提供幫助的AI助手。請使用繁體中文回覆,保持簡潔清晰的表達。"
|
| 702 |
+
)
|
| 703 |
|
| 704 |
# 在系統提示前加上用戶名稱
|
| 705 |
if user_name:
|
|
|
|
| 896 |
system_prompt = f"{CARE_MODE_SYSTEM_PROMPT}\n\n{emotion_text}"
|
| 897 |
logger.info(f"💙 使用關懷模式 System Prompt(全局歷史),情緒:{care_emotion}")
|
| 898 |
else:
|
| 899 |
+
system_prompt = (
|
| 900 |
+
"你是 BloomWare 助理,由銘傳大學人工智慧應用學系 BloomWare 團隊開發。"
|
| 901 |
+
"你不是 GPT,也不要自稱 GPT。"
|
| 902 |
+
"你是一個友善、有禮、幽默且能夠提供幫助的AI助手。請使用繁體中文回覆,保持簡潔清晰的表達。"
|
| 903 |
+
)
|
| 904 |
|
| 905 |
# 在系統提示前加上用戶名稱
|
| 906 |
if user_name:
|