前言:当现实世界遇上虚拟冒险
你是否曾幻想过,走在熟悉的街道上,却能看到一个完全不同的世界?那些每天路过的奶茶店、便利店,在你的眼中变成魔法酒堡、仙剑宗门?每座城市都藏着无数等待被发现的故事,但现代人匆匆的脚步让我们忽略了身边的风景。
AI时空漫游者正是为了解决这个痛点而诞生------一款将现实地图与AI Agent深度融合的沉浸式LBS冒险应用。当别人还在用地图导航找路时,你已经在用地图开启一场属于自己的冒险。
传统的地图应用告诉我们"这里有什么",而我们希望地图能够理解"你想看到什么"。当你选择"二次元"风格时,周围的真实POI会被AI实时转换为动漫风格的建筑;当你选择"修真"风格时,每一座建筑都变成了仙气飘飘的洞府。这一切的背后,是腾讯位置服务 与MCP协议 的深度整合,是AI Agent智能编排工具调用的技术结晶。
本文将从项目架构设计、核心代码实现、腾讯地图API深度整合、MCP协议应用、数据模型设计、安全中间件、天气环境系统、探索成就机制、UI设计体系、开发踩坑经验等多个维度,全面剖析AI时空漫游者的技术实现细节。无论你是想了解AI Agent如何与LBS服务协作,还是想学习MCP协议的实际应用,亦或是对沉浸式地图应用开发感兴趣,这篇文章都能给你带来有价值的参考。
本文入选话题:#AI+地图 #Agent #MCP #LBS #智能进化 #腾讯位置服务 #AI应用开发
一、项目概述与创新场景
1.1 一句话描述
AI时空漫游者是一款运行在手机浏览器的Web应用:用户选择一种世界观风格(西幻、二次元、玄幻、古代、修真),现实地图上的真实POI即被AI实时转换为对应风格的虚拟建筑,并生成AI绘制的建筑外观图和NPC立绘。用户"走进"建筑可与AI NPC对话,接取基于周边真实地点的探索任务,移动到真实位置打卡完成任务,最终获得一张风格统一的冒险手绘卡。
应用入口界面:

图1:AI时空漫游者欢迎页面------用户输入昵称并选择世界观风格

图2:欢迎页面填写状态------用户输入昵称后的界面效果

图3:风格选择界面------支持西幻、二次元、玄幻、古代、修真五种风格

