写在前面:腾讯位置服务开放了丰富的地图API,结合AI大模型的能力,可以开发出智能对话式地图应用。本文将手把手教你如何从零开发一个能够「听懂人话」的智能地图助手。



一、项目概述

1.1 我们要做什么?

🎯 项目目标:

开发一个智能对话式地图应用,用户可以用自然语言查询:

✅ "帮我找附近人少、有插座、好喝的咖啡馆"
✅ "从公司到机场最优路线推荐"
✅ "明天去深圳开会,帮我规划行程"

核心能力:
- 自然语言理解意图
- 调用腾讯地图API获取数据
- 智能推荐和展示结果

1.2 技术架构

🏗️ 系统架构:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  用户输入   │ ──▶ │  意图识别    │ ──▶ │  地图API   │
│ (自然语言)  │      │  (AI大模型)  │     │  (腾讯位置) │
└─────────────┘     └─────────────┘     └─────────────┘
                           │                   │
                           ▼                   ▼
                    ┌─────────────┐     ┌─────────────┐
                    │  参数提取   │     │  结果展示   │
                    │  (经纬度)   │     │  (地图呈现) │
                    └─────────────┘     └─────────────┘

二、准备工作

2.1 申请腾讯位置服务Key

# 1. 访问腾讯位置服务官网:https://lbs.qq.com/
# 2. 注册/登录腾讯账号
# 3. 控制台 → 我的Key → 创建新Key
# 4. 勾选 WebServiceAPI 和 JavaScriptAPI

2.2 安装依赖

# 创建项目
mkdir ai-map-app
cd ai-map-app
npm init -y

# 安装依赖
npm install express body-parser axios dotenv
npm install tencentcloud-sdk-nodejs

三、核心代码实现

3.1 后端服务 - 意图识别与API调用

// server.js
const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

// 腾讯地图API配置
const TENCENT_MAP_KEY = process.env.TENCENT_MAP_KEY;

// 意图识别prompt
const intentPrompt = `
你是一个地图助手,请分析用户的查询意图,并提取关键信息。

用户可能的意图:
1. search_poi - 搜索地点(如:附近的咖啡馆、医院)
2. route_plan - 路线规划(如:怎么去、从哪到哪)
3. nearby_search - 周边搜索(如:附近有什么)

请以JSON格式返回:
{
  "intent": "意图类型",
  "params": {
    "keyword": "搜索关键词",
    "location": "位置描述",
    "start": "起点",
    "end": "终点"
  }
}

用户查询:{{query}}
`;

// 意图识别与参数提取
async function recognizeIntent(query) {
    const prompt = intentPrompt.replace('{{query}}', query);
    
    // 这里调用AI大模型API(如DeepSeek、文心一言等)
    const response = await axios.post('https://api.deepseek.com/v1/chat/completions', {
        model: 'deepseek-chat',
        messages: [{role: 'user', content: prompt}],
        temperature: 0.3
    }, {
        headers: {'Authorization': `Bearer ${process.env.AI_API_KEY}`}
    });
    
    return JSON.parse(response.data.choices[0].message.content);
}

// 搜索POI
async function searchPOI(keyword, location) {
    let url = `https://apis.map.qq.com/ws/place/v1/search?`;
    url += `keyword=${encodeURIComponent(keyword)}`;
    url += `&key=${TENCENT_MAP_KEY}`;
    url += `&boundary=nearby(${location.lat},${location.lng},5000)`;
    url += `&output=json`;
    
    const response = await axios.get(url);
    return response.data;
}

// 路线规划
async function routePlanning(from, to, mode = 'driving') {
    let url = `https://apis.map.qq.com/ws/direction/v1/${mode}?`;
    url += `from=${encodeURIComponent(from)}`;
    url += `&to=${encodeURIComponent(to)}`;
    url += `&key=${TENCENT_MAP_KEY}`;
    
    const response = await axios.get(url);
    return response.data;
}

// 对话接口
app.post('/api/chat', async (req, res) => {
    try {
        const {message, userLocation} = req.body;
        
        // 1. 意图识别
        const intentResult = await recognizeIntent(message);
        
        // 2. 根据意图调用不同API
        let result;
        switch (intentResult.intent) {
            case 'search_poi':
                const searchLoc = userLocation || intentResult.params.location;
                result = await searchPOI(
                    intentResult.params.keyword,
                    {lat: 39.9042, lng: 116.4074} // 默认北京
                );
                break;
                
            case 'route_plan':
                result = await routePlanning(
                    intentResult.params.start || '我的位置',
                    intentResult.params.end
                );
                break;
                
            default:
                result = {status: 0, message: '理解了你的问题,请稍等...'};
        }
        
        res.json({
            success: true,
            intent: intentResult,
            data: result
        });
        
    } catch (error) {
        res.json({success: false, error: error.message});
    }
});

