LawrenceBai commited on
Commit
f216ef7
·
1 Parent(s): 9660a46

Update agent_bridge.py and tools.js

Browse files
features/mcp/agent_bridge.py CHANGED
@@ -104,7 +104,7 @@ class MCPAgentBridge:
104
  return arguments
105
 
106
  tool_name = (tool_name or "").strip()
107
- if tool_name not in {"weather_query"}:
108
  return arguments
109
 
110
  ctx = await self._fetch_env_context(user_id)
@@ -135,6 +135,17 @@ class MCPAgentBridge:
135
  if not city_arg and ctx_city:
136
  enriched["city"] = ctx_city
137
 
 
 
 
 
 
 
 
 
 
 
 
138
  if enriched != arguments:
139
  logger.info(f"📍 已自動補齊 {tool_name} 參數: {_safe_json(enriched)}")
140
 
@@ -462,6 +473,10 @@ class MCPAgentBridge:
462
  * 今日新聞、科技新聞、台灣新聞都應該調用此工具
463
 
464
  - 地點查詢與導航(重要!):
 
 
 
 
465
  * **導航需求判斷**:
466
  - 問「怎麼去 X」「如何去 X」「去 X 怎麼走」「到 X 怎麼走」→ 使用 forward_geocode 查詢目的地座標
467
  - 問「從 A 到 B 要多久」「A 到 B 怎麼走」→ 同時使用 forward_geocode 查詢起點與終點
@@ -472,6 +487,7 @@ class MCPAgentBridge:
472
  1. 先使用 forward_geocode 將地點名稱轉換為座標
473
  2. 再使用 directions 規劃路線(系統會自動處理)
474
  * **範例**:
 
475
  - 「怎麼去桃園火車站」→ forward_geocode:query=桃園火車站
476
  - 「從銘傳大學到桃園火車站」→ forward_geocode:query=銘傳大學桃園校區
477
  - 「台北車站到淡水捷運站」→ forward_geocode:query=台北車站
@@ -491,6 +507,8 @@ class MCPAgentBridge:
491
  - emotion: 用戶情緒標籤(必填)
492
 
493
  示例:
 
 
494
  - "台北天氣" → {{"is_tool_call": true, "tool_name": "weather_query:city=Taipei", "emotion": "neutral"}}
495
  - "好開心!今天天氣好嗎" → {{"is_tool_call": true, "tool_name": "weather_query:city=Taipei", "emotion": "happy"}}
496
  - "美元匯率" → {{"is_tool_call": true, "tool_name": "exchange_query:from_currency=USD,to_currency=TWD,amount=1.0", "emotion": "neutral"}}
 
104
  return arguments
105
 
106
  tool_name = (tool_name or "").strip()
107
+ if tool_name not in {"weather_query", "reverse_geocode"}:
108
  return arguments
109
 
110
  ctx = await self._fetch_env_context(user_id)
 
135
  if not city_arg and ctx_city:
136
  enriched["city"] = ctx_city
137
 
138
+ # 🔥 新增:reverse_geocode 自動注入當前 GPS 座標
139
+ if tool_name == "reverse_geocode":
140
+ if enriched.get("lat") is None:
141
+ lat = _safe_float(ctx.get("lat"))
142
+ if lat is not None:
143
+ enriched["lat"] = lat
144
+ if enriched.get("lon") is None:
145
+ lon = _safe_float(ctx.get("lon"))
146
+ if lon is not None:
147
+ enriched["lon"] = lon
148
+
149
  if enriched != arguments:
150
  logger.info(f"📍 已自動補齊 {tool_name} 參數: {_safe_json(enriched)}")
151
 
 
473
  * 今日新聞、科技新聞、台灣新聞都應該調用此工具
474
 
475
  - 地點查詢與導航(重要!):
476
+ * **當前位置查詢**:
477
+ - 問「我在哪」「這是哪裡」「現在在哪」「我的位置」→ 使用 reverse_geocode(不需參數,系統自動用 GPS 座標)
478
+ - ❌ 錯誤:forward_geocode:query=我在哪
479
+ - ✅ 正確:reverse_geocode
480
  * **導航需求判斷**:
481
  - 問「怎麼去 X」「如何去 X」「去 X 怎麼走」「到 X 怎麼走」→ 使用 forward_geocode 查詢目的地座標
