从零构建 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 全栈应用的核心架构思想:
- 前后端分离:前端专注交互,后端专注数据与逻辑;
- 数据驱动 AI:LLM 的输入来源于真实业务数据,而非静态文本;
- 低耦合设计:AI 服务作为独立中间层,可被多个前端复用;
- 快速原型验证:借助
json-server和 Bootstrap,10 分钟内即可跑通 MVP。
可进一步优化的方向:
- 使用 Express 替代原生
http模块,增强路由管理与错误处理; - 前端升级为 Vue 或 React,提升交互体验与状态管理;
- 添加请求缓存机制,避免对相同问题重复调用 LLM;
- 部署到 Vercel(前端) + Render(后端),实现线上演示。
安全提示:本文代码适用于本地开发与学习。在生产环境中,务必加强安全性措施,包括但不限于:CORS 策略限制、用户输入校验、API Key 保密、以及防止 Prompt 注入攻击。