从零到一!前端搭建本地轻量化 RAG 问答系统
1. 引言大模型虽强但面对私有知识库、企业内部文档时往往存在「幻觉」和「知识过时」的问题。RAGRetrieval-Augmented Generation检索增强生成正是解决这一痛点的主流方案先检索出与问题相关的文档片段再交给大模型生成回答。传统 RAG 方案往往依赖后端服务、向量数据库和复杂的部署链路。而随着浏览器能力的增强前端完全可以在本地搭建一套轻量化的 RAG 问答系统——无需服务器、无需安装数据库数据不出浏览器隐私安全。本文将从零到一带你用纯前端技术栈Vite React Transformers.js localStorage搭建一套可运行的本地 RAG 问答系统。2. 技术选型与整体架构2.1 技术栈模块选型说明构建工具Vite极快的开发体验UI 框架React组件化开发向量化模型Transformers.js all-MiniLM-L6-v2纯浏览器端运行无需后端向量存储localStorage 自研余弦相似度检索轻量、零依赖生成模型可选本地模型 / 云端 API按需接入2.2 整体流程用户上传文档文本切分向量化 Embedding向量存入 localStorage用户提问问题向量化余弦相似度检索 Top-K拼接上下文 问题调用大模型生成回答3. 环境准备与项目初始化首先确保本机已安装 Node.js 18然后执行npmcreate vitelatest local-rag ----templatereact-tscdlocal-ragnpminstallnpminstallhuggingface/transformers安装完成后启动开发服务器npmrun dev4. 文本切分把长文档拆成小块RAG 检索的粒度很关键。整篇文档直接向量化检索精度差切得太碎又丢失上下文。这里实现一个简单的按段落 字符数控制的切分器。// src/utils/chunker.tsexportinterfaceChunk{id:string;text:string;source:string;}exportfunctionchunkText(text:string,source:string,maxLen500):Chunk[]{constparagraphstext.split(/\n\s*\n/);constchunks:Chunk[][];letbuffer;for(constparaofparagraphs){consttrimmedpara.trim();if(!trimmed)continue;if((buffer\ntrimmed).lengthmaxLenbuffer){chunks.push({id:crypto.randomUUID(),text:buffer.trim(),source,});buffertrimmed;}else{bufferbuffer?buffer\ntrimmed:trimmed;}}if(buffer.trim()){chunks.push({id:crypto.randomUUID(),text:buffer.trim(),source,});}returnchunks;}5. 向量化在浏览器中运行 Embedding 模型借助 Transformers.js我们可以在浏览器端直接加载并运行 MiniLM 模型把文本转成 384 维向量。// src/utils/embedder.tsimport{pipeline}fromhuggingface/transformers;letembedder:anynull;exportasyncfunctiongetEmbedder(){if(!embedder){embedderawaitpipeline(feature-extraction,Xenova/all-MiniLM-L6-v2);}returnembedder;}exportasyncfunctionembedTexts(texts:string[]):Promisenumber[][]{constmodelawaitgetEmbedder();constoutputsawaitmodel(texts,{pooling:mean,normalize:true});returnoutputs.tolist();}提示首次加载模型需要下载约 90MB 文件之后浏览器会缓存二次访问秒开。6. 向量存储与检索localStorage 余弦相似度6.1 存储层localStorage 无法直接存高维数组这里用 JSON 序列化存储并封装读写接口。// src/utils/store.tsimporttype{Chunk}from./chunker;interfaceStoredChunkextendsChunk{vector:number[];}constSTORAGE_KEYlocal-rag-chunks;exportfunctionsaveChunks(chunks:StoredChunk[]):void{localStorage.setItem(STORAGE_KEY,JSON.stringify(chunks));}exportfunctionloadChunks():StoredChunk[]{constrawlocalStorage.getItem(STORAGE_KEY);returnraw?JSON.parse(raw):[];}exportfunctionclearChunks():void{localStorage.removeItem(STORAGE_KEY);}6.2 余弦相似度检索// src/utils/search.tsimporttype{StoredChunk}from./store;functioncosineSimilarity(a:number[],b:number[]):number{letdot0,normA0,normB0;for(leti0;ia.length;i){dota[i]*b[i];normAa[i]*a[i];normBb[i]*b[i];}returndot/(Math.sqrt(normA)*Math.sqrt(normB)1e-8);}exportfunctionsearchChunks(queryVector:number[],chunks:StoredChunk[],topK3):StoredChunk[]{returnchunks.map((chunk)({chunk,score:cosineSimilarity(queryVector,chunk.vector),})).sort((a,b)b.score-a.score).slice(0,topK).map((item)item.chunk);}7. 组装问答把检索结果交给大模型检索到 Top-K 相关片段后把它们拼进 Prompt再调用大模型生成回答。这里以 OpenAI 兼容接口为例你也可以替换为本地 Ollama 或其他模型。// src/utils/qa.tsimport{searchChunks}from./search;import{loadChunks}from./store;exportasyncfunctionaskQuestion(question:string,queryVector:number[],apiKey:string,baseUrlhttps://api.openai.com/v1):Promisestring{constchunksloadChunks();consttopChunkssearchChunks(queryVector,chunks,3);constcontexttopChunks.map((c,i)【片段${i1}】\n${c.text}).join(\n\n);constprompt请基于以下资料回答问题。如果资料中没有相关信息请直接说明资料中未找到相关内容。 资料${context}问题${question}回答;constrespawaitfetch(${baseUrl}/chat/completions,{method:POST,headers:{Content-Type:application/json,Authorization:Bearer${apiKey},},body:JSON.stringify({model:gpt-4o-mini,messages:[{role:user,content:prompt}],temperature:0.3,}),});constdataawaitresp.json();returndata.choices[0].message.content;}8. 前端界面串联完整流程最后用 React 把以上模块串起来实现「上传文档 → 建立索引 → 提问 → 得到回答」的完整交互。// src/App.tsx import { useState } from react; import { chunkText } from ./utils/chunker; import { embedTexts } from ./utils/embedder; import { saveChunks, loadChunks, clearChunks } from ./utils/store; import { askQuestion } from ./utils/qa; export default function App() { const [docText, setDocText] useState(); const [question, setQuestion] useState(); const [answer, setAnswer] useState(); const [loading, setLoading] useState(false); const [indexed, setIndexed] useState(false); const handleIndex async () { setLoading(true); try { const chunks chunkText(docText, 本地文档); const vectors await embedTexts(chunks.map((c) c.text)); saveChunks(chunks.map((c, i) ({ ...c, vector: vectors[i] }))); setIndexed(true); alert(索引完成共 ${chunks.length} 个片段); } finally { setLoading(false); } }; const handleAsk async () { if (!question.trim()) return; setLoading(true); try { const [queryVector] await embedTexts([question]); const apiKey prompt(请输入 API Key本地运行仅存于内存) || ; const res await askQuestion(question, queryVector, apiKey); setAnswer(res); } finally { setLoading(false); } }; return ( div style{{ maxWidth: 720, margin: 0 auto, padding: 24 }} h1本地轻量化 RAG 问答/h1 section h21. 上传文档/h2 textarea value{docText} onChange{(e) setDocText(e.target.value)} rows{8} style{{ width: 100% }} placeholder粘贴你的文档内容... / button onClick{handleIndex} disabled{loading || !docText.trim()} {loading ? 处理中... : 建立索引} /button {indexed ( button onClick{() { clearChunks(); setIndexed(false); }} 清空索引 /button )} /section section h22. 提问/h2 input value{question} onChange{(e) setQuestion(e.target.value)} style{{ width: 100%, padding: 8 }} placeholder输入你的问题... / button onClick{handleAsk} disabled{loading || !indexed} 提问 /button /section {answer ( section h2回答/h2 div style{{ whiteSpace: pre-wrap, background: #f5f5f5, padding: 16, borderRadius: 8 }} {answer} /div /section )} /div ); }9. 效果演示与运行在项目根目录执行npm run dev浏览器打开http://localhost:5173粘贴一段产品文档或技术资料点击「建立索引」等待模型加载与向量化首次约 10~30 秒输入问题点击「提问」系统检索相关片段并调用大模型生成回答。10. 总结与优化方向本文实现了一套完全运行在浏览器端的轻量化 RAG 系统核心亮点零后端向量化、存储、检索全部在前端完成隐私安全文档数据不离开本地浏览器轻量依赖仅需 Transformers.js 一个核心库。后续可优化方向接入 Web Worker避免向量化阻塞 UI 线程使用 IndexedDB 替代 localStorage突破 5MB 存储上限引入重排序Rerank模型提升检索精度支持 PDF、Word 等格式解析接入本地 Ollama 模型实现完全离线运行。希望这篇文章能帮你快速上手前端 RAG 开发动手试试吧

