🔥别再死磕枯燥理论了!AI 时代,拿实战作品说话才是硬道理!(原创不易哈,希望可以帮到还有些许学习劲儿的同学们)🔥
【进阶版还在创作中,耗费精力中……】
跳转到专栏目录,你学习更有方向和思路……
入门实践工程十五:基于 RAG 的知识库问答(检索增强生成)~附:安装依赖库及工程源码
简介
构建完整 RAG 流水线:文档分块 → 向量化 → 本地向量检索 → 拼接上下文 → 调用大模型生成答案,是当前大模型应用最主流的工程范式。内置一份 AI/Python 知识库,大模型调用可选。
工程详细介绍
核心思想:检索增强生成(RAG)= 先检索后生成。把私有知识库分块向量化建索引,提问时检索最相关片段作为上下文拼进提示词,再由大模型基于上下文生成答案,既支持私有知识又缓解幻觉,是当下大模型落地主流范式。
实现方法:
- 数据:内置 AI/Python 知识库文档(可替换为任意文档)。
- 模型:sentence-transformers 多语言嵌入(检索)+ OpenAI 兼容大模型(生成,可选)。
- 流程:文档按 200 字带 40 重叠分块 → 嵌入归一化建 numpy 向量索引 → 提问编码 → 余弦相似度取 Top-3 → 拼接上下文 + 问题送大模型生成(无 API key 则退化为检索模式返回片段)→ Gradio 网页交互。
- 输出:命令行测试 + Gradio 网页,返回回答 + 运行模式 + 检索详情。
目录结构
15_rag_qa/ ├── main.py # 分块 + 嵌入 + 检索 + 生成 + Gradio 界面 └── requirements.txt正确安装
pipinstallsentence-transformers gradio numpy openai python main.py- 首次运行自动下载多语言 MiniLM 嵌入模型(约 500MB),需联网。
- 大模型生成可选:配置 OPENAI_API_KEY 等环境变量后即走生成,未配置则退化为检索模式(见下文「大模型生成模式」)。
运行方式
pipinstall-rrequirements.txt python main.py# 命令行测试 + 启动 Gradiopython main.py --no-web# 仅命令行测试大模型生成模式(可选)
默认为检索模式。若要让大模型基于上下文生成自然语言答案,设置环境变量:
# PowerShell(OpenAI 兼容接口均可,如百炼/glm/通义/DeepSeek 等)$env:OPENAI_API_KEY="your-key"$env:OPENAI_API_BASE="https://dashscope.aliyuncs.com/compatible-mode/v1"$env:LLM_MODEL="qwen-plus"python main.py未配置 key 时自动退化为检索模式(返回最相关片段),依然可运行。
说明
- 嵌入模型
paraphrase-multilingual-MiniLM-L12-v2首次运行需联网下载。 - 向量库用 numpy 实现(余弦相似度),无需安装 FAISS 即可跑通。
- 知识库内置在
main.py的DOCUMENTS中,可自行替换为任意文档。
预期结果
- 命令行先测试 3 个问题
- 浏览器打开 Gradio,提问得到回答 + 运行模式 + 检索详情
扩展方向
- 用 FAISS / Chroma 替换 numpy 检索,支持百万级文档
- 加载本地 PDF/Markdown 文档库(LangChain 加载器)
- 加入重排序模型(reranker)提升检索精度
- 支持多轮对话与引用溯源
工程源码
main.py
""" 入门实践工程十五:基于 RAG 的知识库问答(检索增强生成) ================================================= 构建完整 RAG 流水线:文档分块 -> 向量化 -> 本地向量检索 -> 拼接上下文 -> 调用大模型生成答案。 内置一份 AI/Python 知识库文档,无需额外准备。 大模型调用可选:设置了 OPENAI_API_KEY + OPENAI_API_BASE 即走生成, 否则退化为检索模式(返回最相关片段 + 模板回复)。 运行: python main.py # 命令行问答 + Gradio 网页界面 """importargparseimportosimportre BASE_DIR=os.path.dirname(os.path.abspath(__file__))# ---------------- 内置知识库文档 ----------------DOCUMENTS=["""PyTorch 是一个基于 Python 的开源深度学习框架,由 Meta(原 Facebook)AI 研究团队开发并维护。 它采用动态计算图机制,定义即运行,调试方便,深受研究者欢迎。 PyTorch 的核心数据结构是张量(Tensor),类似于 NumPy 的 ndarray,但支持 GPU 加速与自动求导。 常用模块包括:torch.nn(神经网络层)、torch.optim(优化器)、torch.utils.data(数据加载)。 训练循环通常包含:前向传播、计算损失、反向传播、优化器更新四个步骤。""","""卷积神经网络(CNN)是一类专门处理具有网格结构数据(如图像)的深度学习模型。 它通过卷积层提取局部特征、池化层降采样降维、全连接层完成分类。 典型结构有 LeNet、AlexNet、VGG、ResNet 等。ResNet 引入残差连接解决了深层网络梯度消失问题, 使得训练上百层的网络成为可能。CNN 在图像分类、目标检测、语义分割等视觉任务上表现优异。""","""BERT(Bidirectional Encoder Representations from Transformers)是 Google 于 2018 年提出的预训练语言模型。 它基于 Transformer 的编码器,使用双向自注意力机制,在大规模无标注文本上做掩码语言模型预训练。 微调后可用于文本分类、命名实体识别、问答系统等多种 NLP 任务。 BERT 的出现大幅提升了多项自然语言理解任务的基准,是预训练模型的里程碑。""","""梯度下降是机器学习中最常用的优化算法,通过沿损失函数梯度负方向更新参数来最小化损失。 常见变体:批量梯度下降(BGD)使用全部样本、随机梯度下降(SGD)使用单个样本、小批量梯度下降使用一批样本。 为加速收敛与稳定训练,发展出 Momentum、AdaGrad、RMSProp、Adam 等自适应优化器。 Adam 结合了 Momentum 的动量思想与 RMSProp 的自适应学习率,是当前最常用的优化器之一。""","""RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合检索与生成的大模型应用范式。 它先从知识库中检索与用户问题相关的文档片段,再将检索到的上下文与问题一起送入大模型, 让模型基于上下文生成答案,从而缓解大模型的幻觉问题并支持私有知识。 RAG 的关键组件包括:文档分块、嵌入模型、向量数据库、检索器、生成模型。 常见向量数据库有 FAISS、Milvus、Chroma 等;分块策略常用固定长度窗口或按语义边界切分。""",]# ---------------- 文档分块 ----------------defchunk_text(text,chunk_size=200,overlap=40):"""按字符长度分块(带重叠)。"""text=re.sub(r"\s+"," ",text).strip()chunks=[]i=0whilei<len(text):chunks.append(text[i:i+chunk_size])i+=chunk_size-overlapreturnchunksdefbuild_corpus():chunks=[]fordocinDOCUMENTS:chunks.extend(chunk_text(doc))returnchunks# ---------------- 向量库 + 检索器 ----------------classVectorStore:def__init__(self,model_name="paraphrase-multilingual-MiniLM-L12-v2"):fromsentence_transformersimportSentenceTransformer self.model=SentenceTransformer(model_name)self.chunks=build_corpus()print(f"知识库分块完成:共{len(self.chunks)}个片段")self.embeddings=self.model.encode(self.chunks,normalize_embeddings=True)defretrieve(self,query,top_k=3):q_emb=self.model.encode([query],normalize_embeddings=True)sims=(q_emb @ self.embeddings.T)[0]# 余弦相似度top_idx=sims.argsort()[::-1][:top_k]return[(self.chunks[i],float(sims[i]))foriintop_idx]# ---------------- 生成器(可选大模型) ----------------defgenerate_answer(query,context_chunks):"""若配置了 OpenAI 兼容接口则调用大模型生成;否则走模板回复。"""api_key=os.environ.get("OPENAI_API_KEY")base_url=os.environ.get("OPENAI_API_BASE")oros.environ.get("LLM_BASE_URL")model=os.environ.get("LLM_MODEL","gpt-3.5-turbo")context="\n\n".join(context_chunks)ifapi_key:try:fromopenaiimportOpenAI client=OpenAI(api_key=api_key,base_url=base_url)resp=client.chat.completions.create(model=model,messages=[{"role":"system","content":"你是一个知识库问答助手,仅根据提供的上下文回答问题。若上下文没有相关信息,请说明知识库中未包含该内容。"},{"role":"user","content":f"上下文:\n{context}\n\n问题:{query}"},],temperature=0.3,)returnresp.choices[0].message.content.strip(),TrueexceptExceptionase:returnf"[大模型调用失败:{e},退化为检索结果]",False# 检索模式reply=("(未检测到 LLM_API_KEY/OPENAI_API_KEY,当前为检索模式,仅返回相关上下文。)\n\n""最相关片段:\n"+context)returnreply,False# ---------------- 问答主流程 ----------------defask(store:VectorStore,query:str,top_k:int=3):results=store.retrieve(query,top_k=top_k)context_chunks=[r[0]forrinresults]answer,used_llm=generate_answer(query,context_chunks)mode="生成模式"ifused_llmelse"检索模式"detail="\n".join(f"· 相似度{s:.2f}:{c[:60]}..."forc,sinresults)returnanswer,mode,detaildefbuild_gradio(store:VectorStore):importgradioasgrdefanswer_fn(query):ifnotquery.strip():return"请输入问题。","",""ans,mode,detail=ask(store,query)returnans,mode,detailwithgr.Blocks(title="RAG 知识库问答")asdemo:gr.Markdown("# 📚 RAG 知识库问答 Demo\n内置 AI/Python 知识库,提问后将检索相关片段并由大模型生成答案。")inp=gr.Textbox(label="你的问题",placeholder="例如:PyTorch 的训练循环包含哪些步骤?")withgr.Row():out=gr.Textbox(label="回答",lines=5)mode=gr.Textbox(label="运行模式")detail=gr.Textbox(label="检索详情",lines=3)btn=gr.Button("提问")btn.click(answer_fn,inputs=inp,outputs=[out,mode,detail])gr.Examples(examples=["什么是 RAG?","Adam 优化器有什么特点?","ResNet 解决了什么问题?"],inputs=inp,)returndemodefmain():parser=argparse.ArgumentParser()parser.add_argument("--no-web",action="store_true",help="只跑命令行,不启动 Gradio")args=parser.parse_args()print("加载嵌入模型(首次需联网下载)...")store=VectorStore()print("\n--- 命令行快速测试 ---")forqin["什么是 RAG?","Adam 优化器有什么特点?","BERT 是什么?"]:ans,mode,_=ask(store,q)print(f"\nQ:{q}[{mode}]\nA:{ans[:200]}\n{'-'*60}")ifnotargs.no_web:print("\n启动 Gradio 网页界面 ...")demo=build_gradio(store)demo.launch()if__name__=="__main__":main()