一篇文章搞懂前后端分离以及AI聊天框背后的运作原理

240 阅读5分钟

在阅读本文章之前,提醒各位读者,阅读文章需要一点js和node.js基础,没有的化也没有关系,只需要花一点点时间了解一下即可。

准备工作

整体要求

首先,我们需要三个文件夹,分别是frontend(前端),backend(后端),llm(大模型)

image.png

在frontend中准备index.html文件

在backend中安装json-server依赖,以及一个user.json文件

在llm中安装openai依赖和一个main.js文件

在安装依赖之前,记得初始化仓库
npm init -y
npm i json-server  安装json-server依赖库
npm i openai       安装openai依赖,用来调用openai大模型

如果没有openai key的读者,可以在302.AI这个国内ai转发平台进行注册,可以免费拿额度

后端部分

{
    "user":[
        {
            "id":1,
            "name":"zh",
            "hometown":"cs"
        },
        {
            "id":2,
            "name":"zh",
            "hometown":"hn"
        },
        {
            "id":3,
            "name":"zh",
            "hometown":"hn"
        }
    ]
}

这是user.json文件内的内容

在backend文件夹中的package.json中把scripts{}里的内容改为

"dev": "json-server --watch user.json --port 3000"  如果3000已经被占用,请改为其他端口
npm run dev   使用该命令使得后端node.js运行起来

这样我们可以在浏览器内输入localhost:3000向后端发送请求,然后在前端获取数据

前端部分

<!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" action="www.baidu.com">
                <div class="from-gropu">
                    <label for="questionInput">提问</label>
                    <input 
                        type="text"
                        id="questionInput"
                        class="form-control"
                        name="question"
                        placeholder="请输入问题"
                    >
                </div>
                <button type="submit" class="btn btn-default" name="btn">提交</button>
            </form>
        </div>
        <div class="row" id="message"></div>
    </div>
</div>
<script>
// js 主动向后端发送数据请求
// 前端向后端拉取数组
const tableBody = document.querySelector('table tbody')
const oForm = document.forms['aiForm'];
let usersData ;
fetch('http://localhost:3000/user')
  // 数据到达前端 二进制 -> json
  .then(res => res.json())
  .then(users => {
    usersData = users;
    // console.log(data);
    // for in json 对象遍历
    for (let user of users){
        // console.log(user)
        const tr = document.createElement('tr')
        for (let key in user){
            console.log(key, user[key])
            const td = document.createElement('td')
            td.innerText = user[key]
            tr.appendChild(td)
        }


        tableBody.appendChild(tr)
    }
  })
  oForm.addEventListener('submit',e=>{
    // 事件对象
    // console.log(e);

    e.preventDefault() // 阻止表单的默认行为
    // fetch 在页面不刷新的时候 向ai server 发出请求
    // const question = oForm.question.value
    // 发送数据到后端
    // web 2.0 动态页面开发 js fetch 可以主动拉取数据
    const question = oForm.question.value.trim()
    if (!question){
        alert('请输入问题');
        return;
    }
    console.log(question)
    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>

这是html文件部分,需要的读者可以自取,接下来,我将为你逐一介绍各个部分

<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">

首先是该文件的引入,它在本文中提供了以下四个类

container row col-md-6 col-md-offset-3

在bootstrap中,默认把每行分为12列,在container中 每row 就会占据col-md-6,也就是6列,并且会偏移col-md-offset-3,也就是向右移动3格,形成居中格局

接下来是js的重点部分讲解:

核心是fetch

在前端,我们使用fetch向后端发起请求来获取数据,也就是以下

fetch('http://localhost:3000/user')

这里就是向http://localhost:3000/user 发起请求,并得到user.json中的数据,接着处理数据

let usersData ;
fetch('http://localhost:3000/user')
  // 数据到达前端 二进制 -> json
  .then(res => res.json())
  .then(users => {})         //在{}内对数据进行处理

这样我们完成了前端向后端发送请求后获取数据的整个过程了

接着我们往table中添加内容

{for (let user of users){
        // console.log(user)
        const tr = document.createElement('tr')
        for (let key in user){
            console.log(key, user[key])
            const td = document.createElement('td')
            td.innerText = user[key]
            tr.appendChild(td)
        }


        tableBody.appendChild(tr)}

其次是form表单

const oForm = document.forms['aiForm'];
oForm.addEventListener('submit',e=>{
    // 事件对象
    // console.log(e);

    e.preventDefault() // 阻止表单的默认行为
    // fetch 在页面不刷新的时候 向ai server 发出请求
    // const question = oForm.question.value
    // 发送数据到后端
    // web 2.0 动态页面开发 js fetch 可以主动拉取数据
    const question = oForm.question.value.trim()
    if (!question){
        alert('请输入问题');
        return;
    }
    console.log(question)
    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
    })
  })

const oForm = document.forms['aiForm']; 我们通过.forms方法拿到整个form表,并为其增加了监听事件 ,以便发出请求和添加数据

在form中input中输入问题和把从后端请求来的user.json数据连接到对应的网址后面,并向网址发送请求,得到返回的数据,然后展现在页面上,也就是以下部分

document.getElementById('message').innerText =
         data.result

LLM部分

首先展示main.js

// node 内置的http 模块
const http = require('http')
const OpenAi = require('openai')
const url = require('url');// node 内置的

const client = new OpenAi({
    apiKey : your_key,
    baseURL : 'https://api.302.ai/v1'
});

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,
      // LLM 生成内容的随机性
      temperature: 0.1
    }) 
    return response.choices[0].message.content
  }

const server = http.createServer(async (req , res) =>{
    res.setHeader('Access-Control-Allow-Origin','*');//允许所有来源访问,也可以指定具体的域名,如'http://esample.com'
    res.setHeader('Access-Control-Allow-Methods','GET,POST,OPTIONS');//允许的请求方法
    res.setHeader('Access-Control-Allow-Headers','Content-Type,Authorization');//允许的请求头
    
    const parseUrl = url.parse(req.url , true)
    const queryObj = parseUrl.query
    const promot = `
    ${queryObj.data}
    请根据上面的JSON数据,回答${queryObj.question} 这个问题
    `

    const result = await getCompletion(promot)
    let info = {
        result
    }
    res.statusCode = 200
    res.setHeader('Content-Type', 'text/json');
    res.end(JSON.stringify(info))
})

server.listen(1314)

在文件的开头,我们首先引入对应的模块: 包括用于创建HTTP服务器的http模块、处理URL的url模块,以及一个名为openai的第三方模块 http模块,它提供了创建HTTP服务器和客户端的功能。使用这个模块,你可以建立一个服务器来监听HTTP请求,并发送响应。 openai的第三方模块。通常,这个模块是用来与OpenAI的服务(如GPT-3等API)进行交互的。 Node.js内置的url模块,该模块提供了一些实用工具,用于解析URL或者构造URL字符串。

其次是一个client常量和getCompletion方法,这里是固定模板,需要的读者可以记下

server常量

const server 接受了从index.html中form表单提交过来的数据,我们通过解析链接,得到question和question对应的数据,然后生成prompt转发给AI大模型,交给AI大模型进行分析,得出结论后,以json串的格式返回给前端

最后需要设置server监听1314端口