mickeywu520 commited on
Commit
89879a0
·
1 Parent(s): 7e828dc

修復商品查詢功能,解決貓砂等商品無法找到的問題

Browse files
backend/services/database_service.py CHANGED
@@ -400,29 +400,15 @@ class DatabaseService:
400
  close_database_session(db)
401
 
402
  def save_message(self, user_id: str, message: str, message_type: str = "text") -> bool:
403
- """儲存訊息記錄"""
404
- db = None
405
  try:
406
- db = get_database_session()
407
-
408
- new_message = LineMessage(
409
- user_id=user_id,
410
- message=message,
411
- message_type=message_type
412
- )
413
-
414
- db.add(new_message)
415
- db.commit()
416
  return True
417
-
418
  except Exception as e:
419
- if db:
420
- db.rollback()
421
- logger.error(f"儲存訊息錯誤: {str(e)}")
422
  return False
423
- finally:
424
- if db:
425
- close_database_session(db)
426
 
427
  def get_user_profile(self, user_id: str) -> Optional[Dict[str, Any]]:
428
  """取得用戶資料"""
 
400
  close_database_session(db)
401
 
402
  def save_message(self, user_id: str, message: str, message_type: str = "text") -> bool:
403
+ """儲存訊息記錄 - 暫時停用,僅記錄到日誌"""
 
404
  try:
405
+ # 暫時停用資料庫記錄,只記錄到日誌
406
+ logger.info(f"訊息記錄 - 用戶: {user_id[:10]}..., 類型: {message_type}, 內容: {message[:50]}...")
 
 
 
 
 
 
 
 
407
  return True
408
+
409
  except Exception as e:
410
+ logger.error(f"訊息記錄錯誤: {str(e)}")
 
 
411
  return False
 
 
 
412
 
413
  def get_user_profile(self, user_id: str) -> Optional[Dict[str, Any]]:
414
  """取得用戶資料"""
backend/services/enhanced_product_service.py CHANGED
@@ -218,17 +218,23 @@ class EnhancedProductService:
218
 
219
  # 分析查詢關鍵字
220
  keywords = self._extract_keywords(query_text)
221
-
222
  # 建立推薦查詢
223
  query = db.query(Product).filter(Product.is_deleted == False)
224
-
225
- # 多關鍵字匹配
226
- for keyword in keywords:
227
- search_filter = or_(
228
- Product.productName.ilike(f"%{keyword}%"),
229
- Product.productCode.ilike(f"%{keyword}%")
230
- )
231
- query = query.filter(search_filter)
 
 
 
 
 
 
232
 
233
  # 優先顯示有庫存的商品
234
  query = query.order_by(Product.stock.desc())
@@ -290,12 +296,29 @@ class EnhancedProductService:
290
  return "低"
291
 
292
  def _extract_keywords(self, query_text: str) -> List[str]:
293
- """從查詢文字中提取關鍵字"""
294
  # 移除常見的查詢詞彙
295
  stop_words = ['推薦', '有沒有', '是否有', '請問', '想要', '需要', '找', '查詢', '搜尋']
296
-
297
  # 分割並清理關鍵字
298
  words = query_text.replace('?', '').replace('?', '').split()
299
  keywords = [word for word in words if word not in stop_words and len(word) > 1]
300
-
301
- return keywords if keywords else [query_text.strip()]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
  # 分析查詢關鍵字
220
  keywords = self._extract_keywords(query_text)
221
+
222
  # 建立推薦查詢
223
  query = db.query(Product).filter(Product.is_deleted == False)
224
+
225
+ # 多關鍵字匹配 - 使用 OR 邏輯,任一關鍵字匹配即可
226
+ if keywords:
227
+ search_filters = []
228
+ for keyword in keywords:
229
+ search_filters.extend([
230
+ Product.productName.ilike(f"%{keyword}%"),
231
+ Product.productCode.ilike(f"%{keyword}%"),
232
+ Product.barcode.ilike(f"%{keyword}%")
233
+ ])
234
+
235
+ # 使用 OR 連接所有搜尋條件
236
+ if search_filters:
237
+ query = query.filter(or_(*search_filters))
238
 
239
  # 優先顯示有庫存的商品
240
  query = query.order_by(Product.stock.desc())
 
296
  return "低"
297
 
298
  def _extract_keywords(self, query_text: str) -> List[str]:
299
+ """從查詢文字中提取關鍵字,並擴展相關詞彙"""
300
  # 移除常見的查詢詞彙
301
  stop_words = ['推薦', '有沒有', '是否有', '請問', '想要', '需要', '找', '查詢', '搜尋']
302
+
303
  # 分割並清理關鍵字
304
  words = query_text.replace('?', '').replace('?', '').split()
305
  keywords = [word for word in words if word not in stop_words and len(word) > 1]
306
+
307
+ # 擴展相關關鍵字
308
+ expanded_keywords = []
309
+ for keyword in keywords:
310
+ expanded_keywords.append(keyword)
311
+
312
+ # 貓砂相關擴展
313
+ if '貓砂' in keyword or '貓' in keyword:
314
+ expanded_keywords.extend(['礦砂', '豆腐砂', '水晶砂', '木屑砂', 'litter'])
315
+
316
+ # 狗糧相關擴展
317
+ if '狗糧' in keyword or '狗' in keyword:
318
+ expanded_keywords.extend(['犬糧', '犬種', '狗食', 'dog'])
319
+
320
+ # 寵物相關擴展
321
+ if '寵物' in keyword:
322
+ expanded_keywords.extend(['貓', '狗', '犬', 'pet', 'cat'])
323
+
324
+ return expanded_keywords if expanded_keywords else [query_text.strip()]
backend/services/message_router.py CHANGED
@@ -180,26 +180,30 @@ class MessageRouter:
180
  def _handle_smart_routing(self, message: str, user_id: str) -> Dict[str, Any]:
