AI 时代的交互变革,是从"我点点点"到"我说你办"——用腾讯位置服务打造一个真正懂你的地图向导。


一、从一个"吃饭难题"开始的思考

你有没有过这样的经历?

出差到一个陌生城市,忙完工作已经晚上七点多,饥肠辘辘。你打开地图 App 想找个附近的好馆子,然后开始了熟悉的操作流程:输入"餐厅" -> 点搜索 -> 划拉结果列表 -> 挨个点进去看评价 -> 对比评分 -> 看距离 -> 切回列表继续划拉 -> 终于选定一家 -> 再点"路线" -> 选"步行" -> 等路线加载。一顿操作下来,少说五六分钟,饿得前胸贴后背。

这不是个例。仔细想想,传统地图 App 的交互模式这么多年几乎没变过:

打开地图 -> 手动输入地址 -> 选关键词 -> 点搜索 -> 看结果列表 -> 点路线 -> 看路线详情

每一步都需要用户亲自动手。而在 AI 时代,交互的本质正在被重新定义——从图形用户界面的"我找"到对话式交互的"我问"。

如果有一款地图应用,我对它说一句"附近有什么好吃的,推荐一个",它就能告诉我附近有哪些馆子、评分如何、步行多远——那该多好?

这正是我做 “步步” 的初衷——一个融入了腾讯位置服务能力、NVIDIA LLM 和对话式 AI Agent 的智能地图向导。

你说"附近有什么好吃的",它帮你搜;你说"去故宫要走多久",它帮你算;你说"最近的充电站在哪",它帮你找。你只说一句话,剩下的交给步步。


二、架构设计:三步走战略

整个项目命名为 “行步行”

┌─────────────────────────────────────────────────┐
│              用户交互层 (Streamlit)               │
│  地图展示  │  搜索面板  │  路线规划  │  步步对话框 │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│              服务集成层 (Service Layer)            │
│  腾讯地图 API  │  NVIDIA LLM  │  网页搜索  │ 天气  │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│              智能决策层 (Agent Layer)              │
│  步步文本意图识别  │  上下文构建  │  记忆管理       │
└─────────────────────────────────────────────────┘

📸 项目主界面: 打开"行步行",左侧是腾讯地图实时渲染,右侧是搜索面板与步步对话框,布局清晰、功能一目了然。

在这里插入图片描述

(上图为项目启动后的主界面,左侧地图、右侧操作面板与对话框)

技术栈一览

层级 技术选型 职责
前端框架 Streamlit 快速构建 Web UI,适合数据应用
地图引擎 腾讯地图 JavaScript API v2 地图渲染、定位、路线展示
位置服务 腾讯位置服务 API POI 搜索、地理编码、逆编码、距离矩阵、路线规划
AI 引擎 NVIDIA NIM (Llama-Nemotron) 自然语言理解、对话生成、意图识别
工具链 Place Web Grounding (联网搜索) 地点联网信息检索、生成评价

三、腾讯地图能力封装:坚实的地基

整个项目的核心是对腾讯位置服务 API 的封装。所有 API 都通过一个统一的 TencentMapAPI 类管理,采用了域名白名单认证方式,让配置和使用变得极其简单。

核心 API 封装

class TencentMapAPI:
    def __init__(self, api_key: str = None):
        self.api_key = api_key or TENCENT_MAP_API_KEY
        self.base_url = "https://apis.map.qq.com"
        self.timeout = 10

    def _request(self, endpoint: str, params: Dict) -> Dict:
        params['key'] = self.api_key
        try:
            url = f"{self.base_url}{endpoint}"
            response = requests.get(url, params=params, timeout=self.timeout)
            return response.json()
        except requests.exceptions.RequestException as e:
            return {"status": -1, "message": str(e)}

五大核心接口

1. 附近搜索

def nearby_search(self, lat, lon, keyword='', radius=1000, page=1):
    endpoint = '/ws/place/v1/search'
    params = {
        'keyword': keyword,
        'boundary': f"nearby({lat},{lon},{radius})",
        'page_index': page, 'page_size': 10, 'orderby': '_distance'
    }
    return self._parse_poi_result(self._request(endpoint, params))

