手把手教你搭建生产级 RAG 问答系统:Milvus + BM25 + 多查询 + 重排序

发布时间:2026/8/21 0:39:48

手把手教你搭建生产级 RAG 问答系统:Milvus + BM25 + 多查询 + 重排序
本文基于一个完整的实战项目带你从零构建一个高精度的智能问答机器人。我们会使用Milvus 向量数据库存储知识库结合BM25 关键词检索和向量检索进行混合召回再通过多查询生成和FlashRank 重排序提升答案准确率。所有代码都有详细注释读完你就能理解现代 RAG 系统的核心设计思路。环境准备Python 版本建议使用Python 3.12如果下载速度慢可配置国内 PyPI 镜像。项目背景为什么要这么复杂一个简单的 RAG 流程是用户提问 → 向量数据库检索相似段落 → 拼接上下文 → 大模型生成答案。但在实际落地中你会遇到以下问题用户问法千奇百怪同一个意图“文石哪款 pad 尺寸最大”用户可能问“最大的电纸书是哪台”向量检索可能召回不同结果。向量相似 ≠ 语义相关向量模型可能把“大尺寸”和“大电池”混淆导致无关段落排在前面。纯向量检索会漏掉专有名词、型号等精确关键词比如“Tab13”这类词向量模型可能不认识。因此我们设计了如下流水线多查询生成让大模型根据原始问题生成 3 个不同角度的问法分别去检索扩大召回范围这个我们上一篇也专门聊过。混合召回每条问法既走Milvus 向量检索语义相似也走BM25 关键词检索字面匹配合并候选文档。重排序用专门的轻量模型对所有候选段落按与原始问题的真实相关性重新打分只保留最相关的前 2 条送入大模型这个上一篇也用过。最终大模型只看到少量高质量上下文答案自然更准确、更省钱。完整代码与逐行解析3.1 基础配置与镜像劫持import os import shutil import time os.environ[HF_ENDPOINT] https://hf-mirror.com from dotenv import load_dotenv from langchain_core.documents import Document from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_deepseek import ChatDeepSeek from langchain_huggingface import HuggingFaceEmbeddings from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough, RunnableLambda from langchain_core.output_parsers import StrOutputParser from pymilvus import MilvusClient, DataType from flashrank import Ranker, RerankRequest from langchain_community.retrievers import BM25Retriever import requests作用HF_ENDPOINT设置 HuggingFace 国内镜像避免下载模型超时。从.env文件加载DEEPSEEK_API_KEY。导入了 Milvus 客户端、FlashRank 重排序器、BM25 检索器等关键组件。3.2 拦截 HuggingFace 下载请求防止模型下载失败original_get requests.get def mirror_get(url, *args, **kwargs): if huggingface.co in url: url url.replace(https://huggingface.co, https://hf-mirror.com) return original_get(url, *args, **kwargs) requests.get mirror_get有些库如 FlashRank内部使用requests下载模型文件此段代码强制替换为国内镜像保证下载成功。3.3 清理 Milvus 数据库文件Windows 下常见占用问题DB_PATH milvus_demo.db COLLECTION_NAME docs def clean_milvus_db(db_path: str, retry_times3): # 先尝试关闭所有可能的连接 for _ in range(2): try: tmp_cli MilvusClient(db_path) tmp_cli.close() del tmp_cli except Exception: pass time.sleep(0.6) # 删除 LOCK 文件Windows 下常因进程未释放而残留 lock_path os.path.join(db_path, LOCK) if os.path.exists(lock_path): try: os.unlink(lock_path) except Exception: pass # 循环删除整个数据库目录 for i in range(retry_times): try: if os.path.exists(db_path): shutil.rmtree(db_path) print(f[清理成功] {db_path} 已删除) return except PermissionError as e: print(f[清理重试{i1}] 文件占用: {e}) time.sleep(1) raise Exception(自动清理失败执行这条命令手动清理Get-Process milvus-lite* -EA SilentlyContinue | Stop-Process -Force; Remove-Item milvus_demo.db -Recurse -Force)为什么需要这个函数Milvus Lite 在 Windows 下可能因为异常退出、多进程访问等原因导致数据库目录被占用重新运行时会出现 PermissionError。这个函数尝试优雅关闭连接、删除锁文件实在不行就抛出提示让用户手动清理。3.4 读取文档并切分成小块with open(knowledge.txt, r, encodingutf-8) as f: text f.read() documents [Document(page_contenttext)] text_splitter RecursiveCharacterTextSplitter( chunk_size300, # 每块最多300字符 chunk_overlap50, # 重叠50字符避免语义断裂 separators[\n\n, \n, 。, , , , , ] ) docs text_splitter.split_documents(documents) target_chunk_num len(docs) # 记录切片总数用于后续判断数据库是否需要重建切分策略块大小 300 字符适合大部分段落重叠 50 字符确保上下文连贯。分隔符顺序从大到小优先在段落、换行、句号处切分保证语义完整性。3.5 初始化向量化模型embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-large-zh-v1.5, model_kwargs{device: cpu}, encode_kwargs{normalize_embeddings: True} ) dim len(embeddings.embed_query(test)) # 获取向量维度bge-large-zh-v1.5是中文语义向量模型的优选效果好、速度快。normalize_embeddingsTrue会将向量归一化使得余弦相似度计算更稳定。3.6 Milvus 初始化与数据写入3.6.1 判断是否需要重建数据库need_rebuild True try: check_client MilvusClient(DB_PATH) if check_client.has_collection(COLLECTION_NAME): stats check_client.get_collection_stats(COLLECTION_NAME) db_chunk_num stats.get(row_count, 0) if db_chunk_num target_chunk_num: need_rebuild False print(f✅ 向量库匹配复用现有数据库内切片:{db_chunk_num}) else: print(f 切片数量不一致执行重建库:{db_chunk_num} 本地:{target_chunk_num}) check_client.close() del check_client except Exception as e: print(f 向量库读取异常强制重建: {e}) try: check_client.close() except: pass del check_client time.sleep(0.5)这段代码避免了重复写入。如果本地知识库没有变化切片数量相同就直接复用已有的 Milvus 数据库加快启动速度。3.6.2 创建集合并写入向量client None if need_rebuild: client MilvusClient(DB_PATH) # 定义表结构 schema client.create_schema(auto_idFalse, enable_dynamic_fieldFalse) schema.add_field(field_nameid, datatypeDataType.INT64, is_primaryTrue) schema.add_field(field_namevector, datatypeDataType.FLOAT_VECTOR, dimdim) schema.add_field(field_nametext, datatypeDataType.VARCHAR, max_length2000) # 创建索引FLAT 索引余弦相似度 index_params client.prepare_index_params() index_params.add_index(field_namevector, index_typeFLAT, metric_typeCOSINE) client.create_collection(collection_nameCOLLECTION_NAME, schemaschema, index_paramsindex_params) # 批量写入向量 insert_data [] for idx, doc in enumerate(docs): vec embeddings.embed_query(doc.page_content) insert_data.append({id: idx, vector: vec, text: doc.page_content}) client.insert(collection_nameCOLLECTION_NAME, datainsert_data) # 关键将集合加载到内存否则检索会报错 client.load_collection(COLLECTION_NAME) print(f✅ 向量库构建完成并加载写入 {len(insert_data)} 条切片) else: client MilvusClient(DB_PATH) client.load_collection(COLLECTION_NAME) # 复用库也要加载解释id是主键手动赋值为 0,1,2,… 方便对应。text字段保存切片的原文max_length2000足够容纳 300 字符的文本。FLAT索引是暴力搜索适合小规模数据几万条以内准确率 100%COSINE相似度与归一化向量配合使用。重要Milvus Lite 需要显式调用load_collection()将数据加载到内存后才能执行search否则会报错。3.7 带阈值过滤的 Milvus 检索函数def milvus_retrieve(query, top_k5, score_threshold0.6): query_vec embeddings.embed_query(query) results client.search( collection_nameCOLLECTION_NAME, data[query_vec], limittop_k, output_fields[text] ) doc_list [] for hit in results[0]: if hit[distance] score_threshold: # distance 是余弦相似度值越大越相似 doc_list.append(Document(page_contenthit[entity][text])) return doc_listscore_threshold0.6用来过滤掉相似度太低的片段避免引入噪声。注意由于向量已归一化余弦相似度范围是 [-1,1]0.6 表示中等以上相似度。返回的仍是 LangChain 的Document对象方便后续统一处理。3.8 初始化大模型与重排序器llm ChatDeepSeek(modeldeepseek-v4-pro, temperature0)ranker Ranker()temperature0保证大模型输出确定、严谨。Ranker()默认使用ms-marco-TinyBERT-L-2-v2模型也可通过参数指定其他模型。3.9 多查询生成query_gen_prompt ChatPromptTemplate.from_template( 为以下问题生成3个不同角度的相关中文问题换行分隔。\n原问题{question}\n相关问法 ) query_gen_chain query_gen_prompt | llm | StrOutputParser()大模型会输出类似文石最大屏幕的电子书阅读器是哪款哪款文石平板尺寸最大文石电纸书尺寸对比随后按换行分割成列表。3.10 构建 BM25 检索器bm25_retriever BM25Retriever.from_documents(docs) # docs 是所有切片文档BM25 是经典的关键词检索算法擅长精确匹配专有名词、型号等。我们基于同样的切片构建 BM25 索引实现关键词层面的召回。3.11 多查询 BM25 混合召回def multi_query_retrieve(question): variants query_gen_chain.invoke(question).split(\n) all_docs [] seen set() # 用于去重 q_list [question] [v.strip() for v in variants if v.strip()] # 1. 对每个问法进行 Milvus 向量检索 for q in q_list: chunk_list milvus_retrieve(q, top_k3) for chunk in chunk_list: cnt chunk.page_content if cnt not in seen: seen.add(cnt) all_docs.append(chunk) # 2. BM25 关键词召回 bm25_docs bm25_retriever.invoke(question) # 默认 top_k4 for doc in bm25_docs: if doc.page_content not in seen: seen.add(doc.page_content) all_docs.append(doc) return all_docs[:8] # 限制候选池大小流程说明原问题 3 个生成变体共 4 个查询每个查询用 Milvus 召回 top 3理论上最多 12 条。BM25 再召回 4 条合并后去重。最终限制最多 8 条候选文档进入重排序避免候选太多导致重排过慢。3.12 重排序选出最相关的 2 条def retrieve_multi_and_rerank(question): raw_docs multi_query_retrieve(question) if not raw_docs: return [] passages [{text: doc.page_content} for doc in raw_docs] rerank_res ranker.rerank(RerankRequest(queryquestion, passagespassages)) top_texts [item[text] for item in rerank_res[:2]] return [d for d in raw_docs if d.page_content in top_texts]FlashRank 会对每个候选段落与原始问题计算相关性分数并按分数降序排列。我们只取前 2 条确保送入大模型的上下文最相关。3.13 组装 RAG 链并执行template 根据下面提供的上下文回答问题。如果不知道答案就说不知道。 上下文 {context} 问题{question} 回答 prompt ChatPromptTemplate.from_template(template) def format_docs(docs): return \n\n.join(doc.page_content for doc in docs) rag_chain ( { context: RunnableLambda(retrieve_multi_and_rerank) | RunnableLambda(format_docs), question: RunnablePassthrough() } | prompt | llm | StrOutputParser() ) if __name__ __main__: question 文石哪款的pad尺寸最大 answer rag_chain.invoke(question) print(回答, answer) # 释放资源 client.release_collection(COLLECTION_NAME) client.close()LCEL 链执行顺序用户问题同时传给两个分支上下文分支和问题分支。上下文分支retrieve_multi_and_rerank获得最佳文档列表 →format_docs拼成字符串。将上下文和问题填入 Prompt交给 DeepSeek 生成答案。StrOutputParser提取纯文本输出。运行效果演示假设knowledge.txt内容如下文石 Tab10 拥有一块 10.3 英寸的电子墨水屏适合日常阅读。文石 Tab13 配备 13.3 英寸柔性屏幕是目前文石尺寸最大的电纸书适合阅读 PDF。文石 Nova Air 采用 7.8 英寸屏幕主打便携。文石 Leaf 2 是 7 英寸非常轻巧。运行脚本后控制台会输出类似 向量库读取异常强制重建: ...✅ 向量库构建完成并加载写入 4 条切片回答 文石 Tab13 的 pad 尺寸最大它配备了 13.3 英寸的屏幕。常见问题与解决方案5.1 Windows 下 Milvus 数据库文件被占用症状PermissionError: [WinError 32]或[WinError 5]。解决确保上次运行的 Python 进程已完全退出或手动运行提示中的 PowerShell 命令强制结束 milvus-lite 进程并删除文件夹。5.2 向量检索返回空列表检查score_threshold是否设置过高可尝试降低到 0.3。确认client.load_collection(COLLECTION_NAME)是否已执行否则search会报错或返回空。5.3 重排序模型下载失败请确保requests.get镜像劫持代码在Ranker()初始化之前已执行。也可以手动下载模型并指定cache_dir参数如Ranker(model_namems-marco-TinyBERT-L-2-v2, cache_dir你的路径)。持久化向量库Milvus Lite 让数据落盘重启不丢失也方便接入Milvus 。混合召回向量检索 BM25 关键词检索互补增强。多查询生成自动扩展用户意图提高召回覆盖率。重排序精排FlashRank 轻量高效筛选出真正相关的段落。阈值过滤过滤低相似度噪声提升上下文质量。接下来我们会逐步的封装成可对外使用的api。学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