181
  """智能路由 - 根據內容判斷意圖"""
182
  try:
183
- # 1. 先檢查是否為商品查詢 (使用 Pydantic AI)
184
  if self.product_query_service.is_available():
185
  product_intent = self.product_query_service.analyze_query_intent(message)
186
 
187
- if product_intent["is_product_query"] and product_intent["confidence"] > 0.6:
188
- logger.info(f"智能路由 -> Pydantic AI 商品查詢 (信心度: {product_intent['confidence']})")
 
189
  self.route_stats["product_query"] += 1
190
  return self._handle_product_query_mode(message, user_id)
 
 
 
 
191
 
192
- # 2. 進行簡單的關鍵字預篩選
193
  quick_intent = self._quick_intent_check(message)
194
 
195
- if quick_intent == "search":
196
- # 高信心度的搜尋關鍵字 -> 直接轉到搜尋模式
197
- logger.info(f"快速意圖識別: 搜尋模式 - {message[:30]}...")
198
- return self._handle_search_mode(message, user_id)
199
-
200
- elif quick_intent == "help":
201
  # 幫助相關 -> 轉到幫助模式
202
  return self._handle_help_mode(user_id)
 
 
 
 
203
 
204
  # 使用 Groq 進行深度意圖分析
205
  if self.groq_service.is_available():
@@ -290,24 +294,24 @@ class MessageRouter:
290
  }
291
 
292
  def _quick_intent_check(self, message: str) -> str:
293
- """快速意圖檢查 - 基於關鍵字"""
294
  message_lower = message.lower()
295
-
296
- # 搜尋相關關鍵字
297
- search_keywords = [
298
- '查詢', '搜尋', '找', '商品', '產品', '價格', '庫存', '訂單',
299
- '多少錢', '有沒有', '還有嗎', '剩多少', '存貨', '現貨',
300
- 'iphone', 'macbook', 'ipad', 'airpods' # 常見商品名
 
 
301
  ]
302
-
303
- # 幫助相關關鍵字
304
- help_keywords = ['幫助', 'help', '說明', '怎麼用', '指令', '功能']
305
-
306
- if any(keyword in message_lower for keyword in search_keywords):
307
- return "search"
308
- elif any(keyword in message_lower for keyword in help_keywords):
309
  return "help"
 
 
310
  else:
 
311
  return "chat"
312
 
313
  def _handle_help_mode(self, user_id: str) -> Dict[str, Any]:
 
180
  def _handle_smart_routing(self, message: str, user_id: str) -> Dict[str, Any]:
181
  """智能路由 - 根據內容判斷意圖"""
182
  try:
183
+ # 1. 優先檢查是否為商品查詢 (使用 Pydantic AI)
184
  if self.product_query_service.is_available():
185
  product_intent = self.product_query_service.analyze_query_intent(message)
186
 
187
+ # 降低信心度閾值,讓更多商品查詢被 Pydantic AI 處理
188
+ if product_intent["is_product_query"] and product_intent["confidence"] > 0.5:
189
+ logger.info(f"智能路由 -> Pydantic AI 商品查詢 (信心度: {product_intent['confidence']:.2f})")
190
  self.route_stats["product_query"] += 1
191
  return self._handle_product_query_mode(message, user_id)
192
+ else:
193
+ logger.info(f"Pydantic AI 意圖分析: 商品查詢={product_intent['is_product_query']}, 信心度={product_intent['confidence']:.2f}")
194
+ else:
195
+ logger.warning("Pydantic AI 服務不可用,使用傳統路由")
196
 
197
+ # 2. 進行簡單的關鍵字預篩選(只處理非商品查詢)
198
  quick_intent = self._quick_intent_check(message)
199
 
200
+ if quick_intent == "help":
 
 
 
 
 
201
  # 幫助相關 -> 轉到幫助模式
202
  return self._handle_help_mode(user_id)
203
+ elif quick_intent == "search":
204
+ # 傳統搜尋關鍵字 -> 轉到搜尋模式
205
+ logger.info(f"快速意圖識別: 搜尋模式 - {message[:30]}...")
206
+ return self._handle_search_mode(message, user_id)
207
 
208
  # 使用 Groq 進行深度意圖分析
209
  if self.groq_service.is_available():
 
294
  }
295
 
296
  def _quick_intent_check(self, message: str) -> str:
297
+ """快速意圖檢查 - 基於關鍵字(避免與 Pydantic AI 商品查詢衝突)"""
298
  message_lower = message.lower()
299
+
300
+ # 幫助相關關鍵字(優先處理)
301
+ help_keywords = ['幫助', 'help', '說明', '怎麼用', '指令', '功能', '統計', 'stats', '選單', 'menu']
302
+
303
+ # 非商品的業務查詢關鍵字(避免與商品查詢衝突)
304
+ business_keywords = [
305
+ '訂單狀態', '訂單查詢', '我的訂單', '交易記錄', '購買記錄',
306
+ '客戶資料', '會員資料', '帳戶資訊', '銷售報表', '財務報表'
307
  ]
308
+
309
+ if any(keyword in message_lower for keyword in help_keywords):
 
 
 
 
 
310
  return "help"
311
+ elif any(keyword in message_lower for keyword in business_keywords):
312
+ return "search"
313
  else:
314
+ # 預設返回 "chat",讓 Pydantic AI 有機會處理商品查詢
315
  return "chat"
316
 
317
  def _handle_help_mode(self, user_id: str) -> Dict[str, Any]:
