☰
RAG知识库问答系统毕业设计:架构设计与实操避坑指南
2026/10/1 5:39:12 网站建设 项目流程

1. 项目缘起与整体设计思路

1.1 为什么选“RAG + 知识库问答”作为毕业设计

先说结论:这个选题在近两年的计算机毕业设计里属于“性价比极高”的一类。原因很直接——它同时踩中了三个点:技术栈主流、业务场景好讲、答辩时容易演示。传统的关键词检索问答系统,用户问“报销流程要几天”,系统只能匹配到包含“报销”两个字的文档,至于文档里到底写的是三天还是五天,它根本理解不了。而大模型虽然能理解语义,但它有两个致命短板:一是知识截止到训练时间,二是会一本正经地胡说八道(业内叫“幻觉”)。

RAG(Retrieval-Augmented Generation,检索增强生成)就是来解决这个矛盾的。它的核心思路用一句话概括:先从你自己的知识库里检索出相关片段,再把这些片段作为“参考资料”塞给大模型,让它基于资料回答。这样既保留了大模型的语言理解能力,又保证了答案有据可查、来源可控。

我当初选这个题,还有一个很现实的考虑:它不像纯算法课题那样需要跑大量实验调参,也不像纯管理系统那样毫无技术亮点。它刚好卡在“有技术含量”和“能按时做完”之间。你只要把文档切分、向量检索、提示词组装这三块打通,一个能跑通演示的系统就成型了。

1.2 系统整体架构拆解

整个系统我采用的是前后端分离 + 独立AI服务层的三段式结构。这么设计不是为了炫技,而是因为RAG的检索和生成逻辑跟普通的增删改查差别太大,硬塞进业务后端会让代码变得非常难维护。

具体分层是这样的:

  • 前端层(Vue):负责聊天界面、知识库管理界面、文档上传界面。用户在这里提问,答案以流式方式逐字显示。
  • 业务后端(Spring Boot):负责用户认证、知识库的增删改查、文档元数据管理、会话记录存储。它不直接处理向量计算。
  • AI服务层(Python):负责文档解析、文本切分、向量化、向量检索、调用大模型生成答案。这一层用Python是因为LangChain、sentence-transformers这些生态在Python里最成熟。

三层之间通过HTTP接口通信。业务后端把用户问题转发给AI服务层,拿到答案后再返回给前端。有同学会问:为什么不全部用Java做?因为Java侧的RAG生态(比如LangChain4j)虽然这两年进步很快,但文档解析、embedding模型加载这些环节,Python的库更全、踩坑更少。毕业设计时间有限,选成熟生态比追求“技术统一”更明智。

1.3 技术选型的取舍逻辑

技术栈这块我列个表,把每个选择背后的理由说清楚,答辩时老师大概率会问“你为什么用这个不用那个”。

技术选型替代方案选择理由
后端框架Spring Boot 3.xDjango / Flask国内企业主流,答辩认可度高,生态成熟
前端框架Vue 3 + Element PlusReact上手快,组件库全,适合快速搭管理界面
数据库MySQL 8.0PostgreSQL存业务数据足够,学校环境普遍已装
向量存储内存向量库(FAISS/Chroma)Milvus / Qdrant毕业设计数据量小,无需独立部署向量数据库
大模型本地Ollama / 在线API纯本地训练本地部署零成本,在线API效果好但需额度
文档解析PyPDF2 + python-docx商业解析服务免费够用,支持PDF和Word两种主流格式

这里重点说向量存储。很多教程一上来就让你装Milvus,结果光Docker配置就卡两天。我的建议是:毕业设计阶段,知识库文档通常就几十到几百个片段,用FAISS或者Chroma这种嵌入式向量库完全够用,它就是一个Python库,pip装完就能用,数据存本地文件。等以后真要做企业级应用,再迁移到Milvus也不迟。这个取舍能帮你省下至少一周的部署时间。

2. 核心细节解析与实操要点

2.1 RAG的完整数据流:从文档到答案

很多人对RAG的理解停留在“检索+生成”四个字,但真正动手时你会发现,中间有大量细节决定成败。我把完整流程拆成两个阶段来讲。

离线阶段(文档入库):

  1. 用户上传PDF或Word文档
  2. 解析文档,提取纯文本
  3. 按规则切分成若干文本块(chunk)
  4. 每个文本块通过embedding模型转成向量
  5. 向量 + 原文 + 元数据存入向量库

在线阶段(用户提问):

  1. 用户输入问题
  2. 问题同样转成向量
  3. 在向量库中做相似度检索,取Top-K个最相关的文本块
  4. 把检索结果拼进提示词模板
  5. 调用大模型生成答案
  6. 返回答案 + 引用来源

