前言

由于我在旅游的时候很喜欢探索和了解当地的历史文化,特别是建筑,商铺,公园等等,这些地名感兴趣的我都会习惯去网上搜索有关百科来看,或者去询问AI大模型有关信息。这一过程的确存在些许不方便,于是灵光一闪,能不能将地图上的poi传入,让大模型输出有关该poi的相关信息呢?说干就干,开发的流程自然也就是要从地图上获取到poi信息接入大模型API即可。

地图准备

根据高德地图文档的地图 JS API 2.0快速上手很容易就做好地图示例,至于像中心点,缩放基础的参数以及控制组件等等,可以根据自己需要添加即可

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    <link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" />
    <title>地图显示</title>
    <style>
        html,
        body,
        #container {
          width: 100%;
          height: 100%;
        }
    </style>
</head>
<body>
<div id="container"></div>
<script src="https://webapi.amap.com/maps?v=2.0&key=您申请的key值"></script>
<script>
    var map = new AMap.Map('container', {
        viewMode: '2D', // 默认使用 2D 模式,如果希望使用带有俯仰角的 3D 模式,请设置 viewMode: '3D'
        zoom:11, // 初始化地图层级
        center: [116.397428, 39.90923] // 初始化地图中心点
    });
	//加载控件的示例
	AMap.plugin('AMap.ToolBar',function(){ 
  	var toolbar = new AMap.ToolBar(); //缩放工具条实例化
  	map.addControl(toolbar); //添加控件
});
</script>
</body>
</html>

poi信息获取

我一开始的设想是通过绑定点击事件直接获取地图上poi的经纬度,再将经纬度通过逆向编码的接口来查询。经过尝试,我发现这里存在很大的问题,直接在地图上点击是不可能精确获取到我想要poi的经纬度,一定存在偏差,而这里的偏差表现为查询出来的是未显示在地图上但是放大能看到的poi。因此,我猜想是不是要将缩放级别作为参数限制在可见范围内搜索poi,但似乎并没有这个参数。 比如说下图我要查询西单大悦城 屏幕截图 2024-08-14 200729.png

屏幕截图 2024-08-14 200741.png 在一番尝试和寻找方法无果之后,最终只能将我的问题提交工单给工程师,很惊喜的是,几个小时就得到回复。原来是需要开启使用到地图热点来查询。回复中没有告诉实现的原理,揣测应该是通过根据poi的热度不同,展示在不同缩放级别,然后在该级别的范围内寻找poi。

var map = new AMap.Map('container', {
        // ... 省略代码 ...
        isHotspot: true
    });
    var placeSearch = new AMap.PlaceSearch();  //构造地点查询类
    var infoWindow=new AMap.InfoWindow({
        offset: new AMap.Pixel(0, -50)
    });
    var markers = [];
    map.on('hotspotclick', function(result) { 
        placeSearch.getDetails(result.id, function(status, result) {
            if (status === 'complete' && result.info === 'OK') {
                placeSearch_CallBack(result);
            }
        });
    });
    //回调函数
    function placeSearch_CallBack(data) { 
        var poiArr = data.poiList.pois;
        if(poiArr[0]){
          var location = poiArr[0].location;
          infoWindow.setContent(createContent(poiArr[0]));
          infoWindow.open(map,location);
        }
    }

智能体搭建及调用

随着大模型的普及,智能体变得门槛很低,不再一定需要编程技巧,仅通过自然语言即可创作。但是如何得到自己想要的回复,还需要掌握有关提示词的技巧。这里有两个应用较为广泛的提示词框架***CRISPE***和***CO-STAR***可以供大家了解参考。就算不想写也没事,一句话也能让大模型帮你生成,然后自己再修修补补就行。 image.png 至于选择哪个大模型来搭建,可以按照自己喜好来。我这里使用的是Coze扣子来搭建,因为相较于其他的平台,这里能够选择自由选择市面上较为流行的开源大模型,并且编排和调试也很友好方便。 屏幕截图 2024-08-14 232109.png 在搭建好之后就可以根据扣子提供的开发文档调用API了

async function sendMessage(message) {
const additionalMessages = Array.from(previousMessages).map(message => {
      const role = message.parentNode.classList.contains('right') ? 'user' : 'assistant';
      const content = message.innerText;
      return {
        role,
        content,
        content_type: 'text'
      };
    });

      // 发起对话请求
      try {
      const response = await axios.post('https://api.coze.cn/v3/chat', {
        bot_id: botId,
        user_id: userId,
        stream: false,
        auto_save_history: true,
        additional_messages: additionalMessages.concat({
          role: 'user',
          content: message,
          content_type: 'text'
        })
      }, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        }
      });

      const chatId = response.data.data.id;
      const conversationId = response.data.data.conversation_id;

      // 轮询查看对话状态,直到对话完成
      while (true) {
        const statusResponse = await axios.get('https://api.coze.cn/v3/chat/retrieve', {
          params: {
            chat_id: chatId,
            conversation_id: conversationId
          },
          headers: {
            Authorization: `Bearer ${accessToken}`,
            'Content-Type': 'application/json'
          }
        });

        if (statusResponse.data.data.status === 'completed') {
          break;
        }
        await new Promise(resolve => setTimeout(resolve, 1000)); // 等待 1 秒再次查询
      }

      // 获取对话消息详情
      const messagesResponse = await axios.get('https://api.coze.cn/v3/chat/message/list', {
        params: {
          chat_id: chatId,
          conversation_id: conversationId
        },
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        }
      });

成果展示

为了方便演示就仿照对话窗口写一个样式来接收大模型的回复,这部分可以根据自己喜欢窗口的样式来写CSS。 image.png 这里有附视频演示供大家查看 地百通Demo

总结

这是我第一次将自己的需求用代码来实现,整个过程用到了前端三件套了,并且也是现学现做的,所以还没来得及用上相关的前端框架,比如Vue,Node,Three这些。我计划做成一个小程序方便使用,会持续更新,敬请期待。