在当今快速发展的 Web 开发领域,全栈开发 早已不再是"会点前后端"的代名词,而是要求开发者能够高效整合各类服务------从前端界面到数据接口,再到人工智能能力。本文将带你从零开始,构建一个具备 用户数据展示 + 自然语言问答能力 的全栈应用,涵盖前端页面、本地模拟 API 服务、以及基于大语言模型(LLM)的智能问答后端。
🧱 整体架构概览
我们的系统由三大部分组成:
- 前端界面(Frontend) :使用原生 HTML + Bootstrap 快速搭建响应式页面,展示用户列表并提供问题输入表单。
- 模拟后端服务(Mock Backend) :通过
json-server工具快速启动 RESTful API,提供静态 JSON 数据。 - AI 智能服务(LLM Service) :基于 Node.js 编写 HTTP 服务,集成第三方大模型 API(如 OpenAI 兼容接口),接收自然语言问题并结合上下文数据生成答案。
✅ 所有服务均运行在本地开发环境,便于调试与演示。
🖥️ 前端:简洁而强大的交互界面
前端采用纯 HTML + 内联 JavaScript 实现,无需构建工具,开箱即用。
核心功能
- 使用 Bootstrap 3 快速实现响应式布局;
- 通过
fetch从本地 API 获取用户数据,并动态渲染为表格; - 提供一个表单,允许用户输入自然语言问题(如"谁来自北京?");
- 将用户数据和问题一起发送给 LLM 服务,展示 AI 回答。
关键代码逻辑
ini
// 1. 获取用户数据
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('');
});
// 2. 提交问题给 LLM 服务
oForm.addEventListener('submit', (e) => {
e.preventDefault();
const question = oForm.question.value;
fetch(`http://localhost:443/?question=${encodeURIComponent(question)}&data=${encodeURIComponent(JSON.stringify(users))}`)
.then(res => res.json())
.then(data => {
document.getElementById('message').innerHTML = data.result;
});
});
🔒 注意:前端同时请求两个不同端口的服务(3001 和 443),因此 LLM 服务必须设置 CORS 头以避免跨域错误。
⚙️ 模拟后端:零代码 API 快速搭建
为了简化开发流程,我们使用 json-server ------ 一个基于 JSON 文件自动生成 REST API 的轻量级工具。
配置步骤
-
初始化 Node.js 项目:
csharpnpm init -y -
安装
json-server(推荐使用pnpm提升速度与磁盘效率):csspnpm i json-server -
创建
users.json文件,内容如下:css[ { "id": 1, "username": "张三", "hometown": "北京" }, { "id": 2, "username": "李四", "hometown": "上海" }, { "id": 3, "username": "王五", "hometown": "广州" }] -
在
package.json中添加脚本:json"scripts": { "dev": "json-server --watch users.json --port 3001" } -
启动服务:
arduinonpm run dev
此时访问 http://localhost:3001/users 即可获取用户列表,完全符合 REST 规范。
🤖 LLM 服务:让 AI 理解你的数据
这是整个系统的"大脑"------一个基于 Node.js 的 HTTP 服务,它接收前端传来的 结构化数据 + 自然语言问题,调用大模型生成精准回答。
技术栈
- Node.js(ES 模块语法)
http模块:创建基础 Web 服务器openaiSDK:调用兼容 OpenAI 的大模型 APIdotenv:安全加载 API 密钥
核心流程
-
加载环境变量
使用
.env文件存储敏感信息(如OPENAI_API_KEY),避免硬编码:luaimport { config } from 'dotenv'; config({ path: '.env' });⚠️ 若未配置
.env文件,process.env.OPENAI_API_KEY将为undefined,导致后续调用失败。 -
初始化大模型客户端
指定 API 密钥与自定义 Base URL(例如使用 302.ai 等国内代理服务):
phpconst client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, baseURL: 'https://api.302.ai/v1' }); -
封装 AI 调用函数
构造提示词(Prompt),引导模型基于提供的 JSON 数据回答问题:
iniconst getCompletion = async (prompt) => { const messages = [{ role: 'user', content: prompt }]; const result = await client.chat.completions.create({ model: 'gpt-3.5-turbo', messages, temperature: 0.1 // 降低随机性,提高准确性 }); return result.choices[0].message.content; }; -
创建 HTTP 服务
监听请求,解析 URL 参数中的
question和data,拼接 Prompt 并返回 AI 结果:inihttp.createServer(async (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); const parsedUrl = url.parse(req.url, true); const prompt = ` ${parsedUrl.query.data} 请根据上面的JSON数据,回答"${parsedUrl.query.question}"这个问题。 `; try { const result = await getCompletion(prompt); res.end(JSON.stringify({ result })); } catch (err) { res.statusCode = 500; res.end(JSON.stringify({ error: String(err) })); } }).listen(443);
📌 为什么监听 443 端口?
虽然注释中提到"因为 302.ai 服务的端口是 443",但实际上这是本地服务监听的端口 。选择 443 可能是为了模拟 HTTPS(但此处仍是 HTTP),更合理的做法是使用非特权端口如
1314或8080。若坚持用 443,需确保无其他服务占用且有 root 权限。
🔄 三端协同工作流程
-
用户打开
index.html; -
页面自动请求
http://localhost:3001/users,获取用户数据并渲染表格; -
用户在输入框中提问(如"谁来自上海?");
-
前端将问题 + 当前用户数据(JSON 字符串)编码后,发送至
http://localhost:443/...; -
LLM 服务收到请求,构造 Prompt:
css[{"id":1,"username":"张三","hometown":"北京"}, ...] 请根据上面的JSON数据,回答"谁来自上海?"这个问题。 -
调用大模型 API,返回结构化答案(如"李四来自上海。");
-
前端接收响应,将答案显示在页面上。
🛠️ 开发与调试建议
| 服务 | 启动命令 | 默认端口 |
|---|---|---|
| 模拟后端 | npm run dev |
3001 |
| LLM 服务 | node llm/index.mjs |
443(建议改为 1314) |
| 前端 | 直接双击 index.html 或用 Live Server |
无 |
💡 常见问题排查:
- API Key 未加载 :确保项目根目录存在
.env文件,内容为OPENAI_API_KEY=your_key_here; - 跨域错误 :LLM 服务必须设置
Access-Control-Allow-Origin: *; - 端口冲突 :若 443 被占用,修改
.listen(443)为.listen(1314),并同步更新前端 fetch 地址; - 中文乱码:确保所有文件保存为 UTF-8 编码。
🌟 总结:全栈 × AI 的未来已来
这个小项目虽简单,却完整体现了现代 Web 应用的典型架构:
- 前端负责体验与交互;
- 后端(哪怕是模拟的)提供数据支撑;
- AI 服务赋予应用"理解"与"推理"能力。
通过将 LLM 作为"智能中间件",我们可以轻松为任何结构化数据添加自然语言查询功能------无论是 CRM 系统、日志分析,还是内部知识库。
🚀 下一步你可以:
- 将 LLM 服务升级为 Express 应用,支持 POST 请求与更复杂的路由;
- 添加 loading 动画与错误重试机制;
- 使用 WebSocket 实现实时流式回答;
- 部署到云服务器,打造真正的 AI 数据助手!
全栈开发不再只是"连接数据库",而是编织智能与体验的网络。而你,已经迈出了关键一步。✨