1. 项目概述:AI与地图融合的智能行程规划应用
在当今数字化出行时代,如何将地图服务与人工智能技术结合,打造更智能的行程规划工具,成为开发者关注的热点。腾讯地图Map Skills平台为开发者提供了强大的地图API和AI集成能力,让我们能够构建理解自然语言、自动规划路线的智能应用。
这个项目将带您从零开始,使用Vue3和TypeScript技术栈,结合腾讯地图API和AI Agent架构,开发一个能理解用户需求、自动推荐路线和景点的智能行程规划系统。不同于传统地图应用,我们的系统将通过MCP协议实现自然语言交互,让用户用日常对话的方式获取个性化行程建议。
提示:本教程假设您已具备基础的JavaScript和Vue开发经验,但即使您是地图开发新手,也能通过详细的步骤说明快速上手。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与腾讯地图配置
2.1 腾讯地图开发者账号注册
首先需要访问腾讯位置服务官网完成开发者注册:
- 打开腾讯位置服务官网(https://lbs.qq.com)
- 点击右上角"控制台"按钮
- 选择"立即注册",使用邮箱或手机号创建账号
- 完成实名认证(个人开发者选择个人认证,企业用户选择企业认证)
实名认证通常需要1-2小时审核时间,建议提前完成。认证通过后,您将获得更高的API调用配额和更完整的功能权限。
2.2 创建应用与获取API Key
在控制台创建您的第一个地图应用:
- 进入"应用管理"页面
- 点击"创建应用"按钮
- 填写应用信息:
- 应用名称:智能行程助手
- 应用类型:Web端(JS API)
- 启用服务:全部勾选
- 域名白名单:添加localhost和127.0.0.1用于开发测试
- 创建成功后,复制保存您的API Key
重要:API Key是调用地图服务的凭证,请妥善保管不要泄露。如果意外泄露,应立即在控制台重置。
2.3 本地开发环境搭建
推荐使用以下开发环境配置:
- Node.js v18+
- pnpm 8.x(比npm/yarn更快的包管理器)
- VS Code编辑器
- Vue Language Features (Volar)插件
创建项目目录并初始化:
bash复制# 创建Vue3项目
pnpm create vite@latest smart-trip --template vue-ts
# 进入项目目录
cd smart-trip
# 安装基础依赖
pnpm install @tencentmap/jsapi-gl axios pinia vue-router element-plus
3. 基础地图集成与展示
3.1 地图容器组件实现
创建src/components/TMap.vue文件,实现基础地图展示:
vue复制<template>
<div class="map-container">
<div ref="mapEl" class="map-element"></div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { TMap } from '@tencentmap/jsapi-gl'
const props = defineProps<{
center?: [number, number]
zoom?: number
}>()
const mapEl = ref<HTMLElement | null>(null)
let mapInstance: TMap | null = null
onMounted(() => {
if (!mapEl.value) return
// 初始化地图实例
mapInstance = new TMap.Map(mapEl.value, {
center: new TMap.LatLng(...(props.center || [39.9042, 116.4074])),
zoom: props.zoom || 11,
viewMode: '2D',
baseMap: {
type: 'vector'
}
})
// 添加默认控件
mapInstance.addControl(new TMap.Control.Zoom())
mapInstance.addControl(new TMap.Control.Scale())
})
</script>
<style scoped>
.map-container {
width: 100%;
height: 100%;
}
.map-element {
width: 100%;
height: 100%;
min-height: 500px;
}
</style>
3.2 环境变量配置
在项目根目录创建.env文件配置API Key:
ini复制VITE_TENCENT_MAP_KEY=您的实际API_KEY
VITE_APP_TITLE=智能行程助手
在vite.config.ts中配置环境变量:
typescript复制import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
plugins: [vue()],
define: {
__APP_ENV__: env
}
}
})
3.3 地图服务封装
创建src/services/map.service.ts封装常用地图操作:
typescript复制import { TMap } from '@tencentmap/jsapi-gl'
export class MapService {
private static instance: MapService
private map: TMap.Map | null = null
private constructor() {}
public static getInstance(): MapService {
if (!MapService.instance) {
MapService.instance = new MapService()
}
return MapService.instance
}
public initMap(container: HTMLElement, options: any): void {
this.map = new TMap.Map(container, options)
}
public addMarker(position: { lat: number; lng: number }, title: string): void {
if (!this.map) return
new TMap.Marker({
map: this.map,
position: new TMap.LatLng(position.lat, position.lng),
title
})
}
// 其他地图操作方法...
}
4. AI Agent架构设计与实现
4.1 MCP协议基础
MCP(Message Control Protocol)是腾讯地图提供的一套用于智能交互的协议规范,它定义了AI Agent与地图服务之间的通信格式。一个典型的MCP交互流程包括:
- 用户输入自然语言请求
- NLU模块解析意图和实体
- 根据意图选择对应的Tool
- 通过MCP协议调用地图API
- 处理结果并生成自然语言回复
4.2 核心模块划分
我们的AI Agent将分为以下几个核心模块:
| 模块名称 | 职责描述 | 关键技术 |
|---|---|---|
| NLU模块 | 自然语言理解,解析用户意图 | 规则引擎/小型NLP模型 |
| Planning Agent | 行程规划逻辑处理 | 决策树/强化学习 |
| Search Agent | POI搜索与推荐 | 协同过滤/内容推荐 |
| Route Agent | 路径规划与交通建议 | 图算法/实时交通数据 |
| MCP Client | 与腾讯地图API通信 | RESTful API/WebSocket |
4.3 NLU模块实现
创建src/agents/nlu.agent.ts实现基础的自然语言理解:
typescript复制interface Intent {
type: 'trip_plan' | 'poi_search' | 'route_query' | 'unknown'
entities: Record<string, any>
confidence: number
}
export class NLUAgent {
private static instance: NLUAgent
private constructor() {}
public static getInstance(): NLUAgent {
if (!NLUAgent.instance) {
NLUAgent.instance = new NLUAgent()
}
return NLUAgent.instance
}
public parse(input: string): Intent {
// 简单规则匹配
const lowerInput = input.toLowerCase()
if (/规划|行程|安排/.test(lowerInput)) {
return this.parseTripPlan(input)
}
if (/附近|推荐|哪里有|去哪找/.test(lowerInput)) {
return this.parsePOISearch(input)
}
if (/怎么去|路线|导航/.test(lowerInput)) {
return this.parseRouteQuery(input)
}
return {
type: 'unknown',
entities: {},
confidence: 0
}
}
private parseTripPlan(input: string): Intent {
const durationMatch = input.match(/(\d+)\s*天/)
const locationMatch = input.match(/去(.+?)玩|到(.+?)旅行/)
return {
type: 'trip_plan',
entities: {
duration: durationMatch ? `${durationMatch[1]}天` : '3天',
location: locationMatch ? (locationMatch[1] || locationMatch[2]) : '北京',
preferences: this.extractPreferences(input)
},
confidence: 0.8
}
}
// 其他解析方法...
}
5. 行程规划核心逻辑实现
5.1 行程数据结构设计
定义行程规划的核心数据结构:
typescript复制interface TripDay {
date: string
morning: TripItem[]
afternoon: TripItem[]
evening: TripItem[]
}
interface TripItem {
id: string
name: string
type: 'attraction' | 'restaurant' | 'hotel' | 'transport'
location: {
lat: number
lng: number
}
startTime: string
endTime: string
duration: number // 分钟
description?: string
rating?: number
priceLevel?: number
}
interface TripPlan {
id: string
city: string
startDate: string
endDate: string
days: TripDay[]
preferences: {
budgetLevel: 'low' | 'medium' | 'high'
travelPace: 'relaxed' | 'moderate' | 'intensive'
interests: string[]
}
}
5.2 规划算法实现
创建src/agents/planning.agent.ts实现基础规划逻辑:
typescript复制export class PlanningAgent {
async generatePlan(intent: Intent): Promise<TripPlan> {
// 1. 获取城市基础信息
const cityInfo = await this.getCityInfo(intent.entities.location)
// 2. 根据用户偏好筛选POI
const attractions = await this.filterPOIs({
city: cityInfo.name,
type: 'attraction',
filters: {
interests: intent.entities.preferences
}
})
// 3. 智能分配景点到每天
const days = this.allocateToDays(
attractions,
intent.entities.duration,
intent.entities.preferences
)
// 4. 添加餐饮和交通建议
return this.enrichPlan(days, cityInfo)
}
private allocateToDays(pois: POI[], duration: string, preferences: any): TripDay[] {
const dayCount = parseInt(duration) || 3
const days: TripDay[] = []
// 简单实现:平均分配景点
const poisPerDay = Math.ceil(pois.length / dayCount)
for (let i = 0; i < dayCount; i++) {
const dayPois = pois.slice(i * poisPerDay, (i + 1) * poisPerDay)
days.push({
date: `Day ${i + 1}`,
morning: this.createTripItems(dayPois.slice(0, 2), 'morning'),
afternoon: this.createTripItems(dayPois.slice(2, 4), 'afternoon'),
evening: []
})
}
return days
}
// 其他辅助方法...
}
6. 前端交互实现
6.1 聊天界面组件
创建src/components/ChatInterface.vue实现用户交互界面:
vue复制<template>
<div class="chat-container">
<div class="chat-messages">
<div
v-for="(msg, index) in messages"
:key="index"
:class="['message', msg.role]"
>
<div class="message-content">
{{ msg.content }}
</div>
</div>
</div>
<div class="chat-input">
<el-input
v-model="inputMessage"
placeholder="请输入您的行程需求,例如:我想去北京玩3天,喜欢历史文化"
@keyup.enter="sendMessage"
>
<template #append>
<el-button @click="sendMessage">
<el-icon><Promotion /></el-icon>
</el-button>
</template>
</el-input>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Promotion } from '@element-plus/icons-vue'
interface ChatMessage {
role: 'user' | 'assistant'
content: string
}
const inputMessage = ref('')
const messages = ref<ChatMessage[]>([])
const sendMessage = async () => {
if (!inputMessage.value.trim()) return
// 添加用户消息
messages.value.push({
role: 'user',
content: inputMessage.value
})
const userInput = inputMessage.value
inputMessage.value = ''
// 添加加载中的助手消息
const assistantMessageIndex = messages.value.push({
role: 'assistant',
content: '正在为您规划行程...'
}) - 1
try {
// 调用AI Agent生成行程
const response = await generatePlan(userInput)
messages.value[assistantMessageIndex].content = formatResponse(response)
} catch (error) {
messages.value[assistantMessageIndex].content = '抱歉,行程规划失败,请稍后再试'
console.error('行程规划错误:', error)
}
}
</script>
6.2 行程可视化展示
创建src/components/TripVisualization.vue展示生成的行程:
vue复制<template>
<div class="trip-container">
<el-tabs v-model="activeDay" type="card">
<el-tab-pane
v-for="day in trip.days"
:key="day.date"
:label="day.date"
>
<div class="day-schedule">
<h3>{{ day.date }} 行程安排</h3>
<el-timeline>
<el-timeline-item
v-for="(item, index) in [
...day.morning,
...day.afternoon,
...day.evening
]"
:key="index"
:timestamp="item.startTime"
placement="top"
>
<el-card>
<h4>{{ item.name }}</h4>
<p>{{ item.description }}</p>
<div class="time-info">
{{ item.startTime }} - {{ item.endTime }}
(约{{ item.duration }}分钟)
</div>
<div class="location-info">
<el-button
type="primary"
size="small"
@click="showOnMap(item.location)"
>
在地图上查看
</el-button>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
7. 常见问题与优化建议
7.1 性能优化方案
-
地图加载优化:
- 使用矢量地图替代栅格地图
- 实现按需加载地图插件
- 对地图实例进行懒加载
-
AI响应加速:
typescript复制// 使用流式响应处理 async function* generatePlanStream(intent: Intent) { yield { status: 'started', message: '开始规划行程' } // 获取城市信息 const cityInfo = await getCityInfo(intent.entities.location) yield { status: 'progress', message: `已获取${cityInfo.name}信息` } // 并行获取各类POI const [attractions, restaurants] = await Promise.all([ getPOIs({ type: 'attraction', city: cityInfo.name }), getPOIs({ type: 'restaurant', city: cityInfo.name }) ]) yield { status: 'progress', message: `已加载${attractions.length}个景点` } // 继续其他处理... }
7.2 典型错误排查
-
地图显示空白:
- 检查容器元素是否有固定高度
- 确认API Key已正确配置且未过期
- 验证网络请求是否被浏览器插件拦截
-
POI搜索无结果:
javascript复制// 调试搜索参数 console.log({ keyword: '餐厅', location: '39.9042,116.4074', radius: 1000, key: 'YOUR_KEY' }) // 直接测试API调用 fetch(`https://apis.map.qq.com/ws/place/v1/search?keyword=餐厅&boundary=nearby(39.9042,116.4074,1000)&key=YOUR_KEY`) .then(res => res.json()) .then(console.log) -
跨域问题解决:
javascript复制// vite.config.js export default defineConfig({ server: { proxy: { '/api': { target: 'https://apis.map.qq.com', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } } })
7.3 项目扩展方向
-
增强AI能力:
- 集成大型语言模型提升自然语言理解
- 实现多轮对话和上下文记忆
- 添加个性化推荐算法
-
丰富地图功能:
- 实时交通状况展示
- 3D建筑和室内地图
- 自定义地图样式
-
多平台支持:
- 开发微信小程序版本
- 实现PWA离线功能
- 构建Electron桌面应用
在实际开发中,我建议采用渐进式增强策略,先实现核心功能再逐步添加高级特性。对于时间有限的开发者,可以优先完善行程规划的核心算法和用户体验,确保基础功能稳定可靠。
