【腾讯位置服务开发者征文大赛】WorkBuddy + tencentmap skill 打造从“查地图“到“对话地图“—— AI 赋能 + 腾讯地图 API 打造智能出行规划助手
文章目录
💡 核心观点:地图不应该是"你输入坐标、它输出路线"的被动工具,而应该是听得懂自然语言、看得见真实道路、会主动规划行程的出行大脑。本文完整呈现从原型到进阶的升级全过程。

腾讯位置服务官网:https://lbs.qq.com
一、痛点:地图交互的最后一个短板
1.1 我们和地图之间,还差一个"翻译层"
你有没有过这种经历:
- 周末想带孩子和老人出去转转,打开地图 App,输入目的地……然后不知道去哪
- 想规划一个"不太累、有好吃、适合拍照"的半日游,结果在搜索框里只能一个关键词一个关键词地搜
- 出差到陌生城市,想找酒店附近步行可达的餐馆,还要自己判断距离、方向、评价
问题出在哪?
传统地图的交互逻辑是 “坐标驱动”——你给坐标,它画线。但用户真正的需求是**“意图驱动”**的自然语言:「帮我规划一个适合周末亲子的路线,不要太累,有室内外结合,午餐人均 80 以内。」
这就是 AI + 地图 要解决的核心问题:让地图从"工具"进化为"大脑"。
1.2 本文升级版 Demo 解决的核心问题
初版 Demo 存在几个明显短板(自我剖析):
| 问题 | 影响 | 升级方案 |
|---|---|---|
AI 意图解析用简单 includes 匹配,融合度约 40% |
用户说"带娃出去玩"无法准确识别为亲子游 | 接入 LLM API + 扩展本地语义映射表 |
| 路线用直线连接,未贴合真实道路 | 可视化效果差,用户不信任 | 解码 polyline + 逐段调用 direction API |
| 景点排序按 popularity,未考虑距离 | 推荐路线绕远、不紧凑 | 调用距离矩阵 API + 贪心最近邻算法 |
| IP 定位函数定义了但未调用 | 每次都要手动选城市 | 初始化时自动 IP 定位 |
| 搜索框无自动补全 | 用户输入体验差 | 接入 suggestion API + 300ms 防抖下拉 |
二、技术架构:三层 + 双引擎
2.1 三层架构

| 层级 | 核心能力说明 |
|---|---|
| 用户交互层 (UI Layer) | 自然语言输入、意图解析、搜索补全、结果可视化 |
| AI 决策层 (AI Layer) | LLM意图理解、参数抽取、距离矩阵编排 (支持OpenAI兼容接口/本地降级双引擎) |
| 腾讯位置服务层 (Map Layer) | WebService API/JSAPI GL/小程序 地理编码、POI搜索、路线规划、距离矩阵 IP定位、输入提示、坐标转换、热力图 |
核心升级:AI 决策层现在支持"双引擎"——有 LLM API 走云端,没有则自动降级到本地智能规则引擎,保证 Demo 在任何环境下都可运行。
与 workbuddy 对话过程如下图。
2.2 涉及的腾讯位置服务能力
| 能力 | API | 用途 | 对应 Skill |
|---|---|---|---|
| 地理编码 | /ws/geocoder/v1 |
地址 → 坐标 | tencentmap-webservice-skill |
| 逆地理编码 | /ws/geocoder/v1 |
坐标 → 地址描述 | tencentmap-webservice-skill |
| 关键词搜索 | /ws/place/v1/search |
搜索景点/餐厅/酒店 | tencentmap-webservice-skill |
| 输入提示 | /ws/place/v1/suggestion |
搜索框自动补全 | tencentmap-webservice-skill |
| 驾车路线 | /ws/direction/v1/driving |
驾车导航 | tencentmap-webservice-skill |
| 步行路线 | /ws/direction/v1/walking |
步行导航 | tencentmap-webservice-skill |
| 公交路线 | /ws/direction/v1/transit |
公交出行 | tencentmap-webservice-skill |
| 骑行路线 | /ws/direction/v1/bicycling |
骑行规划 | tencentmap-webservice-skill |
| 距离矩阵 | /ws/distance/v1/matrix |
批量距离/时间计算 | tencentmap-webservice-skill |
| IP 定位 | /ws/location/v1/ip |
用户位置自动获取 | tencentmap-webservice-skill |
| 坐标转换 | /ws/coord/v1/translate |
GPS→GCJ-02 坐标转换 | tencentmap-webservice-skill |
| JSAPI GL 初始化 | TMap.Map |
地图初始化 + 体验 Key 获取 | tencentmap-jsapi-gl-skill |
| MultiMarker | TMap.MultiMarker |
多点标记渲染 | tencentmap-jsapi-gl-skill |
| MultiPolyline | TMap.MultiPolyline |
真实道路折线渲染 | tencentmap-jsapi-gl-skill |
| 热力图 | TMap.visualization.Heat |
POI 密集度可视化 | tencentmap-jsapi-gl-skill |
| InfoWindow | TMap.InfoWindow |
点击标记弹出详情 | tencentmap-jsapi-gl-skill |
| 小程序地图组件 | <map> + MapContext |
移动端地图交互 | tencentmap-miniprogram-skill |
三、实现详解:从原型到进阶的完整升级
3.1 AI 意图解析:双引擎设计(升级核心)
与 workbuddy 对话过程如下图。

