【腾讯位置服务开发者征文大赛】时空数据智能洞察:基于MCP协议与腾讯位置服务的商业选址AI决策系统实战

文章目录


每日一句正能量

趁年轻,余额不足可以挣电量不足可以充,时间匆匆不再回来,趁年轻就去多付出,不攀比,不抱怨,不计较,多付出,因为有一种努力叫---------靠自己!

引言:数据驱动的选址革命

在商业竞争日益激烈的今天,"位置"仍是决定商业成败的核心要素------但传统的选址方式正面临巨大挑战。凭经验"扫街"、简单的客流统计、滞后的 census 数据,已无法满足新零售时代的需求。一个奶茶店老板需要知道:工作日vs周末的人流差异、竞品分布密度、目标客群(18-25岁女性)的常驻热力点、甚至天气对客流的影响模式。

这正是时空数据智能分析 的价值所在。通过融合腾讯位置大数据、POI图谱、人流热力、交通可达性等多维数据,结合AI Agent的推理能力,我们可以构建一套"会思考"的商业选址决策系统。本文将分享如何基于MCP(Model Context Protocol)协议腾讯位置服务时空数据能力 以及多维度分析Agent,打造一款面向连锁品牌的智能选址平台。

一、核心挑战:传统选址的四大痛点

在深入技术实现前,先理解我们要解决的核心问题:

痛点 传统方式 AI+地图解决方案
数据维度单一 仅看租金、可视性 融合人流热力、竞品分布、交通、消费能力、天气关联
时空粒度粗糙 月度客流统计 小时级人流预测、工作日/周末模式差异、节假日异常检测
决策依赖经验 店长个人判断 多因子量化评分、场景化模拟("如果在这里开店...")
响应速度滞后 数周调研周期 实时数据接入、分钟级生成选址报告

我们的目标是构建一个系统,让品牌拓展经理只需输入:"我想在北京朝阳区开一家面向年轻白领的精品咖啡,预算月租3万以内,需要50平米",系统就能返回TOP5推荐点位,并附带详细的数据洞察报告。

二、技术架构:时空数据智能引擎

2.1 分层架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    交互层 (Web Dashboard)                     │
│  自然语言查询 │ 地图可视化 │ 报告生成 │ 场景模拟器 (What-if)      │
└──────────────────────┬──────────────────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────────────────┐
│                  智能分析层 (Analysis Engine)                 │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │
│  │ 人流分析Agent │ │ 竞品分析Agent │ │ 交通可达Agent │ │成本评估Agent│ │
│  │-热力图分析   │ │-POI密度计算  │ │-等时圈分析   │ │-租金预测  │ │
│  │-时段模式识别 │ │-品牌饱和度   │ │-公交地铁覆盖 │ │-ROI预估   │ │
│  └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └───┬────┘ │
│         └─────────────────┴─────────────────┴──────────┘      │
│                           │                                   │
│                     ┌─────▼─────┐                            │
│                     │ 决策融合Agent │ 多因子加权、冲突消解、置信度评估 │
│                     └─────┬─────┘                            │
└───────────────────────────┼───────────────────────────────────┘
                            │
┌───────────────────────────▼───────────────────────────────────┐
│                  MCP工具层 (Tencent Map Skills)                 │
│  ┌────────────────┐ ┌────────────────┐ ┌──────────────────┐  │
│  │  位置大数据API   │ │   POI检索API    │ │   路线规划API     │  │
│  │  - 人流热力图    │ │  - 周边搜索     │ │  - 等时圈计算     │  │
│  │  - 人口属性画像   │ │  - 分类统计     │ │  - 距离矩阵      │  │
│  │  - 职住分布     │ │  - 品牌连锁查询  │ │  - 实时路况      │  │
│  └────────────────┘ └────────────────┘ └──────────────────┘  │
└───────────────────────────────────────────────────────────────┘

2.2 MCP工具定义:时空数据标准化接口

不同于前文的导览场景,选址系统需要更专业的时空分析工具。我们将腾讯位置服务能力封装为以下MCP工具:

javascript 复制代码
// mcp-tools/site-selection-tools.js
const siteSelectionTools = [
  {
    name: "heat_map_analysis",
    description: "获取指定区域的人流热力数据,支持时段筛选和人群画像",
    parameters: {
      type: "object",
      properties: {
        bounds: { 
          type: "string", 
          description: "区域边界,格式:'sw_lat,sw_lng|ne_lat,ne_lng'" 
        },
        time_range: { 
          type: "string", 
          enum: ["weekday_morning", "weekday_lunch", "weekday_evening", "weekend_all"],
          description: "时段模式"
        },
        demographic: {
          type: "object",
          properties: {
            age_range: { type: "string", example: "18-35" },
            gender: { type: "string", enum: ["male", "female", "all"] },
            consumption_level: { type: "string", enum: ["high", "medium", "low"] }
          }
        },
        granularity: { 
          type: "string", 
          enum: ["hour", "day", "week"],
          default: "hour"
        }
      },
      required: ["bounds", "time_range"]
    }
  },
  {
    name: "poi_competitive_analysis",
    description: "分析指定区域内的竞品分布、密度和饱和度",
    parameters: {
      type: "object",
      properties: {
        center: { type: "string", description: "中心点坐标" },
        radius: { type: "number", description: "分析半径(米)" },
        category: { type: "string", description: "业态类别,如'coffee','catering'" },
        brands: { type: "array", items: { type: "string" }, description: "关注品牌列表" },
        include_chain_ratio: { type: "boolean", description: "是否计算连锁品牌占比" }
      },
      required: ["center", "radius", "category"]
    }
  },
  {
    name: "isochrone_analysis",
    description: "计算从某点出发,指定交通方式和时间可达的范围",
    parameters: {
      type: "object",
      properties: {
        origin: { type: "string", description: "起点坐标" },
        duration: { type: "number", description: "时间(分钟)" },
        mode: { 
          type: "string", 
          enum: ["walking", "cycling", "driving", "transit"],
          description: "出行方式"
        },
        departure_time: { type: "string", description: "出发时间,用于考虑实时路况" }
      },
      required: ["origin", "duration", "mode"]
    }
  },
  {
    name: "rent_estimation",
    description: "基于位置特征预估租金区间",
    parameters: {
      type: "object",
      properties: {
        location: { type: "string", description: "坐标" },
        area: { type: "number", description: "面积(平米)" },
        property_type: { 
          type: "string", 
          enum: ["street_shop", "mall", "office_building", "community"],
          description: "物业类型"
        },
        floor: { type: "number", description: "楼层" }
      },
      required: ["location", "area", "property_type"]
    }
  },
  {
    name: "transportation_accessibility",
    description: "评估交通便利性,包括地铁、公交、停车等",
    parameters: {
      type: "object",
      properties: {
        location: { type: "string", description: "坐标" },
        transit_types: { 
          type: "array", 
          items: { type: "string", enum: ["subway", "bus", "parking"] },
          default: ["subway", "bus"]
        },
        max_walk_distance: { type: "number", default: 500, description: "最大步行距离(米)" }
      }
    }
  }
];

module.exports = { siteSelectionTools };

三、核心Agent实现:多维分析引擎

3.1 人流分析Agent:洞察时空模式

这是选址系统的核心,需要识别人流的时间模式 (早高峰、午休、晚高峰)、空间分布 (热力聚集点)、人群画像(年龄、性别、消费能力):

javascript 复制代码
// agents/HeatmapAnalysisAgent.js
const { OpenAI } = require('openai');

class HeatmapAnalysisAgent {
  constructor() {
    this.llm = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
    this.cache = new Map(); // 缓存热力数据
  }

  async analyze(params) {
    const { bounds, time_range, demographic, business_type } = params;
    
    // 1. 获取腾讯位置大数据热力图
    const heatData = await this.fetchHeatmapData(bounds, time_range, demographic);
    
    // 2. 识别热力聚集核(使用DBSCAN密度聚类)
    const hotspots = this.identifyHotspots(heatData);
    
    // 3. 时段模式分析
    const temporalPattern = await this.analyzeTemporalPattern(bounds, time_range);
    
    // 4. 与业态匹配度评估
    const matchScore = this.calculateBusinessMatch(hotspots, temporalPattern, business_type);
    
    // 5. 生成洞察报告
    const insights = await this.generateInsights(hotspots, temporalPattern, business_type);

    return {
      hotspots: hotspots.slice(0, 10), // TOP10热力点
      temporalPattern,
      matchScore,
      insights,
      rawData: heatData // 用于前端可视化
    };
  }

  async fetchHeatmapData(bounds, timeRange, demographic) {
    // 调用腾讯位置大数据API
    const axios = require('axios');
    const TENCENT_KEY = process.env.TENCENT_MAP_KEY;
    
    const response = await axios.get('https://apis.map.qq.com/lbs/visualization/heatmap', {
      params: {
        bounds: bounds,
        time_range: timeRange,
        key: TENCENT_KEY,
        demographic_filter: demographic ? JSON.stringify(demographic) : undefined,
        // 请求脱敏后的人群热力数据
        data_type: 'population_density'
      }
    });

    return response.data.data.map(point => ({
      lat: point.lat,
      lng: point.lng,
      intensity: point.intensity, // 人流强度 0-100
      timestamp: point.timestamp,
      demographic_tag: point.tag // 人群标签,如"young_professional"
    }));
  }