相关新闻

面试官:生产环境Agent链路很长,包含了检索工具和多轮推理,你会如何设计把P95的RT降下来?

面试官:生产环境Agent链路很长,包含了检索工具和多轮推理,你会如何设计把P95的RT降下来?

2026/8/21 0:39:48

1. 题目分析 这道题看似在问怎么把某个步骤做快,实际考察的是生产级 Agent 的端到端性能设计。一次请求可能经历任务分类、规划、Query Rewrite、向量检索、Rerank、多个工具、多轮 ReAct、最终合成和流式返回。只把向量查询从 200ms 优化到 100ms,或者…

AI Agent(智能体)的架构设计

AI Agent(智能体)的架构设计

2026/8/21 0:39:48

AI Agent(智能体)的架构设计旨在将大语言模型(LLM)的推理能力与外部工具、记忆、执行环境相结合,实现“感知-规划-执行-反馈”的自主闭环。一个生产级的AI Agent架构通常采用分层设计,结合不同的控制流模式…

超越人脸识别:五维多模态融合重新定义机场边检旅客身份识别技术边界

超越人脸识别:五维多模态融合重新定义机场边检旅客身份识别技术边界

2026/8/21 0:39:48

0. 项目综述当前机场边检旅客身份识别体系,长期依赖传统二维可见光人脸识别单一技术路径,存在天然技术边界:极易受雨雾、逆光、夜间暗光、人员遮挡、妆容伪装、姿态变化干扰,仅能依托表层面部像素特征完成比对核验,无法…