升级前:简单字符串匹配,query.includes('亲子') 这种方式,覆盖面窄,无法处理语义变体。
升级后:支持 LLM + 本地双引擎,融合度从 40% 提升到 90%+。
3.1.1 LLM 引擎(云端)
// AI_ENGINE.config - 支持任何 OpenAI 兼容接口
config: {
mode: 'llm', // 切换为 llm 模式
apiUrl: 'https://api.deepseek.com/v1/chat/completions',
apiKey: 'YOUR_KEY',
model: 'deepseek-chat'
// 也支持腾讯混元、OpenAI、Qwen 等所有兼容接口
}
Prompt 设计要点(保证结构化输出):
你是一个出行意图解析器。根据用户输入,输出结构化 JSON。
可用城市:北京、上海、广州、杭州、深圳
出行类型:general(通用)、family(亲子)、food(美食)、culture(文化)、tour(旅游)、leisure(休闲)
输出格式:
{
"city": "城市名",
"type": "出行类型",
"preferences": ["偏好1", "偏好2"],
"keywords": ["搜索关键词1", "关键词2"],
"from": "起点名(如无则为null)",
"to": "终点名(如无则为null)",
"transport": "driving/walking/bicycling/transit",
"confidence": 0.95
}
3.1.2 本地规则引擎(降级,智能版)
当用户没有配置 LLM API 时,自动降级到扩展后的本地语义映射表:
// 扩展后的语义映射(覆盖 7 种类型 × 3-6 个同义词)
const typeMap = [
{ patterns: ['亲子', '孩子', '小孩', '宝宝', '儿童', '带娃'], type: 'family', prefs: ['亲子友好', '步行距离短', '有室内备选'] },
{ patterns: ['美食', '吃', '餐厅', '小吃', '好吃', '餐饮', '馆子'], type: 'food', prefs: ['特色餐饮', '口碑好'] },
{ patterns: ['一日游', '游玩', '旅游', '旅行', '行程', '打卡'], type: 'tour', prefs: ['景点丰富', '路线合理'] },
{ patterns: ['轻松', '不太累', '休闲', '休闲游', '放松', '佛系', '不赶'], type: 'leisure', prefs: ['轻松休闲', '避免爬山'] },
{ patterns: ['文化', '博物馆', '艺术', '历史', '古迹', '展览'], type: 'culture', prefs: ['文化体验'] },
{ patterns: ['购物', '逛街', '商场', '买东西'], type: 'shopping', prefs: ['购物便利'] },
{ patterns: ['老人', '爸妈', '长辈', '老年', '父母'], type: 'general', prefs: ['轻松休闲', '步行距离短', '避免爬山'] },
];
关键升级:还支持「从 X 到 Y」的起点终点识别正则,以及城市模糊匹配(「魔都」→ 上海,「帝都」→ 北京)。
3.2 真实道路渲染:Polyline 解码 + 逐段规划
与 workbuddy 对话过程如下图。
升级前:用直线连接各景点,视觉效果差,用户不信任。
升级后:调用 direction API 获取每段路线的真实道路 polyline,解码后渲染。
3.2.1 前向差分解码(腾讯地图独有格式)
WebService API 返回的 polyline 是前向差分压缩数组:
- 前两个元素为绝对坐标(纬度、经度,需 ÷10⁶)
- 后续每两个元素为整数差值(同样需 ÷10⁶)
// Polyline 前向差分解码(腾讯地图 WebService API 特有格式)
function decodePolyline(encoded) {
if (!encoded || encoded.length < 2) return [];
const points = [];
let lat = encoded[0] / 1e6; // 第一个点:绝对纬度
let lng = encoded[1] / 1e6; // 第一个点:绝对经度
points.push({ lat, lng });
// 后续点:整数差值累加
for (let i = 2; i < encoded.length; i += 2) {
lat += encoded[i] / 1e6;
lng += encoded[i + 1] / 1e6;
points.push({ lat, lng });
}
return points;
}
3.2.2 逐段调用 direction API 渲染真实道路
// 逐段规划,获取每段真实道路
async function fetchAndRenderRoute(stops) {
const allPaths = [];
for (let i = 0; i < stops.length - 1; i++) {
const from = stops[i];
const to = stops[i + 1];
const route = await TMAP_WS.direction(from, to, currentMode);
if (route && route.decodedPath && route.decodedPath.length > 0) {
allPaths.push(...route.decodedPath); // 真实道路坐标
} else {
// 降级:直线连接
allPaths.push({ lat: from.lat, lng: from.lng });
allPaths.push({ lat: to.lat, lng: to.lng });
}
}
// 用 MultiPolyline 渲染真实道路
polylineLayer = new TMap.MultiPolyline({
map,
styles: {
route: new TMap.PolylineStyle({
color: '#3777FF', width: 6,
borderWidth: 2, borderColor: '#1A3A80',
lineCap: 'round'
})
},
geometries: [{
id: 'route_real',
styleId: 'route',
paths: allPaths.map(p => new TMap.LatLng(p.lat, p.lng))
}]
});
}
3.3 距离矩阵编排:从 popularity 排序到 AI 贪心排序
升级前:按 popularity 字段排序,忽略景点间实际距离,导致路线绕远。
升级后:先调用距离矩阵 API 批量计算所有景点间的距离/时间,再用贪心最近邻算法排序。
// 调用距离矩阵 API(一次请求,N×N 矩阵)
const distResult = await TMAP_WS.distanceMatrix(selected, selected, currentMode);
if (distResult) {
distances = distResult.map(row =>
row.elements.map(el => ({
distance: el.distance, // 米
duration: el.duration // 秒
}))
);
}
// 贪心最近邻排序(AI 决策层)
const visited = [0];
const unvisited = new Set(Array.from({ length: n }, (_, i) => i).slice(1));
while (unvisited.size > 0) {
const last = visited[visited.length - 1];
let nearest = -1, nearestDur = Infinity;
for (const idx of unvisited) {
const row = distances[last]?.[idx];
if (row && row.duration < nearestDur) {
nearestDur = row.duration;
nearest = idx;
}
}
if (nearest >= 0) {
visited.push(nearest);
unvisited.delete(nearest);
} else break;
}
itinerary.stops = visited.map(i => itinerary.stops[i]);