相关新闻

事务 Outbox 与幂等键:跨过“提交后崩溃”的缝隙

事务 Outbox 与幂等键:跨过“提交后崩溃”的缝隙

复用 replay.py 的恢复结论 上一篇产出的 replay.py 会从 EventStore.load 恢复 ReplayResult,并明确让 pending_commands 永远不从重放结果派发命令。这留下一个必须回答的问题:真正待执行的命令存在哪里?本篇以 replay(...).last_seq 作为乐…

2026/10/14 20:08:15 阅读更多 →
AI充电桩管理系统:智能运维与轻量化开发指南-充电桩源码解析

AI充电桩管理系统:智能运维与轻量化开发指南-充电桩源码解析

博主介绍: 所有项目都配有从入门到精通的安装教程,可二开,提供核心代码讲解,项目指导。 项目配有对应开发文档、解析等 项目都录了发布和功能操作演示视频;项目的界面和功能都可以定制,包安装运行&#xff…

2026/9/25 20:10:24 阅读更多 →
如何用AtlasOS优化Windows系统:解决卡顿、隐私泄露的终极方案

如何用AtlasOS优化Windows系统:解决卡顿、隐私泄露的终极方案

如何用AtlasOS优化Windows系统:解决卡顿、隐私泄露的终极方案 【免费下载链接】Atlas 🚀 An open and lightweight modification to Windows, designed to optimize performance, privacy and usability. 项目地址: https://gitcode.com/GitHub_Trendi…