看起来很简单对吧?但每一步都有坑。比如切分这一步,如果你按固定500字硬切,很可能把一句话从中间切断,导致语义不完整。检索出来的片段驴唇不对马嘴,大模型自然答不好。

2.2 文本切分:最容易被忽视的关键环节

文本切分(chunking)是RAG里最不起眼但影响最大的环节。我踩过的坑是:一开始按固定长度切,结果检索命中率惨不忍睹。后来改成按语义边界切分,效果立竿见影。

具体策略是这样的:

  • 优先按段落切:遇到换行符就切一刀,保证每个块是完整段落
  • 设置块大小上限:单块控制在300-500字,太长了检索精度下降,太短了语义不完整
  • 设置重叠区:相邻块之间保留50-100字重叠,防止关键信息刚好卡在边界被切断
  • 保留标题层级:如果文档有“第一章”“1.1节”这种结构,把标题也带进块里,检索时能提供上下文

用代码表示大概是这样(伪代码逻辑):

def split_text(text, chunk_size=400, overlap=80): paragraphs = text.split("\n\n") chunks = [] current = "" for para in paragraphs: if len(current) + len(para) <= chunk_size: current += para + "\n\n" else: if current: chunks.append(current.strip()) # 保留重叠部分 current = current[-overlap:] + para + "\n\n" if current: chunks.append(current.strip()) return chunks

注意:chunk_size不是越大越好。我实测下来,中文文档400字左右是比较舒服的区间。英文可以适当放大到800字符。这个参数需要根据你的文档类型微调,没有万能值。

2.3 向量化与检索:embedding模型怎么选

向量化就是把文本变成一串数字(向量),语义相近的文本,向量距离也近。这一步的核心是选embedding模型。

对于毕业设计,我推荐两条路线:

  • 零成本路线:用text2vec-base-chinese或bge-small-zh这类开源中文模型,本地加载,完全免费。模型大小几百MB,普通笔记本就能跑。
  • 省事路线:调用在线embedding接口。效果好,但要注意额度和网络稳定性。

检索这块,核心参数是Top-K,也就是每次取最相似的几个块。K太小可能漏掉关键信息,K太大又会引入噪声、撑爆提示词长度。我的经验值是K=3到5。如果文档特别多,可以先粗筛再精排,但毕业设计阶段没必要搞这么复杂。

相似度计算一般用余弦相似度。简单理解就是看两个向量的夹角,夹角越小越相似。FAISS默认用的是内积或L2距离,用之前记得把向量归一化,这样内积就等于余弦相似度。

2.4 提示词工程:让大模型“照着资料说话”

检索出来的内容怎么交给大模型,直接决定了答案质量。我见过很多同学的提示词就一句话:“请根据以下内容回答问题”,结果模型该胡说还是胡说。

一个靠谱的提示词模板应该包含这几个要素:

你是一个严谨的知识库助手。请严格根据下面提供的【参考资料】回答用户问题。 规则: 1. 如果参考资料中没有相关信息,直接回答“根据现有资料无法回答该问题”,不要编造。 2. 回答时尽量引用资料中的原文表述。 3. 如果资料之间有冲突,指出冲突并说明。 【参考资料】 {context} 【用户问题】 {question} 【回答】

这个模板的关键在于明确告诉模型“不知道就说不知道”。不加这条约束,模型遇到检索不到的内容时会强行编答案,这是RAG系统最常见的翻车点。

3. 实操过程与核心环节实现

3.1 环境搭建:从零到能跑

先把环境清单列出来,避免你装到一半发现缺东西。

  • JDK 17(Spring Boot 3.x要求)
  • Maven 3.8+
  • Node.js 18+(Vue 3要求)
  • MySQL 8.0
  • Python 3.10+(AI服务层)
  • Ollama(本地大模型运行环境,可选)

后端初始化:用Spring Initializr生成项目骨架,勾选Web、MySQL Driver、MyBatis-Plus。pom.xml里额外加上文件上传相关的依赖。数据库建库建表,核心表就三张:用户表、知识库表、文档表。会话记录表可以后面再加。

前端初始化:npm create vue@latest一路回车,然后装Element Plus和axios。聊天界面用Element Plus的el-input加一个消息列表就行,不需要搞太花哨。

AI服务层初始化:建一个Python虚拟环境,装这几个包:

pip install fastapi uvicorn langchain faiss-cpu sentence-transformers pypdf python-docx

