前言:
在经历了一段时间对于前后端的初步学习、LLM 的了解与实战后,今天给大家分享一下本人第一次AI全栈项目。而通过这篇文章,我希望能够为那些同样希望学习 AI 的充满热情的小白提供一些有用的建议和经验分享。本项目为实现 通过询问 AI 内容来查询表格中的内容,那么话不多说,直接开整!!
准备工作
1.创建好所需文件夹:
首先我们需要准备三个文件夹来分别存放我们项目的backend(后端)、frontend(前端)、LLM(大模型),这样可以提高我们项目的结构清晰度、维护性,同时简化开发和部署流程。
2. user.json的创建:
在backend文件夹中创建一个user.json文件,在这里存放我们的后端数据,例如:
{
"users": [
{
"id": 1,
"name": "张三",
"hometown": "江西"
},
{
"id": 2,
"name": "李四",
"hometown": "北京"
},
{
"id": 3,
"name": "王五",
"hometown": "南京"
}
]
}
在准备工作弄好后,那么直接开整项目来咯!!
正片开始
后端部分(backend):
1.初始化项目 + 创建服务器:
在后端文件夹中,我们需要在命令行中输入以下指令
// 初始化项目:快速生成一个带有默认配置的 package.json 文件
npm init -y
// 创建服务器,快速创建一个 RESTful API 服务器,模拟后端 API,使得前端开发可以在没有实际后端服务的情况下进行。
npm i json-server
2.定义脚本:
在package.json中,我们将设置以下内容:
"scripts": {
"dev": "json-server --watch user.json --port 3001"
},
这里我们进行拆分理解:
"dev":这是脚本的名称,我们可以在终端中通过npm run dev来运行这个脚本。json-server:调用json-server工具的命令。--watch user.json:这个选项告诉json-server去监听(或者说监视)user.json文件的变化。当user.json文件发生变化时,json-server会自动重新加载数据。--port 3001:这个选项指定了服务器监听的端口号为3001。
这意味着我们可以通过 http://localhost:3001/users 访问我们的 API,并且告诉 json-server 监听 user.json 文件的变化,实时反映这些变化。
让我们在命令行中输入npm run dev来看看这个脚本,得到了以下结果:
在这里可以看到已经成功了,那么我们接下来就可以在前端通过 fetch(Endpoints) 来访问后端的数据了。
LLM 部分:
1. 初始化项目 + OpenAI:
在 LLM 文件夹中,我们需要在命令行中输入以下指令
// 初始化项目:快速生成一个带有默认配置的 package.json 文件
npm init -y
// 这将会根据你的 `package.json` 文件中的配置下载并安装最新版本的 `openai` 包以及它的依赖
npm i openai
2. 利用 OpenAI 来处理来自客户端的问题请求:
首先我们进行导入模块
const http = require('http'); // 使用 Node.js 内置的 http 模块来创建 HTTP 服务器
const OpenAi = require('openai'); // 然后引入 OpenAI
const url = require('url'); // 用 Node.js 内置的 url 模块来解析 URL 字符串。
创建了一个 OpenAI API 客户端实例,这里的 apiKey 和 baseURL 请各位自身实际情况填写。apiKey 是你访问 OpenAI API 所需的密钥,而 baseURL 则是指向 OpenAI API 的基础 URL。当然我们这里没有使用 dotenv来保护我们的 key ,请读者自行增添。
const client = new OpenAi({
apiKey: '',
baseURL:''
});
接下来就是创建getCompletion函数来实现与 OpenAI 的对话式 API (Chat API) 进行交互的功能,我们输入提示(prompt)后,AI 模型就会生成文本来响应。
const getCompletion = async (prompt, model="gpt-3.5-turbo") => {
// 用户提的问题
const messages = [{
role: 'user',
content: prompt
}];
// AIGC chat 接口
const response = await client.chat.completions.create({
model: model,
messages: messages,
temperature: 0.1 // 控制输出的随机性
})
return response.choices[0].message.content
}
接下来就是创建了一个 HTTP 服务器,接收带有查询参数的请求,将这些参数构造成提示信息发送给 OpenAI 的 API 获取 AI 回答,然后将回答以 JSON 格式返回给客户端。
const server = http.createServer(async (req, res) => {
// 设置 CORS 相关的响应头
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'); // 允许的请求头
// 解析 URL 并获取查询字符串
const parsedUrl = url.parse(req.url, true);
const queryObj = parsedUrl.query;
console.log(parsedUrl.query);
// 构造用户问题的提示信息
const prompt = `
${queryObj.data}
请根据上面的JSON数据,回答${queryObj.question} 这个问题
`;
// 获取 AI 回答
const result = await getCompletion(prompt);
let info = {
result
};
// 发送响应给客户端
res.statusCode = 200;
res.setHeader('Content-Type', 'text/json');
res.end(JSON.stringify(info));
})
最后,HTTP 服务器开始监听 1314 端口上的连接请求。当有客户端发起 HTTP 请求时,Node.js 会执行上面定义的回调函数来处理请求。
server.listen(1314)
前端部分(frontend):
在前端部分我们要实现创建一个简单的网页应用,包含一个表格用于显示用户信息,以及一个表单用于向后端发送问题并接收 AI 的回答。
首先我们创建一个页面,该页面包含一个用户信息表格,用于展示用户的 ID、姓名和家乡信息。包含一个简单的表单,允许用户输入一个问题并提交给服务器处理。最后,有一个用于显示结果或消息的区域。
<!-- 用户信息表格 -->
<div class="container">
<div class="row col-md-6 col-md-offset-3">
<table class="table table-striped" id="user_table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>家乡</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<!-- 表单 -->
<div class="row">
<form name="aiForm">
<div class="from-group" >
<label for="questionInput">提问</label>
<input
type="text"
id="questionInput"
class="form-control"
name="question"
placeholder="请输入问题"
required
>
</div>
<button type="submit" class="btn btn-default" name="btn">查询</button>
</form>
</div>
<!-- 消息显示区域 -->
<div class="row" id="message"></div>
</div>
</div>
接下来就是加载用户数据了,首先通过fetch发送 HTTP 请求到 http://localhost:3001/users 获取用户数据,然后通过.then(res => res.json())将响应内容解析为 JSON 格式,使用 for...of 循环遍历每个用户对象,并为每个用户创建一行(<tr>)添加到表格中。(别忘了进行错误处理哦~)
// 请求数据
fetch('http://localhost:3001/users')
// 解析内容
.then(res => res.json())
.then(users => {
usersData = users;
for(let user of users){
const tr = document.createElement('tr');
// 在这里我们不对idTd,nameTd,hometownTd分别操作,可以提高代码的美观加可读性
for(let key in user){
const td = document.createElement('td');
td.innerText = user[key];
tr.appendChild(td);
}
tableBody.appendChild(tr);
}
})
.catch(error => console.error('Error:', error));
然后就是处理表单提交事件,当用户提交表单时,阻止默认的页面刷新行为,并使用 fetch API 向 AI 服务器发送问题和用户数据,然后将服务器返回的回答显示在页面上。
oForm.addEventListener('submit', e => {
e.preventDefault(); // 阻止表单默认提交
// 使用 fetch 在页面不刷新的时候 向ai server 发出请求
// 发送数据到后端
// web 2.0 动态页面开发 (js 用fetch实现 可以主动拉取数据)
const question = oForm.question.value.trim();
if(!question){
alert('请输入问题');
return;
}
fetch(`http://localhost:1314/api?question=${question}&data=${JSON.stringify(usersData)}`)
.then(data => data.json())
.then(data => {
document.getElementById('message').innerText = data.result;
})
});
献上代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Users Rag chatbot</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">
<table class="table table-striped" id="user_table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>家乡</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="row">
<form name="aiForm">
<div class="from-group" >
<label for="questionInput">提问</label>
<input
type="text"
id="questionInput"
class="form-control"
name="question"
placeholder="请输入问题"
required
>
</div>
<button type="submit" class="btn btn-default" name="btn">查询</button>
</form>
</div>
<div class="row" id="message"></div>
</div>
</div>
<script>
// js 主动向后端发送数据接口请求
// fetch --> 前端向后端(json-server)拉取数据
const tableBody = document.querySelector('table tbody');
const oForm = document.forms['aiForm'];
let usersData ;
fetch('http://localhost:3001/users')
// 数据到达前端(二进制数据 -> json格式)
.then(res => res.json())
.then(users => {
usersData = users;
// 处理后端返回的数据
// console.log(data);
for(let user of users){
const tr = document.createElement('tr');
// for in 循环 常用于json 对象遍历
for(let key in user){
const td = document.createElement('td');
td.innerText = user[key];
tr.appendChild(td);
}
// 屎山代码
// const idTd = document.createElement('td');
// idTd.innerText = user.id;
// tr.appendChild(idTd);
// const nameTd = document.createElement('td');
// nameTd.innerText = user.name;
// tr.appendChild(nameTd);
// const hometownTd = document.createElement('td');
// hometownTd.innerText = user.hometown;
// tr.appendChild(hometownTd);
tableBody.appendChild(tr);
}
})
.catch(error => {
// 处理请求错误
console.error('Error:', error);
});
oForm.addEventListener('submit', e => {
// 事件对象
// console.log(e);
e.preventDefault(); // 阻止表单默认提交
// 使用 fetch 在页面不刷新的时候 向ai server 发出请求
// 发送数据到后端
// web 2.0 动态页面开发 (js 用fetch实现 可以主动拉取数据)
const question = oForm.question.value.trim();
if(!question){
alert('请输入问题');
return;
}
fetch(`http://localhost:1314/api?question=${question}&data=${JSON.stringify(usersData)}`)
.then(data => data.json())
.then(data => {
console.log(data)
document.getElementById('message').innerText = data.result;
})
})
</script>
</body>
</html>
到这里我们就实现了这个项目的所有功能了,是不是感觉有种坐过山车的感觉,赶紧来检验一下成果吧!!
在 LLM 的终端中输入node main.js后,在提问框中输入我们的需要查询的内容,例如:"有哪些人是江西的",我们就能得到返还内容了。
小结:
本文通过构建包含后端 API 服务器、OpenAI 集成和动态前端界面的全栈应用,展示了如何实现用户与 AI 的交互,希望对各位有所帮助。
---欢迎各位点赞、收藏、关注,如果觉得有收获或者需要改进的地方,希望评论在下方,不定期更新