app.listen(3000, () => {
    console.log('🚀 AI地图助手服务已启动: http://localhost:3000');
});

3.2 前端页面 - 地图展示

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI智能地图助手</title>
    <!-- 引入腾讯地图JSAPI -->
    <script src="https://map.qq.com/api/js?v=2.exp&key=YOUR_TENCENT_MAP_KEY"></script>
    <style>
        * {margin: 0; padding: 0; box-sizing: border-box;}
        body {font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;}
        
        .container {display: flex; height: 100vh;}
        
        /* 左侧聊天区域 */
        .chat-panel {width: 400px; background: #f5f5f5; display: flex; flex-direction: column;}
        .chat-header {padding: 20px; background: #1890ff; color: white; font-size: 18px;}
        .chat-messages {flex: 1; padding: 20px; overflow-y: auto;}
        .message {margin-bottom: 15px; padding: 12px 16px; border-radius: 8px; max-width: 85%;}
        .message.user {background: #1890ff; color: white; margin-left: auto;}
        .message.ai {background: white; box-shadow: 0 2px 8px rgba(0,0,0,0.1);}
        
        .chat-input {padding: 20px; background: white; display: flex; gap: 10px;}
        .chat-input input {flex: 1; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;}
        .chat-input button {padding: 12px 24px; background: #1890ff; color: white; border: none; border-radius: 6px; cursor: pointer;}
        
        /* 右侧地图区域 */
        .map-panel {flex: 1; position: relative;}
        #map-container {width: 100%; height: 100%;}
        
        .poi-list {position: absolute; top: 20px; right: 20px; width: 300px; background: white; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.15); max-height: 500px; overflow-y: auto;}
        .poi-item {padding: 15px; border-bottom: 1px solid #f0f0f0; cursor: pointer;}
        .poi-item:hover {background: #f9f9f9;}
        .poi-name {font-weight: bold; margin-bottom: 5px; color: #333;}
        .poi-address {font-size: 12px; color: #666;}
    </style>
</head>
<body>
    <div class="container">
        <!-- 聊天面板 -->
        <div class="chat-panel">
            <div class="chat-header">🤖 AI智能地图助手</div>
            <div class="chat-messages" id="messages">
                <div class="message ai">你好!我是AI地图助手,用自然语言告诉我你想去哪里~</div>
            </div>
            <div class="chat-input">
                <input type="text" id="userInput" placeholder="例如:帮我找附近的咖啡馆" />
                <button onclick="sendMessage()">发送</button>
            </div>
        </div>
        
        <!-- 地图面板 -->
        <div class="map-panel">
            <div id="map-container"></div>
            <div class="poi-list" id="poiList" style="display: none;"></div>
        </div>
    </div>

    <script>
        // 初始化地图
        const center = new qq.maps.LatLng(39.9042, 116.4074); // 北京
        const map = new qq.maps.Map(document.getElementById('map-container'), {
            center: center,
            zoom: 14
        });
        
        // 用户位置
        let userLocation = null;
        
        // 获取用户位置
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(position => {
                userLocation = {
                    lat: position.coords.latitude,
                    lng: position.coords.longitude
                };
                map.setCenter(new qq.maps.LatLng(userLocation.lat, userLocation.lng));
            });
        }
        
        // 发送消息
        async function sendMessage() {
            const input = document.getElementById('userInput');
            const message = input.value.trim();
            if (!message) return;
            
            // 添加用户消息
            addMessage(message, 'user');
            input.value = '';
            
            // 添加AI等待消息
            addMessage('正在思考中...', 'ai');
            
            try {
                const response = await fetch('/api/chat', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({message, userLocation})
                });
                
                const result = await response.json();
                
                // 移除等待消息
                const messages = document.querySelectorAll('.message.ai');
                messages[messages.length - 1].remove();
                
                if (result.success) {
                    // 显示结果
                    showResult(result);
                } else {
                    addMessage('抱歉,出错了:' + result.error, 'ai');
                }
            } catch (error) {
                addMessage('网络错误,请重试', 'ai');
            }
        }
        
        // 添加消息
        function addMessage(text, type) {
            const div = document.createElement('div');
            div.className = `message ${type}`;
            div.textContent = text;
            document.getElementById('messages').appendChild(div);
            document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight;
        }
        
        // 显示结果
        function showResult(result) {
            const {intent, data} = result;
            
            if (intent.intent === 'search_poi' && data.data) {
                // 显示POI列表
                const poiList = document.getElementById('poiList');
                poiList.style.display = 'block';
                poiList.innerHTML = '<div style="padding:15px;font-weight:bold;">搜索结果</div>';
                
                const pois = data.data;
                pois.forEach((poi, index) => {
                    const div = document.createElement('div');
                    div.className = 'poi-item';
                    div.innerHTML = `
                        <div class="poi-name">${index + 1}. ${poi.title}</div>
                        <div class="poi-address">${poi.address}</div>
                    `;
                    div.onclick = () => {
                        const location = new qq.maps.LatLng(poi.location.lat, poi.location.lng);
                        map.setCenter(location);
                        map.setZoom(16);
                    };
                    poiList.appendChild(div);
                    
                    // 地图上添加标记
                    const marker = new qq.maps.Marker({
                        position: new qq.maps.LatLng(poi.location.lat, poi.location.lng),
                        map: map,
                        title: poi.title
                    });
                });
                
                addMessage(`找到 ${pois.length} 个结果,已在地图上标注`, 'ai');
            }
        }
        
        // 回车发送
        document.getElementById('userInput').addEventListener('keypress', e => {
            if (e.key === 'Enter') sendMessage();
        });
    </script>
</body>
</html>

四、效果演示

4.1 使用效果

💬 对话示例:

用户:帮我找附近人少、有插座、好喝的咖啡馆
AI:好的,正在搜索附近的咖啡馆...
结果:找到 5 家咖啡馆,已在地图上标注

用户:从公司到首都机场怎么走
AI:为您规划了驾车路线,全程约 45 分钟,35 公里

4.2 功能特点

✨ 项目特点:

1. 自然语言理解
   - 无需记住复杂指令
   - 像聊天一样使用地图

2. 智能意图识别
   - 自动识别搜索/导航/周边查询
   - 提取关键参数

3. 实时地图展示
   - 搜索结果直接在地图标注
   - 点击切换查看详情

4. 多轮对话支持
   - 记住上下文
   - 支持追问

五、进阶优化

5.1 添加更多AI能力

// 智能推荐 - 根据用户偏好推荐
async function smartRecommend(userId, location) {
    const prompt = `
用户ID: ${userId}
位置: ${location}
历史行为: [咖啡, 书店, 公园]

请推荐 3 个适合该用户的地点,解释推荐理由。
    `;
    // 调用AI生成推荐
}

// 行程规划 - 多目的地规划
async function planTrip(places, preferences) {
    const prompt = `
目的地: ${places.join(', ')}
偏好: ${preferences}
请规划最优路线和时间安排。
    `;
    // 调用AI规划行程
}

5.2 添加MCP协议支持

// MCP服务器示例
const mcpServer = {
    name: 'tencent-map',
    version: '1.0.0',
    tools: [
        {
            name: 'search_poi',
            description: '搜索地点POI',
            parameters: {
                type: 'object',
                properties: {
                    keyword: {type: 'string', description: '搜索关键词'},
                    location: {type: 'string', description: '位置'},
                    radius: {type: 'number', description: '搜索半径(米)'}
                },
                required: ['keyword']
            }
        },
        {
            name: 'route_plan',
            description: '路线规划',
            parameters: {
                type: 'object',
                properties: {
                    from: {type: 'string', description: '起点'},
                    to: {type: 'string', description: '终点'},
                    mode: {type: 'string', enum: ['driving', 'walking', 'bicycling']}
                },
                required: ['from', 'to']
            }
        }
    ]
};

六、完整代码获取

6.1 项目结构

ai-map-assistant/
├── server.js          # 后端服务
├── public/
│   └── index.html     # 前端页面
├── .env               # 环境变量
└── package.json       # 依赖配置

6.2 配置说明

# .env 文件
TENCENT_MAP_KEY=your_tencent_map_key
AI_API_KEY=your_ai_api_key

七、总结

📊 项目成果:

✅ 完成功能:
- 自然语言查询地点
- 智能路线规划
- 实时地图展示
- POI搜索与标注

🔧 技术栈:
- 前端:HTML + JS + 腾讯地图JSAPI
- 后端:Node.js + Express
- AI:DeepSeek/文心一言/通义千问
- 地图:腾讯位置服务API

💡 扩展方向:
- 添加MCP协议支持
- 多轮对话优化
- 个性化推荐
- 行程智能规划

作者:刘~浪地球
本文声明:原创不易,转载需授权!

Logo

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

更多推荐