482
  - 問「從 A 到 B 要多久」「A 到 B 怎麼走」→ 同時使用 forward_geocode 查詢起點與終點
 
487
  1. 先使用 forward_geocode 將地點名稱轉換為座標
488
  2. 再使用 directions 規劃路線(系統會自動處理)
489
  * **範例**:
490
+ - 「我在哪」→ reverse_geocode(系統自動補 lat/lon)
491
  - 「怎麼去桃園火車站」→ forward_geocode:query=桃園火車站
492
  - 「從銘傳大學到桃園火車站」→ forward_geocode:query=銘傳大學桃園校區
493
  - 「台北車站到淡水捷運站」→ forward_geocode:query=台北車站
 
507
  - emotion: 用戶情緒標籤(必填)
508
 
509
  示例:
510
+ - "我在哪" → {{"is_tool_call": true, "tool_name": "reverse_geocode", "emotion": "neutral"}}
511
+ - "這是哪裡" → {{"is_tool_call": true, "tool_name": "reverse_geocode", "emotion": "neutral"}}
512
  - "台北天氣" → {{"is_tool_call": true, "tool_name": "weather_query:city=Taipei", "emotion": "neutral"}}
513
  - "好開心!今天天氣好嗎" → {{"is_tool_call": true, "tool_name": "weather_query:city=Taipei", "emotion": "happy"}}
514
  - "美元匯率" → {{"is_tool_call": true, "tool_name": "exchange_query:from_currency=USD,to_currency=TWD,amount=1.0", "emotion": "neutral"}}
