【腾讯位置服务开发者征文大赛】「步步」:我用腾讯地图 + AI Agent 给地图装了个会聊天的大脑
欢迎 Star ⭐ 和 Fork!
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 驱动的对话式智能体 |
| 💾 记忆系统 | 文件持久化记忆,短期→长期自动压缩 |
| 🌤️ 天气联动 | 实时天气数据,影响推荐策略 |
| 🔍 联网检索 | 基于真实网页的地点评价,杜绝编造 |
| 📱 跨端适配 | 桌面 + 手机双端可用 |
未来方向
- 集成 MCP(Model Context Protocol) — 让步步通过标准协议连接更多工具
- 多 Agent 协作 — 各领域 Agent(交通、餐饮、天气)协同决策
- 个性化学习 — 长期跟踪用户偏好,越用越懂你
💡 关于 Demo: 本文展示的"步步"是一个技术验证型 Demo,旨在呈现 AI + 位置服务的核心交互思想。当前版本聚焦于核心链路的打通——从自然语言理解到地图服务调用再到 LLM 回复生成。许多功能如多轮对话深度、个性化推荐准确度、大规模并发支持等还有很大的优化空间,欢迎大家一起探讨和改进 🙌
后记
从一次出差在外找餐厅的小经历开始,到一步步构建出一个会聊天、有记忆、能推理的 AI 地图助手,这个过程让我深刻感受到:AI 时代下,人机交互正在从"我操作"走向"我对话"。
腾讯位置服务提供了坚实的地理数据底座,LLM 提供了自然语言理解的大脑,两者的结合正在重新定义"地图"的边界。地图不再是一张需要你"看"的图,而是一个可以和你"聊"的朋友。
希望这篇文章能给正在探索 AI + LBS 方向的你一些启发。毕竟在 AI 时代,最好的交互就是你只说一句话,剩下的事交给 AI 去办。
如果觉得这篇文章有帮助,欢迎 👍 点赞、💬 评论、🔄 转发!你的支持是我持续创作的最大动力~
本文为【腾讯位置服务开发者征文大赛】参赛作品,所有内容均为原创,基于真实项目开发体验撰写。
更多推荐




在这里插入图片描述



所有评论(0)