高德地图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 协议》,转载必须注明作者和本文链接
66666
大家说下,使用感受有时间我再优化下。
6是挺6,感觉没什么大用处