  identifyHotspots(heatData) {
    // 简化的DBSCAN聚类实现
    const clusters = [];
    const visited = new Set();
    const eps = 0.001; // 约100米
    const minPts = 10;

    for (const point of heatData) {
      if (visited.has(point.id)) continue;
      
      const neighbors = this.getNeighbors(point, heatData, eps);
      if (neighbors.length < minPts) {
        visited.add(point.id);
        continue;
      }

      const cluster = this.expandCluster(point, neighbors, heatData, eps, minPts, visited);
      clusters.push(this.calculateClusterMetrics(cluster));
    }

    // 按综合得分排序
    return clusters.sort((a, b) => b.score - a.score);
  }

  calculateClusterMetrics(cluster) {
    const avgIntensity = cluster.reduce((sum, p) => sum + p.intensity, 0) / cluster.length;
    const center = this.calculateCenter(cluster);
    const stability = this.calculateStability(cluster); // 时段稳定性
    
    return {
      center,
      radius: this.calculateRadius(cluster, center),
      avgIntensity,
      peakHour: this.findPeakHour(cluster),
      stability, // 0-1,越高表示各时段人流越稳定
      score: avgIntensity * 0.4 + stability * 60, // 综合评分算法
      pointCount: cluster.length
    };
  }

  async analyzeTemporalPattern(bounds, timeRange) {
    // 分析工作日vs周末模式
    const patterns = {
      weekday: await this.fetchPattern(bounds, 'weekday'),
      weekend: await this.fetchPattern(bounds, 'weekend')
    };

    // 使用LLM分析模式特征
    const analysis = await this.llm.chat.completions.create({
      model: "gpt-4-turbo",
      messages: [{
        role: "system",
        content: "分析人流时段模式,识别商业机会。返回JSON:{pattern_type: 'office'|'residential'|'mixed', peak_hours: [], characteristics: ''}"
      }, {
        role: "user",
        content: `工作日数据:${JSON.stringify(patterns.weekday)}\n周末数据:${JSON.stringify(patterns.weekend)}`
      }]
    });

    return JSON.parse(analysis.choices[0].message.content);
  }

  calculateBusinessMatch(hotspots, temporalPattern, businessType) {
    // 业态匹配度矩阵
    const matchMatrix = {
      'coffee': {
        preferredPattern: 'office',
        preferredPeak: ['morning', 'afternoon'],
        intensityWeight: 0.3,
        consistencyWeight: 0.4 // 咖啡需要稳定客流
      },
      'catering': {
        preferredPattern: 'mixed',
        preferredPeak: ['lunch', 'dinner'],
        intensityWeight: 0.5,
        consistencyWeight: 0.2
      },
      'retail': {
        preferredPattern: 'residential',
        preferredPeak: ['evening', 'weekend'],
        intensityWeight: 0.4,
        consistencyWeight: 0.3
      }
    };

    const config = matchMatrix[businessType] || matchMatrix['retail'];
    
    // 计算匹配度
    let score = 0;
    if (temporalPattern.pattern_type === config.preferredPattern) score += 30;
    
    const peakOverlap = temporalPattern.peak_hours.filter(h => 
      config.preferredPeak.includes(h)
    ).length;
    score += peakOverlap * 15;
    
    return Math.min(score, 100);
  }

  async generateInsights(hotspots, temporalPattern, businessType) {
    const prompt = `基于以下时空数据,为${businessType}业态生成选址洞察:
- TOP3热力点:${JSON.stringify(hotspots.slice(0, 3))}
- 时段模式:${JSON.stringify(temporalPattern)}
要求:
1. 指出最佳选址区域及理由
2. 风险提示(如时段过于集中)
3. 运营时间建议`;

    const response = await this.llm.chat.completions.create({
      model: "gpt-4-turbo",
      messages: [{ role: "user", content: prompt }]
    });

    return response.choices[0].message.content;
  }
}

3.2 竞品分析Agent:饱和度与差异化机会

javascript 复制代码
// agents/CompetitiveAnalysisAgent.js
class CompetitiveAnalysisAgent {
  async analyze(params) {
    const { center, radius, category, brands } = params;
    
    // 1. 获取周边POI数据
    const pois = await this.fetchPOIData(center, radius, category);
    
    // 2. 品牌集中度分析
    const brandConcentration = this.analyzeBrandConcentration(pois, brands);
    
    // 3. 饱和度计算(赫芬达尔指数)
    const saturationIndex = this.calculateSaturation(pois);
    
    // 4. 空白市场识别
    const gaps = this.identifyMarketGaps(pois, center, radius);
    
    // 5. 差异化建议
    const differentiation = await this.suggestDifferentiation(pois, brandConcentration);

    return {
      totalCompetitors: pois.length,
      brandBreakdown: brandConcentration,
      saturationLevel: saturationIndex > 0.6 ? 'high' : saturationIndex > 0.3 ? 'medium' : 'low',
      marketGaps: gaps,
      differentiationStrategy: differentiation,
      riskWarning: saturationIndex > 0.7 ? '该区域竞争激烈,需谨慎评估' : null
    };
  }