backend/services/pydantic_ai_service.py CHANGED
@@ -320,18 +320,19 @@ class ProductQueryService:
320
  """
321
  message_lower = message.lower()
322
 
323
- # 商品查詢關鍵字
324
  product_keywords = [
325
- '推薦', '有沒有', '是否有', '商品', '產品', '貨品',
326
- '查詢', '搜尋', '找', '庫存', '存貨', '價格',
327
- '貓砂', '狗糧', '寵物', '食品', '用品' # 常見商品類別
 
328
  ]
329
-
330
  # 推薦查詢關鍵字
331
- recommendation_keywords = ['推薦', '建議', '介紹', '有什麼', '哪些']
332
-
333
  # 庫存查詢關鍵字
334
- inventory_keywords = ['庫存', '存貨', '剩餘', '還有', '現貨']
335
 
336
  is_product_query = any(keyword in message_lower for keyword in product_keywords)
337
  is_recommendation = any(keyword in message_lower for keyword in recommendation_keywords)
 
320
  """
321
  message_lower = message.lower()
322
 
323
+ # 商品查詢關鍵字(擴展版)
324
  product_keywords = [
325
+ '推薦', '有沒有', '是否有', '請問有', '商品', '產品', '貨品',
326
+ '查詢', '搜尋', '找', '庫存', '存貨', '價格', '多少錢',
327
+ '貓砂', '狗糧', '寵物', '食品', '用品', '貓', '狗', '寵物用品',
328
+ 'cat', 'dog', 'pet', 'litter', 'food' # 英文關鍵字
329
  ]
330
+
331
  # 推薦查詢關鍵字
332
+ recommendation_keywords = ['推薦', '建議', '介紹', '有什麼', '哪些', '什麼好', '推薦一些']
333
+
334
  # 庫存查詢關鍵字
335
+ inventory_keywords = ['庫存', '存貨', '剩餘', '還有', '現貨', '有多少', '剩多少']
336
 
337
  is_product_query = any(keyword in message_lower for keyword in product_keywords)
338
  is_recommendation = any(keyword in message_lower for keyword in recommendation_keywords)
simple_db_test.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 簡化的資料庫測試 - 檢查是否有貓砂相關商品
3
+ """
4
+
5
+ import sys
6
+ import os
7
+
8
+ # 添加專案根目錄到 Python 路徑
9
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
10
+
11
+ def test_database_products():
12
+ """測試資料庫中的商品資料"""
13
+ try:
14
+ from backend.database.connection import get_database_session, close_database_session
15
+ from backend.database.models import Product, Category
16
+ from sqlalchemy.orm import joinedload
17
+ from sqlalchemy import or_
18
+
19
+ print("🗄️ 連接資料庫...")
20
+ db = get_database_session()
21
+
22
+ if not db:
23
+ print("❌ 無法連接資料庫")
24
+ return
25
+
26
+ print("✅ 資料庫連接成功")
27
+
28
+ # 1. 檢查總商品數量
29
+ total_products = db.query(Product).filter(Product.is_deleted == False).count()
30
+ print(f"📊 總商品數量: {total_products}")
31
+
32
+ # 2. 檢查是否有分類
33
+ categories = db.query(Category).all()
34
+ print(f"📂 分類數量: {len(categories)}")
35
+ if categories:
36
+ print("分類列表:")
37
+ for cat in categories[:5]: # 只顯示前5個
38
+ print(f" - {cat.name}")
39
+
40
+ # 3. 搜尋貓砂相關商品
41
+ print("\n🔍 搜尋貓砂相關商品...")
42
+
43
+ search_terms = ["貓砂", "貓", "寵物", "cat", "litter", "pet"]
44
+
45
+ for term in search_terms:
46
+ print(f"\n--- 搜尋關鍵字: '{term}' ---")
47
+
48
+ # 搜尋商品名稱、編號、條碼
49
+ products = db.query(Product).options(
50
+ joinedload(Product.category)
51
+ ).filter(
52
+ Product.is_deleted == False
53
+ ).filter(
54
+ or_(
55
+ Product.productName.ilike(f"%{term}%"),
56
+ Product.productCode.ilike(f"%{term}%"),
57
+ Product.barcode.ilike(f"%{term}%")
58
+ )
59
+ ).limit(5).all()
60
+
61
+ print(f"找到 {len(products)} 個商品:")
62
+ for product in products:
63
+ print(f" - {product.productName} (編號: {product.productCode}, 庫存: {product.stock})")
64
+
65
+ # 4. 檢查分類中是否有寵物相關
66
+ print("\n🐾 檢查寵物相關分類...")
67
+ pet_categories = db.query(Category).filter(
68
+ or_(
69
+ Category.name.ilike("%寵物%"),
70
+ Category.name.ilike("%貓%"),
71
+ Category.name.ilike("%狗%"),
72
+ Category.name.ilike("%pet%"),
73
+ Category.name.ilike("%cat%"),
74
+ Category.name.ilike("%dog%")
75
+ )
76
+ ).all()
77
+
78
+ print(f"找到 {len(pet_categories)} 個寵物相關分類:")
79
+ for cat in pet_categories:
80
+ print(f" - {cat.name}")
81
+
82
+ # 檢查該分類下的商品
83
+ products_in_cat = db.query(Product).filter(
84
+ Product.category_id == cat.id,
85
+ Product.is_deleted == False
86
+ ).limit(3).all()
87
+
88
+ for product in products_in_cat:
89
+ print(f" * {product.productName} (庫存: {product.stock})")
90
+
91
+ # 5. 隨機顯示一些商品名稱,看看資料庫中有什麼
92
+ print("\n📋 隨機商品樣本 (前10個):")
93
+ sample_products = db.query(Product).filter(
94
+ Product.is_deleted == False
95
+ ).limit(10).all()
96
+
97
+ for i, product in enumerate(sample_products, 1):
98
+ category_name = product.category.name if product.category else "無分類"
99
+ print(f" {i}. {product.productName} ({category_name}) - 庫存: {product.stock}")
100
+
101
+ close_database_session(db)
102
+
103
+ except Exception as e:
104
+ print(f"❌ 測試錯誤: {str(e)}")
105
+ import traceback
106
+ traceback.print_exc()
107
+
108
+ def main():
109
+ """主函數"""
110
+ print("🚀 開始簡化資料庫測試\n")
111
+ test_database_products()
112
+ print("\n✅ 測試完成!")
113
+
114
+ if __name__ == "__main__":
115
+ main()
test_api_cat_litter.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 通過 API 測試貓砂查詢功能
3
+ """
4
+
5
+ import requests
6
+ import json
7
+
8
+ def test_api_endpoints():
9
+ """測試 API 端點"""
10
+ base_url = "http://localhost:7860"
11
+
12
+ print("🚀 開始 API 測試\n")
13
+
14
+ # 1. 測試健康檢查
15
+ print("1. 測試健康檢查...")
16
+ try:
17
+ response = requests.get(f"{base_url}/health", timeout=10)
18
+ print(f" 狀態碼: {response.status_code}")
19
+ if response.status_code == 200:
20
+ print(f" 回應: {response.json()}")
21
+ print(" ✅ 健康檢查通過\n")
22
+ except Exception as e:
23
+ print(f" ❌ 健康檢查失敗: {str(e)}\n")
24
+ return False
25
+
26
+ # 2. 測試智能路由 API
27
+ print("2. 測試智能路由 API...")
28
+ test_messages = [
29
+ "你好, 請問有沒有貓砂相關產品?",
30
+ "是否有推薦貓砂?",
31
+ "有什麼寵物用品?",
32
+ "查詢狗糧庫存",
33
+ "你好!今天天氣如何?" # 非商品查詢
34
+ ]
35
+
36
+ for i, message in enumerate(test_messages, 1):
37
+ print(f"\n 測試 {i}: '{message}'")
38
+ try:
39
+ payload = {
40
+ "message": message,
41
+ "user_id": "test_user_api"
42
+ }
43
+
44
+ response = requests.post(
45
+ f"{base_url}/route",
46
+ json=payload,
47
+ timeout=30,
48
+ headers={"Content-Type": "application/json"}
49
+ )
50
+
51
+ print(f" 狀態碼: {response.status_code}")
52
+
53
+ if response.status_code == 200:
54
+ result = response.json()
55
+ print(f" 模式: {result.get('mode', 'unknown')}")
56
+ print(f" 成功: {result.get('success', False)}")
57
+
58
+ # 顯示回應內容
59
+ response_text = result.get('text', 'No response')
60
+ if len(response_text) > 150:
61
+ print(f" 回應: {response_text[:150]}...")
62
+ else:
63
+ print(f" 回應: {response_text}")
64
+
65
+ # 如果是商品查詢,顯示額外資訊
66
+ if result.get('products_found') is not None:
67
+ print(f" 找到商品: {result['products_found']}")
68
+ if result.get('has_recommendations'):
69
+ print(f" 包含推薦: {result['has_recommendations']}")
70
+
71
+ else:
72
+ print(f" ❌ API 錯誤: {response.text}")
73
+
74
+ except Exception as e:
75
+ print(f" ❌ 請求失敗: {str(e)}")
76
+
77
+ # 3. 測試專門的商品查詢 API
78
+ print(f"\n3. 測試專門的商品查詢 API...")
79
+
80
+ product_queries = [
81
+ "是否有推薦貓砂?",
82
+ "有什麼寵物用品?",
83
+ "查詢貓相關產品"
84
+ ]
85
+
86
+ for i, query in enumerate(product_queries, 1):
87
+ print(f"\n 商品查詢 {i}: '{query}'")
88
+ try:
89
+ payload = {
90
+ "message": query,
91
+ "user_id": "test_user_product"
92
+ }
93
+
94
+ response = requests.post(
95
+ f"{base_url}/product-query",
96
+ json=payload,
97
+ timeout=30,
98
+ headers={"Content-Type": "application/json"}
99
+ )
100
+
101
+ print(f" 狀態碼: {response.status_code}")
102
+
103
+ if response.status_code == 200:
104
+ result = response.json()
105
+ print(f" 成功: {result.get('success', False)}")
106
+
107
+ response_text = result.get('text', 'No response')
108
+ if len(response_text) > 150:
109
+ print(f" 回應: {response_text[:150]}...")
110
+ else:
111
+ print(f" 回應: {response_text}")
112
+
113
+ if result.get('products_found') is not None:
114
+ print(f" 找到商品: {result['products_found']}")
115
+ if result.get('error'):
116
+ print(f" 錯誤: {result['error']}")
117
+
118
+ else:
119
+ print(f" ❌ API 錯誤: {response.text}")
120
+
121
+ except Exception as e:
122
+ print(f" ❌ 請求失敗: {str(e)}")
123
+
124
+ # 4. 測試傳統搜尋 API
125
+ print(f"\n4. 測試傳統搜尋 API...")
126
+
127
+ search_queries = [
128
+ "貓砂",
129
+ "寵物用品",
130
+ "貓"
131
+ ]
132
+
133
+ for i, query in enumerate(search_queries, 1):
134
+ print(f"\n 搜尋 {i}: '{query}'")
135
+ try:
136
+ payload = {
137
+ "message": query,
138
+ "user_id": "test_user_search"
139
+ }
140
+
141
+ response = requests.post(
142
+ f"{base_url}/search",
143
+ json=payload,
144
+ timeout=30,
145
+ headers={"Content-Type": "application/json"}
146
+ )
147
+
148
+ print(f" 狀態碼: {response.status_code}")
149
+
150
+ if response.status_code == 200:
151
+ result = response.json()
152
+ print(f" 成功: {result.get('success', False)}")
153
+
154
+ response_text = result.get('text', 'No response')
155
+ if len(response_text) > 150:
156
+ print(f" 回應: {response_text[:150]}...")
157
+ else:
158
+ print(f" 回應: {response_text}")
159
+
160
+ else:
161
+ print(f" ❌ API 錯誤: {response.text}")
162
+
163
+ except Exception as e:
164
+ print(f" ❌ 請求失敗: {str(e)}")
165
+
166
+ def main():
167
+ """主函數"""
168
+ print("🧪 貓砂查詢 API 測試")
169
+ print("=" * 50)
170
+
171
+ test_api_endpoints()
172
+
173
+ print("\n" + "=" * 50)
174
+ print("✅ API 測試完成!")
175
+ print("\n💡 分析建議:")
176
+ print("1. 檢查智能路由是否正確識別商品查詢意圖")
177
+ print("2. 比較不同 API 端點的回應差異")
178
+ print("3. 確認是否真的沒有貓砂相關商品,還是查詢邏輯問題")
179
+
180
+ if __name__ == "__main__":
181
+ main()
test_cat_litter_query.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 測試貓砂查詢功能 - 驗證修復後的智能路由和商品查詢
3
+ """
4
+
5
+ import sys
6
+ import os
7
+
8
+ # 添加專案根目錄到 Python 路徑
9
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
10
+
11
+ from backend.services.message_router import MessageRouter
12
+ from backend.services.pydantic_ai_service import ProductQueryService
13
+ from backend.services.enhanced_product_service import EnhancedProductService
14
+ from backend.database.connection import test_database_connection
15
+
16
+ def test_database_connection_first():
17
+ """首先測試資料庫連接"""
18
+ print("🗄️ 測試資料庫連接...")
19
+
20
+ if test_database_connection():
21
+ print("✅ 資料庫連接正常")
22
+ return True
23
+ else:
24
+ print("❌ 資料庫連接失敗")
25
+ return False
26
+
27
+ def test_product_search_directly():
28
+ """直接測試商品搜尋功能"""
29
+ print("\n🔍 直接測試商品搜尋...")
30
+
31
+ try:
32
+ service = EnhancedProductService()
33
+
34
+ # 測試不同的搜尋關鍵字
35
+ test_keywords = ["貓砂", "貓", "寵物", "cat", "litter"]
36
+
37
+ for keyword in test_keywords:
38
+ print(f"\n--- 搜尋關鍵字: '{keyword}' ---")
39
+
40
+ # 測試推薦功能
41
+ result = service.get_product_recommendations(keyword, limit=3)
42
+ print(f"推薦查詢 - 成功: {result.success}, 數量: {result.count}")
43
+
44
+ if result.success and result.data:
45
+ for i, product in enumerate(result.data, 1):
46
+ print(f" {i}. {product.get('product_name', 'N/A')} (庫存: {product.get('current_stock', 0)})")
47
+ elif result.error:
48
+ print(f" 錯誤: {result.error}")
49
+
50
+ # 測試進階搜尋
51
+ result2 = service.search_products_advanced(keyword, include_stock_info=True, limit=3)
52
+ print(f"進階搜尋 - 成功: {result2.success}, 數量: {result2.count}")
53
+
54
+ if result2.success and result2.data:
55
+ for i, product in enumerate(result2.data, 1):
56
+ print(f" {i}. {product.get('product_name', 'N/A')} (庫存: {product.get('current_stock', 0)})")
57
+ elif result2.error:
58
+ print(f" 錯誤: {result2.error}")
59
+
60
+ except Exception as e:
61
+ print(f"❌ 直接搜尋測試錯誤: {str(e)}")
62
+
63
+ def test_pydantic_ai_intent():
64
+ """測試 Pydantic AI 意圖識別"""
65
+ print("\n🤖 測試 Pydantic AI 意圖識別...")
66
+
67
+ try:
68
+ service = ProductQueryService()
69
+
70
+ if not service.is_available():
71
+ print("❌ Pydantic AI 服務不可用")
72
+ return
73
+
74
+ test_messages = [
75
+ "你好, 請問有沒有貓砂相關產品?",
76
+ "是否有推薦貓砂?",
77
+ "有什麼寵物用品?",
78
+ "今天天氣如何?" # 非商品查詢
79
+ ]
80
+
81
+ for message in test_messages:
82
+ print(f"\n--- 測試訊息: '{message}' ---")
83
+ intent = service.analyze_query_intent(message)
84
+ print(f"商品查詢: {intent['is_product_query']}")
85
+ print(f"推薦查詢: {intent['is_recommendation']}")
86
+ print(f"庫存查詢: {intent['is_inventory_check']}")
87
+ print(f"信心度: {intent['confidence']:.2f}")
88
+ print(f"意圖: {intent['intent']}")
89
+
90
+ except Exception as e:
91
+ print(f"❌ Pydantic AI 測試錯誤: {str(e)}")
92
+
93
+ def test_message_router():
94
+ """測試修復後的訊息路由器"""
95
+ print("\n🔄 測試修復後的訊息路由器...")
96
+
97
+ try:
98
+ router = MessageRouter()
99
+
100
+ test_messages = [
101
+ "你好, 請問有沒有貓砂相關產品?",
102
+ "是否有推薦貓砂?",
103
+ "有什麼狗糧推薦?",
104
+ "查詢寵物用品庫存",
105
+ "你好!今天天氣如何?", # 應該路由到聊天模式
106
+ "/help", # 應該顯示幫助
107
+ ]
108
+
109
+ for message in test_messages:
110
+ print(f"\n--- 路由測試: '{message}' ---")
111
+ try:
112
+ result = router.route_message(message, "test_user")
113
+ print(f"模式: {result.get('mode', 'unknown')}")
114
+ print(f"成功: {result.get('success', False)}")
115
+
116
+ # 顯示回應的前100個字符
117
+ response_text = result.get('text', 'No response')
118
+ if len(response_text) > 100:
119
+ print(f"回應: {response_text[:100]}...")
120
+ else:
121
+ print(f"回應: {response_text}")
122
+
123
+ if result.get('products_found'):
124
+ print(f"找到商品數量: {result['products_found']}")
125
+
126
+ except Exception as e:
127
+ print(f"❌ 路由錯誤: {str(e)}")
128
+
129
+ # 顯示路由統計
130
+ print(f"\n📊 路由統計:")
131
+ stats = router.get_route_statistics()
132
+ for mode, count in stats['stats'].items():
133
+ if count > 0:
134
+ print(f" {mode}: {count}")
135
+
136
+ except Exception as e:
137
+ print(f"❌ 路由器測試錯誤: {str(e)}")
138
+
139
+ def main():
140
+ """主測試函數"""
141
+ print("🚀 開始貓砂查詢功能測試\n")
142
+
143
+ # 1. 測試資料庫連接
144
+ if not test_database_connection_first():
145
+ print("❌ 資料庫連接失敗,無法繼續測試")
146
+ return
147
+
148
+ # 2. 直接測試商品搜尋
149
+ test_product_search_directly()
150
+
151
+ # 3. 測試 Pydantic AI 意圖識別
152
+ test_pydantic_ai_intent()
153
+
154
+ # 4. 測試訊息路由器
155
+ test_message_router()
156
+
157
+ print("\n✅ 測試完成!")
158
+ print("\n💡 如果商品查詢仍然沒有結果,可能的原因:")
159
+ print(" 1. 資料庫中確實沒有貓砂相關商品")
160
+ print(" 2. 商品名稱不包含 '貓砂'、'貓'、'寵物' 等關鍵字")
161
+ print(" 3. 商品被標記為已刪除 (is_deleted=True)")
162
+ print(" 4. 需要檢查實際的商品資料內容")
163
+
164
+ if __name__ == "__main__":
165
+ main()
test_intent_recognition.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 測試意圖識別邏輯 - 不依賴外部套件
3
+ """
4
+
5
+ def test_product_intent_logic():
6
+ """測試商品意圖識別邏輯"""
7
+
8
+ def analyze_query_intent_simple(message: str):
9
+ """簡化版的意圖分析"""
10
+ message_lower = message.lower()
11
+
12
+ # 商品查詢關鍵字(擴展版)
13
+ product_keywords = [
14
+ '推薦', '有沒有', '是否有', '請問有', '商品', '產品', '貨品',
15
+ '查詢', '搜尋', '找', '庫存', '存貨', '價格', '多少錢',
16
+ '貓砂', '狗糧', '寵物', '食品', '用品', '貓', '狗', '寵物用品',
17
+ 'cat', 'dog', 'pet', 'litter', 'food' # 英文關鍵字
18
+ ]
19
+
20
+ # 推薦查詢關鍵字
21
+ recommendation_keywords = ['推薦', '建議', '介紹', '有什麼', '哪些', '什麼好', '推薦一些']
22
+
23
+ # 庫存查詢關鍵字
24
+ inventory_keywords = ['庫存', '存貨', '剩餘', '還有', '現貨', '有多少', '剩多少']
25
+
26
+ is_product_query = any(keyword in message_lower for keyword in product_keywords)
27
+ is_recommendation = any(keyword in message_lower for keyword in recommendation_keywords)
28
+ is_inventory_check = any(keyword in message_lower for keyword in inventory_keywords)
29
+
30
+ confidence = 0.5
31
+ if is_product_query:
32
+ confidence += 0.3
33
+ if is_recommendation:
34
+ confidence += 0.2
35
+ if is_inventory_check:
36
+ confidence += 0.2
37
+
38
+ return {
39
+ "is_product_query": is_product_query,
40
+ "is_recommendation": is_recommendation,
41
+ "is_inventory_check": is_inventory_check,
42
+ "confidence": min(confidence, 1.0),
43
+ "intent": "product_query" if is_product_query else "unknown"
44
+ }
45
+
46
+ def quick_intent_check_simple(message: str):
47
+ """簡化版的快速意圖檢查"""
48
+ message_lower = message.lower()
49
+
50
+ # 幫助相關關鍵字(優先處理)
51
+ help_keywords = ['幫助', 'help', '說明', '怎麼用', '指令', '功能', '統計', 'stats', '選單', 'menu']
52
+
53
+ # 非商品的業務查詢關鍵字(避免與商品查詢衝突)
54
+ business_keywords = [
55
+ '訂單狀態', '訂單查詢', '我的訂單', '交易記錄', '購買記錄',
56
+ '客戶資料', '會員資料', '帳戶資訊', '銷售報表', '財務報表'
57
+ ]
58
+
59
+ if any(keyword in message_lower for keyword in help_keywords):
60
+ return "help"
61
+ elif any(keyword in message_lower for keyword in business_keywords):
62
+ return "search"
63
+ else:
64
+ # 預設返回 "chat",讓 Pydantic AI 有機會處理商品查詢
65
+ return "chat"
66
+
67
+ # 測試案例
68
+ test_messages = [
69
+ "你好, 請問有沒有貓砂相關產品?",
70
+ "是否有推薦貓砂?",
71
+ "有什麼寵物用品?",
72
+ "查詢狗糧庫存",
73
+ "貓砂還有嗎?",
74
+ "推薦一些好的貓砂",
75
+ "你好!今天天氣如何?", # 非商品查詢
76
+ "/help",
77
+ "統計",
78
+ "我的訂單狀態"
79
+ ]
80
+
81
+ print("🧪 測試意圖識別邏輯")
82
+ print("=" * 80)
83
+
84
+ for i, message in enumerate(test_messages, 1):
85
+ print(f"\n{i}. 測試訊息: '{message}'")
86
+
87
+ # 1. 快速意圖檢查
88
+ quick_intent = quick_intent_check_simple(message)
89
+ print(f" 快速意圖: {quick_intent}")
90
+
91
+ # 2. Pydantic AI 意圖分析
92
+ product_intent = analyze_query_intent_simple(message)
93
+ print(f" 商品查詢: {product_intent['is_product_query']}")
94
+ print(f" 推薦查詢: {product_intent['is_recommendation']}")
95
+ print(f" 庫存查詢: {product_intent['is_inventory_check']}")
96
+ print(f" 信心度: {product_intent['confidence']:.2f}")
97
+
98
+ # 3. 路由決策模擬
99
+ should_use_pydantic_ai = (
100
+ product_intent["is_product_query"] and
101
+ product_intent["confidence"] > 0.5
102
+ )
103
+
104
+ if quick_intent == "help":
105
+ final_route = "幫助模式"
106
+ elif should_use_pydantic_ai:
107
+ final_route = "🛍️ Pydantic AI 商品查詢"
108
+ elif quick_intent == "search":
109
+ final_route = "傳統搜尋模式"
110
+ else:
111
+ final_route = "聊天模式"
112
+
113
+ print(f" 👉 最終路由: {final_route}")
114
+
115
+ # 特別標記貓砂相關查詢
116
+ if "貓砂" in message or "貓" in message:
117
+ print(f" 🐱 貓砂查詢檢測: {'✅ 應該被 Pydantic AI 處理' if should_use_pydantic_ai else '❌ 可能被錯誤路由'}")
118
+
119
+ def test_keyword_extraction():
120
+ """測試關鍵字提取邏輯"""
121
+
122
+ def extract_keywords_simple(query_text: str):
123
+ """簡化版關鍵字提取"""
124
+ # 移除常見的查詢詞彙
125
+ stop_words = ['推薦', '有沒有', '是否有', '請問', '想要', '需要', '找', '查詢', '搜尋', '?', '?']
126
+
127
+ # 分割並清理關鍵字
128
+ words = query_text.replace('?', '').replace('?', '').split()
129
+ keywords = [word for word in words if word not in stop_words and len(word) > 1]
130
+
131
+ return keywords if keywords else [query_text.strip()]
132
+
133
+ print(f"\n\n🔍 測試關鍵字提取")
134
+ print("=" * 50)
135
+
136
+ test_queries = [
137
+ "你好, 請問有沒有貓砂相關產品?",
138
+ "是否有推薦貓砂?",
139
+ "有什麼寵物用品?",
140
+ "查詢狗糧庫存"
141
+ ]
142
+
143
+ for query in test_queries:
144
+ keywords = extract_keywords_simple(query)
145
+ print(f"'{query}' → {keywords}")
146
+
147
+ def main():
148
+ """主函數"""
149
+ print("🚀 開始意圖識別測試\n")
150
+
151
+ test_product_intent_logic()
152
+ test_keyword_extraction()
153
+
154
+ print("\n" + "=" * 80)
155
+ print("✅ 測試完成!")
156
+ print("\n💡 分析結果:")
157
+ print("1. 檢查貓砂相關查詢是否被正確識別為商品查詢")
158
+ print("2. 確認信心度是否超過 0.5 閾值")
159
+ print("3. 驗證路由決策是否正確")
160
+
161
+ if __name__ == "__main__":
162
+ main()
test_keyword_matching.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 測試關鍵字匹配邏輯 - 基於實際商品資料
3
+ """
4
+
5
+ def test_keyword_expansion():
6
+ """測試關鍵字擴展邏輯"""
7
+
8
+ def extract_keywords_with_expansion(query_text: str):
9
+ """擴展版關鍵字提取"""
10
+ # 移除常見的查詢詞彙
11
+ stop_words = ['推薦', '有沒有', '是否有', '請問', '想要', '需要', '找', '查詢', '搜尋']
12
+
13
+ # 分割並清理關鍵字
14
+ words = query_text.replace('?', '').replace('?', '').split()
15
+ keywords = [word for word in words if word not in stop_words and len(word) > 1]
16
+
17
+ # 擴展相關關鍵字
18
+ expanded_keywords = []
19
+ for keyword in keywords:
20
+ expanded_keywords.append(keyword)
21
+
22
+ # 貓砂相關擴展
23
+ if '貓砂' in keyword or '貓' in keyword:
24
+ expanded_keywords.extend(['礦砂', '豆腐砂', '水晶砂', '木屑砂', 'litter'])
25
+
26
+ # 狗糧相關擴展
27
+ if '狗糧' in keyword or '狗' in keyword:
28
+ expanded_keywords.extend(['犬糧', '犬種', '狗食', 'dog'])
29
+
30
+ # 寵物相關擴展
31
+ if '寵物' in keyword:
32
+ expanded_keywords.extend(['貓', '狗', '犬', 'pet', 'cat'])
33
+
34
+ return expanded_keywords if expanded_keywords else [query_text.strip()]
35
+
36
+ print("🔍 測試關鍵字擴展邏輯")
37
+ print("=" * 60)
38
+
39
+ test_queries = [
40
+ "你好, 請問有沒有貓砂相關產品?",
41
+ "是否有推薦貓砂?",
42
+ "有什麼寵物用品?",
43
+ "查詢狗糧庫存",
44
+ "貓砂還有嗎?"
45
+ ]
46
+
47
+ for query in test_queries:
48
+ keywords = extract_keywords_with_expansion(query)
49
+ print(f"'{query}'")
50
+ print(f" → 擴展關鍵字: {keywords}")
51
+ print()
52
+
53
+ def test_product_matching():
54
+ """測試商品匹配邏輯"""
55
+
56
+ # 實際商品資料(來自您的 Supabase)
57
+ products = [
58
+ {
59
+ "id": 1,
60
+ "productCode": "OL1100-1",
61
+ "productName": "毆力天然犬種300g 室內成犬無榖小顆粒",
62
+ "stock": 100,
63
+ "category_id": 1
64
+ },
65
+ {
66
+ "id": 2,
67
+ "productCode": "SW-06-01",
68
+ "productName": "Shovel well豪好鏟 破碎型礦砂",
69
+ "stock": 50,
70
+ "category_id": 1
71
+ },
72
+ {
73
+ "id": 3,
74
+ "productCode": "TL-03",
75
+ "productName": "美國極冠貓砂 薰衣草12kg",
76
+ "stock": 48,
77
+ "category_id": 1
78
+ },
79
+ {
80
+ "id": 4,
81
+ "productCode": "SL11002",
82
+ "productName": "首領汪 膠原鴨舌 5入彭湃包",
83
+ "stock": 100,
84
+ "category_id": 1
85
+ }
86
+ ]
87
+
88
+ def search_products_simulation(query_text: str):
89
+ """模擬商品搜尋"""
90
+ # 擴展關鍵字
91
+ def extract_keywords_with_expansion(query_text: str):
92
+ stop_words = ['推薦', '有沒有', '是否有', '請問', '想要', '需要', '找', '查詢', '搜尋']
93
+ words = query_text.replace('?', '').replace('?', '').split()
94
+ keywords = [word for word in words if word not in stop_words and len(word) > 1]
95
+
96
+ expanded_keywords = []
97
+ for keyword in keywords:
98
+ expanded_keywords.append(keyword)
99
+
100
+ if '貓砂' in keyword or '貓' in keyword:
101
+ expanded_keywords.extend(['礦砂', '豆腐砂', '水晶砂', '木屑砂', 'litter'])
102
+
103
+ if '狗糧' in keyword or '狗' in keyword:
104
+ expanded_keywords.extend(['犬糧', '犬種', '狗食', 'dog'])
105
+
106
+ if '寵物' in keyword:
107
+ expanded_keywords.extend(['貓', '狗', '犬', 'pet', 'cat'])
108
+
109
+ return expanded_keywords if expanded_keywords else [query_text.strip()]
110
+
111
+ keywords = extract_keywords_with_expansion(query_text)
112
+ matched_products = []
113
+
114
+ for product in products:
115
+ # 檢查是否有任一關鍵字匹配
116
+ for keyword in keywords:
117
+ if (keyword.lower() in product["productName"].lower() or
118
+ keyword.lower() in product["productCode"].lower()):
119
+ matched_products.append({
120
+ "product": product,
121
+ "matched_keyword": keyword
122
+ })
123
+ break # 找到匹配就跳出
124
+
125
+ return matched_products, keywords
126
+
127
+ print("🛍️ 測試商品匹配邏輯")
128
+ print("=" * 60)
129
+
130
+ test_queries = [
131
+ "你好, 請問有沒有貓砂相關產品?",
132
+ "是否有推薦貓砂?",
133
+ "有什麼寵物用品?",
134
+ "查詢狗糧庫存",
135
+ "礦砂還有嗎?",
136
+ "犬種商品"
137
+ ]
138
+
139
+ for query in test_queries:
140
+ print(f"查詢: '{query}'")
141
+ matched_products, keywords = search_products_simulation(query)
142
+ print(f"使用關鍵字: {keywords}")
143
+ print(f"找到 {len(matched_products)} 個商品:")
144
+
145
+ for match in matched_products:
146
+ product = match["product"]
147
+ keyword = match["matched_keyword"]
148
+ print(f" ✅ {product['productName']} (匹配關鍵字: '{keyword}', 庫存: {product['stock']})")
149
+
150
+ if not matched_products:
151
+ print(" ❌ 沒有找到匹配的商品")
152
+
153
+ print("-" * 60)
154
+
155
+ def test_specific_cat_litter_queries():
156
+ """專門測試貓砂查詢"""
157
+
158
+ print("\n🐱 專門測試貓砂查詢")
159
+ print("=" * 60)
160
+
161
+ # 貓砂商品
162
+ cat_litter_products = [
163
+ "Shovel well豪好鏟 破碎型礦砂",
164
+ "美國極冠貓砂 薰衣草12kg"
165
+ ]
166
+
167
+ # 測試查詢
168
+ cat_queries = [
169
+ "貓砂",
170
+ "礦砂",
171
+ "貓",
172
+ "litter",
173
+ "豪好鏟",
174
+ "極冠"
175
+ ]
176
+
177
+ print("貓砂相關商品:")
178
+ for product in cat_litter_products:
179
+ print(f" - {product}")
180
+
181
+ print(f"\n測試查詢關鍵字:")
182
+ for query in cat_queries:
183
+ matches = []
184
+ for product in cat_litter_products:
185
+ if query.lower() in product.lower():
186
+ matches.append(product)
187
+
188
+ print(f" '{query}' → 匹配 {len(matches)} 個商品")
189
+ for match in matches:
190
+ print(f" ✅ {match}")
191
+
192
+ def main():
193
+ """主函數"""
194
+ print("🚀 開始關鍵字匹配測試\n")
195
+
196
+ test_keyword_expansion()
197
+ test_product_matching()
198
+ test_specific_cat_litter_queries()
199
+
200
+ print("\n" + "=" * 60)
201
+ print("✅ 測試完成!")
202
+ print("\n💡 分析結果:")
203
+ print("1. 關鍵字擴展邏輯應該能找到 '礦砂' 商品")
204
+ print("2. '貓砂' 查詢應該匹配到兩個商品")
205
+ print("3. 確認搜尋邏輯是否正確運作")
206
+
207
+ if __name__ == "__main__":
208
+ main()