如果你用Ollama跑本地模型,再装ollama这个Python包。模型拉一个qwen2:7b或者llama3:8b,前者中文效果更好。

3.2 文档上传与解析的完整链路

用户在Vue页面上传一个PDF,这个文件要经过好几道手才能变成向量库里的数据。我把链路走一遍。

第一步:前端上传。用Element Plus的el-upload组件,设置action指向Spring Boot的上传接口。注意要限制文件类型和大小,不然用户传个视频上来你的解析器直接崩。

第二步:后端接收并转发。Spring Boot收到文件后,先存到本地临时目录,记录元数据到MySQL,然后把文件路径发给Python服务。这里有个细节:文件传输用multipart/form-data,不要用base64编码,大文件base64会撑爆内存。

第三步:Python解析。根据文件后缀选择解析器:

from pypdf import PdfReader from docx import Document def parse_document(file_path): if file_path.endswith(".pdf"): reader = PdfReader(file_path) return "\n\n".join(page.extract_text() for page in reader.pages) elif file_path.endswith(".docx"): doc = Document(file_path) return "\n\n".join(p.text for p in doc.paragraphs) else: raise ValueError("不支持的文件格式")

第四步:切分 + 向量化 + 入库。解析出的文本走前面说的切分逻辑,然后逐块调用embedding模型,把向量和原文一起存进FAISS索引。

实操心得:解析PDF时经常遇到扫描件,extract_text()返回空字符串。这种情况要么用OCR,要么直接提示用户“该PDF为扫描件,请上传文字版”。毕业设计阶段建议直接跳过扫描件,别给自己找麻烦。

3.3 问答接口的实现细节

问答接口是整个系统的心脏。我用FastAPI写一个/chat接口,接收问题,返回答案。

from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class ChatRequest(BaseModel): question: str kb_id: int top_k: int = 4 @app.post("/chat") def chat(req: ChatRequest): # 1. 问题向量化 q_vec = embed(req.question) # 2. 检索 docs = vector_store.search(q_vec, top_k=req.top_k) # 3. 组装提示词 context = "\n\n".join(d.page_content for d in docs) prompt = build_prompt(context, req.question) # 4. 调用大模型 answer = llm.generate(prompt) # 5. 返回答案和来源 return { "answer": answer, "sources": [d.metadata for d in docs] }

前端拿到答案后,用打字机效果逐字显示。这个效果不是必须的,但演示时观感好很多。实现方式很简单:后端返回完整答案,前端用setInterval每隔几十毫秒显示一个字符。

3.4 流式输出的实现思路

如果想让答案像ChatGPT那样一个字一个字蹦出来,需要用到流式输出。Spring Boot这边可以用SseEmitter,Python这边用FastAPI的StreamingResponse。

核心逻辑是:大模型生成时是逐token输出的,我们把这个过程透传给前端。Python侧:

from fastapi.responses import StreamingResponse @app.post("/chat/stream") def chat_stream(req: ChatRequest): def generate(): for token in llm.stream(prompt): yield f"data: {token}\n\n" return StreamingResponse(generate(), media_type="text/event-stream")

Spring Boot侧接收SSE流,再转发给前端。这块代码稍微绕一点,但网上模板很多,照着改就行。

注意:流式输出和引用来源展示有点冲突,因为流式过程中你还没拿到完整的检索结果。我的做法是先返回来源列表,再流式返回答案,前端分两块显示。

4. 常见问题与排查技巧实录

4.1 检索不准:答案答非所问

这是RAG系统最高频的问题。用户问“年假怎么算”,检索出来的却是“病假规定”。排查思路按顺序来:

先看切分。把检索到的块打印出来,看看内容是不是完整的。如果块被切得七零八落,先调切分参数。

再看embedding模型。有些通用模型对中文支持不好,换成bge-small-zh这类中文专用模型试试。

然后看Top-K。K太小可能漏掉正确块,调大到5或6试试。如果调大后噪声变多,说明需要加个相似度阈值,低于阈值的块直接丢弃。

最后看提示词。有时候检索是对的,但模型没好好利用资料。在提示词里强调“必须基于参考资料回答”,通常能改善。

4.2 大模型胡说八道:明明没资料却硬编

这个问题的根源是模型被训练成“有问必答”,它不愿意说“我不知道”。解决办法是在提示词里给明确的“逃生通道”:

如果参考资料中找不到答案,请直接回复: “抱歉,当前知识库中没有相关信息。”

另外,可以在检索后加一个相似度判断:如果最高相似度低于某个阈值(比如0.5),直接返回“未找到相关内容”,根本不调用大模型。这样既省算力,又避免幻觉。

