新手小白第一尝试全栈和AI的结合

806 阅读4分钟

前言

全栈前后端分离的意义是啥?

  1. 提高开发效率:前后端分离使得前端和后端开发者可以并行工作,互不干扰。前端专注于用户界面的设计与交互逻辑的实现,而后端专注于数据处理、业务逻辑和服务端功能的开发。这种分工合作大大加快了开发进程。
  2. 提升代码质量和可维护性:遵循“单一职责”原则,每个部分的代码更加专注和清晰。前端和后端代码的解耦使得代码更容易理解和维护,修改一处不会轻易影响到另一处,降低了系统的复杂度。
  3. 易于扩展和迭代:分离的架构便于添加新的功能或调整现有功能,因为每个部分都可以相对独立地进行修改。这对于快速迭代的产品开发尤为重要。
  4. 提高可测试性:分离后的前后端可以独立进行单元测试和集成测试,测试范围更加明确,测试效率和质量得以提升。
  5. 利于团队协作:清晰的职责划分有助于团队成员之间的沟通和协作,每个人对自己的工作内容有明确的认识,减少了不必要的协调成本。

准备工作

  • 新建文件夹

image.png

  • 文件夹里建子文件
  1. 对于frontend文件夹新建一个index.html文件
  2. 对于backend文件夹,先在终端输入npm init -y,使其变成后端的项目,继续在终端输入npm i json-server(json-server是一个轻量级的Node.js库,用于快速搭建一个模拟的REST API服务器,适用于前端开发人员在开发阶段,当后端API尚未完成时,用来模拟后端数据交互,以便于前端独立进行开发和测试),再新建一个users.json用于存放用户数据 成功安装json-serverpackage.json

image.png

  1. 对于ai_server文件夹,在终端分别输入npm init -y npm i openai(OpenAI库是一系列编程库,它们提供了与OpenAI API进行交互的能力,允许开发者利用OpenAI的强大模型来进行自然语言处理、图像生成等多种任务)和 npm i dotenv(dotenv是一个简单的零依赖Node.js模块,主要用于管理应用程序的环境变量。它允许开发者创建一个.env文件来存储如数据库凭据、API密钥和其他敏感信息,而不是直接将这些信息硬编码在代码中或者提交到版本控制系统中。dotenv通过读取.env文件并将其中的键值对加载到process.env对象中,使得程序可以在运行时访问这些环境变量),再创建一个.env文件用于存放API密钥,最后创建main.js 成功安装时package.json

image.png

总的文件布局:

image.png

正文

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>椰汁33</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>
                </tbody>
            </table>

        </div>
        <div class="row col-md-6 col-md-offset-3">
            <form name="aiForm" method="get" action="http://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>
        </div>
        <div class="row" id="message">
        </div>
    </div>
    <script>
    // 获取存放输出结果的div标签元素
    const oMessage = document.querySelector('#message');
    // 获取表格的tbody元素
    const oBody = document.querySelector('#user_table tbody');
    // 获取表单form元素
    const oForm = document.forms['aiForm'];
    // 定义一个变量usersData用于存放后端的用户数据进行渲染展示
    let usersData = [];

    // 通过ajax请求进行连接后端的数据
    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) {
        // 阻止表单的默认行为
        event.preventDefault();
        // 通过name属性去找    性能更好
        const question = this["question"].value.trim(); // this.question.value 没有这个性能好
        // 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>
</body>
</html>

此时页面效果为:

image.png

users.json(用户数据)

{"users":[
    {
        "id": 1,
        "name": "北北",
        "hometown": "抚州"
    },
    {
        "id": 2,
        "name": "京京",
        "hometown": "赣州"
    },
    {
        "id": 3,
        "name": "欢欢",
        "hometown": "赣州"
    },
    {
        "id": 4,
        "name": "椰汁",
        "hometown": "长沙"
    },
    {
        "id": 5,
        "name": "言覃",
        "hometown": "抚州"
    }

]
}

在package.json文件加个dev,实现对用户数据的读取:

image.png

启动后端:

  • 右键backend文件夹,新建终端

image.png

  • 在终端输入npm run dev

image.png

  • 刷新页面

image.png

main.js:

接收HTTP GET请求,解析特定格式的查询参数,利用OpenAI API来生成回答,并以JSON格式响应给客户端,同时设置支持跨域访问。

  1. 引入必要的模块:
  • 定义一个变量http,通过 require('http')引入http模块,http 是Node.js的内置模块,用于创建HTTP服务器;
  • 定义一个变量url,通过require('url')引入url模块,url 解析URL的模块,用于解析请求中的查询参数;
  • 定义一个变量OpenAI,通过require('openai')引入openai模块,OpenAI 是OpenAI官方提供的Node.js客户端库,用于与OpenAI的API交互;
  • 引入dotenv模块,dotenv 用于加载.env文件中的环境变量,用于加载OpenAI的API密钥;
const http = require('http');
const url = require('url');
const OpenAI = require('openai');
require('dotenv').config();
  1. 初始化OpenAI客户端: 使用从环境变量(process.env.OPENAI_KEY)获取的API密钥来实例化OpenAI客户端:
const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  //proxy 代理
  baseURL: 'https://api.chatanywhere.tech/v1'
})
  1. 创建HTTP服务器: 使用http.createServer()函数来创建一个HTTP服务器。传入的回调函数会在每次接收到请求时被调用。

回调函数:

  • 设置跨域头: 为了允许来自不同源的请求,设置了跨域资源共享(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');
  • 处理/users路径的请求:

    • 通过检查req.url是否包含/users来判断是否是针对AI服务的请求;
    • 使用url.parse()解析请求的URL,获取查询参数(questionusers);
    • 构造一个prompt(问题),其中包含了从查询参数得到的用户数据以及要回答的问题;
    • 调用OpenAI的ChatCompletion API (gpt-3.5-turbo模型),传入构造好的prompt,获取AI的回答;
    • 将AI的回答封装成JSON格式并通过HTTP响应返回给客户端;
 // http 基于请求响应的简单协议 req --> 请求对象  res --> 响应对象
    if (req.url.indexOf('/users') >= 0) {
        // users ai 服务
        const parseUrl = url.parse(req.url, true);
        // console.log(parseUrl);
        const {question, users} = parseUrl.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))
    }
  1. 监听端口: 服务器监听8888端口,启动成功后会在控制台打印出“服务器启动成功了”。
server.listen(8888, function () {
  console.log('server is running')
})

总代码:

// 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) {
  //http 基于请求响应的简单协议 req 请求 res 响应
  // if(req)
  //console.log(req.url);

  if (req.url.indexOf('/users') >= 0) {

    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');


    const urlParams = url.parse(req.url, true);
    // console.log(urlParams);
    const { question, users } = urlParams.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))
  }
})



server.listen(8888, function () {
  console.log('server is running')
})

.env

存放自己的API密钥,密钥免费获取方法可以前往juejin.cn/post/736912… look

OPENAI_API_KEY="sk-xxxxxxxxx"
  • 右键ai_server文件夹,新建终端

image.png

  • 在终端输入node main.js

image.png

  • 页面输入问题

image.png

  • 页面输出结果

image.png

结语

在这个智能技术遍地开花的时代,全栈+AI的组合无疑将为我们的社会和经济带来深远的影响,开启无限可能的未来。总之,全栈与AI的结合,不仅是技术的升级,更是对未来生活的美好憧憬与实践。

image.png