2026/10/14 16:45:29 阅读更多 →

最新新闻

Helm Chart中ConfigMap挂载与配置管理:从模板渲染到滚动更新实战

Helm Chart中ConfigMap挂载与配置管理:从模板渲染到滚动更新实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 20:08:16 阅读更多 →
RK3588边缘部署图像分类模型实测:INT8量化与性能全解析

RK3588边缘部署图像分类模型实测:INT8量化与性能全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 20:08:16 阅读更多 →
实习校招通关指南:从简历筛选到面试反问的完整策略

实习校招通关指南:从简历筛选到面试反问的完整策略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 20:08:16 阅读更多 →
硅基流动上手实测:150多款模型免费用,闭源缺口这样补齐

硅基流动上手实测:150多款模型免费用,闭源缺口这样补齐

硅基流动(SiliconFlow)是国产MaaS赛道关注度最高的平台之一,本文从功能、易用性、性价比、创新与稳定性五个角度做一次实测复盘,并给出闭源模型能力的补齐思路。平台背景与定位 硅基流动成立于2023年,总部位于北京,定位第三方中立…

2026/10/14 20:08:16 阅读更多 →
PowerShell SuperPowers实战指南:安装、技能清单与踩坑经验

PowerShell SuperPowers实战指南:安装、技能清单与踩坑经验

如果你长期在 Windows 环境下用 PowerShell 处理运维和日常自动化,大概率早就听说过 SuperPowers 这个名字。它不是一个游戏外挂,而是社区驱动的 PowerShell 功能模块,核心思路是把平时要写半天的脚本能力,封装成一个个可以直接调…

2026/10/14 20:08:16 阅读更多 →
Windows进程隔离与DLL加载原理科普

Windows进程隔离与DLL加载原理科普

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 20:07:15 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →