File size: 28,490 Bytes
cd9bca9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68c8519
 
 
 
cd9bca9
 
68c8519
 
cd9bca9
68c8519
 
 
 
 
cd9bca9
68c8519
cd9bca9
68c8519
cd9bca9
68c8519
 
 
 
 
 
 
 
 
 
 
 
 
cd9bca9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
import re
import json
from typing import Dict, Any, List
from backend.models.schemas import NLPAnalysisResult, QueryType, DatabaseResult
from backend.config import settings
from backend.services.openrouter_service import OpenRouterService
import logging
import asyncio

logger = logging.getLogger(__name__)

class NLPService:
    """自然語言處理服務"""
    
    def __init__(self):
        self.openrouter_service = OpenRouterService()
        self.business_intent_patterns = {
            "product_search": [
                r"查詢.*商品|找.*商品|搜尋.*商品|商品.*資料",
                r"商品.*查詢|商品.*搜尋|產品.*查詢|產品.*搜尋",
                r"有什麼.*商品|商品.*價格|產品.*價格"
            ],
            "inventory_check": [
                r"庫存.*查詢|查詢.*庫存|庫存.*多少|剩餘.*數量",
                r"存貨.*查詢|查詢.*存貨|還有.*多少|庫存.*狀況",
                r".*庫存|.*存貨|.*剩餘"
            ],
            "order_search": [
                r"查詢.*訂單|找.*訂單|搜尋.*訂單|訂單.*資料",
                r"訂單.*查詢|訂單.*搜尋|訂單.*狀態|我的.*訂單",
                r"訂單編號|購買.*記錄"
            ],
            "customer_search": [
                r"查詢.*客戶|找.*客戶|搜尋.*客戶|客戶.*資料",
                r"客戶.*查詢|客戶.*搜尋|客戶.*聯絡"
            ],
            "low_stock_alert": [
                r"低庫存|缺貨|庫存.*不足|存貨.*不足",
                r"快.*沒有|即將.*缺貨|庫存.*警告"
            ],
            "business_summary": [
                r"統計|分析|報表|數據|摘要",
                r"總計|總數|多少.*筆|幾.*筆|業務.*狀況"
            ]
        }
        self.intent_patterns = {
            "search_user": [
                r"查詢.*用戶|找.*用戶|搜尋.*用戶|用戶.*資料",
                r"用戶.*查詢|用戶.*搜尋|用戶.*找",
                r"誰是|哪個用戶|用戶名.*是"
            ],
            "search_order": [
                r"查詢.*訂單|找.*訂單|搜尋.*訂單|訂單.*資料",
                r"訂單.*查詢|訂單.*搜尋|訂單.*狀態",
                r"我的訂單|訂單編號"
            ],
            "search_product": [
                r"查詢.*商品|找.*商品|搜尋.*商品|商品.*資料",
                r"商品.*查詢|商品.*搜尋|產品.*查詢",
                r"有什麼.*商品|商品.*價格"
            ],
            "create_order": [
                r"建立.*訂單|新增.*訂單|下訂|購買",
                r"我要.*買|我想.*買|訂購"
            ],
            "update_profile": [
                r"更新.*資料|修改.*資料|變更.*資料",
                r"更新.*個人|修改.*個人|個人.*資料"
            ],
            "analytics": [
                r"統計|分析|報表|數據",
                r"總計|總數|多少.*筆|幾.*筆"
            ]
        }
        
        self.entity_patterns = {
            "user_id": r"用戶ID[::]?\s*([A-Za-z0-9]+)",
            "user_name": r"用戶名[::]?\s*([^\s]+)|名字[::]?\s*([^\s]+)",
            "order_id": r"訂單[編號ID][::]?\s*([A-Za-z0-9\-]+)",
            "product_name": r"商品[::]?\s*([^\s]+)|產品[::]?\s*([^\s]+)",
            "price_range": r"價格.*?(\d+).*?到.*?(\d+)|(\d+).*?元.*?到.*?(\d+).*?元",
            "date_range": r"(\d{4}[-/]\d{1,2}[-/]\d{1,2})",
            "number": r"(\d+)"
        }

    def analyze_message(self, message: str, use_advanced: bool = True) -> NLPAnalysisResult:
        """分析用戶訊息"""
        try:
            # 如果啟用進階分析且有 OpenRouter API Key
            if use_advanced and self.openrouter_service.api_key:
                try:
                    # 使用 asyncio 執行異步分析
                    loop = asyncio.new_event_loop()
                    asyncio.set_event_loop(loop)
                    advanced_result = loop.run_until_complete(
                        self.openrouter_service.analyze_intent_advanced(message)
                    )
                    loop.close()
                    
                    if not advanced_result.get("fallback", False):
                        return NLPAnalysisResult(
                            query_type=QueryType(advanced_result.get("query_type", "unknown")),
                            intent=advanced_result.get("intent", "unknown"),
                            entities=advanced_result.get("entities", {}),
                            confidence=advanced_result.get("confidence", 0.5),
                            parameters=advanced_result.get("parameters", {})
                        )
                except Exception as e:
                    logger.warning(f"進階 NLP 分析失敗,使用基礎分析: {str(e)}")
            
            # 使用基礎規則引擎分析
            return self._basic_analyze_message(message)
            
        except Exception as e:
            logger.error(f"NLP 分析錯誤: {str(e)}")
            return NLPAnalysisResult(
                query_type=QueryType.UNKNOWN,
                intent="unknown",
                entities={},
                confidence=0.0,
                parameters={}
            )
    
    def _basic_analyze_message(self, message: str) -> NLPAnalysisResult:
        """基礎訊息分析(規則引擎)"""
        # 清理訊息
        cleaned_message = self._clean_message(message)
        
        # 識別意圖
        intent, confidence = self._identify_intent(cleaned_message)
        
        # 提取實體
        entities = self._extract_entities(cleaned_message)
        
        # 確定查詢類型
        query_type = self._determine_query_type(intent)
        
        # 生成查詢參數
        parameters = self._generate_parameters(intent, entities)
        
        return NLPAnalysisResult(
            query_type=query_type,
            intent=intent,
            entities=entities,
            confidence=confidence,
            parameters=parameters
        )

    def _clean_message(self, message: str) -> str:
        """清理訊息"""
        # 移除多餘空白
        message = re.sub(r'\s+', ' ', message.strip())
        return message

    def _identify_intent(self, message: str) -> tuple[str, float]:
        """識別用戶意圖"""
        best_intent = "unknown"
        best_score = 0.0
        
        for intent, patterns in self.intent_patterns.items():
            for pattern in patterns:
                if re.search(pattern, message, re.IGNORECASE):
                    score = len(re.findall(pattern, message, re.IGNORECASE)) / len(message.split())
                    if score > best_score:
                        best_score = score
                        best_intent = intent
        
        # 如果沒有匹配到任何模式,設定基本信心度
        confidence = max(best_score, 0.3) if best_intent != "unknown" else 0.1
        
        return best_intent, min(confidence, 1.0)

    def _extract_entities(self, message: str) -> Dict[str, Any]:
        """提取實體"""
        entities = {}
        
        for entity_type, pattern in self.entity_patterns.items():
            matches = re.findall(pattern, message, re.IGNORECASE)
            if matches:
                if entity_type == "price_range":
                    # 處理價格範圍
                    for match in matches:
                        if isinstance(match, tuple):
                            prices = [p for p in match if p]
                            if len(prices) >= 2:
                                entities["min_price"] = int(prices[0])
                                entities["max_price"] = int(prices[1])
                elif entity_type == "user_name":
                    # 處理用戶名(可能有多個捕獲組)
                    for match in matches:
                        if isinstance(match, tuple):
                            name = next((n for n in match if n), None)
                            if name:
                                entities[entity_type] = name
                        else:
                            entities[entity_type] = match
                else:
                    entities[entity_type] = matches[0] if isinstance(matches[0], str) else matches[0][0]
        
        return entities

    def _determine_query_type(self, intent: str) -> QueryType:
        """確定查詢類型"""
        if intent.startswith("search_"):
            return QueryType.SEARCH
        elif intent.startswith("create_"):
            return QueryType.CREATE
        elif intent.startswith("update_"):
            return QueryType.UPDATE
        elif intent.startswith("delete_"):
            return QueryType.DELETE
        elif intent == "analytics":
            return QueryType.ANALYTICS
        else:
            return QueryType.UNKNOWN

    def _generate_parameters(self, intent: str, entities: Dict[str, Any]) -> Dict[str, Any]:
        """生成查詢參數"""
        parameters = {}
        
        # 根據意圖設定表名
        if "user" in intent:
            parameters["table"] = "users"
        elif "order" in intent:
            parameters["table"] = "orders"
        elif "product" in intent:
            parameters["table"] = "products"
        
        # 設定查詢條件
        conditions = {}
        if "user_id" in entities:
            conditions["user_id"] = entities["user_id"]
        if "user_name" in entities:
            conditions["name"] = entities["user_name"]
        if "order_id" in entities:
            conditions["order_id"] = entities["order_id"]
        if "product_name" in entities:
            conditions["name"] = entities["product_name"]
        if "min_price" in entities and "max_price" in entities:
            conditions["price"] = {
                "gte": entities["min_price"],
                "lte": entities["max_price"]
            }
        
        if conditions:
            parameters["conditions"] = conditions
        
        # 設定限制
        if "number" in entities:
            parameters["limit"] = min(int(entities["number"]), 50)  # 最多50筆
        else:
            parameters["limit"] = 10  # 預設10筆
        
        return parameters

    def format_response(self, db_result: DatabaseResult, analysis_result: NLPAnalysisResult, user_message: str = "", use_advanced: bool = True) -> str:
        """格式化回應訊息"""
        try:
            # 如果啟用進階回應且有 OpenRouter API Key
            if use_advanced and user_message and self.openrouter_service.api_key:
                try:
                    # 準備查詢結果資料
                    query_result = {
                        "success": db_result.success,
                        "data": db_result.data,
                        "count": db_result.count,
                        "error": db_result.error
                    }
                    
                    # 使用 asyncio 執行異步回應生成
                    loop = asyncio.new_event_loop()
                    asyncio.set_event_loop(loop)
                    advanced_response = loop.run_until_complete(
                        self.openrouter_service.generate_response(query_result, user_message)
                    )
                    loop.close()
                    
                    if advanced_response and len(advanced_response.strip()) > 0:
                        return advanced_response
                except Exception as e:
                    logger.warning(f"進階回應生成失敗,使用基礎格式: {str(e)}")
            
            # 使用基礎格式化
            return self._basic_format_response(db_result, analysis_result)
                
        except Exception as e:
            logger.error(f"格式化回應錯誤: {str(e)}")
            return "資料處理時發生錯誤,請稍後再試。"
    
    def _basic_format_response(self, db_result: DatabaseResult, analysis_result: NLPAnalysisResult) -> str:
        """基礎回應格式化"""
        if not db_result.success:
            return f"抱歉,查詢時發生錯誤:{db_result.error}"
        
        if not db_result.data:
            return "沒有找到相關資料。"
        
        intent = analysis_result.intent
        data = db_result.data
        
        if intent.startswith("search_user"):
            return self._format_user_response(data)
        elif intent.startswith("search_order"):
            return self._format_order_response(data)
        elif intent.startswith("search_product"):
            return self._format_product_response(data)
        elif intent == "analytics":
            return self._format_analytics_response(data)
        else:
            return f"找到 {len(data)} 筆資料。"

    def _format_user_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化用戶查詢回應"""
        if len(data) == 1:
            user = data[0]
            return f"用戶資料:\n名稱:{user.get('name', 'N/A')}\nID:{user.get('user_id', 'N/A')}\n電子郵件:{user.get('email', 'N/A')}"
        else:
            response = f"找到 {len(data)} 位用戶:\n"
            for i, user in enumerate(data[:5], 1):  # 最多顯示5筆
                response += f"{i}. {user.get('name', 'N/A')} (ID: {user.get('user_id', 'N/A')})\n"
            if len(data) > 5:
                response += f"... 還有 {len(data) - 5} 筆資料"
            return response

    def _format_order_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化訂單查詢回應"""
        if len(data) == 1:
            order = data[0]
            return f"訂單資料:\n訂單編號:{order.get('order_id', 'N/A')}\n狀態:{order.get('status', 'N/A')}\n金額:${order.get('total_amount', 'N/A')}"
        else:
            response = f"找到 {len(data)} 筆訂單:\n"
            for i, order in enumerate(data[:5], 1):
                response += f"{i}. {order.get('order_id', 'N/A')} - ${order.get('total_amount', 'N/A')}\n"
            if len(data) > 5:
                response += f"... 還有 {len(data) - 5} 筆資料"
            return response

    def _format_product_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化商品查詢回應"""
        if len(data) == 1:
            product = data[0]
            return f"商品資料:\n名稱:{product.get('name', 'N/A')}\n價格:${product.get('price', 'N/A')}\n庫存:{product.get('stock', 'N/A')}"
        else:
            response = f"找到 {len(data)} 項商品:\n"
            for i, product in enumerate(data[:5], 1):
                response += f"{i}. {product.get('name', 'N/A')} - ${product.get('price', 'N/A')}\n"
            if len(data) > 5:
                response += f"... 還有 {len(data) - 5} 筆資料"
            return response

    def _format_analytics_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化分析回應"""
        if data and len(data) > 0:
            if 'count' in data[0]:
                return f"統計結果:共 {data[0]['count']} 筆資料"
            else:
                return f"分析結果:找到 {len(data)} 筆相關資料"
        return "無統計資料"

    def analyze_business_query(self, message: str, user_id: str = None) -> NLPAnalysisResult:
        """分析業務相關的自然語言查詢"""
        try:
            # 檢測查詢意圖
            intent = self._detect_business_intent(message)
            
            # 提取實體
            entities = self._extract_business_entities(message, intent)
            
            # 根據意圖設定查詢類型和參數
            query_type, parameters = self._build_query_parameters(intent, entities, user_id)
            
            # 計算信心度
            confidence = self._calculate_confidence(message, intent, entities)
            
            return NLPAnalysisResult(
                query_type=query_type,
                intent=intent,
                entities=entities,
                confidence=confidence,
                parameters=parameters
            )
            
        except Exception as e:
            logger.error(f"業務查詢分析錯誤: {str(e)}")
            return NLPAnalysisResult(
                query_type=QueryType.UNKNOWN,
                intent="unknown",
                entities={},
                confidence=0.0,
                parameters={}
            )

    def _detect_business_intent(self, message: str) -> str:
        """檢測業務查詢意圖"""
        message_lower = message.lower()
        
        # 檢查業務相關的意圖模式
        for intent, patterns in self.business_intent_patterns.items():
            for pattern in patterns:
                if re.search(pattern, message_lower):
                    return intent
        
        # 如果沒有匹配到業務意圖,使用原有的意圖檢測
        for intent, patterns in self.intent_patterns.items():
            for pattern in patterns:
                if re.search(pattern, message_lower):
                    return intent
        
        return "general_search"

    def _extract_business_entities(self, message: str, intent: str) -> Dict[str, Any]:
        """提取業務相關實體"""
        entities = {}
        
        # 商品名稱提取
        product_patterns = [
            r"商品[::]?\s*([^\s,。!?]+)",
            r"產品[::]?\s*([^\s,。!?]+)",
            r"貨品[::]?\s*([^\s,。!?]+)"
        ]
        
        for pattern in product_patterns:
            match = re.search(pattern, message)
            if match:
                entities["product_name"] = match.group(1)
                break
        
        # 如果沒有明確的商品名稱,嘗試提取關鍵字
        if "product_name" not in entities:
            # 移除查詢關鍵字後的剩餘內容可能是商品名稱
            keywords_to_remove = ['查詢', '搜尋', '找', '商品', '產品', '庫存', '有沒有', '請問', '的', '嗎']
            cleaned_message = message
            for keyword in keywords_to_remove:
                cleaned_message = cleaned_message.replace(keyword, '')
            
            cleaned_message = cleaned_message.strip()
            if cleaned_message and len(cleaned_message) > 0:
                entities["search_text"] = cleaned_message
        
        # 客戶相關實體
        customer_patterns = [
            r"客戶[::]?\s*([^\s,。!?]+)",
            r"客戶編號[::]?\s*([A-Za-z0-9]+)",
            r"客戶名稱[::]?\s*([^\s,。!?]+)"
        ]
        
        for pattern in customer_patterns:
            match = re.search(pattern, message)
            if match:
                entities["customer_info"] = match.group(1)
                break
        
        # 訂單相關實體
        order_patterns = [
            r"訂單[編號ID][::]?\s*([A-Za-z0-9\-]+)",
            r"訂單[::]?\s*([A-Za-z0-9\-]+)"
        ]
        
        for pattern in order_patterns:
            match = re.search(pattern, message)
            if match:
                entities["order_id"] = match.group(1)
                break
        
        # 數量相關實體
        quantity_patterns = [
            r"(\d+)\s*個",
            r"(\d+)\s*件",
            r"(\d+)\s*箱",
            r"數量[::]?\s*(\d+)"
        ]
        
        for pattern in quantity_patterns:
            match = re.search(pattern, message)
            if match:
                entities["quantity"] = int(match.group(1))
                break
        
        # 狀態相關實體
        status_keywords = {
            "待處理": ["待處理", "pending"],
            "已確認": ["已確認", "confirmed"],
            "已出貨": ["已出貨", "shipped"],
            "已完成": ["已完成", "completed"],
            "已取消": ["已取消", "cancelled"]
        }
        
        message_lower = message.lower()
        for status, keywords in status_keywords.items():
            if any(keyword in message_lower for keyword in keywords):
                entities["status"] = status
                break
        
        return entities

    def _build_query_parameters(self, intent: str, entities: Dict[str, Any], user_id: str = None) -> tuple:
        """根據意圖和實體建立查詢參數"""
        
        if intent == "product_search":
            return QueryType.SEARCH, {
                "method": "search_products",
                "query_text": entities.get("product_name") or entities.get("search_text"),
                "category": entities.get("category"),
                "limit": 10
            }
        
        elif intent == "inventory_check":
            return QueryType.SEARCH, {
                "method": "check_inventory",
                "product_name": entities.get("product_name") or entities.get("search_text"),
                "category": entities.get("category")
            }
        
        elif intent == "order_search":
            return QueryType.SEARCH, {
                "method": "search_orders",
                "user_id": user_id,
                "order_id": entities.get("order_id"),
                "status": entities.get("status"),
                "limit": 10
            }
        
        elif intent == "low_stock_alert":
            return QueryType.SEARCH, {
                "method": "get_low_stock_products",
                "threshold": 10
            }
        
        elif intent == "business_summary":
            return QueryType.ANALYTICS, {
                "method": "get_business_summary"
            }
        
        else:
            # 預設為商品搜尋
            return QueryType.SEARCH, {
                "method": "search_products",
                "query_text": entities.get("search_text") or entities.get("product_name"),
                "limit": 10
            }

    def _calculate_confidence(self, message: str, intent: str, entities: Dict[str, Any]) -> float:
        """計算查詢信心度"""
        confidence = 0.5  # 基礎信心度
        
        # 如果有明確的意圖匹配,增加信心度
        if intent in self.business_intent_patterns:
            confidence += 0.3
        
        # 如果提取到實體,增加信心度
        if entities:
            confidence += 0.2 * len(entities)
        
        # 如果訊息長度適中,增加信心度
        if 2 <= len(message) <= 50:
            confidence += 0.1
        
        return min(confidence, 1.0)

    def format_response_message(self, result: DatabaseResult, intent: str) -> str:
        """格式化回應訊息"""
        if not result.success:
            return f"抱歉,查詢時發生錯誤:{result.error}"
        
        if not result.data or result.count == 0:
            return "沒有找到相關資料。"
        
        # 根據不同的查詢意圖格式化回應
        if intent == "product_search":
            return self._format_product_response(result.data)
        elif intent == "inventory_check":
            return self._format_inventory_response(result.data)
        elif intent == "order_search":
            return self._format_order_response(result.data)
        elif intent == "low_stock_alert":
            return self._format_low_stock_response(result.data)
        elif intent == "business_summary":
            return self._format_summary_response(result.data)
        else:
            return self._format_general_response(result.data)

    def _format_product_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化商品查詢回應"""
        if len(data) == 1:
            product = data[0]
            return f"找到商品:\n" \
                   f"名稱:{product.get('name', 'N/A')}\n" \
                   f"描述:{product.get('description', 'N/A')}\n" \
                   f"價格:${product.get('price', 0)}\n" \
                   f"類別:{product.get('category', 'N/A')}"
        else:
            response = f"找到 {len(data)} 個商品:\n"
            for i, product in enumerate(data[:5], 1):
                response += f"{i}. {product.get('name', 'N/A')} - ${product.get('price', 0)}\n"
            if len(data) > 5:
                response += f"... 還有 {len(data) - 5} 個商品"
            return response

    def _format_inventory_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化庫存查詢回應"""
        if len(data) == 1:
            item = data[0]
            return f"庫存資訊:\n" \
                   f"商品:{item.get('product_name', 'N/A')}\n" \
                   f"目前庫存:{item.get('current_stock', 0)} 件\n" \
                   f"類別:{item.get('category', 'N/A')}\n" \
                   f"價格:${item.get('price', 0)}"
        else:
            response = f"找到 {len(data)} 個商品的庫存:\n"
            for i, item in enumerate(data[:5], 1):
                response += f"{i}. {item.get('product_name', 'N/A')} - 庫存:{item.get('current_stock', 0)}\n"
            return response

    def _format_order_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化訂單查詢回應 - 適配銷售訂單資料結構"""
        if not data:
            return "沒有找到符合條件的訂單。"

        if len(data) == 1:
            order = data[0]
            status_display = order.get('status_display', order.get('status', 'N/A'))
            return f"📋 訂單詳細資訊:\n" \
                   f"訂單編號:{order.get('order_id', 'N/A')}\n" \
                   f"銷售日期:{order.get('sales_date', 'N/A')}\n" \
                   f"狀態:{status_display}\n" \
                   f"客戶:{order.get('customer_name', 'N/A')}\n" \
                   f"銷售人員:{order.get('salesperson_name', 'N/A')}\n" \
                   f"付款條件:{order.get('payment_term', 'N/A')}\n" \
                   f"總金額:${order.get('total_amount', 0)}\n" \
                   f"更新時間:{order.get('updated_at', 'N/A')[:10] if order.get('updated_at') else 'N/A'}"
        else:
            response = f"📋 找到 {len(data)} 筆訂單:\n\n"
            for i, order in enumerate(data[:5], 1):
                status_display = order.get('status_display', order.get('status', 'N/A'))
                sales_date = order.get('sales_date', 'N/A')
                if isinstance(sales_date, str) and len(sales_date) > 10:
                    sales_date = sales_date[:10]  # 只顯示日期部分

                response += f"{i}. {order.get('order_id', 'N/A')}\n"
                response += f"   狀態:{status_display} | 日期:{sales_date}\n"
                response += f"   客戶:{order.get('customer_name', 'N/A')} | 金額:${order.get('total_amount', 0)}\n\n"

            if len(data) > 5:
                response += f"... 還有 {len(data) - 5} 筆訂單"

            return response.strip()

    def _format_low_stock_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化低庫存警告回應"""
        if not data:
            return "目前沒有低庫存商品。"
        
        response = f"⚠️ 發現 {len(data)} 個低庫存商品:\n"
        for i, item in enumerate(data[:10], 1):
            response += f"{i}. {item.get('product_name', 'N/A')} - 剩餘:{item.get('current_stock', 0)} 件\n"
        return response

    def _format_summary_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化業務摘要回應"""
        if data:
            summary = data[0]
            return f"📊 業務摘要:\n" \
                   f"商品總數:{summary.get('total_products', 0)} 個\n" \
                   f"訂單總數:{summary.get('total_orders', 0)} 筆\n" \
                   f"用戶總數:{summary.get('total_users', 0)} 人\n" \
                   f"低庫存商品:{summary.get('low_stock_items', 0)} 個\n" \
                   f"統計時間:{summary.get('report_date', 'N/A')}"
        return "無法取得業務摘要資料。"

    def _format_general_response(self, data: List[Dict[str, Any]]) -> str:
        """格式化一般查詢回應"""
        return f"找到 {len(data)} 筆資料。"