当然啦,现在的 AI 也不是完美的,比如有时候会瞎编东西(就是大家说的 “AI 幻觉”),但给 APP 加个 AI 功能,确实能让它变好玩、互动感更强。那咱们自己的 APP,怎么快速加上 AI 功能呢?其实不用自己从头搞,直接用现成平台提供的模型和 API 就行,今天就来聊聊怎么用阿里云百炼。
它是阿里云出的大模型服务平台,把复杂的技术都打包好了,就算你没什么 AI 基础,也能很快把 AI 功能集成到自己的 APP 里。
以下内容可配合视频一起食用
前端基础项目搭建
首先我们先搭建一个基础的前端项目,我这里用的是vue框架,UI组件使用了ant-design-vue和ant-design-x-vue,其中ant-design-x-vue增加了对ai交互的支持,可以让开发效率更高。
接下来我们就来尝试「直接」使用通义千问API来实现ai对话功能。
前置准备
在编写代码之前,我们先完成一些准备工作,注册登录,以及获取API key。
构造请求并显示返回结果
然后我们就可以开始构造请求进行API的调用了。
// ...
const handleSubmit = message => {
const newMessage: chatItem = {
key: chatList.value.length,
role: 'user',
content: message,
};
chatList.value.push(newMessage);
fetchReply();
}
const fetchReply = async () => {
return fetch( // +
'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', // +
{ // +
method: 'POST', // +
headers: { // +
Authorization: `Bearer ${import.meta.env.VITE_ALIYUN_API_APPKEY}`, // +
'Content-Type': 'application/json', // +
}, // +
body: JSON.stringify({ // +
model: 'qwen-plus', // +
messages: chatList.value // +
}) // +
} // +
) // +
};
// ...
参数model是指定了我们使用的模型,我们还给接口传递了一个messages的消息数组,这是因为通义千问API是无状态的,它不会自动记录历史对话,所以要实现多轮对话,就需要在每次请求中显式地传递完整的上下文信息,很显然这样随着聊天记录增加,请求体会变得越来越大,文档后面呢也提供了一些优化策略,不过这不是我们今天的重点。
现在我们刷新页面试着发送消息去请求一下API,就可以看到接口返回的ai消息了,消息的内容就在choices数组里message的content属性里。
ai_api2
我们把它显示到页面上就可以了。
const handleSubmit = message => {
const newMessage: chatItem = {
key: chatList.value.length,
role: 'user',
content: message,
};
chatList.value.push(newMessage);
fetchReply() // M
.then (response => response.json()) // +
.then(result => { // +
const newReply: chatItem = { // +
key: chatList.value.length, // +
role: 'assistant', // +
content: result.choices[0].message.content, // +
}; // +
chatList.value.push(newReply); // +
}); // +
}
ai_chat1
问题1:长时间等待
这时我们再发送消息,让ai输出更详细的自我介绍,可以注意到明显地有一段比较长的等待时间,这是因为这次返回的内容比较多。
ai_chat2
解决:改为流式输出
为了能有更好的用户体验,我们可以将返回的形式改为流式输出,在请求参数里增加stream参数的设置,将它设置为true。
const fetchReply = async () => {
return fetch(
'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions',
{
method: 'POST',
headers: {
Authorization: `Bearer ${import.meta.env.VITE_ALIYUN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'qwen-plus',
messages: chatList.value,
stream: true // +
})
}
)
};
当我们再次请求时,会发现这个请求返回的内容和普通的请求不一样,network显示的请求里多了一个tab,标题是EventStream。
ai_api
这是因为我们设置了流式输出。流式输出通过持续返回模型生成的文本片段,可以提供给用户更好的应用体验,避免长时间的等待,那我们要怎么处理这类请求返回的流式内容呢?
这一类请求我以前也没有处理过,所以我找了MDN的文档来参考,MDN上有一个关于fetch API response的文档:ReadableStream,对返回的流式内容进行处理,我们把它复制过来。
const handleSubmit = message => {
const newMessage: chatItem = {
key: chatList.value.length,
role: 'user',
content: message,
};
chatList.value.push(newMessage);
const index = chatList.value.length; // +
let reply = ''; // +
fetchReply()
// .then (response => response.json())
// .then(result => {
// const newReply: chatItem = {
// key: chatList.value.length,
// role: 'assistant',
// content: result.choices[0].message.content,
// };
// chatList.value.push(newReply);
// });
.then(response => response.body)
.then(rb => {
const reader = rb.getReader();
return new ReadableStream({
start(controller) {
// The following function handles each data chunk
function push() {
// "done" is a Boolean and value a "Uint8Array"
reader.read().then(({ done, value }) => {
// If there is no more data to read
if (done) {
console.log("done", done);
controller.close();
return;
}
// Get the data and send it to the browser via the controller
controller.enqueue(value);
// Check chunks by logging to the console
// console.log(done, value); // M
const decoder = new TextDecoder(); // +
const decodedString = decoder.decode(value); // +
console.log(decodedString); // +
push();
});
}
push();