作者: 怪兽不爱打野 发布时间: 已于 2026-04-15 14:45:30 修改
来源: https://blog.csdn.net/2401_84624302/article/details/160181556
多人出行场景(朋友聚会、团队建设、家庭出游)中,如何快速找到一个对所有人出行成本最低的汇合点,是一个典型的多目标空间决策问题。传统做法是:每个人打开地图App查看彼此位置,手动估算中心点,再逐一搜索周边餐厅或场所。当参与人数≥3人时,这个过程的复杂度呈指数上升。
本文利用腾讯位置服务的WebService API(地址解析、周边搜索、距离矩阵)和JavaScript API GL(地图可视化),结合大模型的Tool Calling能力,构建了一个AI Agent驱动的多人汇合点智能规划工具。用户只需用自然语言输入“我和三个朋友分别从A、B、C、D出发,想找个中间位置吃川菜”,Agent自动完成地址转坐标、候选点筛选、通行成本计算,并在地图上标注出发点和推荐汇合点,同时给出每个人的预估距离和时间。
系统采用纯前端实现,无需后端服务。架构分为三层:
工作流程:

Agent拥有三个核心工具,每个工具对应一个腾讯位置服务API:
Agent的决策逻辑:用户输入 → 解析出地址列表 → 调用getCoordinates批量转换 → 计算几何中心 → 调用searchNearbyPOI获取候选汇合点 → 调用getRouteMatrix计算每个人到各候选点的成本 → 选择总成本最低的点作为推荐结果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>多人汇合点智能规划助手</title>
<style>
#map-container { width: 100%; height: 500px; }
#chat-input { width: 80%; padding: 10px; margin: 10px; }
#send-btn { padding: 10px 20px; }
</style>
</head>
<body>
<div id="map-container"></div>
<input type="text" id="chat-input" placeholder="例如:我和朋友分别从望京、国贸、中关村出发,找个中间的火锅店" />
<button id="send-btn">发送</button>
<div id="result"></div>
<script src="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_API_KEY"></script>
<script>
// 初始化地图
let map = new TMap.Map('map-container', {
center: new TMap.LatLng(39.908823, 116.397470),
zoom: 12
});
// 启用服务类(用于后续WebService调用)
const service = new TMap.service.Service({
key: 'YOUR_API_KEY'
});
</script>
</body>
</html>

async function getCoordinates(address) {
const url = `https://apis.map.qq.com/ws/geocoder/v1/?address=${encodeURIComponent(address)}&key=YOUR_API_KEY`;
const response = await fetch(url);
const data = await response.json();
if (data.status === 0) {
return {
lat: data.result.location.lat,
lng: data.result.location.lng,
formattedAddress: data.result.address
};
} else {
throw new Error(`地址解析失败: ${data.message}`);
}
}

async function searchNearbyPOI(lat, lng, keyword, radius = 2000) {
const url = `https://apis.map.qq.com/ws/place/v1/search?boundary=nearby(${lat},${lng},${radius})&keyword=${encodeURIComponent(keyword)}&page_size=20&key=YOUR_API_KEY`;
const response = await fetch(url);
const data = await response.json();
if (data.status === 0) {
return data.data.map(poi => ({
id: poi.id,
title: poi.title,
lat: poi.location.lat,
lng: poi.location.lng,
distance: poi._distance,
address: poi.address
}));
} else {
throw new Error(`POI搜索失败: ${data.message}`);
}
}

async function getRouteMatrix(origins, destinations) {
// origins/destinations: 数组,元素为 "lat,lng" 字符串
const fromStr = origins.join(';');
const toStr = destinations.join(';');
const url = `https://apis.map.qq.com/ws/distance/v1/matrix?from=${fromStr}&to=${toStr}&mode=driving&key=YOUR_API_KEY`;
const response = await fetch(url);
const data = await response.json();
if (data.status === 0) {
// 解析距离矩阵
return data.result.rows.map((row, idx) => ({
originIndex: idx,
elements: row.elements.map(elem => ({
distance: elem.distance,
duration: elem.duration
}))
}));
} else {
throw new Error(`距离矩阵计算失败: ${data.message}`);
}
}

