文章目录
-
- 每日一句正能量
- 引言:数据驱动的选址革命
- 一、核心挑战:传统选址的四大痛点
- 二、技术架构:时空数据智能引擎
-
- [2.1 分层架构设计](#2.1 分层架构设计)
- [2.2 MCP工具定义:时空数据标准化接口](#2.2 MCP工具定义:时空数据标准化接口)
- 三、核心Agent实现:多维分析引擎
-
- [3.1 人流分析Agent:洞察时空模式](#3.1 人流分析Agent:洞察时空模式)
- [3.2 竞品分析Agent:饱和度与差异化机会](#3.2 竞品分析Agent:饱和度与差异化机会)
- [3.3 决策融合Agent:多因子综合评分](#3.3 决策融合Agent:多因子综合评分)
- 四、前端可视化:时空数据大屏
- 五、实战案例:连锁咖啡品牌北京选址
- 六、技术亮点与创新点
-
- [6.1 时空数据融合创新](#6.1 时空数据融合创新)
- [6.2 MCP协议的行业适配](#6.2 MCP协议的行业适配)
- [6.3 可解释AI](#6.3 可解释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米内有地铁或写字楼。
系统分析流程:
-
区域扫描:锁定朝阳区国贸、望京、三里屯,海淀区中关村,西城区金融街五个商圈
-
热力分析:
- 国贸:工作日早8-9点、午12-13点出现明显双峰,符合白领特征
- 望京:晚间热力持续,周末不降反升,偏社区属性
- 中关村:热力峰值在晚9-10点,IT从业者加班特征明显
-
竞品饱和度:
- 国贸:已有12家咖啡品牌,HHI指数0.68(高饱和),但某星巴克门店评分仅3.8,存在体验缺口
- 望京:HHI指数0.45(中等),独立咖啡馆占比60%,连锁品牌有机会
-
综合推荐:
- 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决策系统,核心创新包括:
- 多维度时空分析:融合人流热力、竞品分布、交通可达、成本效益四大维度
- 专业Agent协作:人流分析、竞品分析、决策融合Agent各司其职,通过MCP协议协调
- 场景化智能:支持自然语言输入、What-if模拟、可解释决策,降低使用门槛
未来改进方向:
- 动态监测:接入实时数据,当竞品开业或地铁施工时主动预警
- 全生命周期:从选址扩展到开业后经营监测,形成数据闭环
- 跨城复制:基于成功案例,自动识别相似城市/商圈进行机会挖掘
时空数据智能正在重塑商业决策模式。当AI不仅能"看地图",还能"读懂地图背后的商业逻辑",每一个开店决策都将更加科学、高效、可控。
在线体验 :https://site-selection-demo.tencent.com
技术文档 :https://lbs.qq.com/bigdata
转载自:https://blog.csdn.net/u014727709/article/details/160155326
欢迎 👍点赞✍评论⭐收藏,欢迎指正