mtkclient-gui解锁救砖实战手册:从零搭好环境到bootloader解锁,所有坑一次讲透

mtkclient-gui解锁救砖实战手册:从零搭好环境到bootloader解锁,所有坑一次讲透

2026/8/21 1:39:51

mtkclient-gui解锁救砖实战手册:从零搭好环境到bootloader解锁,所有坑一次讲透 【免费下载链接】mtkclient-gui GUI tool for unlocking bootloader and bypassing authorization on Mediatek devices (Not maintained anymore) 项目地址: https://git…

PCB逆向工程:一键反向生成原理图的高效方法与实战指南

PCB逆向工程:一键反向生成原理图的高效方法与实战指南

2026/8/21 1:39:51

这次我们来看一个PCB逆向工程中的痛点问题:抄板时最让人崩溃的往往不是布线,而是从PCB反向生成原理图。对于很多Layout工程师来说,这是个耗时费力、容易出错的手动过程。有没有一种方法,能一键从PCB文件反向生成原理图&#xff0c…

PDD校招内推全攻略:技术实现与实战技巧

PDD校招内推全攻略:技术实现与实战技巧

2026/8/21 1:39:51

1. 项目背景与核心价值最近在技术社区看到不少同学在讨论PDD校招实习生内推的事,作为经历过多次校招季的老兵,我完全理解大家对于这类实时更新内推链接的需求。2027届实习和2026届春招这两个时间节点特别关键,前者是大二升大三同学的第一次正…

