作为一名前端开发者,你是否也有这样的困扰:
- 😫 想学 AI 开发,但看到 Python、PyTorch、CUDA 就头大?
- 😫 好不容易配好环境,下载模型又要几十 GB,等了半天还失败?
- 😫 网上的教程都是 Python 的,作为前端开发者完全看不懂?
- 😫 想做个 AI 项目练手,但不知道从何下手?
别担心!这篇文章就是为你准备的。
我开源了一个完全基于 JavaScript/TypeScript 的 RAG(检索增强生成)智能问答系统,让你用熟悉的技术栈,5 分钟就能搭建一个属于自己的 AI 助手!快年底了,还不整点活儿!
🎯 项目效果
先看看最终效果:


核心功能:
- 📚 上传你的文档(PDF、Word、Markdown、TXT)
- 🤖 用自然语言提问
- 💬 获得基于文档内容的精准回答
- 🔍 自动标注答案来源
就像拥有一个读过所有文档的智能助手!
💡 为什么这个项目适合前端开发者?
1. 100% 熟悉的技术栈
scss
传统 AI 项目 本项目
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Python + PyTorch → Node.js + TypeScript
Conda 环境配置 → npm install
本地模型下载 (50GB+) → API 调用 (0 安装)
GPU 服务器 → 普通电脑即可
复杂的依赖管理 → package.json 搞定
前端技术栈:
- ✅ React 18 + TypeScript + Vite
- ✅ TailwindCSS(现代化样式)
- ✅ React Markdown(Markdown 渲染)
后端技术栈:
- ✅ Node.js + Express + TypeScript
- ✅ LangChain.js(AI 应用框架的 JS 版本)
- ✅ LanceDB(向量数据库,无需额外安装)
2. 真正的开箱即用
bash
# 只需三步
git clone https://github.com/jinghaonode/rag-knowledge-assistant.git
cd rag-knowledge-assistant
./start.sh # 或 start.bat (Windows)
# 就这么简单!
前置要求:
- Node.js 18+(前端开发者必备)
- 智谱 API Key(免费注册,新用户有免费额度)
3. 完整的学习资源
这不仅是一个工具,更是一个学习 AI 开发的完整案例:
- 📚 文档处理:学习如何解析 PDF、Word 等格式
- 🔍 向量检索:理解语义搜索的原理
- 🤖 AI 集成:掌握 LangChain.js 的使用
- 💬 流式输出:实现实时对话体验
- 🎨 UI 设计:现代化的聊天界面
🏗️ 技术架构详解
RAG 是什么?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合了信息检索和文本生成的 AI 技术。
简单来说:
- 📤 用户上传文档 → 系统将文档切分并向量化存储
- 💬 用户提问 → 系统检索相关文档片段
- 🤖 AI 基于检索到的内容生成回答
为什么需要 RAG?
- 大语言模型的知识是有限的(训练数据截止日期)
- 无法回答你私有文档中的内容
- RAG 让 AI 能够基于你的文档回答问题
系统架构
graph LR
A[用户上传文档] --> B[文档解析]
B --> C[文本分块]
C --> D[向量化]
D --> E[LanceDB 存储]
F[用户提问] --> G[查询优化]
G --> H[混合检索]
H --> I[向量检索]
H --> J[关键词检索]
I --> K[相关文档]
J --> K
K --> L[GLM-4 生成回答]
L --> M[流式输出]
核心技术点
1. 文档处理(Document Processing)
typescript
// 支持多种文档格式
const loaders = {
".pdf": PDFLoader,
".docx": DocxLoader,
".md": TextLoader,
".txt": TextLoader,
};
// 智能分块
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 500, // 每块 500 字符
chunkOverlap: 100, // 重叠 100 字符(保持上下文连贯)
});
为什么要分块?
- AI 模型有 Token 限制
- 分块可以提高检索精度
- 重叠部分保证上下文连贯性
2. 向量化存储(Vector Store)
typescript
// 使用智谱 Embedding 模型
const embeddings = new OpenAIEmbeddings({
modelName: "embedding-3",
openAIApiKey: process.env.GLM_API_KEY,
configuration: {
baseURL: "https://open.bigmodel.cn/api/paas/v4",
},
});
// 存储到 LanceDB
const vectorStore = await LanceDB.fromDocuments(documents, embeddings, {
uri: "./lancedb",
});
什么是向量化?
- 将文本转换为数字向量(一串数字)
- 语义相似的文本,向量也相似
- 可以通过向量相似度进行语义搜索
3. 混合检索(Hybrid Search)
typescript
// 向量检索(语义搜索)
const vectorDocs = await vectorStore.similaritySearch(query, 6);
// 关键词检索(精确匹配)
const keywordDocs = await keywordSearch(query);
// 智能合并去重
const finalDocs = mergeAndDeduplicate(vectorDocs, keywordDocs);
为什么要混合检索?
- 向量检索:理解语义,找到相关内容
- 关键词检索:精确匹配专业术语
- 两者结合,提高准确率
4. 流式输出(Streaming)
typescript
// 后端:使用 SSE(Server-Sent Events)
const stream = await chain.stream({ question: query });
for await (const chunk of stream) {
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
}
// 前端:实时接收并显示
const response = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ message }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
setMessages((prev) => [...prev, { text }]);
}
流式输出的好处:
- 实时显示 AI 生成过程
- 提升用户体验
- 类似 ChatGPT 的打字效果
🚀 快速开始
1. 获取 API Key
访问 智谱 AI 开放平台:
- 注册并登录
- 进入控制台 → API 管理
- 创建 API Key
- 复制 Key
💡 新用户有免费额度,足够测试使用!
2. 克隆并启动项目
bash
# 克隆项目
git clone https://github.com/jinghaonode/rag-knowledge-assistant.git
cd rag-knowledge-assistant
# 配置 API Key
cp backend/.env.example backend/.env
# 编辑 backend/.env,填入你的 API Key
# 一键启动
./start.sh # macOS/Linux
start.bat # Windows
3. 访问应用
- 🌐 前端界面:http://localhost:5173
- 🔌 后端 API:http://localhost:3001
📁 项目结构
bash
rag-knowledge-assistant/
├── backend/ # 后端服务
│ ├── src/
│ │ ├── server.ts # Express 服务器
│ │ ├── routes/ # API 路由
│ │ │ ├── chat.ts # 聊天接口
│ │ │ ├── upload.ts # 上传接口
│ │ │ └── knowledge.ts # 知识库管理
│ │ └── services/ # 核心服务
│ │ ├── vectorstore.ts # 向量存储
│ │ └── ragChain.ts # RAG 检索链
│ └── .env.example # 环境变量模板
├── frontend/ # 前端应用
│ └── src/
│ ├── App.tsx # 主应用
│ └── components/ # React 组件
│ ├── ChatMessage.tsx
│ ├── ChatInput.tsx
│ └── KnowledgeModal.tsx
└── README.md
🎓 核心代码解析
1. 文档上传处理
typescript
// backend/src/routes/upload.ts
import multer from "multer";
import { vectorStore } from "../services/vectorstore.js";
const upload = multer({ dest: "uploads/" });
router.post("/", upload.single("file"), async (req, res) => {
try {
const file = req.file;
// 1. 加载文档
const loader = getLoader(file.path, file.mimetype);
const docs = await loader.load();
// 2. 文本分块
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 500,
chunkOverlap: 100,
});
const splitDocs = await splitter.splitDocuments(docs);
// 3. 向量化并存储
await vectorStore.addDocuments(splitDocs);
res.json({ success: true, chunks: splitDocs.length });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
2. 智能问答实现
typescript
// backend/src/routes/chat.ts
import { ragChain } from "../services/ragChain.js";
router.post("/", async (req, res) => {
const { message } = req.body;
// 设置 SSE 响应头
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
try {
// 流式生成回答
const stream = await ragChain.stream({
question: message,
});
for await (const chunk of stream) {
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
}
res.write("data: [DONE]\n\n");
res.end();
} catch (error) {
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
res.end();
}
});
3. RAG 检索链
typescript
// backend/src/services/ragChain.ts
import { ChatOpenAI } from "@langchain/openai";
import { createRetrievalChain } from "langchain/chains/retrieval";
// 初始化 GLM-4 模型
const llm = new ChatOpenAI({
modelName: "glm-4-flash",
openAIApiKey: process.env.GLM_API_KEY,
configuration: {
baseURL: "https://open.bigmodel.cn/api/paas/v4",
},
streaming: true,
});
// 创建检索器
const retriever = vectorStore.asRetriever({
k: 6, // 检索 6 个最相关的文档块
});
// 创建 RAG 链
export const ragChain = createRetrievalChain({
llm,
retriever,
prompt: `你是一个智能助手。基于以下文档内容回答问题:
{context}
问题:{question}
请给出准确、详细的回答。如果文档中没有相关信息,请明确说明。`,
});
4. 前端聊天组件
typescript
// frontend/src/components/ChatInput.tsx
const ChatInput = ({ onSend }) => {
const [message, setMessage] = useState("");
const handleSend = async () => {
if (!message.trim()) return;
// 发送消息
onSend(message);
setMessage("");
// 调用 API
const response = await fetch("http://localhost:3001/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message }),
});
// 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split("\n");
for (const line of lines) {
if (line.startsWith("data: ")) {
const data = line.slice(6);
if (data === "[DONE]") break;
const parsed = JSON.parse(data);
onReceive(parsed);
}
}
}
};
return (
<div className="flex gap-2">
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && handleSend()}
placeholder="输入你的问题..."
className="flex-1 px-4 py-2 border rounded-lg"
/>
<button
onClick={handleSend}
className="px-6 py-2 bg-blue-500 text-white rounded-lg"
>
发送
</button>
</div>
);
};
🎨 进阶优化
1. 查询优化
typescript
// 提取关键词
const extractKeywords = async (query: string) => {
const prompt = `从以下问题中提取 3-5 个关键词:${query}`;
const response = await llm.call(prompt);
return response.split(",").map((k) => k.trim());
};
// 查询改写
const rewriteQuery = async (query: string) => {
const prompt = `将以下问题改写为更适合检索的形式:${query}`;
return await llm.call(prompt);
};
2. 相似度过滤
typescript
// 只保留相似度高于阈值的文档
const filteredDocs = docs.filter((doc) => {
return doc.metadata.score > 0.45; // 相似度阈值
});
3. 答案质量提升
typescript
const prompt = `你是一个专业的智能助手。请基于以下文档内容回答问题。
要求:
1. 回答要准确、详细
2. 如果文档中没有相关信息,明确说明
3. 引用具体的文档来源
4. 使用 Markdown 格式,让回答更易读
文档内容:
{context}
问题:{question}
回答:`;
🚀 部署上线
1. 本地构建
bash
# 后端构建
cd backend
npm run build
# 前端构建
cd frontend
npm run build
2. 部署到服务器
bash
# 使用 PM2 管理进程
npm install -g pm2
# 启动后端
cd backend
pm2 start dist/server.js --name rag-backend
# 启动前端(使用 nginx 或其他静态服务器)
3. Docker 部署
dockerfile
# Dockerfile
FROM node:18-alpine
WORKDIR /app
# 安装依赖
COPY package*.json ./
RUN npm install
# 复制代码
COPY . .
# 构建
RUN npm run build
# 启动
CMD ["npm", "start"]
💡 扩展思路
基于这个项目,你可以:
1. 接入其他 AI 模型
typescript
// OpenAI
const llm = new ChatOpenAI({
modelName: "gpt-4",
openAIApiKey: process.env.OPENAI_API_KEY,
});
// DeepSeek
const llm = new ChatOpenAI({
modelName: "deepseek-chat",
openAIApiKey: process.env.DEEPSEEK_API_KEY,
configuration: {
baseURL: "https://api.deepseek.com",
},
});
2. 添加更多文档格式
typescript
// Excel
import { ExcelLoader } from "langchain/document_loaders";
// CSV
import { CSVLoader } from "langchain/document_loaders";
// 网页
import { CheerioWebBaseLoader } from "langchain/document_loaders";
3. 实现多轮对话
typescript
// 添加对话历史
const chatHistory = [];
const response = await ragChain.call({
question: message,
chat_history: chatHistory,
});
chatHistory.push({ question: message, answer: response });
4. 添加用户认证
typescript
import jwt from "jsonwebtoken";
// JWT 认证中间件
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return res.status(401).json({ error: "Unauthorized" });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: "Invalid token" });
}
};
📊 性能优化建议
1. 缓存策略
typescript
import NodeCache from "node-cache";
const cache = new NodeCache({ stdTTL: 600 }); // 10 分钟缓存
// 缓存检索结果
const getCachedResults = async (query: string) => {
const cached = cache.get(query);
if (cached) return cached;
const results = await vectorStore.similaritySearch(query);
cache.set(query, results);
return results;
};
2. 批量处理
typescript
// 批量上传文档
const uploadBatch = async (files: File[]) => {
const promises = files.map((file) => processDocument(file));
await Promise.all(promises);
};
3. 数据库优化
typescript
// 定期清理过期数据
const cleanupOldDocuments = async () => {
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
await vectorStore.delete({
filter: `timestamp < ${thirtyDaysAgo}`,
});
};
🎯 学习路径建议
如果你是 AI 开发新手,建议按以下顺序学习:
第一阶段:运行起来(1 天)
- ✅ 克隆项目并成功运行
- ✅ 上传文档并测试问答
- ✅ 理解整体流程
第二阶段:理解原理(3-5 天)
- 📚 学习 RAG 的基本概念
- 🔍 理解向量化和语义搜索
- 🤖 了解 LangChain.js 的使用
- 💬 学习流式输出的实现
第三阶段:动手改造(1-2 周)
- 🎨 修改 UI 样式
- ⚙️ 调整检索参数
- 🔧 添加新功能
- 🚀 部署到服务器
第四阶段:深入优化(持续)
- 📊 性能优化
- 🔐 安全加固
- 📈 监控和日志
- 🎯 用户体验提升
🤝 参与贡献
欢迎提交 Issue 和 Pull Request!
项目地址: github.com/jinghaonode...
如果这个项目对你有帮助,请给个 Star ⭐️
📚 参考资源
💬 最后的话
作为前端开发者,我们不应该被 Python 和复杂的环境配置挡在 AI 开发的门外。
这个项目证明了:用 JavaScript/TypeScript 也能做出强大的 AI 应用!
希望这个项目能帮助更多前端开发者:
- 🎯 快速入门 AI 开发
- 📚 理解 RAG 技术原理
- 🚀 搭建自己的 AI 应用
- 💡 激发更多创意想法
AI 时代已经到来,让我们一起用熟悉的技术栈,创造更多可能!
如果你觉得这篇文章有帮助,欢迎:
- 👍 点赞支持
- 💬 评论交流
- 🔗 分享给更多前端小伙伴
- ⭐️ 给项目一个 Star
有任何问题,欢迎在评论区讨论!
作者: \hnode
项目地址: github.com/jinghaonode...
联系方式: 1137772623@qq.com