static/frontend/js/tools.js CHANGED
@@ -177,9 +177,16 @@ function getIconForTool(toolName, category) {
177
  const iconMap = {
178
  // 分類映射
179
  '健康': '❤️',
 
180
  '天氣': '🌤️',
181
  '新聞': '📰',
182
  '匯率': '💱',
 
 
 
 
 
 
183
  '時間': '⏰',
184
  '提醒': '⏰',
185
  '日曆': '📅',
@@ -192,7 +199,16 @@ function getIconForTool(toolName, category) {
192
  'healthkit_query': '❤️',
193
  'weather_query': '🌤️',
194
  'news_query': '📰',
195
- 'exchange_rate': '💱',
 
 
 
 
 
 
 
 
 
196
  'time_query': '⏰',
197
  'reminder': '⏰',
198
  'calendar': '📅'
@@ -292,9 +308,15 @@ function renderCardContent(toolName, toolData) {
292
  return renderExchangeRate(toolData);
293
  }
294
 
295
- // 模式 5:通用 raw_data 物件
 
 
 
 
 
 
296
  if (toolData.raw_data && typeof toolData.raw_data === 'object') {
297
- console.log('✅ 匹配到模式 5: 通用 raw_data');
298
  return renderKeyValuePairs(toolData.raw_data);
299
  }
300
 
@@ -496,6 +518,92 @@ function renderExchangeRate(data) {
496
  return html || '<p>無匯率數據</p>';
497
  }
498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  /**
500
  * Fallback:顯示 JSON
501
  */
 
177
  const iconMap = {
178
  // 分類映射
179
  '健康': '❤️',
180
+ '健康數據': '❤️',
181
  '天氣': '🌤️',
182
  '新聞': '📰',
183
  '匯率': '💱',
184
+ '生活資訊': '💬',
185
+ '地理定位': '📍',
186
+ '軌道運輸': '🚇',
187
+ '道路運輸': '🚌',
188
+ '微型運具': '🚲',
189
+ '停車與充電': '🅿️',
190
  '時間': '⏰',
191
  '提醒': '⏰',
192
  '日曆': '📅',
 
199
  'healthkit_query': '❤️',
200
  'weather_query': '🌤️',
201
  'news_query': '📰',
202
+ 'exchange_query': '💱',
203
+ 'forward_geocode': '📍',
204
+ 'reverse_geocode': '📍',
205
+ 'directions': '🗺️',
206
+ 'tdx_bus_arrival': '🚌',
207
+ 'tdx_metro': '�',
208
+ 'tdx_train': '🚆',
209
+ 'tdx_thsr': '🚄',
210
+ 'tdx_youbike': '🚲',
211
+ 'tdx_parking': '🅿️',
212
  'time_query': '⏰',
213
  'reminder': '⏰',
214
  'calendar': '📅'
 
308
  return renderExchangeRate(toolData);
309
  }
310
 
311
+ // 模式 5:地理定位數據(forward_geocode / reverse_geocode)
312
+ if (toolData.best_match && toolData.best_match.lat && toolData.best_match.lon) {
313
+ console.log('✅ 匹配到模式 5: 地理定位數據');
314
+ return renderLocationData(toolData);
315
+ }
316
+
317
+ // 模式 6:通用 raw_data 物件
318
  if (toolData.raw_data && typeof toolData.raw_data === 'object') {
319
+ console.log('✅ 匹配到模式 6: 通用 raw_data');
320
  return renderKeyValuePairs(toolData.raw_data);
321
  }
322
 
 
518
  return html || '<p>無匯率數據</p>';
519
  }
520
 
521
+ /**
522
+ * 渲染地理定位數據(forward_geocode / reverse_geocode)
523
+ */
524
+ function renderLocationData(data) {
525
+ const bestMatch = data.best_match || {};
526
+ const results = data.results || [];
527
+ const query = data.query || '';
528
+
529
+ let html = '';
530
+
531
+ // 顯示查詢字串(如果有)
532
+ if (query) {
533
+ html += `
534
+ <div class="data-row">
535
+ <span class="data-label">🔍 查詢</span>
536
+ <span class="data-value">${query}</span>
537
+ </div>
538
+ `;
539
+ }
540
+
541
+ // 最佳匹配地點
542
+ if (bestMatch.label || bestMatch.display_name) {
543
+ html += `
544
+ <div class="data-row">
545
+ <span class="data-label">📍 地點</span>
546
+ <span class="data-value">${bestMatch.label || bestMatch.display_name}</span>
547
+ </div>
548
+ `;
549
+ }
550
+
551
+ // 座標
552
+ if (bestMatch.lat !== undefined && bestMatch.lon !== undefined) {
553
+ html += `
554
+ <div class="data-row">
555
+ <span class="data-label">🌐 座標</span>
556
+ <span class="data-value">${bestMatch.lat.toFixed(6)}, ${bestMatch.lon.toFixed(6)}</span>
557
+ </div>
558
+ `;
559
+ }
560
+
561
+ // 詳細地址(如果與 label 不同)
562
+ if (bestMatch.detailed_address && bestMatch.detailed_address !== bestMatch.label) {
563
+ html += `
564
+ <div class="data-row">
565
+ <span class="data-label">🏠 詳細</span>
566
+ <span class="data-value" style="font-size: 0.9em;">${bestMatch.detailed_address}</span>
567
+ </div>
568
+ `;
569
+ }
570
+
571
+ // 郵遞區號
572
+ if (bestMatch.postcode) {
573
+ html += `
574
+ <div class="data-row">
575
+ <span class="data-label">📮 郵遞區號</span>
576
+ <span class="data-value">${bestMatch.postcode}</span>
577
+ </div>
578
+ `;
579
+ }
580
+
581
+ // 如果有多個結果,顯示數量
582
+ if (results.length > 1) {
583
+ html += `
584
+ <div class="data-row" style="margin-top: 8px; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 8px;">
585
+ <span class="data-label">📊 其他結果</span>
586
+ <span class="data-value">共 ${results.length} 個地點</span>
587
+ </div>
588
+ `;
589
+ }
590
+
591
+ // Google Maps 連結(可選)
592
+ if (bestMatch.lat && bestMatch.lon) {
593
+ const mapsUrl = `https://www.google.com/maps?q=${bestMatch.lat},${bestMatch.lon}`;
594
+ html += `
595
+ <div class="data-row" style="margin-top: 8px;">
596
+ <a href="${mapsUrl}" target="_blank" rel="noopener noreferrer"
597
+ style="color: #4fc3f7; text-decoration: none; font-size: 0.9em;">
598
+ 🗺️ 在 Google Maps 中查看
599
+ </a>
600
+ </div>
601
+ `;
602
+ }
603
+
604
+ return html || '<p>無地理數據</p>';
605
+ }
606
+
607
  /**
608
  * Fallback:顯示 JSON
609
  */