Milvus 2.6 + RAG实战:从向量检索到企业级知识库
当你把 RAG 从 Demo 推向生产时通常会发现最难的不是写 Prompt不是调大模型而是那几万份文档的向量到底存在哪里、怎么查得快、怎么在权限模型下不串数据。Milvus 2.6 在这个阶段值得单独拿出来讲因为它已经不再只是“向量数据库”四个字而是一套支撑召回率、并发、多租户、运维观察的工程基础设施。这篇文章会把 Milvus 2.6 和 RAG 的组合从原理讲到落地包括 Standalone 安装、pymilvus 最小闭环、切片策略、Embedding 选型、LlamaIndex 集成以及生产环境最容易翻车的点。读完你可以少走大量弯路。本文是码士集团推出的 Milvus 2.6 RAG 实战系列的一部分目标是让读者不仅“会装”还能“会用、会查、会排错”。全文会保持一个完整的项目推进节奏先把单机环境搭起来然后写一个能跑的向量检索 Demo再把真实 RAG 链路串起来最后给出企业落地视角的工程建议。1. 为什么 RAG 项目越做到后期越绕不开向量数据库先抛一个判断RAG 的效果并不完全由大模型决定。决定知识召回准确率的关键一环是向量检索。很多团队在验证阶段的做法非常朴素把所有文档切块后塞进内存用 numpy 逐条算余弦相似度。数据量几百条时没问题几万条时还能忍一旦到了百万条且需要支持字段过滤、增量更新、按租户隔离裸写代码的复杂度会直接击穿项目节奏。这背后的原因不是“numpy 算得慢”而是 RAG 对检索层提出了几个真实要求索引构建不能每次查询都对全量数据暴力扫描需要支持 HNSW、IVF_FLAT 等近似最近邻索引。标量过滤业务上经常要按来源、文档类型、权限范围过滤这要求向量检索和标量过滤在同一个查询里完成。一致性文档更新后索引里的数据不能出现“已经删除还能检索到”的明显脏读。水平扩展知识库从 10 万条涨到 1000 万条不能靠换一台更大的机器硬扛。这些能力如果全部自己写相当于在业务项目里再造一个数据库。Milvus 解决的正是这层工程问题它把“向量索引构建、相似度检索、标量过滤、数据持久化、分布式扩展”封装成了可以对外服务的组件RAG 应用只需要专注文档处理和 Prompt 编排。2. Milvus 与 RAG 的核心概念在真正动手之前先把概念对齐。RAGRetrieval-Augmented Generation检索增强生成的本质是让大模型在生成回答时先从外部知识库中检索出相关内容再结合上下文进行生成。这个过程里用户问题需要被转成向量知识库里的每一条文档片段也需要被转成向量然后通过向量相似度找到最相关的片段。这里有两个关键术语Embedding把文本映射成固定维度数值向量的过程。向量中的数值关系可以表达语义相似度比如“如何安装 Milvus”和“Milvus 安装步骤”的向量距离会很近。向量检索在向量空间中找到与查询向量最相近的若干条记录常见度量方式包括余弦相似度COSINE、欧氏距离L2、内积IP。Milvus 的数据模型则可以用数据库类比来理解Milvus 概念类比说明Collection数据库表一组相关向量的集合Field字段主键、向量字段、标量字段Partition分区按分区键自动分片查询时可跳过无关分区Index索引加速向量检索的数据结构如 HNSW、IVF_FLATSegment存储段数据写入后生成的数据文件单元Load加载到内存查询前需要把集合加载到内存或显存对 RAG 项目来说Milvus 2.6 几个能力特别关键第一是动态 Schema。插入数据时允许携带任意业务字段未预定义的字段会进入$meta保留字段。这意味着你不需要提前把所有业务属性都设计成正式字段文档的来源、权限、标签可以直接跟着一条向量记录走。第二是 Partition Key。可以把数据按某个字段自动分区查询时只扫描相关分区。多租户 RAG 场景里这个功能能显著降低检索延迟。第三是多种一致性级别。RAG 场景通常不需要强一致读取Milvus 支持 Strong、Bounded、Eventually 等一致性级别合理配置可以在吞吐和一致性之间取得平衡。第四是 RBAC 权限控制。到了企业环境不能让所有业务方都共用一套账号Milvus 的账号权限体系可以支撑更规范的安全治理。3. 环境准备与 Milvus Standalone 安装Milvus 的部署模式分为 Standalone 和 Cluster。本文使用 Standalone 模式适合学习、Demo 以及中小规模的内部知识库。生产环境如果需要高可用和水平扩展再考虑 Cluster 或 Kubernetes 部署。3.1 前置条件安装前先确认机器满足以下条件操作系统Linux / macOS / WindowsWSL2Docker Desktop 模式Docker20.10 或更高版本docker compose v2 插件内存建议至少 8 GB尤其是后面要跑本地 Embedding 模型磁盘至少 20 GB 可用空间Milvus 依赖 MinIO 存储数据文件检查命令docker --version docker compose version3.2 下载官方 standalone 配置Milvus Standalone 并不是一个单独容器而是一套包含三个服务的编排etcd 负责元数据存储MinIO 负责对象存储Milvus 本身负责向量检索。官方已经提供了完整的 docker compose 文件不需要自己从零写。mkdir -p ~/milvus-rag cd ~/milvus-rag wget https://github.com/milvus-io/milvus/releases/download/v2.6.1/milvus-standalone-docker-compose.yml -O docker-compose.yml如果网络环境无法访问 GitHub也可以从 Milvus 官网 Releases 页面手动下载milvus-standalone-docker-compose.yml保存为docker-compose.yml。3.3 启动服务docker compose up -d第一次启动会拉取三个镜像milvusdb/milvus、quay.io/coreos/etcd、minio/minio具体镜像版本以官方 compose 文件为准。拉取完成后检查容器状态docker compose ps正常状态下三个服务的 STATUS 都应该是Up或Up (healthy)。Milvus 启动到健康状态下通常需要几十秒到几分钟取决于磁盘和网络速度。3.4 验证服务是否可用curl http://localhost:9091/healthz预期输出OK看到 OK说明 Milvus 服务已经就绪。后续所有 SDK 默认连接 19530 端口这是 Milvus 的 gRPC 端口。停止和启动服务的方式也需要知道docker compose stop docker compose start如果要彻底停止并删除容器保留数据卷docker compose down注意docker compose down -v会连数据卷一起删除执行后所有知识库数据都会丢失除非确认要重建环境否则不要加-v。4. 用 pymilvus 跑通向量检索最小闭环环境就绪后先不要急着接 LlamaIndex 和业务系统。先用 pymilvus 写一个最小可运行的脚本把“建集合、建索引、插数据、查向量”这条链路跑通。这一步能验证环境也能帮助你理解 Milvus 的核心操作顺序。4.1 安装依赖pip install pymilvus sentence-transformerssentence-transformers这步可以先安装后面会用。如果只需要验证 Milvuspymilvus一个包就够了。4.2 创建集合并构建索引创建文件milvus_demo.py# 文件路径milvus_demo.py from pymilvus import ( connections, CollectionSchema, FieldSchema, DataType, Collection, utility, ) # 连接 Milvus connections.connect(hostlocalhost, port19530) collection_name rag_demo # 如果集合已存在先释放再删除保证脚本可重复执行 if utility.has_collection(collection_name): collection Collection(namecollection_name) collection.release() collection.drop() fields [ FieldSchema(nameid, dtypeDataType.INT64, is_primary_keyTrue, auto_idTrue), FieldSchema(namecontent, dtypeDataType.VARCHAR, max_length8192), FieldSchema(namesource, dtypeDataType.VARCHAR, max_length512), FieldSchema(nameembedding, dtypeDataType.FLOAT_VECTOR, dim1024), ] schema CollectionSchema(fields, descriptionRAG 演示集合) collection Collection(namecollection_name, schemaschema) # 构建 HNSW 索引使用余弦相似度 index_params { index_type: HNSW, metric_type: COSINE, params: {M: 16, efConstruction: 200}, } collection.create_index(field_nameembedding, index_paramsindex_params) print(f集合 {collection_name} 创建成功索引构建完成)这里说明几个容易踩坑的点主键id设置了auto_idTrue插入数据时不需要手动传主键Milvus 会自动生成。向量字段维度dim1024对应bge-m3这类中文友好 Embedding 模型的输出维度。create_index必须指定向量字段名如果创建集合后忘了建索引直接 search 会报错。4.3 插入向量数据接着在原文件中追加插入数据逻辑import random random.seed(42) # 这里先用随机向量模拟 Embedding 结果验证链路可通 sentences [f这是第 {i} 条测试文档片段用于演示 Milvus 向量检索功能 for i in range(100)] sources [fsource-{i % 5} for i in range(100)] embeddings [[random.random() for _ in range(1024)] for _ in range(100)] # 注意主键字段是 auto_id插入数据只需要三列content、source、embedding collection.insert([sentences, sources, embeddings]) collection.flush() print(f当前实体数量{collection.num_entities})flush会把内存中的数据落盘确保后续查询可以看到这批数据。如果不调用 flush在强一致性级别下可能看不到刚插入的数据。4.4 查询向量再来是向量检索# 查询前需要 load把集合加载到内存 collection.load() # 实际场景中这里应该是用户问题经过 Embedding 模型生成的向量 query_embedding [random.random() for _ in range(1024)] results collection.search( data[query_embedding], anns_fieldembedding, param{metric_type: COSINE, params: {ef: 64}}, limit3, output_fields[content, source], ) for hits in results: for hit in hits: print(fdistance: {hit.distance:.4f}, source: {hit.entity.get(source)}, content: {hit.entity.get(content)})param参数和索引类型强相关。HNSW 查询时使用efIVF_FLAT 查询时使用nprobe。ef越大召回精度越高但查询延迟也会上升。4.5 运行验证python milvus_demo.py如果一切正常你会看到类似输出集合 rag_demo 创建成功索引构建完成 当前实体数量100 distance: 0.3521, source: source-2, content: 这是第 12 条测试文档片段... distance: 0.3418, source: source-0, content: 这是第 33 条测试文档片段... distance: 0.3295, source: source-4, content: 这是第 87 条测试文档片段...这里随机向量没有真实语义重点验证“写入到检索”的链路是通的。真实项目中会把 random 向量替换成 Embedding 模型生成的向量下一章展开。5. RAG 链路里的核心设计切块、Embedding 与重排Milvus 跑通之后真正的 RAG 功夫还在链路设计。切块和 Embedding 质量直接决定检索命中率。5.1 切块策略切块是 RAG 项目里最容易被低估的环节。切太碎单条信息不完整切太粗向量里混入大量无关内容检索精度下降。常见的切块策略有四种固定大小切块按字符数或 token 数滑动窗口切分。实现简单适合通用文档但容易切断句子语义。递归切块先按段落切段落太长再按句子切句子太长再按 token 切。比固定大小更贴近文档结构。语义切块先对句子做 Embedding根据相邻句子向量相似度找到语义断点。效果更好但计算成本更高。结构感知切块按 Markdown 标题、PDF 章节、代码块等结构边界切分。适合技术文档。一个通用且可靠的参考基线是中文场景下chunk_size 取 256 到 512 个字符chunk_overlap 取 50 到 100 个字符。最小可运行版本可以手写一个滑动窗口切块# 文件路径chunking.py def fixed_size_chunk(text: str, chunk_size: int 500, overlap: int 50): 按字符数切块保留重叠区域避免切断语义。 chunks [] start 0 n len(text) while start n: end min(start chunk_size, n) chunks.append(text[start:end]) if end n: break start end - overlap return chunks实际项目中用 LlamaIndex 的SentenceSplitter或 LangChain 的RecursiveCharacterTextSplitter会更省事这些组件内置了段落、句子、token 的多级切分逻辑。5.2 Embedding 模型选择Embedding 模型决定了向量空间的语义表达质量。对中文 RAG 项目优先考虑中文友好的模型。常见选择包括BGE-M31024 维支持中文、英文等多语言当前中文 RAG 项目的主流选择。text2vec-large-chinese中文领域经典模型。OpenAI text-embedding-3-small1536 维英文和代码效果优秀中文也够用。这里有一个必须保持的原则写入和查询必须使用同一个 Embedding 模型且向量维度必须和 Collection 里的dim保持一致。团队里如果有人换了模型但没改维度插入时 Milvus 会直接报错。用 BGE-M3 生成查询向量的示例from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-m3) query_vec model.encode(如何配置 Milvus 索引).tolist()5.3 检索过滤与重排真实企业知识库里一条向量通常还跟着来源、项目、权限等业务字段。检索时可以先按标量字段过滤再在过滤结果里做向量搜索。results collection.search( data[query_vec], anns_fieldembedding, param{metric_type: COSINE, params: {ef: 128}}, limit10, exprsource source-1, output_fields[content, source], )expr是 Milvus 的标量过滤表达式语法类似 SQL 的 WHERE 条件。这个能力在 RAG 的权限控制里非常有用先过滤掉当前用户无权访问的文档再检索相关片段。重排环节建议用专门的 Rerank 模型。向量召回拿回 top 50 后把候选片段和用户问题一起交给 Rerank 模型打分再取 top 5 作为大模型的上下文。这个步骤通常能显著提升回答质量尤其是文档数量多、主题相近的时候。6. 用 LlamaIndex 串起企业级 RAG 工程直接用 pymilvus 写检索控制力最强但工程效率偏慢。LlamaIndex 这类框架把文档加载、切块、Embedding、存储、检索、生成整合成了统一 API和 Milvus 的集成也比较成熟。6.1

相关新闻

最新新闻

日新闻

周新闻

月新闻