效果:用户说「帮我规划北京亲子游」,系统会自动把距离近的景点排在相邻位置,减少绕路。
3.4 IP 自动定位
与 workbuddy 对话过程如下图。
升级前:TMAP_WS.locateByIP() 函数已定义但未调用,每次都要手动选城市。
升级后:地图初始化时自动调用 IP 定位,识别用户所在城市并居中显示。
// initMap() 中新增:IP 定位自动获取用户城市
async function initMap() {
// Step1: 获取体验 Key(动态从腾讯地图官网抓取)
const resp = await fetch('https://lbs.qq.com/webApi/uriV1/uriGuide/uriMobileMarker');
const html = await resp.text();
const match = html.match(/referer=([a-zA-Z0-9_-]+)/);
mapKey = match ? match[1] : 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77';
// Step2: 加载 JSAPI GL SDK(+ visualization 库)
await loadJSAPI(mapKey);
// Step3: 创建地图实例
map = new TMap.Map('mapContainer', {
center: new TMap.LatLng(39.9042, 116.4074),
zoom: 11
});
// Step4: ⭐ IP 定位(升级新增)
try {
const loc = await TMAP_WS.locateByIP();
if (loc && loc.ad_info && CITY_DATA[loc.ad_info.city]) {
map.setCenter(new TMap.LatLng(
CITY_DATA[loc.ad_info.city].center.lat,
CITY_DATA[loc.ad_info.city].center.lng
));
showToast(`已定位到:${loc.ad_info.city}`, 'success');
}
} catch { /* IP 定位失败不影响使用 */ }
}

