RAG外挂知识库问答系统:Python实现从文档入库到检索生成

发布时间:2026/10/19 0:38:32
RAG外挂知识库问答系统:Python实现从文档入库到检索生成 简介一套基于大语言模型API可接入本地部署或商用接口的外挂知识库问答系统Python源码包面向计算机相关专业在校生、毕业生及企业开发者适用于毕业设计、课程设计或项目初期演示等场景。系统以外部文档为知识来源借助大模型API完成私有知识库检索与智能问答可帮助读者快速构建原型也可作为二次开发的基础工程。压缩包整体约10.26MB工程内包含Python源码、文档说明和项目报告代码均经过运行验证项目答辩评审平均分达96.5分。目前已有96人学习下载属于既有示例价值又有可操作性的AI应用类资源。随包文档有助于理解整体架构、API调用方式、知识库组织与问答流程若运行中遇到问题下载后可与作者私聊还可获得远程教学支持。整体上这份资源既能满足课程作业与毕业设计的交付要求也为希望入门大模型落地的学习者提供了贴近实战的参考。1. 基于大语言模型API的外挂知识库问答系统不微调模型让私有文档开口说话之前给某实验室搭过一个问答demo遇到一个很典型的问题模型懂很多通用知识但一问到内部的实验规范、历史项目文档就开始一本正经地胡说八道。与其反复调prompt不如把知识库“外挂”到模型外面让模型在回答前先去自己的文档库里检索一圈拿到证据再回答。这个基于Python实现的问答系统干的就是这件事它支持对接本地模型接口和商用模型API是一个可完整跑通的毕业设计/课程设计级项目源码、文档说明、答辩报告一次打包适合拿来当毕设框架、课设作业或者作为企业知识库问答的最小原型。2. 系统整体架构检索增强生成是如何把知识“挂”到模型外面的外挂知识库的核心思想并不复杂就是RAGRetrieval-Augmented Generation检索增强生成。在聊这段之前先把整个系统的处理链路画清楚后面看代码的时候脑子里才有地图。2.1 外挂知识库的工作链路从提问到回答的完整流程整个问答系统分为“知识入库”和“在线问答”两条链路入库是一次性的离线操作问答是每来一个问题都要走一遍的在线操作。离线入库阶段原始文档通常是markdown、txt、pdf先被读取出来切成多个文本块每个文本块交给嵌入模型转成向量向量连同原文一起存入向量数据库。这里的核心是“切块”和“向量化”切得好不好直接影响检索质量。在线问答阶段用户提问后系统先把问题也转成向量然后去向量库做相似度检索找出与问题最相关的几个文本块最后把这些文本块作为“参考资料”拼进prompt连同问题一起发给大语言模型接口让模型基于资料作答。这条链路的价值在于模型本身的知识边界没有被改动但回答时多了一个“先查资料再回答”的环节幻觉问题能被明显压制。对于毕设答辩来说这条链路从原理到实现都能讲清楚评委问“为什么不用微调”你回答“外挂方案成本低、更新快、不污染模型权重”这句话本身就是得分点。2.2 技术选型为什么是Python 向量检索 API调用这个项目的技术栈是典型的Python AI应用组合没有引入重型框架非常适合作为毕业设计和课程设计。核心选型如下开发语言Python理由很简单AI生态最成熟文档加载、向量化、API调用都有现成库。整个项目对Python版本要求不高3.8及以上即可。嵌入模型Embedding负责把文本转成向量。常见做法是接入本地嵌入模型通过模型接口暴露或商用Embedding API维度一般是1024或1536。项目中把嵌入模型的调用封装成了统一接口方便切换。向量数据库负责存储嵌入向量并做相似度检索。项目里用的方案是轻量级向量库支持持久化到本地文件无需单独部署数据库服务这对课设和毕设非常友好评委演示时不会因为环境问题翻车。大语言模型接口统一封装成OpenAI兼容格式本地模型Ollama这类工具暴露出来的接口和商用API都能通过同一个客户端调用切换只需改配置项。从架构眼光来看这套选型把最容易被模型参数规模吓到的人从深度学习训练里解放出来你不需要GPU训练不需要微调框架只要有一个能调用的模型接口就能把知识库系统完整搭起来。这也是外挂方案在工程上比微调方案更落地的原因。2.3 依赖组件与运行环境一句话讲清配置组成整个系统的运行环境依赖可以分成三组基础库、AI相关库、文档解析库。基础库包括os、json这类标准库AI相关的核心依赖包括用于调用模型接口的openai库、用于向量检索和数据库操作的向量库客户端文档解析类包括读取PDF和docx的库。这些依赖集中在根目录的requirements.txt里安装命令一条就能装完。配置上项目把API地址、API密钥、模型名称、嵌入模型名称、知识库路径、向量库持久化路径全部集中在一个配置文件中。这一点做得比较到位对二次开发很友好后面会专门讲怎么改配置对接自己的模型。3. 核心代码拆解从文档入库到检索问答的实现细节这一段是给想真正读懂代码的人准备的。我不打算把整个项目的代码全部贴出来那样篇幅太长也没意义而是挑出四个核心功能模块的关键代码逐段讲解配合逻辑说明和参数解释看完你就能理解这套系统是怎么转起来的。3.1 文档加载与文本拆分知识库的第一步是把文档变成小片段文档加载做得很规矩先扫描知识库目录下的所有文件按扩展名做分流.txt和.md直接用标准库读取.pdf和.docx走对应的解析库。这里我一般会建议子目录扫描因为现实中的知识库往往是按主题分文件夹的。import os from docx import Document import fitz # PyMuPDF用于读取pdf def load_documents(data_dir: str): 扫描data_dir下所有文档返回文档内容列表 docs [] for root, _, files in os.walk(data_dir): for filename in files: filepath os.path.join(root, filename) ext filename.rsplit(., 1)[-1].lower() if ext txt or ext md: with open(filepath, r, encodingutf-8) as f: docs.append({source: filepath, text: f.read()}) elif ext pdf: with fitz.open(filepath) as pdf: text .join(page.get_text() for page in pdf) docs.append({source: filepath, text: text}) elif ext docx: doc Document(filepath) text \n.join(p.text for p in doc.paragraphs) docs.append({source: filepath, text: text}) return docs这段代码的逻辑是按扩展名分流读取把每个文档的内容连同来源路径一起存入列表。参数说明data_dir是知识库根目录函数会递归扫描所有子目录返回的docs是一个字典列表每个字典包含source文件路径和text全文内容两个字段。文本拆分是个容易被忽视但影响很大的环节。常见的错误是直接塞整篇文档导致向量化后语义被稀释或者切得太碎导致检索到的片段缺乏上下文。项目里用的是“按段落聚合 按字符数滑动切分”的组合方案def split_text(text: str, chunk_size: int 500, overlap: int 50): 将长文本按chunk_size切块块与块之间保留overlap的重叠 if len(text) chunk_size: return [text] chunks [] start 0 while start len(text): end start chunk_size chunks.append(text[start:end]) if end len(text): break start end - overlap return chunks参数说明chunk_size是每块的最大字符数对中文场景来说500左右比较合适因为中文一个字的信息密度高于英文500字已经能表达一个完整的论点overlap是相邻块之间的重叠字符数作用是避免在一个语义完整的段落恰好被切断时丢失关键信息。这个参数调大一些会让检索召回更稳但会增加向量库的存储量和检索耗时需要权衡。3.2 向量化与知识库存入让文本变成可计算的向量文本切块之后下一步是调用嵌入模型把每个文本块转成向量然后写入向量库。项目里对所有嵌入模型的调用做了一个统一封装def get_embedding(text: str, model_name: str, client) - list: 调用嵌入模型返回text对应的向量列表 resp client.embeddings.create( modelmodel_name, inputtext ) return resp.data[0].embedding这里的client就是OpenAI兼容客户端的实例无论是本地模型接口还是商用API只要接口兼容就能直接复用这段代码。model_name是嵌入模型的名称需要和你启动的服务或购买的API保持一致。返回的向量是一个float列表维度取决于嵌入模型本身。知识库存入阶段项目使用了轻量级向量数据库逐条写入并持久化到本地文件。这里贴一下写入的核心逻辑def build_knowledge_base(client, embed_model: str, chunks: list, persist_dir: str): 将文本块向量化后写入向量库并持久化 collection get_or_create_collection(persist_dir) for idx, chunk in enumerate(chunks): vector get_embedding(chunk, embed_model, client) collection.add( ids[fchunk_{idx}], embeddings[vector], documents[chunk] ) collection.persist()逻辑说明get_or_create_collection负责加载已有的向量库文件如果不存在则创建新的循环中把每个文本块向量化后连同文本内容一起写入集合ids是每条记录的唯一标识。全部写完后调用persist()把内存中的数据落盘。参数说明persist_dir是向量库的持久化目录下次程序启动时可以从这个目录直接加载不需要重新向量化文档。需要注意的是如果你的知识库有几百份文档逐条向量化会很慢一个常见的优化是批量调用嵌入接口。但毕设场景下文档量一般不大逐条入库的写法更容易理解也方便调试时查看每一条的入库状态。3.3 检索与问答合成核心的RAG链路都在这个函数里检索和问答是系统最重要的部分项目把这两步放在一个函数里完成从“接收问题”到“返回答案”一条链路走完def ask(question: str, client, llm_model: str, embed_model: str, collection, top_k: int 4) - str: RAG问答主链路向量检索 - 拼装prompt - 调用LLM生成回答 # 1. 问题向量化 question_vector get_embedding(question, embed_model, client) # 2. 向量相似度检索取top_k个最相关的文本块 results collection.query( query_embeddings[question_vector], n_resultstop_k ) retrieved_docs results[documents][0] # 3. 将检索到的文本块拼入prompt context \n\n.join(retrieved_docs) system_prompt ( 你是一个基于知识库回答问题的助手。\n 请只根据下面的资料回答问题不要使用资料之外的知识。\n 如果资料中没有答案请直接说明。\n\n f资料如下\n{context} ) # 4. 调用大语言模型生成回答 resp client.chat.completions.create( modelllm_model, messages[ {role: system, content: system_prompt}, {role: user, content: question} ], temperature0.2 ) return resp.choices[0].message.content这段是理解整个项目最关键的代码。逻辑分四步第一步把问题向量化注意问题和文本块必须使用同一个嵌入模型否则向量空间不一致检索结果会非常离谱第二步调向量库的query接口按余弦相似度或内积返回最相关的top_k个文本块第三步把检索到的文本块拼进system prompt明确告诉模型“只根据资料回答”第四步调用大语言模型的chat补全接口生成答案temperature设为0.2尽量降低回答的随机性。参数说明top_k控制检索返回的文本块数量毕设场景设4比较合理太多会把不相关的内容塞进prompt导致模型被干扰temperature是采样温度0表示每次回答基本一致适合知识问答场景。如果把top_k调大或temperature调高你会发现模型偶尔会“自由发挥”超出资料范围这也是答辩时可以和评委展开聊的一个点。3.4 配置管理切换本地模型和商用API只改一个文件这个项目在配置设计上的亮点是统一配置文件。所有模型相关的参数包括API地址、密钥、模型名、嵌入模型名都集中在配置模块里。切换本地模型和商用API的方式就是改几个配置值# config.py 核心配置项 LLM_API_BASE http://localhost:11434/v1 # 本地模型接口地址商用API填对应地址 LLM_API_KEY sk-xxx # 商用API的密钥本地模型可填任意占位符 LLM_MODEL qwen2.5:7b # 大语言模型名称 EMBED_MODEL bge-m3 # 嵌入模型名称 EMBED_API_BASE http://localhost:11434/v1 # 嵌入模型的服务地址 VECTOR_DB_DIR ./knowledge_base # 向量库持久化目录 DATA_DIR ./docs # 原始文档目录逻辑说明项目通过OpenAI兼容客户端同时适配本地和商用API核心逻辑是base_url指向哪里客户端就会把请求发到哪里。本地部署的模型服务如Ollama支持在/v1路径下暴露OpenAI兼容接口所以商用和本地共用同一套代码。参数说明LLM_API_KEY在本地场景下随便填一个字符串即可服务端不校验VECTOR_DB_DIR是向量库文件存放路径如果需要清空重建知识库删掉这个目录重新运行入库脚本即可。如果你的商用API不是OpenAI兼容格式怎么办项目中打包的文档说明里有适配指南通常就是在client的初始化处写一个适配层把请求格式转换一下。毕设项目只要能跑通一套接口就行这个设计足够用了。4. 项目目录与运行指南下载后从零跑起来的完整路径拿到源码包之后不建议直接双击一个脚本就开始跑。先花十分钟把目录结构看懂再按顺序执行入库和问答两个环节整体会顺很多。4.1 目录结构详解每个文件和文件夹的职责项目的目录结构是典型的“入口脚本 核心逻辑 文档资料”三层结构。解压后你会看到类似下面的布局knowledge-qa-system/ ├── README.md # 项目说明包含运行步骤、环境要求、常见问题 ├── requirements.txt # Python依赖清单 ├── config.py # 配置文件模型接口、路径等 ├── ingest.py # 知识入库脚本加载文档 - 切块 - 向量化 - 写入向量库 ├── query.py # 问答脚本接收提问 - 检索 - 生成回答 ├── docs/ # 示例知识库文档目录放你自己的文档 ├── knowledge_base/ # 向量库持久化目录运行后自动生成 ├── 设计文档.md # 系统设计文档架构图、流程说明 └── 实验报告.docx # 实验数据与分析含效果对比、调参记录各文件职责README.md是第一个要看的文件里面写了运行顺序和环境要求requirements.txt负责一键安装依赖config.py是全局配置入口ingest.py和query.py是两条主链路的入口脚本前者负责知识入库后者负责问答。docs目录下放的是用于测试的知识文档你可以换成自己的文档重新入库。4.2 环境准备Python环境与依赖安装避坑环境准备这一步卡住了很多人但踩坑经验可以帮你绕过去。建议用虚拟环境不要直接装在系统Python里否则后面装库时容易把系统环境搞乱。具体步骤如下# 1. 创建并激活虚拟环境Windows和macOS/Linux命令略有差别 python -m venv venv source venv/bin/activate # macOS/Linux # venv\Scripts\activate # Windows # 2. 安装依赖 pip install -r requirements.txtrequirements.txt里包含的核心依赖有openai、向量数据库客户端、python-docx和PyMuPDF。安装时如果遇到网络超时换国内镜像源再装pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里有个容易忽视的点如果你用的是较新的Python版本3.10以上部分库可能需要升级到最新版才有预编译包老版本库在新Python下可能会报编译错误。遇到这类报错检查一下Python版本然后升级对应的依赖包即可。4.3 模型接口准备本地模型和商用API分别怎么配置跑这个项目需要一个可用的大语言模型接口和一个嵌入模型接口项目通过OpenAI兼容协议同时支持两类来源配置时二选一本地模型方案推荐用Ollama这类本地模型工具先拉取一个大语言模型如qwen2.5系列和一个嵌入模型如bge-m3启动服务后接口地址就是http://localhost:11434/v1。这种方式的好处是断网可用、无API费用答辩演示时不容易出现网络层面的意外。商用API方案如果你有自己的商业API密钥把config.py里的LLM_API_BASE和LLM_API_KEY改成对应服务的地址和密钥即可。商用方案的好处是模型能力强、回答质量高但演示时要保证网络稳定。这里有一个真实踩过的坑本地模型的嵌入模型和大语言模型必须同时在线系统在ingest.py阶段只调嵌入模型但在query.py阶段两个模型都会调用。如果只启动了对话模型没有启动嵌入模型入库时就会报连接错误。4.4 一键入库与启动问答跑通整个流程环境准备好后运行顺序是“先入库后问答”。第一次运行入库脚本时系统会读取docs目录下的所有文档切块、向量化、写入向量库并持久化python ingest.py运行成功会输出每个文档的处理状态和入库的文本块数量。如果文档较多这个过程会持续一会儿属正常现象。入库完成后启动问答python query.py问答脚本启动后会进入交互模式你输入一个问题系统返回一个答案同时打印出检索到的参考资料来源。建议测试几个和你知识库内容密切相关的问题验证检索是否准确。如果回答明显跑偏优先检查切块大小和top_k参数。5. 常见问题排查跑通这个项目遇到最多的5个坑及对策这部分内容来自我实际跑类似项目时积累的经验。项目本身测试是没问题的但换个环境、换个模型、换个文档格式就可能冒出各种意外。下面的5个问题前4个属于一定会遇到或很可能遇到的第5个属于进阶调优但提早知道能省很多时间。5.1 现象一连接本地模型接口时反复报错连接失败完整报错信息一般是ConnectionError或requests.exceptions.ConnectionError后面跟着/v1/chat/completions的地址。原因有两类一是模型服务没有启动检查终端里是否有“listening on”之类的日志二是config.py里的LLM_API_BASE地址写错比如多写了路径或者端口不对。解决方法是先确认服务监听地址然后在浏览器直接访问该地址的/v1/models路径如果能返回模型列表说明接口正常再检查配置与地址是否完全一致。还有一个细节是本地服务默认只监听本机回环地址如果前端页面部署在容器里需要在启动服务时加--host 0.0.0.0。5.2 现象二入库时提示向量维度不一致这个报错的触发点是向量库里已有的向量和新写入的向量维度不同。最常见的原因是你换了嵌入模型第一天用A模型入库第二天换了B模型继续入库A的向量是1024维B的向量是768维向量库不认。解决方法是把knowledge_base持久化目录下的向量库文件删掉重新执行ingest.py入库。记住一个原则嵌入模型换了向量库必须重建这个没得商量也别想着做向量维度转换那是给自己挖坑。5.3 现象三检索到了文档但回答内容不在文档里模型偶尔会基于自己的知识“自由发挥”一段明明知识库里没有的内容它也能编得像模像样。这是RAG系统的经典问题原因是这个项目的temperature默认是0.2如果被调高过模型会更倾向于发挥。解决办法是把temperature调回0.2以内同时检查prompt里“请只根据资料回答”这句话是否保留。另外还有一种情况是检索到的文本块本身不相关top_k设得太大时容易混进低相似度的内容这时候适当调小top_k并在prompt里明确说“如果资料与问题无关回答无法从资料中找到答案”。5.4 现象四中文文档切块后检索效果差表现为检索返回的文本块看着相关但就是答不到点子上。多数情况是切块方式没有考虑中文语义边界在句子中间硬切导致每块文本的语义都不完整。解决方法是优先按段落切分段落太长再按字符数切并设置overlap重叠区。还有一个常见的工程做法在切块前先把文档按标题结构拆分成章节再对每个章节单独切块检索效果会比纯字符切分好不少。这属于值得写入实验报告的优化项答辩时讲出来很加分。5.5 现象五知识库更新后问答结果还是旧的修改了docs目录下的文档但问出来的答案还是旧内容。原因是知识库是离线构建的修改源文档不会自动同步到向量库。解决方法是重新运行ingest.py代码里默认是“追加”模式不会删除旧的向量。如果你希望文档更新后旧向量被清理建议先删除向量库持久化目录再重新入库。项目代码里没有做增量更新毕设场景下全量重建完全够用答辩时这个问题可以作为“未来改进方向”来提。6. 二次开发把这个问答系统改造成你自己的毕设或产品原型源码能跑通只是第一步把它改造成一个能展示你个人工作量的项目才是最终目的。基于这个外挂知识库问答系统有三个改动方向投入产出比最高。第一个方向是改造前端交互方式。原始项目是命令行交互答辩演示的视觉效果一般。你可以快速套一个Web界面框架比如用现成的Web框架写一个聊天页面把query.py里的ask函数包装成一个后端接口前端做一个类似聊天软件的窗口支持流式输出。这个改动不算大但演示效果和答辩观感会明显提升。第二个方向是增加文档类型支持。原始项目支持txt、md、pdf、docx可以扩展支持Excel表格、PPT、HTML网页等格式核心是修改load_documents函数为每种新格式增加一个解析分支。这里给你一个判断标准如果知识库中这类格式的文档占比较多这个扩展就有实际价值。第三个方向是优化检索策略。原始方案是简单的向量相似度检索可以加入“关键词过滤 向量检索”的混合模式先用关键词粗筛再用向量检索精排最后合并结果。这个优化在中文场景下通常能明显提升检索精度而且实现代码量不大。我做这个改造时吃过一次亏一开始直接把混合检索写复杂了调了两天效果还是不稳定后来把逻辑简化为“先在候选集里过滤再向量排序”很快就跑通了。验证改造效果时建议准备一组固定的测试问题改造前后各跑一遍对比回答是否更准确、更稳定。我自己的习惯是准备10个问题其中8个来自知识库覆盖范围2个故意超出知识库范围用来测试系统“不知道就说不知道”的能力。从那以后每次改完检索参数或prompt都强制走一遍这10个问题的回归测试避免“改了这个坏了那个”。这套固定的验证方法也是答辩现场你最有力的支撑材料。希望这份解析能帮你把这个外挂知识库问答系统真正跑起来并在它的基础上做出属于自己的东西。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询