引言
💡 一句话总结本项目:
👉 用 HTML + Node.js + OpenAI 搭建一个能“看懂用户数据”的智能问答系统——输入问题,直接返回答案,像聊天一样查数据!
这不是玩具,而是现代 AI 全栈架构的核心缩影:前端交互 + 数据服务 + 智能推理三层解耦,未来所有“AI 原生应用”的雏形就在这里。
🚀 为什么要做这个项目?
2024 年了,只会写 CRUD 的开发者正在被淘汰。真正值钱的能力是:把大模型(LLM)融入业务场景中。
但很多同学卡在第一步:不知道怎么把“调 API”变成“做一个完整应用”。
今天我们就来手把手实现一个极简但完整的 AI 全栈查询系统:
- ✅ 前端展示用户列表
- ✅ 用户输入自然语言问题(如:“谁来自南昌?”)
- ✅ 系统自动分析并返回答案(“万明翰来自南昌”)
整个项目不到 200 行代码,却完整展示了 “数据 → 接口 → 模型 → 回答” 的闭环流程。
⚠️ 注意:本项目仅为演示思想,生产环境需加强安全与性能设计。
🧱 架构设计:三层分离,职责清晰
我们采用经典的三明治架构:
[前端页面] ←HTTP→ [LLM服务] ←HTTP→ [数据API]
↓ ↓ ↓
UI交互 智能问答引擎 用户数据管理
| 层级 | 技术栈 | 功能说明 |
|---|---|---|
| 前端 | HTML + JS + Bootstrap | 展示数据 & 提交问题 |
| 数据层 | json-server | 提供 RESTful 用户数据接口 |
| AI层 | OpenAI API | 解析问题 + 结合数据生成回答 |
✅ 优势:
- 各模块独立运行,便于调试和扩展
- 可轻松替换为真实数据库或私有化模型
- 完美体现“AI 即服务”的设计理念
🔧 第一步:搭建数据服务(json-server 快速起飞)
不想写后端?用 json-server 一行命令搞定假 API!
1. 创建 backend/uses.json
{
"users": [
{ "id": 1, "username": "欧阳政", "hometown": "宜春" },
{ "id": 2, "username": "万明翰", "hometown": "南昌" },
{ "id": 3, "username": "王宏财", "hometown": "吉安" },
{ "id": 4, "username": "江北", "hometown": "九江" }
]
}
2. 初始化项目并启动服务
cd backend
npm init -y
npm install json-server --save
添加脚本到 package.json:
"scripts": {
"dev": "json-server --watch uses.json --port 3001"
}
启动服务:
npm run dev
🎉 访问 http://localhost:3001/users 即可看到所有用户数据!
💻 第二步:前端页面开发(简洁美观就够了)
使用 Bootstrap 快速构建响应式界面,核心功能只有两个:展示用户表 + 输入问题查询。
页面结构 (frontend/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">
<div class="col-md-6 col-md-offset-3">
<h3>👥 用户信息</h3>
<table class="table table-striped" id="user_table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>家乡</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<!-- 查询表单 -->
<div class="row">
<form name="aiForm" class="col-md-6 col-md-offset-3">
<div class="form-group">
<label for="questionInput">💬 请输入你的问题:</label>
<input type="text" class="form-control" id="questionInput" name="question" placeholder="例如:谁来自南昌?" required>
</div>
<button type="submit" class="btn btn-success">🚀 提交查询</button>
</form>
</div>
<!-- 结果展示 -->
<div class="row" style="margin-top: 20px;">
<div class="col-md-6 col-md-offset-3" id="message"></div>
</div>
</div>
JavaScript 数据加载与提交逻辑
<script>
const oForm = document.forms["aiForm"];
const oBody = document.querySelector('#user_table tbody');
let users;
// 页面加载时获取用户数据
fetch('http://localhost:3001/users')
.then(res => res.json())
.then(userData => {
users = userData;
// 渲染表格
oBody.innerHTML = userData.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();
const btn = e.target.querySelector('.btn');
if (!question) return alert("请先输入问题哦~");
btn.disabled = true;
btn.textContent = "思考中...";
try {
const response = await fetch(
`http://localhost:1314/?question=${encodeURIComponent(question)}&data=${encodeURIComponent(JSON.stringify({ users }))}`
);
const result = await response.json();
document.getElementById("message").innerHTML = `
<div class="alert alert-info">
<strong>🤖 AI 回答:</strong> ${result.result}
</div>`;
} catch (err) {
document.getElementById("message").innerHTML = `
<div class="alert alert-danger">请求失败,请检查 LLM 服务是否启动</div>`;
} finally {
btn.disabled = false;
btn.textContent = "🚀 提交查询";
}
});
</script>
📌 小技巧:用了 encodeURIComponent 防止中文乱码问题!
界面如下:
🤖 第三步:LLM 服务开发(灵魂所在!)
这是整个系统的“大脑”,负责理解问题并结合数据给出答案。
项目结构
llm/
├── main.mjs
├── package.json
└── .env
安装依赖
npm init -y
npm install openai dotenv
.env 文件(记得 gitignore!)
OPENAI_API_KEY=sk-your-real-apikey-here
核心服务代码 main.mjs
import http from 'http';
import { OpenAI } from 'openai';
import { config } from 'dotenv';
import url from 'url';
config({ path: '.env' });
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: 'https://api.302.ai/v1', // 支持兼容 OpenAI 的代理接口
});
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, // 更确定的回答
max_tokens: 200,
});
return result.choices[0].message.content;
};
http.createServer(async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'application/json');
const parsedUrl = url.parse(req.url, true);
const { question, data } = parsedUrl.query;
if (!question || !data) {
res.statusCode = 400;
return res.end(JSON.stringify({ error: '缺少参数' }));
}
try {
const prompt = `${data}\n\n请根据以上 JSON 数据,回答这个问题:${question}`;
const result = await getCompletion(prompt);
res.statusCode = 200;
res.end(JSON.stringify({ result }));
} catch (err) {
console.error(err);
res.statusCode = 500;
res.end(JSON.stringify({ error: 'AI 调用失败' }));
}
}).listen(1314);
console.log('✅ LLM 服务已启动,监听端口 1314');
🧠 关键点解析:
| 特性 | 说明 |
|---|---|
baseURL | 可换为国内可用的 OpenAI 兼容接口 |
temperature: 0.1 | 让回答更稳定,避免胡说八道 |
| CORS 支持 | 前端跨域访问必备 |
| 错误捕获 | 生产级服务必须有的兜底机制 |
▶️ 启动全流程(三步走)
-
启动数据服务
cd backend && npm run dev -
启动 LLM 服务
cd llm && node main.mjs -
打开前端页面
打开 frontend/index.html(建议用 live-server)
🎯 效果演示:
| 输入问题 | 输出结果 |
|---|---|
| 谁来自南昌? | 万明翰来自南昌 |
| 宜春的人是谁? | 欧阳政来自宜春 |
| 最大的 ID 是多少? | 最大 ID 是 4,对应用户是江北 |
✅ 成功让 AI “读懂”了我们的数据库!
🛠️ 可拓展方向(进阶思路)
虽然这是一个 demo,但它具备极强的延展性:
| 方向 | 实现建议 |
|---|---|
| 🔐 安全增强 | 使用 JWT 认证、限制 Origin、过滤恶意输入 |
| 📦 缓存优化 | Redis 缓存常见问题的回答 |
| 🧩 多轮对话 | 维护 session 上下文,支持追问 |
| 🗃️ 数据库对接 | 替换 json-server 为 MySQL + Express |
| 🌐 私有模型部署 | 接入本地 Llama3 / Qwen 等开源模型 |
| 📊 日志监控 | 记录用户提问行为,用于后续分析 |
📣 总结:这不仅仅是一个 Demo
通过这个小小的项目,我们完成了:
✅ 前后端通信
✅ 数据建模与暴露
✅ LLM 提示工程实践
✅ 全链路 HTTP 调用打通
更重要的是——我们验证了一个理念:
未来的软件 = 数据 + 接口 + 自然语言入口
你可以把这个模式套用到任何场景:
- 内部知识库问答机器人
- 客服工单智能检索
- CRM 客户信息语音查询
- 数据报表口语化解读