传统前后端如何引入AI成为AI全栈项目?一个demo让你彻底理解前言 曾经也想在自己的传统项目中引入ai,让自己的项目变
前言
曾经也想在自己的传统项目中引入ai,让自己的项目变得“聪明起来”,奈何苦于生不逢时,但现如今,2024AI元年的到来,一切皆有可能,手里有AI,一切可可能。
接下来就以一个实战demo,让你彻底理解,前端+后端+AI,为你展示一个AI全栈项目
如何搭建。
项目搭建
项目结构
本次ai全栈项目是一个ai_user项目(可以输入自己的问题,让AI回答关于user上的问题),在fullstack文件夹下创建ai_user文件夹,其中又包含前端frontend
、后端backend
、AI ai_server
文件夹
前端
前端主要负责页面的搭建、展示,用户的交互。
结构搭建非常简单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ai_user</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row col-md-6 col-md-offset-3">
<h1>AI能力驱动的userData</h1>
<table class="table table-striped" id="user_table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>家乡</th>
</tr>
</thead>
<tbody>
<!-- <tr>
<td>1</td>
<td>陈总</td>
<td>武汉</td>
</tr>
<tr>
<td>2</td>
<td>罗总</td>
<td>厦门</td>
</tr> -->
</tbody>
</table>
</div>
<div class="row col-md-6 col-md-offset-3">
<form name="aiForm" method="get" >
<div class="form-group">
<label for="questionInput">向AI助理提问:</label>
<input type="text" name="question" class="form-control" id="questionInput"
placeholder="请输入您想问的users相关问题">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</div>
<div class="row" id="message">
</div>
</div>
</body>
</html>
知识点:
- css不用自己写,我们通过link引入了
bootstrap库
,需要用的时候,只需要去网上翻阅一下他们的使用手册就可以轻松使用!例如row、col-md-6这些类 - 页面展示的数据不用写死,而是通过
后端传入数据
,我们再刚开始页面搭建之初可以先写死看看效果。 <label>
标签的for
属性与<input>
元素的id
属性相互配合,当用户点击<label>
文本时,与该label
对应的具有相同id
的<input>
元素会自动获得焦点,这极大地提升了表单交互的便利性和易用性。它使得用户可以更直观、便捷地与表单元素进行交互,而无需精确地点击到小小的输入框区域。这就是一种极其友好的体验,照顾所有人包括存在视力障碍
的人员。placeholder
属性主要用于<input>
等表单元素,它的作用是在输入框为空时显示一个提示性的文本,用于提示用户该输入框期望输入的内容类型或格式等信息。当用户开始在输入框中输入内容时,这个提示文本会自动消失。它有助于为用户提供更明确的输入引导,提升用户体验。
接下来,我们需要编写后端接口,为前端提供数据
后端接口编写
- 初始化
在我们的backend文件夹下,首先我们在终端执行初始化命令,将其初始化为一个后端项目
npm init -y
- 初始化成功,将会在backend文件夹下生成一个package.json,项目描述文件
- 安装json-server
在我们的backend文件夹下,安装json-server库
npm i json-server
- 创建json数据文件
在backend文件夹下创建users.json文件,伪造数据,通过json-server服务为前端提供数据
{
"users": [
{
"id": 1,
"name": "陈总",
"hometown": "北京"
},
{
"id": 2,
"name": "李总",
"hometown": "上海"
},
{
"id": 3,
"name": "恬总",
"hometown": "上海"
},
{
"id": 4,
"name": "钟总",
"hometown": "深圳"
},
{
"id": 5,
"name": "王总",
"hometown": "武汉"
}
]
}
- 修改package.json项目描述文件 在项目描述文件夹中,添加新的脚本命令,后续我们只要启动这个脚本,后端打开服务器,就可以为前端提供数据源
"scripts": {
"dev": "json-server user.json"
},
- 启动服务器
在终端执行命令,启动json-server服务
npm run dev
启动完毕之后,后端就会启动3000端口的服务器,执行我们名称为dev的json-server脚本,以user.json为数据源。
后端服务启动之后,我们的前端通过fetch请求,请求后端3000端口的数据,并将这些数据渲染到我们的form表单中
接下来前后端就已经完成,我们在前端界面中还嵌入了提问unput框和提交按钮,目的是为后续ai服务提供按钮,我们通过input框输入问题,向ai提问,接下来就到了AI一展身手的时候了
AI赋能
前端后端+AI全栈,接下来让我们一起走向AI Native,成为全栈工程师
我们在前端html页面中最后添加了一个div,id为message,目的就是拿到ai的回到之后嵌入到这里,提前挖好了坑。我们只需要为form表单添加一个submit监听事件
- 项目初始化
在ai_server文件夹下启动终端,执行初始化命令
npm init -y
2.安装openai 在ai_server文件夹下启动终端,执行安装openai命令
npm i openai
3.安装dotenv
Dotenv
是一个 Node.js 的第三方模块,用于加载环境变量。它的作用是从一个名为 .env
的文件中加载环境变量,并将这些变量添加到 Node.js 的 process.env
对象中,使得在应用程序中可以轻松地访问这些环境变量。
npm i dotenv
4.创建main.js入口文件
// ai openai, :8888/users?question=
// node 的内置模块
// - 搭建http服务
const http = require('http');
const url = require('url');
const OpenAI = require('openai');
require('dotenv').config();
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
//proxy 代理
baseURL: 'https://api.chatanywhere.tech/v1'
})
const server = http.createServer(async function (req, res) {
res.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有来源访问,也可以指定具体的域名,如'http://example.com'
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的请求方法
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// http 基于请求响应的简单协议 req 请求 res 响应
if (req.url.indexOf('/users') >= 0) {
// users ai 服务
const parsedUrl = url.parse(req.url, true);
// console.log(parsedUrl);
// 解构
const { question, users } = parsedUrl.query;
console.log(question, users)
const prompt = `
${users}
请根据以上用户的json数据,回答${question}这个问题.
如果回答不了,就返回不清楚
`
const response = await client.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: "user", content: prompt }],
temperature: 0, // 控制输出的随机性,0表示更确定的输出
});
const result = response.choices[0].message.content || '';
console.log(result);
let info = {
message: result
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/json');
res.end(JSON.stringify(info))
}
// console.log(req.url, '////');
res.end('hello');
})
server.listen(8888, function () {
console.log('服务器启动了')
})
- 引入node内置模块http、url,我们还下载了openai的包,也通过require引入一下
- 创建openai的实例对象
- 在服务器的处理函数中,设置了跨域访问的相关头部信息。否则会报错
- 当请求的 URL 中包含
/users
时,解析请求参数中的问题和用户数据,构建提示语,然后使用 OpenAI客户端发送请求获取回答,并将结果以特定格式返回给客户端。 - 最后启动服务器监听 8888 端口
前端js
<script>
const oMessage = document.querySelector('#message')
const oBody = document.querySelector('#user_table tbody')
const oForm = document.forms['aiForm'];
let usersData = [];
fetch('http://localhost:3000/users')
.then(data => data.json())
.then(users => {
usersData = users;
// console.log(usersData);
// console.log(users);
oBody.innerHTML = users.map(user => `
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.hometown}</td>
</tr>
`).join('')
})
oForm.addEventListener('submit', function (event) {
// 组织表单的默认行为 例如跳转到action
event.preventDefault();
// name 属性去找 性能更好
const question = this["question"].value.trim();
// console.log(question);
fetch(`http://localhost:8888/users?question=${question}&users=${JSON.stringify(usersData)}`)
.then(data => data.json())
.then(res => {
console.log(res)
document.querySelector('#message').innerHTML = res.message
})
})
</script>
注意点:
- 为什么要使用
join(“”)
?
- oBody.innerHTML存在隐式类型转换,如果不用join(""),数组中的每个元素转换成字符串时,数组中的每个元素是以逗号分隔的,对每一个元素进行字符串拼接时会把逗号也添加进去,如果存在逗号,我们将每一个tr、td渲染近html时就会存在错误,因此需要用join对每一个元素的逗号进行去除。
- 为什么要使用this?
const question = this["question"].value.trim();
我们为事件进行了监听,此时的this就会指向触发事件的元素,在这里我们的submit监听事件中的this就指向oForm元素也就是我们的表单标签
表单标签比较特殊,通过表单标签加中括号名字,可以轻松访问到名字为question的输入框,并通过value获取值,其实我们可以通过id的方式去获取,但是我们通过name去获取的话,性能会更好,当表单被提交时,表单元素的值会被发送到服务器。name
属性用于标识每个表单元素,以便服务器可以正确地处理这些值。如果使用 id
来获取元素,服务器可能无法正确识别表单元素的值。
<form name="aiForm" method="get" action="https://www.baidu.com">
<div class="form-group">
<label for="questionInput">向AI助理提问:</label>
<input type="text" name="question" class="form-control" id="questionInput"
placeholder="请输入您想问的users相关问题">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
总结
通过这一次实战demo,我成功圆梦,将ai引入传统编程,这也是我们真正走向AI全栈开发的坚实一步,未来,前后端的界限一定会越来越模糊,ai的存在,让一切都成为了可能。手里有AI,一切可可能。
如有不清楚的地方,欢迎私信讨论,创作不易,希望各位朋友一键三连,让我们一起努力,全面拥抱ai。
转载自:https://juejin.cn/post/7379806770363711515