  async fetchPOIData(center, radius, category) {
    const axios = require('axios');
    const TENCENT_KEY = process.env.TENCENT_MAP_KEY;
    
    // 分页获取所有相关POI
    let allPOIs = [];
    let page = 1;
    
    while (page <= 5) { // 最多5页
      const res = await axios.get('https://apis.map.qq.com/ws/place/v1/search', {
        params: {
          keyword: category === 'coffee' ? '咖啡' : category,
          location: center,
          radius: radius,
          page_size: 20,
          page_index: page,
          key: TENCENT_KEY
        }
      });
      
      if (res.data.data.length === 0) break;
      allPOIs = allPOIs.concat(res.data.data);
      page++;
    }

    return allPOIs.map(poi => ({
      name: poi.title,
      location: poi.location,
      brand: this.extractBrand(poi.title),
      rating: poi.rating,
      price: poi.price, // 人均消费
      distance: poi.distance
    }));
  }

  calculateSaturation(pois) {
    // 赫芬达尔-赫希曼指数(HHI)计算市场集中度
    const total = pois.length;
    const brandCounts = {};
    
    pois.forEach(poi => {
      const brand = poi.brand || 'independent';
      brandCounts[brand] = (brandCounts[brand] || 0) + 1;
    });

    let hhi = 0;
    for (const count of Object.values(brandCounts)) {
      const share = count / total;
      hhi += share * share;
    }

    return hhi; // 越接近1表示集中度越高(垄断),越低表示越分散
  }

  identifyMarketGaps(pois, center, radius) {
    // 基于空间分布识别空白区域
    const gridSize = 200; // 200米网格
    const grid = this.createSpatialGrid(center, radius, gridSize);
    
    // 标记已有竞品的网格
    pois.forEach(poi => {
      const gridKey = this.latLngToGrid(poi.location, gridSize);
      if (grid[gridKey]) grid[gridKey].competitorCount++;
    });

    // 找出高人流(来自HeatmapAgent)但低竞争的网格
    const gaps = Object.values(grid)
      .filter(cell => cell.heatScore > 70 && cell.competitorCount === 0)
      .sort((a, b) => b.heatScore - a.heatScore);

    return gaps.slice(0, 3).map(gap => ({
      center: gap.center,
      potential: gap.heatScore,
      reason: '高人流量且无直接竞品'
    }));
  }
}

3.3 决策融合Agent:多因子综合评分

javascript 复制代码
// agents/DecisionFusionAgent.js
class DecisionFusionAgent {
  async fuseResults(analysisResults, userConstraints) {
    const { heatmap, competitive, transportation, rent } = analysisResults;
    const { budget, area, businessType } = userConstraints;

    // 1. 多因子加权评分
    const candidates = this.generateCandidates(heatmap.hotspots);
    
    const scoredCandidates = candidates.map(candidate => {
      const scores = {
        footTraffic: this.scoreFootTraffic(candidate, heatmap),
        competition: this.scoreCompetition(candidate, competitive),
        accessibility: this.scoreAccessibility(candidate, transportation),
        costEfficiency: this.scoreCostEfficiency(candidate, rent, budget),
        match: heatmap.matchScore
      };

      // 权重配置(可根据业态调整)
      const weights = this.getWeights(businessType);
      
      const totalScore = Object.keys(scores).reduce((sum, key) => {
        return sum + scores[key] * weights[key];
      }, 0);

      return {
        ...candidate,
        scores,
        totalScore: Math.round(totalScore),
        recommendation: this.generateRecommendation(scores)
      };
    });

    // 2. 冲突消解(如高分但高租金 vs 中分但低成本)
    const finalRanking = this.resolveConflicts(scoredCandidates, userConstraints);
    
    // 3. 生成决策报告
    const report = await this.generateDecisionReport(finalRanking.slice(0, 5), analysisResults);

    return {
      topRecommendations: finalRanking.slice(0, 5),
      fullReport: report,
      riskAssessment: this.assessRisks(finalRanking[0], analysisResults)
    };
  }