2. 地理编码 / 逆编码

def geocoding(self, address, region=''):
    """地址→坐标"""
    endpoint = '/ws/geocoder/v1'
    params = {'address': address, 'region': region or '全国'}
    return self._parse_geocoding_result(self._request(endpoint, params))

def reverse_geocoding(self, lat, lon):
    """坐标→地址"""
    endpoint = '/ws/geocoder/v1'
    params = {'location': f"{lat},{lon}"}
    return self._parse_reverse_geocoding_result(self._request(endpoint, params))

3. 距离矩阵

def get_distance(self, origins, destinations, mode='walking'):
    endpoint = '/ws/distance/v1/matrix'
    params = {
        'mode': mode,
        'from': ';'.join([f"{lat},{lon}" for lat, lon in origins]),
        'to': ';'.join([f"{lat},{lon}" for lat, lon in destinations]),
    }
    return self._parse_distance_result(self._request(endpoint, params))

4. 路线规划

def get_direction(self, from_lat, from_lon, to_lat, to_lon, mode='walking'):
    selected_mode = mode if mode in {'driving', 'walking', 'bicycling'} else 'walking'
    endpoint = f'/ws/direction/v1/{selected_mode}'
    params = {'from': f"{from_lat},{from_lon}", 'to': f"{to_lat},{to_lon}"}
    return self._parse_direction_result(self._request(endpoint, params))

5. IP 定位

def ip_location(self):
    endpoint = '/ws/location/v1/ip'
    result = self._request(endpoint, {})
    if result.get('status') == 0:
        location = result.get('result', {}).get('location', {})
        ad_info = result.get('result', {}).get('ad_info', {})
        return {
            'success': True,
            'lat': location.get('lat'), 'lon': location.get('lng'),
            'nation': ad_info.get('nation'),
            'province': ad_info.get('province'), 'city': ad_info.get('city'),
        }

这五大接口覆盖了地图应用的最核心场景:定位、搜索、导航、计算。腾讯位置服务的 API 文档清晰、响应稳定,整个封装过程非常顺畅。


四、步步智能体:让地图学会理解人话

这是整个项目的灵魂所在。"步步"不是简单的关键词匹配,而是一个具备上下文感知能力的 AI Agent。

4.1 自然语言意图识别

用户可能说"附近有没有咖啡店"“我想喝奶茶”“帮我找一下附近的厕所”。步步需要准确理解这些意图,并转化为具体的搜索参数。

class BubuSearchSkill:
    SEARCH_ALIASES = [
        (("咖啡店", "咖啡馆", "咖啡", "coffee", "cafe"), "咖啡"),
        (("厕所", "洗手间", "卫生间", "公厕"), "厕所"),
        (("奶茶店", "奶茶", "茶饮"), "奶茶"),
        (("餐厅", "饭店", "吃饭", "午饭", "晚饭", "早餐", "美食"), "餐厅"),
        (("便利店",), "便利店"),
        (("药店", "药房"), "药店"),
    ]
    SEARCH_TRIGGERS = ("附近", "周边", "找", "搜索", "推荐", "哪里有", "有没有")

    def parse_nearby_intent(self, question_text, current_radius=1000):
        text = question_text.strip().lower()
        radius = self._parse_radius(text, current_radius)
        keyword = self._match_keyword(text)
        if not keyword and not any(t in text for t in self.SEARCH_TRIGGERS):
            return None
        return {"keyword": keyword, "radius": radius}

4.2 上下文构建

步步之所以能做出有意义的回答,核心在于它每次请求都会携带完整的上下文信息:

def build_companion_context(search_result, route_result, route_target):
    context_parts = []
    # 时间感知
    context_parts.append(f"当前时间:{now_text},时段:{_get_period()}")
    # 天气感知
    context_parts.append(f"天气:{weather.get('summary')}{weather.get('temperature')}°C")
    # 搜索结果
    context_parts.append(f"附近有 {len(results)} 个结果")
    # 路线信息
    if route_result:
        context_parts.append(f"已规划到 {target_title} 的路线,距离 {route_result.get('distance')} 米")
    return "\n\n".join(context_parts)

