花半小时写一个属于自己的ChatGPT
最近ChatGPT爆火,未来绝对是各行各业的新趋势,对于程序员来说这是不可不学的新技术新领域,一定要把握时代的浪潮,才能不被这个卷到离谱的社会所淘汰。
废话不多说直接上演示结果!
准备
为了保证咱们得数据安全,也为了前端同学更好的开发一个前后端分离的项目,这里我们需要用到的后端框架是基于nodejs的egg后端框架,不是很了解的同学可以去egg官网了解下。
需要准备以下技术点和工具:
- 阿里云-FC函数计算
- 百度文心一言API/阿里云灵积模型API
- vue3+vant+vite
- egg框架
开始
后端部分
搭建部署后端项目
1.打开阿里云,搜索FC函数计算
2.点击应用-创建应用
3.选择web开发框架,选择egg,熟悉node开发的同学也可以选择express框架,再点立即创建。
这个选择通过代码仓库部署,可以选择自己的github也可以选择自己的私有仓库,其他选项根据自己情况填写即可。
4.创建完成后自动部署代码,并且会生成访问地址。
点击链接就可以直接访问这个项目啦~
编写代码
我们可以直接在阿里云内云端开发,也可以去我们的代码仓库开发。
下面直接上代码:
code/app/router.js
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
router.post('/api/v1/createBaiduAiChat', controller.v1.aichat.createBaiduAiChat); // 直接创建对话
};
code/app/controller/v1/aichat.js
'use strict';
const Controller = require('egg').Controller;
const request = require('request')
class AichatController extends Controller {
async createBaiduAiChat() {
const { ctx } = this;
const key = this.app.config.baiduAiKey.key; //这里直接替换成自己百度的key
const secret = this.app.config.baiduAiKey.secret; //这里直接替换成自己百度的secret
const { data } = ctx.request.body;
const res = await new Promise((resolve,reject)=>{
var options = {
'method': 'POST',
'url': 'https://aip.baidubce.com/oauth/2.0/token?client_id='+key+'&client_secret='+secret+'&grant_type=client_credentials',
'headers': {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
};
request(options, function (error, response) {
if(error) reject(error)
resolve(response.body)
});
})
const resToJson = JSON.parse(res)
if(!resToJson.error){
const result = await new Promise((resolve,reject)=>{
var options = {
'method': 'POST',
'url': 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token='+resToJson.access_token,
'headers': {
'Content-Type': 'application/json'
},
'body':JSON.stringify(data)
};
request(options, function (error, response) {
if(error) reject(error)
resolve(response.body)
});
})
const resultToJson = JSON.parse(result)
if(!resultToJson.error_code){
ctx.body = {
code: 200,
msg: 'success',
data: resultToJson
};
}else{
ctx.body = {
code: -1,
msg: 'error',
data: resultToJson
};
}
}else{
ctx.body = {
code: -1,
msg: 'error',
data: resToJson
};
}
}
module.exports = AichatController;
最后修改配置接口调用方式
s.yaml
关于使用百度文心一言调用接口API可以看这里
https://cloud.baidu.com/doc/WENXINWORKSHOP/s/flfmc9do2
关于使用阿里云千义通问调用接口API可以看这里
https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.51364bc1DGkLIH#341800c0f8w0r
这里主要都是需要去注册key和secret,注册都是免费的,但是调用token是收费的,具体收费标准可以去看看文档,前期都有免费额度可以进行调用。
按照上面的代码直接提交代码到仓库,阿里云内会自动部署,这样一个后端接口
完整的接口地址应该类似如下:
http://egg.web-framework-bjct.1873764751702483.cn-hangzhou.fc.devsapp.net/api/v1/createBaiduAiChat
后面我们也可以通过绑定自定义域名调整接口地址
前端部分
编写代码
全局安装create-vite 方便我们创建项目
npm install -g create-vite
使用 create-vite 命令创建一个新的 Vite 项目,选择 Vue 3 和 TypeScript 作为选项:
create-vite my-aichat --template vue-ts
进入项目
cd my-aichat
npm install
npm i vant
npm i @kangc/v-md-editor@next -S
/src/main.ts 配置
import { createApp } from 'vue'
import 'vant/lib/index.css';
import './style.css'
import App from './App.vue'
import VMdPreview from '@kangc/v-md-editor/lib/preview';
import '@kangc/v-md-editor/lib/style/preview.css';
import githubTheme from '@kangc/v-md-editor/lib/theme/github.js';
import '@kangc/v-md-editor/lib/theme/style/github.css';
import createCopyCodePlugin from '@kangc/v-md-editor/lib/plugins/copy-code/index';
import '@kangc/v-md-editor/lib/plugins/copy-code/copy-code.css';
// import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js';
import '@kangc/v-md-editor/lib/theme/style/vuepress.css';
// highlightjs
import hljs from 'highlight.js';
VMdPreview.use(githubTheme, {
Hljs: hljs,
});
// VMdPreview.use(vuepressTheme)
VMdPreview.use(createCopyCodePlugin())
createApp(App).use(VMdPreview).mount('#app')
/src/App.vue
<template>
<div class="page_content">
<div class="page_bottom">
<template v-for="(item, index) in chatList">
<div class="pad_05rem" v-if="item.type === 'sys'" :key="index">
<div class="flex_item_center">
<van-image class="padright_05rem" round width="2rem" height="2rem" :src="avatarurl" />
AI智能机器人
</div>
<v-md-preview :text="item.text" preview-class="vuepress-markdown-body"></v-md-preview>
</div>
<div class="pad_05rem" v-if="item.type === 'user'" :key="index">
<div class="flex_item_center">
<van-image class="padright_05rem" round width="2rem" height="2rem" :src="myavatarurl" />
我
</div>
<v-md-preview :text="item.text" preview-class="vuepress-markdown-body"></v-md-preview>
</div>
</template>
<div class="btn_bottom">
<van-cell-group inset>
<van-field v-model="message" rows="1" autosize type="textarea" placeholder="请输入您要问的问题">
<template #button>
<van-button size="small" icon="guide-o" :loading="isLoading" type="primary" @click="getAIchatFun"
loading-text="发送中...">发送</van-button>
</template>
</van-field>
</van-cell-group>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { showToast } from 'vant';
import axios from 'axios';
interface chatObj {
type: 'sys' | 'user'
text: string
}
const message = ref('')
const isLoading = ref(false)
const chatList = ref<Array<chatObj>>([])
const avatarurl = "https://img0.baidu.com/it/u=1180901446,1801124233&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501"
const myavatarurl = "http://www.eftik.com/wp-content/uploads/2023/10/202106290021.jpg"
onMounted(() => {
setTimeout(() => {
chatList.value.push({
type: 'sys',
text: '您好,我是AI智能机器人,请问有什么可以帮您?'
})
}, 500);
})
onUpdated(() => {
// 让页面滚动到底部
document.documentElement.scrollTop = document.documentElement.scrollHeight
})
const getAIchatFun = () => {
if (message.value === '') {
showToast('请输入您要问的问题');
return false;
}
chatList.value.push({
type: 'user',
text: message.value
})
isLoading.value = true
axios.post("http://egg.web-framework-bjct.1873764751702483.cn-hangzhou.fc.devsapp.net/api/v1/createBaiduAiChat",
{
data: {
"messages": [
{ "role": "user", "content": message.value }
]
}
},
).then(res => {
console.log(res);
if (res.status === 200) {
if (res.data.code === 200) {
chatList.value.push({
type: 'sys',
text: res.data.data.result
})
} else {
chatList.value.push({
type: 'sys',
text: res.data.data.error_description || res.data.data.error_msg
})
}
} else {
chatList.value.push({
type: 'sys',
text: res.data.data.error_description || res.data.data.error_msg
})
}
isLoading.value = false
message.value = ''
}).catch(e => {
chatList.value.push({
type: 'sys',
text: e.message
})
isLoading.value = false
message.value = ''
})
}
</script>
<style scoped>
.page_content{
height: 100%;
width: 100%;
}
.page_bottom{
padding-bottom: 6rem;
}
.pad_05rem{
padding: .5rem;
}
.padright_05rem{
padding-right: .5rem;
}
.flex_item_center{
display: flex;
align-items: center;
}
.btn_bottom{
bottom: 1rem;
position: fixed;
width: 100%;
z-index: 9999;
}
</style>
启动项目
npm run dev
部署项目
npm run build
大功告成!
评论区