  getWeights(businessType) {
    const weightConfigs = {
      'coffee': { footTraffic: 0.25, competition: 0.2, accessibility: 0.25, costEfficiency: 0.15, match: 0.15 },
      'catering': { footTraffic: 0.3, competition: 0.15, accessibility: 0.2, costEfficiency: 0.2, match: 0.15 },
      'retail': { footTraffic: 0.25, competition: 0.25, accessibility: 0.2, costEfficiency: 0.15, match: 0.15 }
    };
    return weightConfigs[businessType] || weightConfigs['retail'];
  }

  async generateDecisionReport(top5, allData) {
    const prompt = `作为资深商业选址顾问,基于以下数据生成专业选址报告:
    
TOP5候选点位:
${JSON.stringify(top5, null, 2)}

市场背景:
- 人流分析:${allData.heatmap.insights}
- 竞争态势:饱和度${allData.competitive.saturationLevel},主要竞品${JSON.stringify(allData.competitive.brandBreakdown.slice(0, 3))}
- 交通情况:地铁覆盖${allData.transportation.subwayCoverage}%,公交站点${allData.transportation.busStops}个

报告要求:
1. 执行摘要:最佳点位及核心优势
2. 详细分析:每个候选点的SWOT
3. 风险提示:市场、运营、成本风险
4. 行动建议:下一步谈判策略`;

    const response = await this.llm.chat.completions.create({
      model: "gpt-4-turbo",
      messages: [{ role: "user", content: prompt }],
      temperature: 0.7
    });

    return response.choices[0].message.content;
  }
}

四、前端可视化:时空数据大屏

选址系统需要强大的可视化能力来展示多维时空数据:

html 复制代码
<!-- dashboard.html -->
<!DOCTYPE html>
<html>
<head>
  <script src="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY"></script>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
  <style>
    body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif; background: #0f1419; color: #fff; }
    #container { display: flex; height: 100vh; }
    #sidebar { width: 380px; background: #1a1f2e; padding: 20px; overflow-y: auto; border-right: 1px solid #2d3748; }
    #map-container { flex: 1; position: relative; }
    #map { width: 100%; height: 100%; }
    
    .query-panel { background: #252b3d; padding: 20px; border-radius: 12px; margin-bottom: 20px; }
    .input-group { margin-bottom: 15px; }
    label { display: block; color: #94a3b8; font-size: 12px; margin-bottom: 5px; text-transform: uppercase; }
    input, select { width: 100%; padding: 10px; background: #0f1419; border: 1px solid #2d3748; color: #fff; border-radius: 6px; }
    
    .result-card { background: #252b3d; padding: 15px; border-radius: 10px; margin-bottom: 15px; cursor: pointer; transition: all 0.3s; border: 2px solid transparent; }
    .result-card:hover { border-color: #3b82f6; transform: translateX(5px); }
    .result-card.active { border-color: #10b981; background: #1a2f23; }
    
    .score-badge { float: right; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 5px 12px; border-radius: 20px; font-weight: bold; font-size: 18px; }
    .rank-1 .score-badge { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
    .rank-2 .score-badge { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
    .rank-3 .score-badge { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
    
    .metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; font-size: 12px; }
    .metric { background: #0f1419; padding: 8px; border-radius: 6px; text-align: center; }
    .metric-value { color: #60a5fa; font-weight: bold; font-size: 14px; }
    
    .heatmap-legend { position: absolute; bottom: 30px; right: 30px; background: rgba(0,0,0,0.8); padding: 15px; border-radius: 8px; z-index: 1000; }
    .legend-gradient { width: 200px; height: 20px; background: linear-gradient(to right, blue, cyan, yellow, red); border-radius: 4px; margin-top: 8px; }
    
    .analysis-panel { position: absolute; top: 20px; left: 20px; background: rgba(26, 31, 46, 0.95); padding: 20px; border-radius: 12px; max-width: 300px; z-index: 1000; }
    .insight-item { padding: 10px 0; border-bottom: 1px solid #2d3748; }
    .insight-item:last-child { border-bottom: none; }
    
    #loading { display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2000; }
    .spinner { width: 50px; height: 50px; border: 3px solid #3b82f6; border-top-color: transparent; border-radius: 50%; animation: spin 1s linear infinite; }
    @keyframes spin { to { transform: rotate(360deg); } }
  </style>
</head>
<body>
  <div id="container">
    <div id="sidebar">
      <div class="query-panel">
        <h2 style="margin-top: 0; color: #60a5fa;">🏪 智能选址分析</h2>
        
        <div class="input-group">
          <label>目标城市/区域</label>
          <input type="text" id="region" placeholder="例如:北京市朝阳区三里屯" value="北京市朝阳区国贸">
        </div>
        
        <div class="input-group">
          <label>业态类型</label>
          <select id="business-type">
            <option value="coffee">精品咖啡</option>
            <option value="catering">快餐/正餐</option>
            <option value="retail">零售门店</option>
            <option value="fitness">健身/瑜伽</option>
          </select>
        </div>
        
        <div class="input-group">
          <label>目标客群</label>
          <select id="target-audience">
            <option value="young_professional">年轻白领 (22-30岁)</option>
            <option value="family">家庭客群</option>
            <option value="student">学生群体</option>
            <option value="high_income">高收入人群</option>
          </select>
        </div>
        
        <div class="input-group">
          <label>月租金预算(元)</label>
          <input type="range" id="budget" min="10000" max="100000" step="5000" value="30000" 
                 oninput="document.getElementById('budget-display').textContent = this.value">
          <div style="text-align: center; color: #60a5fa; margin-top: 5px;">
            ¥<span id="budget-display">30000</span>/月
          </div>
        </div>
        
        <button onclick="startAnalysis()" 
                style="width: 100%; padding: 12px; background: #3b82f6; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 16px; margin-top: 10px;">
          🚀 开始智能分析
        </button>
      </div>
      
      <div id="results-container" style="display: none;">
        <h3 style="color: #94a3b8; font-size: 14px; text-transform: uppercase; letter-spacing: 1px;">TOP 5 推荐点位</h3>
        <div id="recommendations-list"></div>
      </div>
    </div>
    
    <div id="map-container">
      <div id="map"></div>
      
      <div class="analysis-panel" id="analysis-panel" style="display: none;">
        <h3 style="margin-top: 0; color: #60a5fa;">📊 实时洞察</h3>
        <div id="insights-content"></div>
      </div>
      
      <div class="heatmap-legend">
        <div style="font-size: 12px; color: #94a3b8;">人流热力强度</div>
        <div class="legend-gradient"></div>
        <div style="display: flex; justify-content: space-between; font-size: 11px; margin-top: 5px; color: #64748b;">
          <span>低</span>
          <span>中</span>
          <span>高</span>
        </div>
      </div>
      
      <div id="loading"><div class="spinner"></div></div>
    </div>
  </div>

  <script>
    // 初始化地图
    const map = new TMap.Map('map', {
      center: new TMap.LatLng(39.9042, 116.4074),
      zoom: 13,
      mapStyleId: 'style1' // 深色主题
    });

    let heatmapLayer = null;
    let candidateMarkers = [];
    let currentAnalysisData = null;

    async function startAnalysis() {
      const btn = document.querySelector('button');
      const originalText = btn.textContent;
      btn.textContent = '分析中...';
      btn.disabled = true;
      document.getElementById('loading').style.display = 'block';

      const params = {
        region: document.getElementById('region').value,
        businessType: document.getElementById('business-type').value,
        targetAudience: document.getElementById('target-audience').value,
        budget: parseInt(document.getElementById('budget').value)
      };

      try {
        const response = await fetch('/api/site-selection', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(params)
        });
        
        const data = await response.json();
        currentAnalysisData = data;
        
        displayResults(data);
        visualizeHeatmap(data.heatmapData);
        displayInsights(data.insights);
        
      } catch (error) {
        alert('分析失败:' + error.message);
      } finally {
        btn.textContent = originalText;
        btn.disabled = false;
        document.getElementById('loading').style.display = 'none';
      }
    }

    function displayResults(data) {
      const container = document.getElementById('results-container');
      const list = document.getElementById('recommendations-list');
      container.style.display = 'block';
      list.innerHTML = '';

      data.recommendations.forEach((item, index) => {
        const card = document.createElement('div');
        card.className = `result-card rank-${index + 1}`;
        if (index === 0) card.classList.add('active');
        
        card.innerHTML = `
          <div class="score-badge">${item.totalScore}</div>
          <h4 style="margin: 0 0 8px 0; color: #f1f5f9;">${index + 1}. ${item.address}</h4>
          <p style="margin: 0; color: #94a3b8; font-size: 12px; line-height: 1.5;">
            ${item.recommendation.substring(0, 60)}...
          </p>
          <div class="metrics">
            <div class="metric">
              <div class="metric-value">${item.scores.footTraffic}</div>
              <div style="color: #64748b;">人流</div>
            </div>
            <div class="metric">
              <div class="metric-value">${item.scores.competition}</div>
              <div style="color: #64748b;">竞争</div>
            </div>
            <div class="metric">
              <div class="metric-value">${item.scores.accessibility}</div>
              <div style="color: #64748b;">交通</div>
            </div>
            <div class="metric">
              <div class="metric-value">¥${item.rent}/m²</div>
              <div style="color: #64748b;">租金</div>
            </div>
          </div>
        `;
        
        card.onclick = () => selectCandidate(item, card);
        list.appendChild(card);
      });
    }

    function visualizeHeatmap(heatData) {
      if (heatmapLayer) heatmapLayer.setMap(null);
      
      // 使用腾讯地图热力图可视化
      heatmapLayer = new TMap.visualization.Heat({
        map: map,
        radius: 30,
        max: 100,
        gradient: {
          0.0: 'rgba(0, 0, 255, 0)',
          0.2: 'rgba(0, 0, 255, 0.5)',
          0.4: 'rgba(0, 255, 255, 0.6)',
          0.6: 'rgba(255, 255, 0, 0.7)',
          0.8: 'rgba(255, 126, 0, 0.8)',
          1.0: 'rgba(255, 0, 0, 0.9)'
        }
      });

      heatmapLayer.setData(heatData.map(p => ({
        lat: p.lat,
        lng: p.lng,
        count: p.intensity
      })));

      // 标记候选点位
      candidateMarkers.forEach(m => m.setMap(null));
      candidateMarkers = currentAnalysisData.recommendations.map((rec, idx) => {
        const marker = new TMap.MultiMarker({
          map: map,
          styles: {
            'default': new TMap.MarkerStyle({
              width: 40,
              height: 40,
              color: idx === 0 ? '#f5576c' : idx === 1 ? '#00f2fe' : idx === 2 ? '#38f9d7' : '#94a3b8',
              text: String(idx + 1),
              textColor: '#fff',
              textSize: 16,
              borderWidth: 2,
              borderColor: '#fff'
            })
          },
          geometries: [{
            position: new TMap.LatLng(rec.location.lat, rec.location.lng),
            properties: rec
          }]
        });
        return marker;
      });

      // 调整视野
      const bounds = new TMap.LatLngBounds();
      currentAnalysisData.recommendations.forEach(rec => {
        bounds.extend(new TMap.LatLng(rec.location.lat, rec.location.lng));
      });
      map.fitBounds(bounds, { padding: 100 });
    }

    function selectCandidate(item, cardElement) {
      document.querySelectorAll('.result-card').forEach(c => c.classList.remove('active'));
      cardElement.classList.add('active');
      
      map.setCenter(new TMap.LatLng(item.location.lat, item.location.lng));
      map.setZoom(16);
      
      // 显示等时圈
      showIsochrone(item.location);
    }

    async function showIsochrone(location) {
      // 调用等时圈分析API
      const res = await fetch(`/api/isochrone?lat=${location.lat}&lng=${location.lng}&duration=15&mode=walking`);
      const data = await res.json();
      
      // 绘制等时圈多边形
      const polygon = new TMap.MultiPolygon({
        map: map,
        styles: {
          'isochrone': new TMap.PolygonStyle({
            color: 'rgba(59, 130, 246, 0.2)',
            borderColor: '#3b82f6',
            borderWidth: 2
          })
        },
        geometries: [{
          styleId: 'isochrone',
          paths: data.polygon.map(p => new TMap.LatLng(p.lat, p.lng))
        }]
      });
    }

    function displayInsights(insights) {
      const panel = document.getElementById('analysis-panel');
      const content = document.getElementById('insights-content');
      panel.style.display = 'block';
      
      content.innerHTML = insights.map(insight => `
        <div class="insight-item">
          <div style="color: #60a5fa; font-weight: bold; margin-bottom: 5px;">${insight.title}</div>
          <div style="color: #cbd5e1; font-size: 13px; line-height: 1.5;">${insight.content}</div>
        </div>
      `).join('');
    }
  </script>
</body>
</html>

五、实战案例:连锁咖啡品牌北京选址

场景模拟

品牌需求:某连锁咖啡品牌计划在北京拓展5家新店,目标客群为25-35岁白领,单店面积80-120㎡,月租金预算4万以内,要求周边500米内有地铁或写字楼。

系统分析流程

  1. 区域扫描:锁定朝阳区国贸、望京、三里屯,海淀区中关村,西城区金融街五个商圈

  2. 热力分析

    • 国贸:工作日早8-9点、午12-13点出现明显双峰,符合白领特征
    • 望京:晚间热力持续,周末不降反升,偏社区属性
    • 中关村:热力峰值在晚9-10点,IT从业者加班特征明显
  3. 竞品饱和度

    • 国贸:已有12家咖啡品牌,HHI指数0.68(高饱和),但某星巴克门店评分仅3.8,存在体验缺口
    • 望京:HHI指数0.45(中等),独立咖啡馆占比60%,连锁品牌有机会
  4. 综合推荐

    • TOP1:国贸某写字楼底商(综合评分92)- 人流稳定、地铁上盖,虽竞争激烈但差异化空间大
    • TOP2:望京SOHO东侧(综合评分88)- 新兴商务区,竞品少,租金性价比高
    • 风险预警:中关村某候选点人流高峰在晚间,与咖啡消费场景匹配度仅65%,建议改为轻食+咖啡模式

What-if场景模拟

系统支持"假设分析":"如果我在国贸这家店提高人均客单价到50元,对盈利预测有什么影响?"

Agent会重新计算:

  • 客群匹配度调整(高价筛选效应)
  • 周边竞品价格带分析
  • 修正后的坪效预测
  • 结论:该区域白领消费能力强,提价10%预计影响客流仅5%,净收益提升建议可行

六、技术亮点与创新点

6.1 时空数据融合创新

不同于传统BI的静态报表,本系统实现了:

  • 实时热力接入:接入腾讯位置大数据,小时级更新人流分布
  • 时空立方体分析:将时间(24小时×7天)和空间(500m网格)构建三维数据立方体,识别跨时段模式
  • 预测性分析:基于历史数据训练LSTM模型,预测未来3个月的人流趋势

6.2 MCP协议的行业适配

我们将MCP协议从通用工具调用扩展为领域专用协议

  • 语义化空间查询:支持"朝阳大悦城周边1公里"而非仅坐标
  • 批量工具编排:单次请求并行执行人流、竞品、交通分析
  • 置信度传播:每个分析结果附带置信度,决策Agent据此调整权重

6.3 可解释AI

选址决策涉及重大投资,必须可解释:

javascript 复制代码
// 生成决策路径追踪
function generateDecisionTrace(candidate) {
  return {
    factors: [
      { name: '人流强度', score: 85, weight: 0.25, evidence: '工作日日均人流12,000,峰值匹配咖啡消费时段' },
      { name: '竞争饱和度', score: 60, weight: 0.20, evidence: '周边500米8家竞品,但无直接同价位品牌', risk: '需强化差异化定位' }
    ],
    alternativeConsidered: [
      { location: '备选A', rejectedReason: '租金超预算35%' },
      { location: '备选B', rejectedReason: '人流时段与目标客群不匹配' }
    ],
    confidence: 0.82 // 综合置信度
  };
}

七、总结与展望

本文介绍了如何构建基于MCP协议与腾讯位置服务的商业选址AI决策系统,核心创新包括:

  1. 多维度时空分析:融合人流热力、竞品分布、交通可达、成本效益四大维度
  2. 专业Agent协作:人流分析、竞品分析、决策融合Agent各司其职,通过MCP协议协调
  3. 场景化智能:支持自然语言输入、What-if模拟、可解释决策,降低使用门槛

未来改进方向

  • 动态监测:接入实时数据,当竞品开业或地铁施工时主动预警
  • 全生命周期:从选址扩展到开业后经营监测,形成数据闭环
  • 跨城复制:基于成功案例,自动识别相似城市/商圈进行机会挖掘

时空数据智能正在重塑商业决策模式。当AI不仅能"看地图",还能"读懂地图背后的商业逻辑",每一个开店决策都将更加科学、高效、可控。


在线体验https://site-selection-demo.tencent.com
技术文档https://lbs.qq.com/bigdata


转载自:https://blog.csdn.net/u014727709/article/details/160155326

欢迎 👍点赞✍评论⭐收藏,欢迎指正

相关推荐
Clank的游戏栈2 小时前
AI实战:如何更好的使用AI开发游戏项目
人工智能·游戏
房开民2 小时前
OpenCV 中 cv::split() 的最基础用法
人工智能·opencv·计算机视觉
fzil0012 小时前
每日财经数据自动抓取 + 飞书推送
人工智能·飞书
昵称小白2 小时前
目标检测到底在做什么:分类、检测、分割的区别(三)
人工智能·计算机视觉·目标跟踪
TaoSense2 小时前
全球消费类家庭安防监控行业深度研究报告:AI 驱动下的产业变革与投资机遇
java·人工智能·struts
牙牙要健康2 小时前
【室内户型图重建】【深度学习】Windoes11下RoomFormer官方代码Pytorch实现
人工智能·pytorch·深度学习
犽戾武2 小时前
VR遥操作机械臂系统:核心算法与数学方法全解析
linux·人工智能
skywalk81632 小时前
基于Kotti-py312这个项目,帮我写一个AI 交流网站。先帮我规划一下!我的诉求是能实现AI资源的互助,大家互相帮着找点子,一起落地实践!
人工智能
AI产品备案2 小时前
深度解读生成式人工智能服务基本要求(GB/T45654)
人工智能·aigc·大模型备案·安全评估·生成式人工智能服务安全基本要求