4.3 对话式交互闭环

用户:"附近找个咖啡馆"
  ↓
步步识别意图 → 自动调用 nearby_search("咖啡", radius=1000)
  ↓
搜索完成 → 构建上下文(含时间、天气、搜索结果)
  ↓
调用 NVIDIA LLM 生成自然语言回复
  ↓
步步:"现在下午2点,天气晴朗,附近找到 6 家咖啡店。
       离你最近的是星巴克(约 200 米),推荐步行过去 ☀️"

💬 向步步提问: 用户直接在对话框输入自然语言问题,步步会智能理解意图。

在这里插入图片描述

(上图:用户在步步对话框中输入"附近有什么好吃的",步步正在理解意图)

🤖 步步回答反馈: 步步结合搜索结果、时间、天气等信息,生成自然语言回复。

在这里插入图片描述

(上图:步步基于腾讯位置服务搜索结果,生成带推荐理由的回答)

这就是 AI 时代交互新模式的精髓:用户不再需要手动操作,只需说出需求,步步会自动完成整个流程。


五、记忆系统:步步是有记忆的

真正的 AI 助理不能每次对话都"失忆"。步步实现了文件持久化的记忆系统:

def save_memory_entry(question, answer, context):
    state = _load_memory_state()
    state["recent"].append({
        "ts": datetime.now().strftime("%Y-%m-%d %H:%M"),
        "question": question,
        "answer": answer,
        "tags": " / ".join(tags),
    })
    state = _compress_memory(state)
    MEMORY_FILE.write_text(_render_memory_markdown(state), encoding="utf-8")

下次用户再问时,步步会说:“上次推荐的那家餐厅去了感觉怎么样?”——有记忆的交互,才是真正的智能。


六、对话式交互:从手动到自动

传统地图操作流:

🔍 传统手动搜索界面: 需要手动输入关键词、选择分类、浏览列表,操作路径长。

在这里插入图片描述
(上图:传统的手动搜索模式,需要在多个控件间切换操作)

手动输入地址 → 选关键词 → 点搜索 → 看列表 → 点路线 → 看导航

步步的对话式交互流:

用户:"帮我找附近走路能到的咖啡店,推荐一个适合下午发呆的"
  ↓
步步自动识别意图 → 调用 nearby_search → 获取路线距离 → 查天气
  ↓
回复:"今天下午阳光很好,推荐去'猫屎咖啡',步行只要 3 分钟,
       二楼靠窗位置很适合看书发呆 📚"

联网检索增加可信度

对于地点评价,步步还能通过网页检索获取真实公开信息来增强回答的可信度:

# 联网检索地点公开信息
grounding_result = place_grounding.search_place(title="猫屎咖啡", address="...")
# 基于真实信息生成评价
llm_result = llm.chat(
    user_message=f"评价这个地点:{title}",
    system_prompt="只允许使用提供的信息,不要编造。",
    context=grounding_result.get("context", ""),
)

⭐ 联网评价展示: 步步会到网页上检索地点的公开评价信息,结合搜索结果为用户提供有依据的推荐。

在这里插入图片描述在这里插入图片描述

(上图:步步展示"老北京炸酱面馆"的联网评价信息,回答有据可查)

将搜索引擎 + LLM + 地图服务三者结合,让步步的回答既有信息量又有可信度。


七、完整 Demo 演示

7.1 环境准备

git clone https://github.com/xixihaha-ha/bubu.git
cd bubu
# 编辑 config/.env
TENCENT_MAP_API_KEY=你的Key
NVIDIA_API_KEY=你的NVIDIA API Key
pip install -r requirements.txt
streamlit run app.py

7.2 附近搜索 + 步步推荐

用户:"附近有什么好吃的"
步步:"现在晚上 7 点,正是晚餐时间。附近找到 8 家餐厅,
       推荐'老北京炸酱面馆',评分很高,步行约 5 分钟。"

7.3 路线规划 + 天气联动

用户:"去故宫走路要多久"
步步:"天气晴朗 22°C,非常适合步行。距故宫约 1.8 公里,
       步行约 25 分钟,沿途会经过北海公园。"

