高德地图MCP旅游攻略做成接口方式,20秒做一个旅游攻略

一直对旅游挺感兴趣的,每次做攻略很麻烦,之前看到社区里其他博主,用cursor对接高德地图mcp的攻略挺感兴趣的,一直想能不能用接口的方式对接呢?后来发现用通义千问模型增加地图mcp这种方式对接挺简单的,把这种方式分享给大家!

演示界面


测试地址

实现步骤

1.登录自己的阿里云账号,进入百炼大模型。

阿里云百炼大模型

2.创建自己的大模型智能体



3.绑定地图

4.发布应用,获取应用ID的API_KEY

代码实现

后台我用的webman框架

  public function stream(Request $request)
    {
        $connection = $request->connection;
        $prompt = $request->get('prompt', '你好');
        $api_key = '';
        $application_id = '';
        $url = "https://dashscope.aliyuncs.com/api/v1/apps/$application_id/completion";
        $session_id = $connection->output_session_id ?? null;
        // 关键:创建 Response 对象,设置 header
        $response = response('', 200, [
            "Content-Type" => "text/plain",
            "Transfer-Encoding" => "chunked",
            "Cache-Control" => "no-cache",
            "X-Accel-Buffering" => "no",
        ]);
        ob_start();
        $http = new Client();
        $data = [
            'input' => [
                'prompt' => $prompt,
            ],
            'parameters' => [
                'incremental_output' => true,
            ],
        ];
        if ($session_id) {
            $data['input']['session_id'] = $session_id;
        }
        $buffer = '';
        $http->request($url, [
            'method' => 'POST',
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $api_key,
                'X-DashScope-SSE' => 'enable'
            ],
            'data' => json_encode($data),
            'progress' => function ($chunk) use (&$buffer,$connection) {
                $buffer .= $chunk;
                while (strpos($buffer, "\n\n") !== false) {
                    $event = substr($buffer, 0, strpos($buffer, "\n\n"));
                    $buffer = substr($buffer, strpos($buffer, "\n\n") + 2);
                    $lines = explode("\n", $event);
                    $data = null;
                    foreach ($lines as $line) {
                        if (strpos($line, 'data:') === 0) {
                            $json = trim(substr($line, 5));
                            $data = json_decode($json, true);
                            break;
                        }
                    }
                    if (isset($data['output']['text'])) {
                        echo  $data['output']['text'];
                        $connection->send(new Chunk( $data['output']['text']. "\n"));
                        ob_flush();
                        flush();
                    }
                    if (isset($data['output']['session_id'])) {
                        $connection->output_session_id = $data['output']['session_id'];
                    }
                }
            },
            'success' => function ()use ($connection) {
                echo "data: [DONE]\n\n";
                $connection->send(new Chunk(''));
                ob_end_flush();
                flush();
            },
            'error' => function ($exception) {
                $errorMessage = $exception->getMessage();
                echo "data: {\"error\": \"" . addslashes($errorMessage) . "\"}\n\n";
                error_log("DashScope API Error: " . $errorMessage);
                ob_flush();
                flush();
            }
        ]);
        return $response; // 返回设置好头的响应
    }

2.前端直接写一个简单页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>测试</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

  <style>
    body {
      font-family: "Segoe UI", sans-serif;
      background: #f9f9f9;
      margin: 0;
      padding: 40px;
    }
    .container {
      max-width: 800px;
      margin: 0 auto;
      padding: 24px;
      background: white;
      border-radius: 10px;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
    }
    textarea {
      width: 100%;
      font-size: 16px;
      padding: 10px;
      margin-bottom: 12px;
      border: 1px solid #ccc;
      border-radius: 6px;
      resize: vertical;
    }
    button {
      padding: 8px 16px;
      font-size: 16px;
      margin-right: 10px;
      cursor: pointer;
    }
    .output {
      margin-top: 20px;
      background: #f3f3f3;
      padding: 12px;
      border-radius: 6px;
      white-space: pre-wrap;
      border: 1px solid #ddd;
      line-height: 1.8;
      font-size: 16px;
    }

  </style>
</head>
<body>
<div id="app">
  <div class="container">
    <h2>测试</h2>
    <textarea v-model="input" rows="4" placeholder="测试问题"></textarea>
    <div>
      <button @click="startStream" :disabled="loading">发送</button>
      <button @click="stopStream" v-if="loading">停止</button>
    </div>
    <div class="output" v-if="output">
      <strong>回答:</strong>
      <pre style="white-space: normal;">{{ output }}</pre>
    </div>
  </div>
</div>

<script>
  const { createApp, ref, onUnmounted } = Vue;
  createApp({
    setup() {
      const input = ref('');
      const output = ref('');
      const loading = ref(false);
      let reader = null;
      let typing = false;
      const queue = [];
      let controller = null;

      const normalSpeed = 15; // 平常打字速度(单位ms)
      const fastSpeed = 5;    // 积压太多时加速打字速度(单位ms)

      const startStream = () => {
        if (!input.value.trim()) {
          alert("请输入问题");
          return;
        }
        output.value = '';
        loading.value = true;
        controller = new AbortController();
        fetch(`https://xxx.xxxx.com/demo/stream?prompt=${encodeURIComponent(input.value)}`, {
          signal: controller.signal
        })
                .then(response => {
                  reader = response.body.getReader();
                  const decoder = new TextDecoder('utf-8');

                  let tempBuffer = '';

                  const read = () => {
                    reader.read().then(({ done, value }) => {
                      if (done) {
                        if (tempBuffer) {
                          enqueueSentence(tempBuffer);
                        }
                        stopStream();
                        return;
                      }
                      const text = decoder.decode(value, { stream: true }).trim();
                      tempBuffer += text;

                      const sentenceEndPattern = /([。!??.!])/;
                      let match;
                      while ((match = tempBuffer.match(sentenceEndPattern)) !== null) {
                        const index = match.index;
                        const sentence = tempBuffer.slice(0, index + 1);
                        tempBuffer = tempBuffer.slice(index + 1);
                        enqueueSentence(sentence);
                      }
                      read();
                    }).catch(err => {
                      console.error('读取错误', err);
                      stopStream();
                    });
                  };

                  read();
                })
                .catch(err => {
                  console.error('连接错误', err);
                  stopStream();
                });
      };

      const enqueueSentence = (sentence) => {
        if (typing) {
          queue.push(sentence);
        } else {
          typeSentence(sentence);
        }
      };

      const typeSentence = async (sentence) => {
        typing = true;
        const speed = queue.length > 5 ? fastSpeed : normalSpeed; // 根据积压动态调整速度

        for (let i = 0; i < sentence.length; i++) {
          output.value += sentence[i];
          await new Promise(resolve => setTimeout(resolve, speed));
        }
        typing = false;
        if (queue.length > 0) {
          const nextSentence = queue.shift();
          typeSentence(nextSentence);
        }
      };
      const stopStream = () => {
        loading.value = false;
        if (controller) {
          controller.abort();
        }
        reader = null;
        controller = null;
        typing = false;
        queue.length = 0;
      };
      onUnmounted(() => {
        stopStream();
      });
      return { input, output, loading, startStream, stopStream };
    }
  }).mount('#app');

</script>
</body>
</html>
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5

66666

2天前 评论
无与伦比 (楼主) 2天前

大家说下,使用感受有时间我再优化下。

2天前 评论

6是挺6,感觉没什么大用处

2天前 评论
无与伦比 (楼主) 2天前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!