4.3 性能问题:响应太慢

本地跑7B模型,一次问答等十几秒是常态。优化方向有几个:

  • 减少Top-K:检索块少了,提示词短了,生成就快
  • 限制生成长度:设置max_tokens=500,别让模型写小作文
  • 用更小的模型:3B模型速度翻倍,效果略降但够用
  • 加缓存:相同问题直接返回缓存答案

如果是演示场景,可以提前准备几个问题,把答案缓存好,现场演示时秒回。

4.4 常见问题速查表

现象可能原因排查方向
上传文档后检索不到解析失败或向量未入库检查解析日志,确认向量库数量
答案与问题无关切分不合理或embedding模型差打印检索块,换中文模型
模型编造答案提示词约束不足加“不知道就说不知道”规则
接口超时大模型生成太慢减Top-K,限max_tokens
中文乱码编码问题统一用UTF-8
向量库重启后丢失用了内存模式改用持久化存储路径

4.5 几个我踩过的坑

坑一:MySQL连接池配置不当。Spring Boot默认连接池是HikariCP,最大连接数默认10。如果并发上传文档,很容易连接耗尽。在application.yml里把maximum-pool-size调到20,connection-timeout设长一点。

坑二:文件路径用相对路径。开发时没问题,部署到服务器上就找不到文件了。统一用绝对路径,或者配置在application.yml里。

坑三:忘记处理跨域。前后端分离,Vue跑在5173端口,Spring Boot跑在8080,不配CORS浏览器直接拦截。加一个@CrossOrigin注解或者全局配置就行。

坑四:embedding模型首次加载慢。第一次调用要下载模型文件,几百MB,网络不好能卡十分钟。提前手动下载好,放到缓存目录。

5. 系统扩展与优化方向

5.1 从基础RAG到Agentic RAG

基础RAG的流程是线性的:检索一次,生成一次。但有些问题需要多步推理,比如“对比A文档和B文档中关于X的规定差异”。这时候就需要Agentic RAG——让模型自己决定要不要再检索、检索什么。

实现思路是引入一个“路由”环节:模型先判断问题类型,如果是简单事实查询,走基础RAG;如果是复杂对比,先分别检索两个文档,再让模型对比。这个扩展能让系统显得更有深度,答辩时是个加分项。

5.2 知识库的增量更新

实际使用中,知识库文档会不断新增。如果每次新增都全量重建向量库,效率太低。可以做增量更新:新文档单独解析、切分、向量化,追加到现有索引。FAISS支持add操作,不用重建。

但要注意:如果文档被删除,FAISS不支持直接删除向量。简单做法是重建索引,或者用支持删除的向量库(如Chroma)。

5.3 多知识库隔离

一个系统里可能有多个知识库,比如“人事制度库”“技术文档库”。检索时要限定在指定知识库内。实现方式是在向量元数据里加一个kb_id字段,检索时加过滤条件。FAISS本身不支持元数据过滤,需要检索后手动筛,或者换Chroma这种支持过滤的库。

6. 毕业设计文档与答辩准备

6.1 论文结构建议

计算机毕业设计的论文一般包括:绪论、相关技术介绍、需求分析、系统设计、系统实现、系统测试、总结与展望。RAG这个题的重点应该放在系统设计和实现两章,把切分策略、检索流程、提示词设计讲清楚。测试章节要有对比实验,比如“固定切分 vs 语义切分”的检索命中率对比,这种数据能让论文显得扎实。

6.2 答辩演示的注意事项

演示时最怕现场翻车。我的建议是:

  • 提前录屏:把完整问答流程录下来,万一现场网络或模型出问题,直接放录屏
  • 准备固定问题:选3-5个知识库里明确有答案的问题,确保演示效果
  • 展示引用来源:让老师看到答案是有出处的,这是RAG的核心卖点
  • 准备对比:演示一下“不用RAG时模型胡说”和“用RAG后准确回答”的对比,冲击力很强

6.3 源码与文档的组织

源码按模块分目录:backend(Spring Boot)、frontend(Vue)、ai-service(Python)。每个目录下放一个README说明启动方式。数据库建表脚本单独放一个sql目录。文档方面,除了论文,建议再写一份部署文档.md,把环境要求、启动步骤、常见问题写清楚。这份文档在答辩时能体现你的工程素养。

最后分享一个我在调试RAG时的小技巧:把每次检索到的文本块和最终答案都打到日志里。这样当答案不对时,你能快速判断是检索环节的问题还是生成环节的问题。这个习惯帮我省了大量排查时间,你也一定要养成。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询