7.4 智能厕所查找 + 路线规划

def find_nearest_toilets_with_routes(self, from_lat, from_lon, radius=2000, mode='walking'):
    nearby = self.nearby_search(lat=from_lat, lon=from_lon, keyword='厕所', radius=radius)
    for item in candidates:
        route = self.get_direction(from_lat, from_lon, 
                                   to_lat=float(item['lat']), to_lon=float(item['lon']), mode=mode)
    return {"results": merged, "start": start_point}

7.5 多候选点出行分析

输入:起点-景山前街4号
候选:故宫(39.9163,116.3972) / 天坛(39.8822,116.4066) / 颐和园(39.9999,116.2755)

输出:
1. 故宫博物院 - 500米,步行7分 ⭐ 推荐
2. 天坛公园 - 2.3公里,步行30分
3. 颐和园 - 10.5公里,驾车35分

八、踩坑实录与优化

8.1 请求频率控制

def request_guard(action, payload, cooldown_seconds=3):
    """同一请求 3 秒内不允许重复"""
    payload_key = f"{action}:{json.dumps(payload, sort_keys=True)}"
    now = time.time()
    last = st.session_state.get('request_guard', {})
    if last.get('key') == payload_key:
        delta = now - float(last.get('ts', 0.0))
        if delta < cooldown_seconds:
            return False, round(cooldown_seconds - delta, 1)
    st.session_state['request_guard'] = {'key': payload_key, 'ts': now}
    return True, 0.0

8.2 Caching 优化

@st.cache_data(ttl=300, show_spinner=False)
def cached_reverse_geocoding(lat, lon):
    return api.reverse_geocoding(lat=lat, lon=lon)

@st.cache_data(ttl=86400, show_spinner=False)
def cached_ip_location():
    return api.ip_location()

九、总结与展望

🔗 项目开源地址: https://github.com/xixihaha-ha/bubu 欢迎 Star ⭐ 和 Fork!

核心亮点

维度 说明
🗺️ 位置服务 腾讯地图 API 全能力覆盖:搜索、编码、导航、矩阵计算
🤖 AI Agent NVIDIA LLM 驱动的对话式智能体
💾 记忆系统 文件持久化记忆,短期→长期自动压缩
🌤️ 天气联动 实时天气数据,影响推荐策略
🔍 联网检索 基于真实网页的地点评价,杜绝编造
📱 跨端适配 桌面 + 手机双端可用

未来方向

  1. 集成 MCP(Model Context Protocol) — 让步步通过标准协议连接更多工具
  2. 多 Agent 协作 — 各领域 Agent(交通、餐饮、天气)协同决策
  3. 个性化学习 — 长期跟踪用户偏好,越用越懂你

💡 关于 Demo: 本文展示的"步步"是一个技术验证型 Demo,旨在呈现 AI + 位置服务的核心交互思想。当前版本聚焦于核心链路的打通——从自然语言理解到地图服务调用再到 LLM 回复生成。许多功能如多轮对话深度、个性化推荐准确度、大规模并发支持等还有很大的优化空间,欢迎大家一起探讨和改进 🙌


后记

从一次出差在外找餐厅的小经历开始,到一步步构建出一个会聊天、有记忆、能推理的 AI 地图助手,这个过程让我深刻感受到:AI 时代下,人机交互正在从"我操作"走向"我对话"。

腾讯位置服务提供了坚实的地理数据底座,LLM 提供了自然语言理解的大脑,两者的结合正在重新定义"地图"的边界。地图不再是一张需要你"看"的图,而是一个可以和你"聊"的朋友。

希望这篇文章能给正在探索 AI + LBS 方向的你一些启发。毕竟在 AI 时代,最好的交互就是你只说一句话,剩下的事交给 AI 去办。


如果觉得这篇文章有帮助,欢迎 👍 点赞、💬 评论、🔄 转发!你的支持是我持续创作的最大动力~


本文为【腾讯位置服务开发者征文大赛】参赛作品,所有内容均为原创,基于真实项目开发体验撰写。

Logo

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

更多推荐