从零构建 AI 全栈应用:用 Node.js 与 OpenAI 实现自然语言查询用户数据

103 阅读4分钟

从零构建 AI 全栈应用:用 Node.js 与 OpenAI 实现自然语言查询用户数据

随着大语言模型(LLM)技术的日益成熟,越来越多开发者开始尝试将 AI 能力集成到自己的 Web 应用中。本文将带你从零构建一个轻量级但完整的 AI 全栈应用:前端展示用户列表,用户输入自然语言问题,后端调用 LLM 接口进行智能问答。

整个项目由三个核心模块组成:

  • 前端页面:使用 Bootstrap 构建简洁、响应式的用户界面;
  • 模拟数据服务:通过 json-server 快速提供 RESTful API;
  • AI 服务中间层:基于 Node.js 搭建 HTTP 服务,对接 OpenAI 兼容接口。

通过这个项目,你不仅能掌握前后端协作的基本模式,还能理解如何将 LLM 作为“智能引擎”嵌入真实业务场景。


技术栈概览

模块技术
前端HTML + Bootstrap 3 + Fetch API
后端数据服务json-server
AI 中间层Node.js + OpenAI SDK
部署方式本地开发环境

项目结构设计

ai-fullstack-app/
├── frontend/               # 前端页面
│   └── index.html
├── backend/                # 数据接口服务
│   ├── users.json
│   └── package.json
├── llm/                    # AI 服务中间层
│   ├── server.js
│   └── .env
└── README.md

第一步:准备模拟数据服务(backend)

我们使用 json-server 快速搭建一个 REST API,用于模拟用户数据。

1. 初始化项目

mkdir backend && cd backend
pnpm init -y
pnpm add json-server@1.0.0-beta.3

2. 创建 users.json

{
  "users": [
    { "id": 1, "username": "张山", "hometown": "北京" },
    { "id": 2, "username": "王二", "hometown": "上海" },
    { "id": 3, "username": "李四", "hometown": "广州" },
    { "id": 4, "username": "赵五", "hometown": "深圳" }
  ]
}

3. 配置启动脚本

package.json 中添加:

{
  "scripts": {
    "dev": "json-server --watch users.json --port 3001"
  }
}

运行 pnpm run dev,即可通过 http://localhost:3001/users 访问用户数据。


第二步:搭建 AI 服务中间层(llm)

该服务接收前端传来的自然语言问题和用户数据,调用 LLM 返回结构化答案。

1. 初始化并安装依赖

mkdir llm && cd llm
pnpm init -y
pnpm add openai dotenv

2. 配置 .env

OpenAI_API_Key=your_api_key_here

注意:请替换为你的实际 API Key,并确保所用接口支持 chat/completions(例如 Moonshot、DeepSeek,或任何兼容 OpenAI 协议的代理服务)。

3. 编写 server.js

import http from 'http';
import OpenAI from 'openai';
import url from 'url';
import { config } from 'dotenv';

config({ path: '.env' });

const client = new OpenAI({
  apiKey: process.env.OpenAI_API_Key,
  baseURL: 'https://api.agicto.cn/v1', // 替换为你的 LLM 代理地址
});

const getCompletion = async (prompt, model = 'gpt-3.5-turbo') => {
  const messages = [{ role: 'user', content: prompt }];
  const result = await client.chat.completions.create({
    model,
    messages,
    temperature: 0.1
  });
  return result.choices[0].message.content;
};

http.createServer(async (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  const parsedUrl = url.parse(req.url, true);
  const { question, data } = parsedUrl.query;

  if (!question || !data) {
    res.statusCode = 400;
    return res.end(JSON.stringify({ error: '缺少必要参数' }));
  }

  const prompt = `${data}\n请根据上面的JSON数据,回答“${question}”这个问题。`;
  const result = await getCompletion(prompt);

  res.statusCode = 200;
  res.setHeader('Content-Type', 'application/json');
  res.end(JSON.stringify({ result }));
}).listen(1314, () => {
  console.log('AI 服务已启动:http://localhost:1314');
});

运行 node server.js 启动服务。


第三步:构建前端页面(frontend)

使用 Bootstrap 3 快速搭建一个响应式界面,展示用户数据并支持提问。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>AI 用户助手</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>

    <div class="row">
      <form name="aiForm">
        <div class="form-group">
          <label for="questionInput">请输入问题:</label>
          <input type="text" class="form-control" id="questionInput" name="question" required placeholder="例如:谁来自北京?">
        </div>
        <button type="submit" class="btn btn-primary">提交</button>
      </form>
    </div>

    <div class="row" id="message" style="margin-top: 20px; padding: 15px; background: #f9f9f9;"></div>
  </div>

  <script>
    const oForm = document.forms["aiForm"];
    const oBody = document.querySelector('#user_table tbody');
    const oBtn = document.querySelector(".btn");
    let users;

    // 加载用户数据
    fetch('http://localhost:3001/users')
      .then(res => res.json())
      .then(data => {
        users = data;
        oBody.innerHTML = data.map(user => `
          <tr>
            <td>${user.id}</td>
            <td>${user.username}</td>
            <td>${user.hometown}</td>
          </tr>
        `).join("");
      });

    // 提交问题
    oForm.addEventListener("submit", async (e) => {
      e.preventDefault();
      const question = oForm.question.value.trim();
      if (!question) return alert("请输入问题");

      oBtn.disabled = true;
      try {
        const res = await fetch(
          `http://localhost:1314/?question=${encodeURIComponent(question)}&data=${encodeURIComponent(JSON.stringify(users))}`
        );
        const { result } = await res.json();
        document.getElementById("message").innerText = result;
      } catch (err) {
        console.error(err);
        document.getElementById("message").innerText = "请求失败,请检查服务是否启动。";
      } finally {
        oBtn.disabled = false;
      }
    });
  </script>
</body>
</html>

关键实现说明

  • 使用 encodeURIComponent 对 URL 参数进行编码,避免 JSON 中的特殊字符导致解析错误;
  • 提交按钮在请求期间禁用,防止重复提交;
  • AI 回答结果显示在一个独立区域,提升用户体验。

启动与测试

依次启动三个服务:

# 终端 1:数据服务
cd backend && pnpm run dev

# 终端 2:AI 服务
cd llm && node server.js

# 终端 3:前端页面(可使用 VS Code Live Server,或)
npx serve frontend

访问前端页面后,你会看到用户表格。尝试提问:

  • “谁来自北京?”
  • “列出所有用户的名字”
  • “张山的 ID 是多少?”

AI 将基于你提供的 JSON 数据返回准确答案。


总结与延伸

这个小项目虽简单,却完整体现了 AI 全栈应用的核心架构思想:

  1. 前后端分离:前端专注交互,后端专注数据与逻辑;
  2. 数据驱动 AI:LLM 的输入来源于真实业务数据,而非静态文本;
  3. 低耦合设计:AI 服务作为独立中间层,可被多个前端复用;
  4. 快速原型验证:借助 json-server 和 Bootstrap,10 分钟内即可跑通 MVP。

可进一步优化的方向:

  • 使用 Express 替代原生 http 模块,增强路由管理与错误处理;
  • 前端升级为 Vue 或 React,提升交互体验与状态管理;
  • 添加请求缓存机制,避免对相同问题重复调用 LLM;
  • 部署到 Vercel(前端) + Render(后端),实现线上演示。

安全提示:本文代码适用于本地开发与学习。在生产环境中,务必加强安全性措施,包括但不限于:CORS 策略限制、用户输入校验、API Key 保密、以及防止 Prompt 注入攻击。