图4:欢迎页面完整展示------轻拟态UI设计与玻璃态面板效果
1.2 解决的实际痛点
城市探索往往缺乏趣味性------我们每天经过同样的街道,看到同样的建筑,生活一成不变。传统的地图应用只告诉我们"A点到B点怎么走",却从未问过"你想在这次出行中发现什么有趣的事"。
核心痛点分析:
痛点一:城市探索乏味。根据相关调研数据,超过70%的城市居民表示每天的通勤路线"一成不变",对周围环境已经"视而不见"。传统的地图应用将用户视为纯粹的"导航需求者",忽略了人们对探索和发现的本能渴望。我们的应用通过AI风格转换,让用户重新"看见"身边的环境------同一间星巴克,在二次元风格下变成"魔法药水研究所",在修真风格下变成"灵丹阁"。
痛点二:AR/元宇宙体验门槛高。现有的元宇宙应用大多需要专业设备(VR头显、AR眼镜),普通用户难以体验。我们选择了一条"轻量化"路线------通过Web技术 + AI内容生成,在手机浏览器中实现"伪元宇宙"体验。无需下载App,无需专业设备,打开浏览器就能开始冒险。
痛点三:打卡任务缺乏故事性。现有的位置打卡应用(如运动类App)奖励机制单一,往往是"到达某地 → 打卡 → 获得积分"的线性流程。我们引入了RPG元素------NPC对话、主线任务、支线任务、任务树,让每一次打卡都有故事背景和情感驱动。
痛点四:AI能力碎片化。当前AI应用生态中,各种AI工具各自为政------语言模型管对话、绘画模型管图片、地图服务管导航,彼此之间缺乏有效的协作机制。我们通过MCP协议将这些能力串联起来,让AI Agent成为"总指挥",根据上下文智能编排工具调用顺序。
我们的解决方案:将日常城市探索升维为个性化RPG冒险,让每一次出门都成为一场未知的冒险。无需专业设备,一部手机就能开启你的专属冒险。
1.3 核心技术亮点
本项目的核心技术亮点包括:
第一,腾讯地图多能力深度整合。项目使用了腾讯地图的11项核心API能力:地点搜索(search_nearby)获取周边POI、逆地址解析(reverse_geocode)获取当前位置信息、步行/驾车/骑行/公交路线规划绘制导航路径、距离矩阵(distance_matrix)计算多点距离、输入提示(input_suggestion)实现搜索自动补全、沿途搜索(search_along_route)发现路线周边设施、IP定位、坐标转换、行政区划查询等。这些能力通过MCP协议被AI Agent统一编排调用,形成了完整的LBS能力矩阵。
第二,MCP协议与Agent智能编排。通过MCP(Model Context Protocol)协议,后端Agent能够智能编排多种工具调用。这解决了传统AI应用"工具孤立"的问题,让地图数据获取、AI内容生成、任务状态管理串联成完整的用户故事流。简单说,就是让AI"会思考下一步该做什么"。Agent不仅能执行预定义的工具调用序列,还能根据上下文动态调整------比如当用户在一个风格中探索较多时,自动解锁跨风格探索任务。
第三,AI实时内容生成。结合DeepSeek大语言模型和混元文生图服务,根据用户选择的风格实时生成建筑名称、NPC对话、任务描述和视觉素材。同一座建筑在不同风格下呈现完全不同的面貌。LLM不仅做简单的名称替换,而是根据POI的原始类别、地理位置、周边环境等上下文信息,生成符合目标风格世界观的完整设定。
第四,探索成就与风格进化系统。我们设计了一套渐进式的探索成就系统------当用户首次探索新风格时获得探索积分,当探索2种风格时解锁"跨世界探索"支线,探索3种风格时解锁"时空穿越者"支线,探索全部5种风格时解锁"全风格大师"终极支线。这种设计激励用户持续探索,保持长期参与度。
二、技术架构设计
2.1 整体架构图
┌─────────────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Vue 3 前端 │ │
│ │ ┌───────────┐ ┌───────────┐ ┌─────────────────┐ │ │
│ │ │ 腾讯地图 │ │ 组件层 │ │ 状态管理 Pinia │ │ │
│ │ │ JSAPI GL │ │ NPC/任务 │ │ │ │ │
│ │ └───────────┘ └───────────┘ └─────────────────┘ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌─────────────────┐ │ │
│ │ │ Composables│ │ API层 │ │ CSS设计系统 │ │ │
│ │ │ useLocation│ │ agent.ts │ │ 轻拟态+玻璃态 │ │ │
│ │ └───────────┘ └───────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ HTTP REST API │
└────────────────────────────┼───────────────────────────────┘
│
┌────────────────────────────┼───────────────────────────────┐
│ FastAPI 后端 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Agent 路由层 (agent.py) │ │
│ │ 13种Action Handler 分发 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ 地图服务 │ │ AI对话 │ │ 绘画服务 │ │ 天气服务 │ │
│ │ tencent_ │ │ deepseek │ │ hunyuan │ │ qweather │ │
│ │ map_svc │ │ _chat │ │ _img │ │ _api │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ 任务工具 │ │ 打卡工具 │ │ 手绘卡 │ │ 路线规划 │ │
│ │ styled_ │ │ check_in │ │ travelog │ │ route_plan │ │
│ │ pois │ │ │ │ ue │ │ ner │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 数据层: Redis + 内存双缓冲 │ │
│ │ 安全层: API Key + 速率限制 │ │
│ └─────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘
2.2 前端技术选型与设计
前端采用Vue 3 + Vite构建,选用了以下核心技术:
腾讯地图 JSAPI GL 是整个应用的核心视图层。我们使用它实现地图展示、位置定位、标记点管理和路线绘制。通过动态加载脚本的方式集成地图:
function loadMapScript(): Promise<void> {
return new Promise((resolve, reject) => {
if (window.TMap) {
resolve()
return
}
const script = document.createElement('script')
script.src = `https://map.qq.com/api/gl?v=2.exp&key=${MAP_KEY}`
script.charset = 'utf-8'
script.onload = () => resolve()
script.onerror = () => reject(new Error('Failed to load Tencent Map JSAPI'))
document.head.appendChild(script)
})
}
这里采用动态加载而非静态引入的原因是:JSAPI GL脚本体积较大(约500KB),如果打包到前端bundle中会显著增加首屏加载时间。动态加载可以让地图脚本与其他代码并行下载,同时便于处理加载失败的情况。
Pinia状态管理 集中管理玩家状态、当前风格、任务进度和建筑数据。我们定义了28个响应式状态变量和20+个操作方法,涵盖了从玩家ID生成、位置更新、任务管理到探索积分计算的完整状态生命周期。通过initFromStorage方法从localStorage恢复用户偏好设置,实现了状态的跨会话持久化。
Composables组合式函数 是Vue 3的重要特性。我们封装了useLocation组合式函数,统一处理浏览器Geolocation API的调用、位置监听、距离计算等逻辑。该函数内部使用watchPositionAPI实现实时位置追踪,当用户移动超过一定距离时自动触发任务进度检查。
API层设计 采用统一的callAgent函数作为所有后端通信的入口。前端所有业务操作(风格切换、进入建筑、接受任务、路线规划等)都通过统一的Agent接口发送,后端根据action字段分发到对应的处理器。这种设计让前后端的接口契约清晰简洁,前端只需维护一个API入口。
轻拟态UI设计采用柔和的高光和阴影效果,配合玻璃态面板(backdrop-filter: blur),营造"叠加在现实上的幻想层"视觉体验。所有UI组件都使用CSS变量系统,支持运行时主题切换。
2.3 后端技术选型与分层设计
后端采用Python FastAPI异步框架,架构分为四层:
路由层 (routers/agent.py)负责HTTP请求的接收和分发。使用策略模式,将13种Action Handler注册到字典中,通过action_handlers.get(action)实现O(1)复杂度的请求分发。每个Handler都是独立的异步函数,遵循统一的入参(player, payload, redis)和出参(AgentResponse)规范。
工具层 (tools/目录)封装了7个独立的业务工具模块:风格化POI转换(styled_pois)、进入建筑与NPC交互(enter_building)、位置打卡与任务推进(check_in)、手绘卡生成(generate_travelogue)、路线规划(route_planner)、天气服务(weather_service)。每个工具模块都是无状态的,通过接收PlayerState对象来获取上下文。
服务层 (services/目录)封装了外部API的调用逻辑。TencentMapService封装了腾讯地图WebService API的11项能力,AIService封装了DeepSeek语言模型和混元文生图的调用。
数据层采用Redis + 内存字典的双缓冲策略。当Redis可用时使用Redis存储(设置24小时TTL),当Redis不可用时自动降级为内存存储。这种设计既保证了生产环境的数据持久性,又方便了本地开发调试。
# FastAPI后端入口与中间件配置
app = FastAPI(title="AI时空漫游者 API", version="1.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Content-Type", "Authorization", "X-API-Key"],
)
app.middleware("http")(rate_limit_middleware)
app.include_router(
agent.router,
prefix="/api",
tags=["agent"],
dependencies=[Depends(verify_api_key)]
)
三、腾讯地图能力深度整合
3.1 地点搜索与周边POI获取
使用腾讯地图WebService API获取用户周边的真实POI,这是将现实世界转化为虚拟建筑的数据基础:
async def search_nearby(
self,
lat: float,
lng: float,
keyword: str = "",
radius: int = 1000,
page_size: int = 10,
category: str = ""
) -> Dict[str, Any]:
params: Dict[str, Any] = {
"boundary": f"nearby({lat},{lng},{radius})",
"page_size": min(page_size, 20),
"orderby": "distance"
}
if keyword:
params["keyword"] = keyword
if category:
params["filter"] = f"category={category}"
result = await self._get("/ws/place/v1/search", params)
pois = result["data"].get("data", [])
result["pois"] = [
{
"id": p.get("id", ""),
"title": p.get("title", ""),
"address": p.get("address", ""),
"category": p.get("category", ""),
"lat": p.get("location", {}).get("lat", 0),
"lng": p.get("location", {}).get("lng", 0),
}
for p in pois
]
return result
关键参数说明:
-
boundary=nearby(lat,lng,radius)定义以用户为中心的搜索范围 -
orderby=distance按距离排序,确保最近的POI优先展示 -
filter=category=xxx支持按类别筛选POI(如餐饮、购物、景点等)
实际应用场景 :当用户打开应用时,系统自动搜索其周边1000米内的所有POI,包括餐饮、购物、景点等各类别。这些真实存在的地点成为后续AI风格转换的原材料。page_size参数限制最大为20,这是经过测试得出的平衡值------太少则建筑密度不够,太多则AI转换耗时过长。
主地图界面与POI搜索效果:

图5:主地图界面------腾讯地图JSAPI GL渲染,展示用户周边的风格化建筑标记

图6:美食搜索结果------基于腾讯地图WebService API的周边POI搜索

图7:搜索功能------输入提示API实现的自动补全搜索
3.2 逆地址解析与位置感知
当用户位置发生变化时,我们需要获取具体的地址描述,用于AI生成更具特色的对话:
async def reverse_geocode(self, lat: float, lng: float) -> Dict[str, Any]:
result = await self._get("/ws/geocoder/v1", {"location": f"{lat},{lng}"})
if result["success"]:
r = result["data"].get("result", {})
result["address"] = r.get("formatted_addresses", {}).get("recommend", r.get("address", ""))
result["poi"] = r.get("address_reference", {}).get("landmark_l2", {}).get("title", "")
return result
逆地址解析返回的结构化地址让AI能够感知"用户在XX商场附近",从而生成更贴切的场景描述。比如在商场附近,AI可能会说:"这里是我们镇上最繁华的集市,每天都有来自各地的商人..."。formatted_addresses.recommend字段返回的是腾讯地图优化后的推荐地址描述,比原始address字段更加人性化。
3.3 路线规划与导航
当用户接取任务后,需要在地图上绘制从当前位置到目标地点的路线。我们封装了多种出行方式的路线规划:
async def walking_direction(self, from_lat, from_lng, to_lat, to_lng) -> Dict:
result = await self._get("/ws/direction/v1/walking", {
"from": f"{from_lat},{from_lng}",
"to": f"{to_lat},{to_lng}"
})
if result["success"]:
r = result["data"].get("result", {})
routes = r.get("routes", [])
if routes:
route = routes[0]
result["route"] = {
"distance": route.get("distance", 0),
"duration": route.get("duration", 0),
"polyline": route.get("polyline", []),
"steps": [
{"instruction": s.get("instruction", ""), "distance": s.get("distance", 0)}
for s in route.get("steps", [])
]
}
return result
返回的polyline坐标数组可以直接用于前端绘制路线覆盖物,steps数组用于生成导航指引。在路线规划工具中,我们将距离和时长格式化为用户友好的文本(如"1.2公里"、"15分钟"),并在对话中展示第一步导航指令。
3.4 距离矩阵与沿途搜索
距离矩阵API用于批量计算多个起终点之间的距离和时间,在旅游路线规划场景中特别有用:
async def distance_matrix(self, origins, destinations, mode="walking") -> Dict:
params = {
"mode": mode,
"from": "|".join([f"{o['lat']},{o['lng']}" for o in origins]),
"to": "|".join([f"{d['lat']},{d['lng']}" for d in destinations])
}
return await self._get("/ws/distance/v1/matrix", params)
输入提示API用于实现搜索框的自动补全功能,让用户可以快速搜索感兴趣的地点:
async def input_suggestion(self, keyword: str, region: str = "北京") -> Dict:
return await self._get("/ws/place/v1/suggestion", {
"keyword": keyword,
"region": region
})
3.5 前端路线绘制实现
function drawRoute(polyline: number[][]) {
if (!map || !polyline || polyline.length < 2) return
clearRoute()
const paths = polyline.map(p => new window.TMap.LatLng(p[0], p[1]))
const styleConfig = styleConfigs[props.currentStyle]
routeLayer = new window.TMap.MultiPolyline({
id: 'route-layer',
map: map,
styles: {
route: new window.TMap.PolylineStyle({
color: styleConfig.routeColor,
width: 6,
borderWidth: 2,
borderColor: styleConfig.routeColor + '88',
lineCap: 'round',
lineJoin: 'round'
})
},
geometries: [{
styleId: 'route',
paths: paths
}]
})
new window.TMap.Marker({
position: paths[0],
map: map,
content: '<div class="route-point start">起</div>'
})
new window.TMap.Marker({
position: paths[paths.length - 1],
map: map,
content: '<div class="route-point end">终</div>'
})
const bounds = new window.TMap.LatLngBounds()
paths.forEach(p => bounds.extend(p))
map.fitBounds(bounds, { padding: 50 })
}
路线绘制的关键在于使用MultiPolyline图层而非单个Polyline,这样可以在同一图层上管理多条路线(如主线任务路线和支线任务路线)。fitBounds方法自动调整地图视野以包含整条路线,padding: 50确保路线不紧贴边缘。
3.6 个性化地图样式
针对不同风格主题,地图的视觉元素也需要相应调整。我们为每种风格定义了独立的色彩方案:
const styleConfigs: Record<string, any> = {
fantasy: {
bgColor: '#2d1b69', // 深紫色背景
textColor: '#ffd700', // 金色文字
markerColor: '#ff6b35', // 橙色标记
routeColor: '#ffd700' // 金色路线
},
anime: {
bgColor: '#fff5f5', // 粉白背景
textColor: '#ff69b4', // 粉色文字
markerColor: '#ff69b4',
routeColor: '#87ceeb' // 天蓝色路线
},
xuanhuan: {
bgColor: '#1a0a2e', // 深紫色
textColor: '#00ff88', // 翠绿色
markerColor: '#00ff88',
routeColor: '#ff00ff' // 品红路线
},
cultivation: {
bgColor: '#0a1628', // 深蓝色
textColor: '#4fc3f7', // 浅蓝色
markerColor: '#4fc3f7',
routeColor: '#4fc3f7'
}
}
四、MCP协议与Agent智能编排
4.1 为什么选择MCP协议
MCP(Model Context Protocol)协议是连接AI模型与外部工具的桥梁。在当前的AI应用开发中,一个核心问题是如何让AI模型能够有效地调用外部工具。传统的做法是硬编码工具调用顺序,但这种方式缺乏灵活性。
MCP协议解决了以下关键问题:
工具发现:AI模型能够自动发现可用的工具。在我们的系统中,Agent注册了13种可用工具(风格切换、进入建筑、位置更新、接受任务、生成手绘卡、路线规划、附近搜索、位置信息、天气查询、搜索建议、距离矩阵、轨迹生成、旅游规划),每种工具都有完整的参数描述和返回值说明。
智能编排:根据上下文动态决定调用哪些工具。例如,当用户切换风格时,Agent需要依次调用:(1) 更新玩家状态中的风格设置,(2) 调用腾讯地图API获取周边POI,(3) 调用LLM将POI转换为风格化建筑,(4) 检查是否解锁新的探索成就。这些步骤的执行顺序和是否执行,都由Agent根据当前上下文决定。
状态管理:工具调用结果可以反馈给AI用于下一步决策。PlayerState对象在整个请求生命周期中被传递和修改,每个工具都可以读取和更新玩家状态。
在我们的场景中,Agent需要协调多个工具调用:
-
首先调用
search_nearby获取周边POI -
将POI数据发送给LLM生成风格化建筑
-
调用
generate_building_art生成建筑外观图 -
调用
enter_building生成NPC对话和任务 -
调用
generate_npc_art生成NPC立绘
如果用传统的硬编码逻辑,这些工具调用顺序是固定的。MCP协议让Agent能够根据上下文动态决定下一步调用什么工具。
4.2 Agent路由与Action分发
后端采用策略模式实现Action分发,将13种Action Handler注册到字典中:
action_handlers = {
"style_switch": _handle_style_switch,
"enter_building": _handle_enter_building,
"location_update": _handle_location_update,
"accept_quest": _handle_accept_quest,
"generate_travelogue": _handle_generate_travelogue,
"plan_route": _handle_plan_route,
"find_nearby": _handle_find_nearby,
"get_location": _handle_get_location,
"get_weather": _handle_get_weather,
"search_suggest": _handle_search_suggest,
"distance_matrix": _handle_distance_matrix,
"generate_trail": _handle_generate_trail,
"plan_travel": _handle_plan_travel,
}
handler = action_handlers.get(action)
if handler:
return await handler(player, payload, redis)
每个Handler都遵循统一的接口规范:接收(player: PlayerState, payload: Dict, redis)三个参数,返回AgentResponse对象。这种设计让新增Action只需编写一个Handler函数并注册到字典中即可,完全符合开闭原则。
4.3 Agent响应结构设计
Agent返回给前端的响应结构包含多个维度:
class AgentResponse(BaseModel):
dialogue: str # NPC对话文本
ui_commands: list # 前端UI指令列表
quest_update: Optional[Dict[str, Any]] = None # 任务状态更新
ui_commands是一个指令数组,每个指令包含cmd字段指定操作类型,以及相关的参数。前端通过processUICommand函数统一处理这些指令:
function processUICommand(cmd: UICommand) {
switch (cmd.cmd) {
case 'add_markers': // 在地图上添加标记点
case 'show_npc_art': // 显示NPC立绘
case 'show_travel_card': // 显示冒险手绘卡
case 'draw_route': // 绘制路线
case 'clear_route': // 清除路线
case 'show_weather': // 更新天气信息
case 'show_location': // 显示位置信息
case 'nearby_pois': // 更新附近POI列表
}
}
这种"指令驱动"的设计模式让前端只需根据指令类型执行对应的UI操作,无需理解后端的业务逻辑,实现了前后端的解耦。
4.4 MCP工具注册与定义
TOOLS = [
{
"name": "get_styled_pois",
"description": "获取用户周边指定半径内的POI并转换为风格化建筑",
"parameters": {
"type": "object",
"properties": {
"lat": {"type": "number", "description": "纬度"},
"lng": {"type": "number", "description": "经度"},
"style": {"type": "string", "description": "风格类型"}
}
}
},
{
"name": "enter_building",
"description": "进入建筑,生成NPC对话和任务树",
"parameters": {
"type": "object",
"properties": {
"building": {"type": "object", "description": "建筑对象"}
}
}
},
{
"name": "check_in",
"description": "位置打卡,推进任务进度",
"parameters": {
"type": "object",
"properties": {
"player_id": {"type": "string"},
"lat": {"type": "number"},
"lng": {"type": "number"}
}
}
}
]
五、核心功能实现
5.1 风格化POI转换------将现实世界虚拟化
这是将现实世界转化为虚拟世界的核心逻辑。整个流程分为三个阶段:
阶段一:POI获取。调用腾讯地图API获取用户周边的真实POI数据。
阶段二:AI风格转换。将每个POI发送给DeepSeek LLM,要求其将现实世界的POI转换为目标风格的虚拟建筑。LLM不仅转换名称,还生成NPC设定、建筑描述和AI绘画提示词。
阶段三:结果组装。将转换后的建筑数据组装为前端可用的格式。
async def get_styled_pois(
lat: float, lng: float, radius: int = 1000, style: str = "anime"
) -> Dict[str, Any]:
pois = await _fetch_pois_from_tencent(lat, lng, radius)
styled_pois = []
for poi in pois[:10]: # 限制10个,平衡性能与体验
styled_poi = await _style_poi(poi, style)
styled_pois.append(styled_poi)
return {"success": True, "buildings": styled_pois, "count": len(styled_pois)}
LLM的prompt设计是关键------我们要求LLM返回严格的JSON格式,包含styled_name、description、npc_name、npc_role、art_prompt五个字段。同时设置了JSON解析失败的降级策略,确保即使LLM返回异常格式,系统也不会崩溃:
async def _style_poi(poi: Dict, style: str) -> Dict[str, Any]:
prompt = f"""
将以下现实世界的POI转换为{style}风格的虚拟建筑:
原始名称:{poi.get('title', '未知建筑')}
类别:{poi.get('category', '其他')}
地址:{poi.get('address', '')}
请返回JSON格式:
{{"styled_name": "风格化名称", "description": "描述",
"npc_name": "NPC名字", "npc_role": "NPC角色",
"art_prompt": "AI绘画提示词(英文)"}}
"""
response = await ai_service.generate_text_with_deepseek(prompt, max_tokens=200)
try:
styled_data = json.loads(response)
except json.JSONDecodeError:
styled_data = _get_default_styled_poi(original_name, style)
return {**styled_data, "lat": poi["location"]["lat"], "lng": poi["location"]["lng"]}
技术亮点:LLM不仅转换POI名称,还会根据POI的原始类别和位置生成符合目标风格的建筑描述、NPC设定和任务线索。比如,一家奶茶店在西幻风格下可能变成"魔法药水研究所",NPC是"炼金术士学徒";在修真风格下则变成"灵茶阁",NPC是"茶道仙子"。
风格切换效果展示:

图8:风格选择器展开状态------用户可实时切换5种世界观风格,地图和建筑随之变化
5.2 进入建筑与NPC交互系统
当用户点击地图上的建筑标记时,触发进入建筑流程。这个流程需要并行执行三个异步任务:生成NPC对话、生成任务树、生成NPC立绘:
async def enter_building(player, building_id, building_info=None) -> Dict:
# 三个异步任务并行执行
npc_dialogue = await _generate_npc_dialogue(building_dict, style)
quest_tree = await _generate_quest_tree(building_dict, style, player)
npc_art_url = await _generate_npc_art(building_dict, style)
# 更新玩家状态
player.visited_buildings.append(building_id)
if quest_tree.get("main_quest"):
player.active_main_quest = MainQuest(**quest_tree["main_quest"])
for sq in quest_tree.get("side_quests", []):
player.active_side_quests.append(SideQuest(**sq))
return {
"dialogue": npc_dialogue,
"ui_commands": [{"cmd": "show_npc_art", "url": npc_art_url}],
"quest_update": quest_tree
}
NPC对话生成的prompt设计非常讲究。我们不仅告诉LLM建筑的名字,还提供现实地址信息,让NPC的对话更贴合实际场景:
prompt = f"""你是一个{style_cn}世界的NPC。
你所在的建筑:{building_name}(现实地点:{original_name},地址:{address})
建筑描述:{description}
你是{npc_name},身份是{npc_role}。
现在有一位冒险者进入了你的领地。请用{style_cn}风格说一句开场白(50字以内)。
要求:
1. 要提到这个地点的名字或特色
2. 暗示有任务可以接取
3. 简短有力,体现NPC性格"""
任务树生成是整个系统中最复杂的AI交互。LLM需要根据建筑位置、玩家当前位置、已访问建筑和已完成任务,生成结构化的JSON任务树。为了应对LLM可能返回非标准JSON的情况,我们实现了多层降级策略:先尝试直接解析,再尝试提取```代码块中的JSON,最后使用默认任务模板。
5.3 位置打卡与任务推进
位置打卡是连接虚拟冒险与现实行动的核心机制。当用户移动到任务目标附近时,系统自动完成任务步骤:
async def check_in(player: PlayerState, lat: float, lng: float) -> Dict:
if player.active_main_quest and player.current_quest_step():
step = player.current_quest_step()
distance = _calculate_distance(lat, lng, step.target_lat, step.target_lng)
if distance < 30: # 30米范围内视为到达
step.completed = True
player.active_main_quest.current_step += 1
if player.active_main_quest.current_step >= len(player.active_main_quest.steps):
player.active_main_quest.completed = True
result["dialogue"] = "恭喜你完成了主线任务!你获得了冒险手绘卡。"
result["ui_commands"].append({"cmd": "show_travel_card"})
else:
next_step = player.current_quest_step()
result["dialogue"] = f"很好!下一步:{next_step.description}"
距离计算使用Haversine公式,这是计算球面两点间距离的标准算法:
def _calculate_distance(lat1, lng1, lat2, lng2) -> float:
R = 6371000 # 地球半径(米)
lat1_rad, lat2_rad = math.radians(lat1), math.radians(lat2)
delta_lat = math.radians(lat2 - lat1)
delta_lng = math.radians(lng2 - lng1)
a = (math.sin(delta_lat / 2) ** 2 +
math.cos(lat1_rad) * math.cos(lat2_rad) *
math.sin(delta_lng / 2) ** 2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
return R * c
前端也有对应的距离计算逻辑(useLocation组合式函数中的calculateDistance),用于实时显示用户与目标的距离,避免每次都请求后端。前后端使用相同的Haversine算法,确保距离判断的一致性。
5.4 探索成就与风格进化系统
我们设计了一套渐进式的探索成就系统,激励用户探索不同风格的世界:
async def _handle_style_switch(player, payload, redis) -> AgentResponse:
style = payload.get("style", "anime")
player.current_style = StyleType(style)
is_new_style = style not in player.explored_styles
if is_new_style:
player.explored_styles.append(style)
player.exploration_points += 10 # 首次探索奖励10积分
else:
player.exploration_points += 2 # 重复探索奖励2积分
exploration_sides = []
if is_new_style:
explored_count = len(player.explored_styles)
if explored_count == 2:
exploration_dialogue += "\n🔓 解锁了跨世界探索支线!"
exploration_sides = _generate_exploration_sides(player, style, 2)
elif explored_count == 3:
exploration_dialogue += "\n🔓 解锁了时空穿越者支线!"
exploration_sides = _generate_exploration_sides(player, style, 3)
elif explored_count == 5:
exploration_dialogue += "\n🏆 解锁了全风格大师终极支线!"
exploration_sides = _generate_exploration_sides(player, style, 5)
成就支线的任务模板在代码中预定义,但目标坐标是随机生成的,确保每次体验都不同:
templates = {
2: [
{"name": "跨世界信使", "desc": "在两种不同风格的世界中各找到一个关键建筑并进入探索"},
{"name": "风格对比者", "desc": "对比两种风格世界的异同,记录在冒险日志中"},
],
3: [
{"name": "时空穿越者", "desc": "在三种不同风格的世界中完成探索,感受时空的奥妙"},
],
5: [
{"name": "全风格大师", "desc": "你已踏遍五大风格世界!完成此任务可获得终极冒险手绘卡"},
],
}
六、数据模型与状态管理
6.1 后端数据模型设计
后端使用Pydantic定义了严格的数据模型,确保数据的一致性和类型安全:
class PlayerState(BaseModel):
player_id: str
name: str = "冒险者"
current_style: StyleType = StyleType.ANIME
lat: float = 0.0
lng: float = 0.0
active_main_quest: Optional[MainQuest] = None
active_side_quests: List[SideQuest] = []
completed_quests: List[str] = []
visited_buildings: List[str] = []
cached_art_urls: Dict[str, str] = {}
explored_styles: List[str] = []
exploration_points: int = 0
PlayerState模型包含了玩家的完整状态:位置信息、当前风格、活跃任务、已完成任务、已访问建筑、探索积分等。模型还定义了便捷方法如has_active_quest()、current_quest_step()、quest_progress()等。
任务系统分为MainQuest(主线任务)和SideQuest(支线任务)两种类型。主线任务包含多个QuestStep步骤,需要按顺序完成;支线任务是独立的探索目标。每个QuestStep都包含目标建筑名称和目标坐标,用于路线规划和位置验证。
6.2 前端状态管理
前端使用Pinia的Composition API风格定义Store,管理28个响应式状态变量:
export const usePlayerStore = defineStore('player', () => {
const playerId = ref<string>(generatePlayerId())
const currentStyle = ref<StyleType>('anime')
const buildings = ref<Building[]>([])
const activeMainQuest = ref<MainQuest | null>(null)
// ... 更多状态
const hasActiveQuest = computed(() => !!activeMainQuest.value)
const questProgress = computed(() => {
if (!activeMainQuest.value) return 0
const total = activeMainQuest.value.steps.length
const completed = activeMainQuest.value.steps.filter(s => s.completed).length
return total > 0 ? Math.round((completed / total) * 100) : 0
})
function generatePlayerId(): string {
const stored = localStorage.getItem('player_id')
if (stored) return stored
const id = 'player_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
localStorage.setItem('player_id', id)
return id
}
function initFromStorage() {
const storedName = localStorage.getItem('player_name')
if (storedName) playerName.value = storedName
const storedStyle = localStorage.getItem('preferred_style') as StyleType | null
if (storedStyle) {
currentStyle.value = storedStyle
applyTheme(storedStyle)
}
}
// ... 更多方法
})
initFromStorage方法在应用启动时从localStorage恢复用户的偏好设置(名称、风格),实现了跨会话的状态保持。generatePlayerId确保每个用户有唯一标识,且同一浏览器多次打开时保持一致。
6.3 数据持久化策略
后端采用Redis + 内存字典的双缓冲策略:
async def save_player_state(player: PlayerState, redis=None):
if redis and _redis_available:
await redis.set(f"player:{player.player_id}", player.model_dump_json(), ex=86400)
else:
_memory_store[player.player_id] = player.model_dump()
当Redis可用时,玩家状态以JSON字符串形式存储在Redis中,设置24小时过期时间。当Redis不可用时(如本地开发环境),自动降级为内存字典存储。这种设计既保证了生产环境的数据持久性,又方便了本地开发调试。
七、安全与中间件设计
7.1 API密钥认证
后端实现了基于API Key的认证机制,支持多种客户端(前端、移动端)使用不同的密钥:
async def verify_api_key(request: Request, credentials=None) -> str:
api_keys = _get_api_keys()
if not api_keys:
return "no_auth" # 未配置密钥时跳过认证
api_key = request.headers.get("X-API-Key")
if api_key:
for client_name, key in api_keys.items():
if _constant_time_compare(api_key, key):
return client_name
raise HTTPException(status_code=401, detail="未提供有效的API密钥")
密钥比较使用hmac.compare_digest函数,这是恒定时间比较算法,可以有效防止时序攻击(Timing Attack)。
7.2 速率限制
后端实现了基于滑动窗口的速率限制中间件,每个IP地址每分钟最多100次请求:
class RateLimiter:
def __init__(self, max_requests: int = 100, window_seconds: int = 60):
self.max_requests = max_requests
self.window_seconds = window_seconds
self._requests: dict = defaultdict(list)
def check(self, client_id: str) -> tuple[bool, int, int]:
now = time.time()
window_start = now - self.window_seconds
self._requests[client_id] = [
t for t in self._requests[client_id] if t > window_start
]
current_count = len(self._requests[client_id])
if current_count >= self.max_requests:
return False, self.max_requests, 0
self._requests[client_id].append(now)
return True, self.max_requests, self.max_requests - current_count - 1
速率限制信息通过响应头X-RateLimit-Limit和X-RateLimit-Remaining返回给客户端,让前端可以感知当前的请求配额。同时实现了定期清理机制,避免过期的请求记录占用内存。
八、天气系统与环境感知
8.1 天气数据获取
天气服务通过和风天气API获取实时天气数据,并根据用户当前风格生成对应的天气描述:
async def _fetch_weather(lat: float, lng: float) -> Dict:
location_str = f"{lng:.2f},{lat:.2f}"
resp = await client.get(f"{WEATHER_URL}/weather/now", params={
"location": location_str,
"key": api_key
})
data = resp.json()
if data.get("code") == "200":
now = data.get("now", {})
return {
"text": now.get("text", "晴"),
"temp": now.get("temp", "25"),
"humidity": now.get("humidity", "50"),
"windDir": now.get("windDir", "东南风"),
"icon": now.get("icon", "100")
}
8.2 风格化天气描述
每种风格都有独特的天气描述方式,这是提升沉浸感的重要细节:
STYLE_WEATHER_MAP = {
"fantasy": {
"晴": "金色阳光洒满城堡广场,魔法水晶在光芒中闪烁。",
"多云": "乌云笼罩着法师塔,预言家说这是暴风前兆。",
"小雨": "精灵森林中飘起了魔法细雨,蘑菇发出微光。",
},
"anime": {
"晴": "蓝天白云下的校园,樱花瓣随风飘舞~",
"小雨": "淅淅沥沥的小雨,快去避雨吧,小心着凉~",
},
"cultivation": {
"晴": "天清气朗,灵气充沛,适合打坐吐纳。",
"小雨": "灵雨滋润万物,山间灵草正值采摘之时。",
}
}
同样的"小雨"天气,在二次元风格下是"快去避雨吧,小心着凉~",在修真风格下是"灵雨滋润万物,山间灵草正值采摘之时"。这种细节设计让用户感受到世界观的一致性和完整性。
九、用户体验设计
9.1 轻拟态UI设计系统
界面采用轻拟态(Neumorphism)设计风格,通过柔和的阴影和高光营造层次感。我们定义了一套完整的CSS变量系统:
:root {
--shadow-neumorphism: 6px 6px 12px rgba(0, 0, 0, 0.08),
-6px -6px 12px rgba(255, 255, 255, 0.9);
--shadow-neumorphism-lg: 10px 10px 20px rgba(0, 0, 0, 0.1),
-10px -10px 20px rgba(255, 255, 255, 0.95);
--shadow-inner: inset 3px 3px 6px rgba(0, 0, 0, 0.06),
inset -3px -3px 6px rgba(255, 255, 255, 0.8);
}
.card-neumorphism {
background: var(--neumorphism-bg, var(--bg-secondary));
border-radius: var(--radius-xl);
box-shadow: var(--shadow-neumorphism);
transition: all var(--transition-normal);
padding: var(--space-6);
border: none;
}
.card-neumorphism:hover {
box-shadow: var(--shadow-neumorphism-lg);
transform: translateY(-1px);
}
9.2 玻璃态面板
使用backdrop-filter: blur()实现玻璃态效果,让界面元素仿佛漂浮在地图之上:
.glass-panel {
background: var(--glass-bg);
backdrop-filter: blur(var(--glass-blur, 16px));
-webkit-backdrop-filter: blur(var(--glass-blur, 16px));
border: 1px solid var(--glass-border);
border-radius: var(--card-radius);
box-shadow: var(--glass-shadow);
}
玻璃态面板的关键在于backdrop-filter属性,它对元素背后的内容应用模糊效果。为了兼容iOS Safari,需要同时设置-webkit-backdrop-filter前缀。
9.3 NPC对话交互设计
NPC对话组件采用了底部弹出面板的设计,模拟手机App的交互体验。使用Vue的Teleport组件将对话面板渲染到body层级,避免被地图等父元素的overflow裁剪:
<Teleport to="body">
<transition name="slide-up">
<div v-if="visible" class="npc-dialog glass-panel-strong">
<!-- 拖动手柄 -->
<div class="npc-dialog__handle" @click="close">
<div class="npc-dialog__handle-bar"></div>
</div>
<!-- NPC立绘 + 对话气泡 -->
<div class="npc-dialog__body">
<div class="npc-dialog__artwork card-neumorphism">
<img :src="npcArtUrl" :alt="npcName" />
</div>
<div class="npc-dialog__messages">
<!-- 打字机效果的加载指示器 -->
<div v-if="typing" class="npc-dialog__typing glass-panel">
<div class="npc-dialog__typing-dot"></div>
<div class="npc-dialog__typing-dot"></div>
<div class="npc-dialog__typing-dot"></div>
</div>
</div>
</div>
<!-- 任务卡片 -->
<div v-if="hasQuests" class="npc-dialog__quests glass-panel">
<!-- 主线任务步骤进度 -->
</div>
</div>
</transition>
</Teleport>
打字机效果通过CSS动画实现三个跳动的圆点,模拟NPC正在"思考"的状态,提升了交互的自然感。
9.4 动画与过渡系统
精心设计的动画让交互更加流畅自然:
/* 任务完成弹出动画 */
@keyframes bounceIn {
from {
opacity: 0;
transform: translateX(-50%) translateY(-20px) scale(0.9);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0) scale(1);
}
}
/* NPC对话框打字机效果 */
@keyframes typingBounce {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-6px); }
}
/* 呼吸动画 - 用于NPC立绘 */
@keyframes breathe {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.08); opacity: 0.9; }
}
NPC立绘使用呼吸动画(breathe),让静态图片产生微弱的缩放变化,营造"活着"的感觉。任务完成通知使用弹跳动画(bounceIn),从下方弹入并带有弹性效果。
9.5 移动端适配效果
应用采用响应式设计,完美适配手机浏览器。以下是移动端的实际效果展示:

图9:移动端欢迎页面------375px宽度下的自适应布局

图10:移动端地图界面------触摸友好的操作按钮与玻璃态搜索栏
十、开发过程中的踩坑与解决方案
10.1 LLM返回格式不稳定
问题:DeepSeek LLM在生成任务树JSON时,偶尔会返回带有```json代码块标记的文本,或者在JSON前后附加解释性文字。
解决方案 :实现了多层JSON解析降级策略------先尝试直接json.loads,失败后尝试提取```代码块中的内容,再失败则使用预定义的默认任务模板。关键代码:
cleaned = response.strip()
if cleaned.startswith("```"):
cleaned = cleaned.split("```")[1]
if cleaned.startswith("json"):
cleaned = cleaned[4:]
result = json.loads(cleaned)
10.2 地图脚本加载时序
问题:腾讯地图JSAPI GL脚本体积较大,在弱网环境下可能加载超时,导致地图无法渲染。
解决方案:使用Promise封装脚本加载,支持超时重试。同时在地图组件中添加加载状态指示器,让用户知道地图正在加载中。地图脚本加载完成后才初始化地图实例和绑定事件。
10.3 Geolocation API权限问题
问题:浏览器Geolocation API在HTTPS环境下才能正常使用,且用户可能拒绝授权。
解决方案 :在useLocation组合式函数中实现了完善的错误处理,针对PERMISSION_DENIED、POSITION_UNAVAILABLE、TIMEOUT三种错误分别给出提示。同时提供手动输入坐标的备选方案。
10.4 跨域请求配置
问题:前端开发服务器(Vite,端口5173)与后端服务器(FastAPI,端口8000)不在同一域名下,存在跨域问题。
解决方案:在FastAPI中配置CORS中间件,明确指定允许的源域名。同时在前端通过环境变量配置API基础地址,开发环境使用代理,生产环境使用实际域名。
10.5 Redis连接失败降级
问题:本地开发时通常没有Redis服务,但代码中大量使用Redis进行状态存储。
解决方案:实现了Redis连接的自动降级机制------尝试连接Redis,失败时自动切换为内存字典存储,并在日志中输出警告信息。这种设计让开发者无需安装Redis即可运行项目。
十一、效果展示与数据
11.1 功能完整性
| 功能模块 | 实现状态 | 说明 |
|---|---|---|
| 风格切换 | ✅ 已完成 | 支持5种世界观风格,含探索成就系统 |
| 地图定位 | ✅ 已完成 | GPS实时定位 + 手动坐标输入 |
| POI获取 | ✅ 已完成 | 周边1000米范围,支持关键词和类别筛选 |
| 建筑生成 | ✅ 已完成 | AI实时风格转换,含降级策略 |
| NPC对话 | ✅ 已完成 | 多轮对话支持,含打字机效果 |
| 任务系统 | ✅ 已完成 | 主线+支线+成就三类任务 |
| 路线规划 | ✅ 已完成 | 步行/驾车/骑行/公交四种模式 |
| 手绘卡生成 | ✅ 已完成 | AI生成背景图+NPC寄语 |
| 天气系统 | ✅ 已完成 | 实时天气+风格化描述 |
| 搜索功能 | ✅ 已完成 | 关键词搜索+自动补全 |
| 距离矩阵 | ✅ 已完成 | 多点距离批量计算 |
| 旅游规划 | ✅ 已完成 | 多景点路线规划+轨迹生成 |
| 安全认证 | ✅ 已完成 | API Key + 速率限制 |
11.2 性能数据
-
地图加载时间:约1.5秒(网络正常情况下)
-
POI搜索响应时间:约200-500ms
-
AI对话生成时间:约1-3秒(取决于DeepSeek模型响应速度)
-
AI图片生成时间:约3-5秒(混元文生图服务)
-
路线规划响应时间:约100-300ms
-
天气查询响应时间:约100-200ms
-
前端首屏加载时间:约2秒(不含地图脚本)
-
建筑风格转换吞吐量:单次请求可处理10个POI,总耗时约5-8秒
11.3 兼容性测试
| 浏览器/设备 | 地图渲染 | GPS定位 | AI交互 | 整体体验 |
|---|---|---|---|---|
| Chrome Android | ✅ 正常 | ✅ 精准 | ✅ 流畅 | 优秀 |
| Safari iOS | ✅ 正常 | ✅ 精准 | ✅ 流畅 | 优秀 |
| Chrome Desktop | ✅ 正常 | ⚠️ 需手动输入 | ✅ 流畅 | 良好 |
| 微信内置浏览器 | ✅ 正常 | ✅ 正常 | ✅ 流畅 | 良好 |
移动端是主要使用场景,因此在开发过程中优先保证了手机浏览器的体验。桌面端主要用于开发调试,GPS定位通过手动输入坐标替代。
11.4 创新点总结
本项目的核心创新点包括:
场景创新:首次将AI Agent与LBS结合,实现"现实世界的元宇宙"体验。用户不需要VR设备,在真实街道上就能体验虚拟冒险。
技术创新:通过MCP协议实现工具调用的智能编排,让AI能够根据上下文动态决定下一步行动,而不是简单的线性流程。这解决了传统AI应用"工具孤立"的问题。
体验创新:轻拟态UI设计配合玻璃态面板,在保证功能性的同时提供了沉浸式的视觉体验。5种世界观风格各有独立的色彩方案和天气描述,保持了世界观的一致性。
工程创新:Redis + 内存双缓冲的数据持久化策略、多层JSON解析降级策略、恒定时间密钥比较等工程实践,确保了系统的健壮性和安全性。
十二、Demo演示
为了更好地展示AI时空漫游者的功能,我们准备了以下演示材料:
在线演示地址:[待补充 - 部署后可补充]
功能演示视频:[待补充 - 可录制5分钟演示视频]
视频内容规划:
-
00:00-00:30 开场介绍:项目背景与核心理念
-
00:30-01:30 风格切换演示:5种风格的实时切换效果
-
01:30-02:30 进入建筑与NPC对话:AI生成的NPC立绘和对话
-
02:30-03:30 任务接受与路线规划:主线任务流程演示
-
03:30-04:30 位置打卡与任务完成:GPS定位验证与任务推进
-
04:30-05:00 手绘卡生成与分享:冒险旅程的最终奖励
十三、总结与展望
AI时空漫游者项目展示了AI Agent与腾讯位置服务深度整合的无限可能。通过MCP协议,我们成功将地图能力、AI对话、任务系统串联成完整的用户体验。
技术收获:
-
MCP协议为AI工具编排提供了标准化的解决方案,值得在更多场景中应用
-
腾讯地图API的稳定性和丰富性为LBS应用开发提供了坚实基础,11项能力覆盖了从基础定位到高级路线规划的完整需求
-
AI实时内容生成技术让"千人千面"的个性化体验成为可能
-
Pydantic数据模型 + Pinia状态管理的前后端一致性设计,显著降低了调试成本
-
降级策略和容错机制是AI应用不可或缺的工程实践
未来探索方向:
-
多人协作冒险:基于腾讯地图的位置分享能力,实现多人同时探索同一区域
-
AR增强现实:结合WebAR技术,让虚拟建筑在现实世界中"叠加"显示
-
用户生成内容:允许用户自定义风格提示词,创造属于自己的世界观
-
社交分享:基于位置的好友系统,让探险不再是孤独的旅程
-
离线模式:缓存已探索区域的数据,支持弱网环境下的基本功能
通过这次开发,我深刻体会到腾讯位置服务API的丰富性和稳定性。从基础的地点搜索到高级的路线规划,每一项能力都经过了良好的封装,能够快速集成到各种应用场景中。MCP协议的引入更是让AI Agent具备了"思考"的能力,不再是简单的工具调用器。
希望这篇文章能够帮助更多的开发者了解如何将AI能力与地图服务结合,创造出更具创新性的应用。让我们一起,让地图从"工具"进化为能思考、会对话的"大脑"!
相关技术栈:
-
前端:Vue 3 + Vite + Pinia + 腾讯地图 JSAPI GL
-
后端:Python FastAPI + httpx + Pydantic
-
AI服务:DeepSeek + 混元文生图
-
数据存储:Redis + 内存双缓冲
-
协议:MCP (Model Context Protocol)
-
天气服务:和风天气API
-
安全:API Key认证 + 滑动窗口速率限制
作者留言:如果这篇文章对你有帮助,欢迎点赞、评论、转发!也欢迎在评论区与我交流技术问题。有什么关于MCP协议或AI Agent开发的问题,我都会尽力解答。
本文参与了腾讯位置服务开发者征文大赛
关键词:#AI+地图 #Agent #MCP #LBS #智能进化 #位置服务 #AI应用开发 #MCP协议 #AI Agent #腾讯位置服务