3.5 搜索框自动补全
升级前:搜索框只有纯文本输入,无提示,用户体验差。
升级后:接入腾讯地图 suggestion API,输入 2 个字符即触发自动补全,300ms 防抖,点击即填入。
// 搜索框自动补全(对接 suggestion API)
async function handleSuggestion(keyword) {
if (!keyword || keyword.length < 2) {
document.getElementById('suggestionList').classList.remove('show');
return;
}
try {
const results = await TMAP_WS.suggestion(keyword);
const list = document.getElementById('suggestionList');
if (results.length === 0) { list.classList.remove('show'); return; }
// 渲染下拉列表(最多 6 条)
list.innerHTML = results.slice(0, 6).map(r => `
<div class="suggestion-item" data-title="${r.title}">
<div class="sug-title">${r.title}</div>
<div class="sug-addr">${r.address || ''}</div>
</div>
`).join('');
list.classList.add('show');
// 点击补全项,自动填入输入框
list.querySelectorAll('.suggestion-item').forEach(item => {
item.addEventListener('click', () => {
document.getElementById('userInput').value = item.dataset.title;
list.classList.remove('show');
});
});
} catch { /* 自动补全失败不影响体验 */ }
}
// 输入框绑定(300ms 防抖)
input.addEventListener('input', () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => handleSuggestion(input.value.trim()), 300);
});

四、Demo 实战:升级后的完整功能清单

4.1 核心功能
升级后的 Demo 实现了以下功能:
| 功能模块 | 实现方式 | 升级点 |
|---|---|---|
| 自然语言输入 | 日常语言描述出行需求 | 支持更丰富的语义变体 |
| 智能意图解析 | LLM API + 本地双引擎 | 🔥 新增 LLM 支持,融合度 90%+ |
| POI 智能推荐 | 基于偏好搜索景点/餐厅 | 支持 7 种类型识别 |
| 多模式路线规划 | 驾车/步行/骑行/公交 | 支持 4 种出行方式切换 |
| 真实道路渲染 | Polyline 解码 + 逐段 direction | 🔥 升级核心,不再画直线 |
| 距离矩阵编排 | 贪心最近邻算法优化顺序 | 🔥 升级核心,路线更紧凑 |
| IP 自动定位 | locateByIP API | 🔥 新增,自动识别用户城市 |
| 搜索自动补全 | suggestion API + 防抖下拉 | 🔥 新增,输入体验提升 |
| 可视化展示 | 热力图 + 路线图 + 标记点 | InfoWindow 点击弹出详情 |
| 智能建议输出 | AI 综合分析给出最优方案 | 支持 LLM 生成个性化建议 |