得到距离矩阵后,计算每个候选POI的总通行成本(所有起点到该POI的距离之和),选择最小的:
function selectBestMeetingPoint(peopleCoordinates, candidatePOIs, distanceMatrix) {
// distanceMatrix: 从 getRouteMatrix 返回的原始数据
// 结构:distanceMatrix[起点索引].elements[候选点索引].distance
let bestPOI = null;
let minTotalDistance = Infinity;
for (let poiIdx = 0; poiIdx < candidatePOIs.length; poiIdx++) {
let totalDistance = 0;
for (let personIdx = 0; personIdx < peopleCoordinates.length; personIdx++) {
totalDistance += distanceMatrix[personIdx].elements[poiIdx].distance;
}
if (totalDistance < minTotalDistance) {
minTotalDistance = totalDistance;
bestPOI = candidatePOIs[poiIdx];
}
}
return { bestPOI, totalDistance: minTotalDistance };
}
以大模型Claude为例,定义工具schema并发起调用:
const tools = [
{
name: "getCoordinates",
description: "将地点名称转换为经纬度坐标",
input_schema: {
type: "object",
properties: {
address: { type: "string", description: "地点名称,如'望京'" }
},
required: ["address"]
}
},
{
name: "searchNearbyPOI",
description: "在指定坐标周边搜索餐厅、咖啡厅等POI",
input_schema: {
type: "object",
properties: {
lat: { type: "number" },
lng: { type: "number" },
keyword: { type: "string" },
radius: { type: "number", default: 2000 }
},
required: ["lat", "lng", "keyword"]
}
},
{
name: "getRouteMatrix",
description: "计算多个起点到多个终点的驾车距离和时间",
input_schema: {
type: "object",
properties: {
origins: { type: "array", items: { type: "string" } },
destinations: { type: "array", items: { type: "string" } }
},
required: ["origins", "destinations"]
}
}
];
async function callAgent(userMessage) {
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'YOUR_CLAUDE_API_KEY',
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 4096,
tools: tools,
messages: [{ role: 'user', content: userMessage }]
})
});
const data = await response.json();
// 处理 tool_use 并执行对应函数,结果再次发给模型...(循环逻辑)
return data;
}
实际应用中需要实现多轮对话循环:模型返回tool_use时,前端执行函数,将结果以tool_result形式再发送给模型,直到模型返回最终文本答案。
将选中的出发点和推荐汇合点绘制在地图上:
function renderOnMap(peopleCoords, meetingPoint) {
// 清除已有图层
if (window.markerLayer) window.markerLayer.setMap(null);
if (window.polylineLayer) window.polylineLayer.setMap(null);
// 多点标记
const markers = [];
peopleCoords.forEach((coord, i) => {
markers.push({
id: `person-${i}`,
styleId: 'person',
position: new TMap.LatLng(coord.lat, coord.lng),
properties: { title: `出发点${i+1}` }
});
});
markers.push({
id: 'meeting',
styleId: 'meeting',
position: new TMap.LatLng(meetingPoint.lat, meetingPoint.lng),
properties: { title: meetingPoint.title }
});
const markerLayer = new TMap.MultiMarker({
map: map,
styles: {
'person': new TMap.MarkerStyle({ width: 30, height: 30, src: 'red.png' }),
'meeting': new TMap.MarkerStyle({ width: 40, height: 40, src: 'blue.png' })
},
geometries: markers
});
// 绘制路线(直线示意,实际可调用路线规划API)
const polylines = peopleCoords.map((coord, i) => ({
id: `route-${i}`,
styleId: 'route',
paths: [
new TMap.LatLng(coord.lat, coord.lng),
new TMap.LatLng(meetingPoint.lat, meetingPoint.lng)
]
}));
const polylineLayer = new TMap.MultiPolyline({
map: map,
styles: { 'route': new TMap.PolylineStyle({ color: '#3777ff', width: 4 }) },
geometries: polylines
});
window.markerLayer = markerLayer;
window.polylineLayer = polylineLayer;
// 自动调整视野
const bounds = new TMap.LatLngBounds();
[...peopleCoords, meetingPoint].forEach(p => bounds.extend(new TMap.LatLng(p.lat, p.lng)));
map.fitBounds(bounds, { padding: 80 });
}
输入示例:
“我和三个朋友分别从北京望京、国贸、中关村、亦庄出发,想找个中间位置的川菜馆,人均100左右。”
Agent处理过程(控制台输出):
输出结果:

推荐汇合点:付小姐在成都(五道口店)望京出发:约14公里,驾车35分钟国贸出发:约12公里,驾车30分钟中关村出发:约6公里,驾车15分钟亦庄出发:约23公里,驾车50分钟地图上已用红色标记各位出发点,蓝色标记推荐餐厅,蓝色线条为示意路线。
本文展示了一个将腾讯位置服务核心API与大模型Tool Calling能力深度结合的实战案例。通过封装地址解析、周边搜索、距离矩阵为AI可调用的工具,实现了自然语言驱动的多人汇合点智能规划。整个系统无需后端,纯前端即可运行,代码完全开源,开发者可在此基础上快速扩展更多场景(如物流配送点优化、多车路径规划等)。
参考资料:
在数字化浪潮下,企业及开发者对地图制作平台的需求已从基础底图绘制转向智能化个性化与高效化。腾讯位置服
作者: 五年小兵勇闯互联网 发布时间: 已于20260417 11:15:55修改 来源: ht
在物流调度外勤管理公共安全智慧零售等场景中,定位人员的能力不仅是基础技术支撑,更是提升效率保障安全的