Polaris:AI科研智能体如何重塑文献调研与知识管理流程

Polaris:AI科研智能体如何重塑文献调研与知识管理流程

2026/8/21 1:39:51

上周,一个朋友深夜发来消息,说想用AI辅助做文献综述,但折腾了一下午,不是生成的摘要驴唇不对马嘴,就是引用的文献根本不存在。他问我:“现在大模型能力这么强,为什么让它正经帮我看篇论文、梳理…

每晚加班手工整理小红书内容?这款免费批量下载工具帮你把3小时压缩成3分钟

每晚加班手工整理小红书内容?这款免费批量下载工具帮你把3小时压缩成3分钟

2026/8/21 1:39:51

每晚加班手工整理小红书内容?这款免费批量下载工具帮你把3小时压缩成3分钟 【免费下载链接】XHS-Downloader 小红书(XiaoHongShu、RedNote)链接提取/作品采集工具:提取账号发布、收藏、点赞、专辑作品链接;提取搜索结果…

WaveTools鸣潮工具箱实测:画质帧率一键解锁,抽卡保底一目了然,效率暴涨

WaveTools鸣潮工具箱实测:画质帧率一键解锁,抽卡保底一目了然,效率暴涨

2026/8/21 1:29:50

WaveTools鸣潮工具箱实测:画质帧率一键解锁,抽卡保底一目了然,效率暴涨 【免费下载链接】WaveTools 🧰鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 深夜十一点,你刚打完一轮BOSS战&…

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