4.2 关键技术决策
| 决策 | 选择 | 原因 |
|---|---|---|
| 前端框架 | 原生 HTML + CSS + JS | 征文 Demo 尽量轻量,零依赖,单文件可运行 |
| 地图渲染 | 腾讯地图 JSAPI GL | 3D 可视化效果最佳,支持 MultiMarker/Polyline/Heat |
| 数据服务 | 腾讯地图 WebService API | 功能全面,覆盖搜索/路线/编码/矩阵 |
| 跨域方案 | JSONP | 体验模式下 h5gw.map.qq.com 不支持 CORS |
| AI 集成 | 双引擎(LLM + 本地降级) | Demo 可独立运行,有 Key 时自动升级 |
4.3 微信小程序端延伸
基于 tencentmap-miniprogram-skill,同样的能力可以快速迁移到微信小程序:
<!-- 小程序地图组件 -->
<map
id="travelMap"
longitude="{{center.lng}}"
latitude="{{center.lat}}"
scale="12"
markers="{{markers}}"
polyline="{{polylines}}"
show-location
bindmarkertap="onMarkerTap"
/>
// 小程序端路线规划(使用 QQMapWX SDK)
const qqmapsdk = new QQMapWX({ key: 'YOUR_KEY' });
qqmapsdk.direction({
mode: 'driving',
from: { latitude: 39.98, longitude: 116.30 },
to: { latitude: 40.01, longitude: 116.40 },
success: (res) => {
const coords = res.result.routes[0].polyline.map(p => ({
latitude: p.lat, longitude: p.lng
}));
this.setData({
polylines: [{ points: coords, color: '#3777FF', width: 6 }]
});
}
});
五、踩坑记录与最佳实践
5.1 坐标系陷阱
腾讯地图使用 GCJ-02(国测局坐标系),GPS 原始坐标需要通过坐标转换接口处理:
// ⚠️ GPS坐标必须先转换
async function convertGPS(lat, lng) {
const res = await jsonpRequest(
'https://h5gw.map.qq.com/ws/coord/v1/translate',
{
locations: `${lat},${lng}`,
type: 1, // GPS坐标
key: 'none',
apptag: 'lbscoord_translate'
}
);
return res.result.locations[0]; // 转换后的GCJ-02坐标
}
5.2 路线 Polyline 解码
这是腾讯地图 WebService API 的一个特有格式,官方文档描述不够清晰,这里详细说明:
编码规则(前向差分):
polyline[0] = 纬度绝对值 × 10⁶(浮点数)
polyline[1] = 经度绝对值 × 10⁶(浮点数)
polyline[2] = 纬度差值₁ × 10⁶(整数)
polyline[3] = 经度差值₁ × 10⁶(整数)
...
解码步骤:
1. 第一个点:lat = polyline[0] / 1e6, lng = polyline[1] / 1e6
2. 后续每个点:lat += polyline[i] / 1e6, lng += polyline[i+1] / 1e6
常见错误:把 polyline 当成 [lat, lng, lat, lng, ...] 的简单交替数组,导致坐标完全错误。
5.3 体验模式注意事项
使用腾讯位置服务体验 Key 开发时,需注意:
- 所有后端服务调用走
h5gw.map.qq.com+ JSONP(不支持 CORS) - 地图前端加载走
map.qq.com/api/gljs(需带key参数) - 天气查询和电动车路线不可用,需正式 Key
- 频次受限,开发测试够用,上线前务必切换正式 Key
- 动态获取体验 Key:可从
https://lbs.qq.com/webApi/uriV1/uriGuide/uriMobileMarker页面动态抓取,避免硬编码失效
5.4 LLM 接口适配技巧
腾讯混元、DeepSeek、OpenAI 的 API 格式略有差异,统一用 OpenAI 兼容格式封装:
// 统一调用入口(支持所有 OpenAI 兼容接口)
async callLLM(messages) {
const res = await fetch(this.config.apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiKey}`
},
body: JSON.stringify({
model: this.config.model,
messages,
temperature: 0.3,
response_format: { type: 'json_object' } // 强制 JSON 输出
})
});
const data = await res.json();
return JSON.parse(data.choices?.[0]?.message?.content);
}
关键:response_format: { type: 'json_object' } 是 OpenAI/DeriveSeek 都支持的参数,可以保证 LLM 输出合法 JSON,避免解析失败。
六、性能优化建议
6.1 结合 AI 分析出的建议
- 合并请求:用距离矩阵替代循环调用路线接口,N×M 次请求降为 1 次
- 数据缓存:POI 搜索结果和地理编码结果按城市+关键词缓存到
localStorage - 按需渲染:JSAPI GL 可视化图层在缩放到一定层级时才加载
- 节流防抖:搜索输入提示使用 300ms 防抖,避免频繁请求
- 点聚合:标记点超过 100 个时启用点聚合(
TMap.MarkerCluster),提升渲染性能 - Polyline 精简:长距离路线用 Douglas-Peucker 算法简化 polyline,减少渲染节点
6.2 关于 Demo 的一些改动
- 地图层级的调整,遮挡了地图上面的按钮交互操作
- 调整按钮的位置布局,避免这点地图默认弹窗的显示位置以及地图默认的操作按钮

七、AI 融合度评估:升级前后对比
| 维度 | 升级前 | 升级后 | 提升 |
|---|---|---|---|
| 意图解析准确率 | ~40%(简单 includes) | ~90%(LLM + 扩展规则) | +50% |
| WebService API 利用率 | 85%(缺距离矩阵/IP定位/suggestion) | 100%(8 个 API 全接入) | +15% |
| JSAPI GL 利用率 | 75%(缺 InfoWindow/热力图) | 95%(全部核心功能) | +20% |
| 路线真实性 | 直线连接(差) | 真实道路 polyline(好) | 质的提升 |
| 用户体验 | 无搜索补全、无自动定位 | 有补全、有定位 | 明显提升 |

八、总结与展望
8.1 我们做了什么
- 用自然语言替代了传统的「选起点、选终点、选出行方式」三步操作
- 用 AI 双引擎(LLM + 本地规则)实现了高融合度的意图理解
- 用 腾讯地图 WebService API 全链路(geocoder + search + direction + distance matrix + suggestion + IP 定位)实现数据获取
- 用 JSAPI GL 可视化让结果不再是一堆文字,而是直观的地图交互
- 用真实道路 polyline 解码让路线规划可信、可用
与 workbuddy 对话过程如下图。
8.2 地图的未来
当 AI 和地图深度融合,地图就不再是一个「你查我画」的工具,而是一个能理解你需求、主动给出建议的出行伙伴。这只是开始,未来还可以:
- 实时路况 AI 调整:根据实时路况自动优化行程顺序
- 多 Agent 协同规划:多个 AI Agent 分工负责不同维度的推荐
- 个性化记忆:记住你的偏好,下次直接推荐符合口味的方案
- AR 导航:结合小程序相机能力实现实景导航
地图的未来,是 AI 的大脑 + 地图的眼睛。
附录:快速运行 Demo
A.1 无需任何配置,直接运行
升级后的 Demo 是单文件 HTML,直接双击即可在浏览器中打开运行(使用体验 Key)。
A.2 接入真实 LLM(可选)
取消 AI_ENGINE.config 中的注释,填入你的 API Key:
AI_ENGINE.config = {
mode: 'llm',
apiUrl: 'https://api.deepseek.com/v1/chat/completions', // 或混元接口
apiKey: 'YOUR_API_KEY',
model: 'deepseek-chat'
};
A.3 切换正式 Key(上线前必做)
将代码中的 key: 'none'(体验模式)替换为你的正式 Key,并去掉 apptag 参数。
📌 Demo 在线体验:智能出行规划助手(仅限本地、局域网预览,未部署)
🎯代码资源已上传置文章顶部,可以直接下载运行
📌 腾讯位置服务官网:https://lbs.qq.com
📌 涉及的 Skill:
tencentmap-jsapi-gl-skill:JSAPI GL 地图初始化、MultiMarker、MultiPolyline、热力图、InfoWindowtencentmap-webservice-skill:geocoder、search、direction、distance matrix、suggestion、IP 定位、坐标转换tencentmap-miniprogram-skill:微信小程序地图组件迁移方案
与 workbuddy 对话过程如下图。
💡 一句话总结:让地图「听懂」你说话,不是魔法,是 AI 意图解析 + 腾讯地图 WebService 全链路 API + JSAPI GL 可视化 的组合拳。技术不复杂,效果很惊艳——这就是 AI + 地图该有的样子。升级的核心在于:不再模拟,直接调用;不再直线,贴真实道路;不再写死,让 AI 决策。# 【腾讯位置服务开发者征文大赛】让地图听懂人话:AI + 腾讯地图 API 打造智能出行规划助手(升级版)

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)