前言
前端时间了解到 mapbox 出了一个车机端的 MapGPT,可以做到智能的路线规划等一系列功能。我发现目前还没有人把AI和旅游很好的结合在一起。
于是我打算用我们国产软件去做一个简单的 AI导游案例,希望能给各位带来启发。
选型
AI
AI方面我选择了可以预定义人设的 Coze,给他一个AI导游、充当后台服务的角色。
人设如下
这样我们就得到了一个可以返回标准 json 的 AI。
将此 AI 发布到Bot as API
和Web SDK
以便可以在前端调用。
地图
地图当然选择了 高德地图,高德地图提供的 Loca API 可以华丽的实现各种可视化效果,其中就包括我们需要的 镜头动画
案例地址:lbs.amap.com/demo/loca-v…
实现
Coze API
这个项目技术上不存在什么难点,重点在于对现有技术的整合利用,打造出一个有用的产品。
话不多说,直接进入编码阶段
首先我们使用常规的 axios 快速将 coze 提供的 接口封装一个函数。
import axios from "axios";
import autolog from 'autolog.js';
const service = axios.create({
baseURL: "https://api.coze.cn/open_api/v2",
timeout: 60000,
headers: {
"Content-Type": "application/json",
'Authorization': `Bearer ${import.meta.env.VITE_COZE_TOKEN}`
},
});
service.interceptors.request.use(
(config) => {
return config;
},
(error) => {
autolog.log('请求错误', 'error');
return Promise.reject(error);
}
);
const getAnswer = async (question: string) => {
const res = await service.post("/chat", {
query: question,
user: "githubPages",
bot_id: "7413640476366602275",
stream: false
});
return res.data;
}
export default getAnswer;
这里我使用的是 coze v2 版本,参数比较简单,便于大家理解,bot_id
即 coze 机器人主页 url 的最后一串数字,大家也可以在 Coze 商店直接体验我的机器人。
虽然此 bot 没有使用高德接口额外扩展,但是经过实测发现,它本身给的经纬度也是非常准确的。
调用此接口,我们将得到如下回复:
content 里就是我们给它预设的回复内容,使用 JSON.parse 即可得到我们想要的内容。
高德地图 Loca 镜头动画
高德为可视化专门提供了另一套API服务,即 Loca。
我这里使用的是 Loca 2.0。
安装并引入 @amap/amap-jsapi-loader
AMap = await AMapLoader.load({
key: import.meta.env.VITE_AMAP_KEY, // 申请好的Web端开发者Key,首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ["AMap.GeoJSON"], //需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['...','...']
Loca: { // 是否加载 Loca, 缺省不加载
"version": '2.0.0' // Loca 版本,缺省 1.3.2
},
})
注意这里需要手动将 Loca 在初始化地图是就加上,不然会获取不到该变量。
地图加载完成后,我们需要 初始化 Loca
async function initLoca() {
// @ts-ignore
loca = new Loca.Container({
map,
})
console.log('loca::: ', loca);
}
由于 Loca 是高德直接注入的,所以这里TS并不会识别,只好先忽略一下。
按照官网案例,我们可以很轻松的让镜头动画跑起来。
function animate() {
let end = endPoint.value
var speed = 1;
// 镜头动画
loca.viewControl.addAnimates(
[{
pitch: {
value: 0,
control: [[0, 20], [1, 0]],
timing: [0, 0, 0.8, 1],
duration: 3000 / speed,
},
}]
, function () {
loca.viewControl.addAnimates([
// 寻迹
{
zoom: {
value: 16,
control: [[map.getZoom(), 3], [map.getZoom(), 15]],
timing: [0.3, 0, 0.9, 1],
duration: 5000 / speed,
},
center: {
value: map.getCenter(),
control: [map.getCenter(), end],
timing: [0.3, 0, 0.1, 1],
duration: 8000 / speed,
},
}, {
// 环绕
pitch: {
value: 50,
control: [[0, 40], [1, 50]],
timing: [0.3, 0, 1, 1],
duration: 7000 / speed,
},
rotation: {
value: 260,
control: [[0, -80], [1, 260]],
timing: [0, 0, 0.7, 1],
duration: 7000 / speed,
},
zoom: {
value: 17,
control: [[0.3, 16], [1, 17]],
timing: [0.3, 0, 0.9, 1],
duration: 5000 / speed,
},
},
],
function () {
setTimeout(animate, 2000);
console.log('结束');
});
});
}
loca.viewControl.addAnimates 的参数也很好理解,分别有:pitch、rotation、zoom等,他们的内部参数则都有:value、control、timing、duration,分别是:动画终点的值、过渡中的轨迹控制点、动画时间控制点、总持续时间。
通过调整以上参数,我们可以实现几乎所有角度的动画展示。
上面的动画表达的是:先晃动一下视角(我们要出发了),视角先拉升再放大,从当前位置飞到终点,再终点环绕展示,最后动画结束,2秒后再次执行。
原谅我不懂镜头语言,但凑合着我们可以感受到飞来飞去的视觉效果了。
结合一下
首先获取到 Coze 的回答:
async function getAnswerFromCoze() {
if (!question.value) {
return
}
loading.value = true
conversations.value.push({
type: 'question',
content: question.value
})
let question_backup = question.value
question.value = ''
conversations.value.push({
type: 'answer',
content: "正在为您查找..."
})
let res = await getAnswer(question_backup)
loading.value = false
let answer = JSON.parse(res.messages[0].content)
endPoint.value = toNumber(answer.lnglat)
conversations.value[conversations.value.length - 1].content = answer.description
loca.animate.start();
animate()
}
在这个函数中,我们通过一个响应式变量question
存储用户的询问字符串(保证同一时间只处理一个),使用conversations
存储对话历史,以便待会我们渲染到对话框中,最后,将得到的结果解析后,调动高德地图启动动画。
将两者分配一下画面,Coze作为对话框在左侧,高德作为地图展示铺满全屏
当我们问询时,首先由扣子查询相关信息
coze回复的同时,高德地图也动了起来
效果
最终我们就得到了这样的效果
结语
此项目已在github开源:github.com/LarryZhu-de…