2026/8/19 3:36:59

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

【双层规划,节点出清价,绿证交易,CVaR方法】两级电力市场环境下计及风险的省间交易商最优购电模型附Matlab代码

【双层规划,节点出清价,绿证交易,CVaR方法】两级电力市场环境下计及风险的省间交易商最优购电模型附Matlab代码

2026/8/20 21:07:35

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

隐式mpc+自适应mpc+时变mpc,线性时变模型预测控制附Simulink仿真

隐式mpc+自适应mpc+时变mpc,线性时变模型预测控制附Simulink仿真

2026/8/19 8:02:16

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

091、主从同步控制策略

091、主从同步控制策略

2026/8/21 0:09:47

091、主从同步控制策略:从一次多轴抖动事故说起 去年调试一台四轴龙门平台,Z轴和两个X轴做主从同步。电机选的是台达A2系列,驱动器工作在位置模式,主站发脉冲指令,从站硬线跟随。调试时发现一个诡异现象:当主站以500rpm匀速运行时,从站电流波形每隔几秒会出现一次毛刺,…

向量检索实验失败后该查什么

向量检索实验失败后该查什么

2026/8/21 0:09:47

向量检索实验失败后该查什么 这篇要解决什么 向量检索实验失败后该查什么讨论的是一个可复查的工程问题。向量检索实验失败后该查什么不拿未经记录的事故、跑分或成本当作论据;判断需要回到当前项目的输入、版本和运行条件。 从边界开始 处理向量检索实验失败后该查…

提示词发布过程中的止损边界

提示词发布过程中的止损边界

2026/8/21 0:09:47

提示词发布过程中的止损边界 这篇要解决什么 提示词发布过程中的止损边界讨论的是一个可复查的工程问题。提示词发布过程中的止损边界不拿未经记录的事故、跑分或成本当作论据;判断需要回到当前项目的输入、版本和运行条件。 从边界开始 处理提示词发布过程中的止损…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/17 12:00:53

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/15 10:10:27

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/18 12:20:24

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…