大模型 RAG 实战:一文搞懂 Embedding 语义向量与向量数据库(Milvus 落地)
在构建基于大语言模型(LLM)的企业级知识库(RAG 系统)时,很多开发者面临的第一道难关就是:如何让 AI 准确定位到企业文档中的关键信息?
上一阶段,我们通常已经完成了 TXT、Markdown、PDF 等格式文档的清洗与切块。本文我们将继续完成 RAG 架构中最核心的一步:将文本转化为数字向量(Embedding),并使用向量数据库实现高效的语义检索。
1. 为什么传统关键词搜索不够用?
在大模型项目中,用户提问的方式与企业文档中的原话往往存在巨大差异。
例如:
用户提问:“迟到半小时要扣多少钱?”
文档原话:“员工考勤规范:未按规定考勤者,根据情节轻重给予警告或相应绩效扣减。”
这两句话在文字层面上几乎没有重合的关键词,但其表达的深层语义是高度一致的。
如果采用传统的正则匹配或关键词搜索(如 ElasticSearch 的 BM25),很容易漏检或召回无关内容。而Embedding(文本嵌入)的作用,就是将自然语言转换成一组包含了语义特征的高维浮点数数组(即向量)。
核心逻辑:语义越接近的文本,其在向量空间中的几何距离越近。
[文本 A: "迟到半小时要扣多少钱"] ----(Embedding 模型)----> [0.12, -0.35, 0.78, ...] ↓ (计算空间余弦距离/相似度) [文本 B: "未按规定考勤给予绩效扣减"] ----(Embedding 模型)----> [0.11, -0.32, 0.75, ...]2. Embedding 模型 vs. 大语言模型(LLM)
很多人容易混淆 Embedding 模型与 DeepSeek、GPT-4 这类生成式聊天模型。简单来说,它们在 RAG 系统中分工极其明确:
| 特性 | 大语言模型 (LLM, 如 DeepSeek-R1) | Embedding 模型 (如 text-embedding-v4 / BGE) |
| 主要作用 | 理解复杂指令、逻辑推理、总结摘要、生成文本 | 将文本映射为连续的高维语义向量 |
| 输出格式 | 自然语言文本 (String) | 高维浮点数列表 (List[float]) |
| 在 RAG 中 | 充当“大脑”:结合检索出的上下文回答问题 | 充当“导航”:精准查找相关的知识片段 |
协同工作流程图
3. Embedding 底层原理拆解:向量与维度究竟是什么?
当你看到一段 Embedding 输出为[-0.0182, 0.8601, 0.0053, ...]时,这些数字到底代表什么?
什么是向量维度?
核心定义:向量维度就是数组里浮点数的个数。例如
[0.12, -0.35, 0.78, 0.09]就是一个 4 维向量。主流模型维度参考:
bge-small-zh-v1.5:512 维bge-base-zh/ 通义千问text-embedding-v4:768 维OpenAI
text-embedding-3-small/ada-002:1536 维OpenAI
text-embedding-3-large:3072 维
维度代表什么含义?
我们可以把高维空间中的每一个维度,理解为模型自动学习到的某种语义特征权重(如:行业、情绪、动作、时态等)。
数值含义:
正数值大:文本在该维度上的特征极强(如:运动领域)。
数值接近 0(如 ±0.01):文本与该特征几乎无关。由于模型维度通常高达上千,一段具体文本只会激活少数几个维度,因此绝大多数维度都会呈现为接近 0 的数值。
向量相似度匹配算法
在向量数据库中,比对两段文本是否相似,最主流的方法是计算余弦相似度(Cosine Similarity):
夹角接近 0°(余弦值接近 1):语义高度相似。
夹角接近 90°(余弦值接近 0):语义毫无关联。
4. 实战一:在 LangChain 中使用 Embedding
LangChain 提供了标准化的统一接口,核心包含两个 API:
embed_documents(texts: List[str]):批量处理文本块,用于构建向量索引。embed_query(text: str):处理单条用户提问,用于语义检索。
批量文本向量化代码示例
Python
import os from langchain_community.embeddings import DashScopeEmbeddings # 初始化通义千问 Embeddings 模型 embeddings = DashScopeEmbeddings( model="text-embedding-v4", dashscope_api_key=os.getenv("DASHSCOPE_API_KEY") ) # 1. 单条查询向量化 query = "公司迟到早退怎么处罚?" query_vector = embeddings.embed_query(query) print(f"查询向量维度: {len(query_vector)}") print(f"向量前5维示例: {query_vector[:5]}\n") # 2. 批量文档向量化 docs = [ "员工无故迟到或早退 15 分钟以内,单次扣除绩效工资 50 元。", "公司提供免费员工午餐,用餐时间为 12:00 至 13:00。", "违反保密协议将直接予以解除劳动合同,并保留追究法律责任的权利。" ] doc_vectors = embeddings.embed_documents(docs) print(f"文档块数量: {len(doc_vectors)}") print(f"每个文档向量维度: {len(doc_vectors[0])}")注意:如果不方便调用云端 API,也可以使用 HuggingFace 上的开源中文本地模型(如
BAAI/bge-small-zh-v1.5)。
5. 向量数据库选型指南
有了向量,我们需要一个能够持久化存储浮点数组并支持毫秒级近邻搜索(ANN)的专门数据库。
主流向量数据库选型参考:
| 数据库 | 部署方式 | 性能/扩展性 | 易用性 | 适用场景 |
| Milvus | 开源自建 / 托管 | ★★★★★ | ★★★★☆ | 企业级大规模 RAG(支持亿级向量,分布式架构) |
| Chroma | 轻量本地自建 | ★★★☆☆ | ★★★★★ | 开发者 MVP 原型/ 本地轻量应用 |
| Pinecone | 云原生 SaaS 全托管 | ★★★★★ | ★★★★★ | 希望免运维的企业级外网项目 |
| pgvector | PostgreSQL 插件 | ★★★☆☆ | ★★★★★ | 中轻量级项目,复用已有关系型数据库 |
6. 实战二:基于 Milvus + LangChain 构建企业检索索引
下面我们模拟一个真实的企业级场景:将公司 HR 手册与客服退款政策导入开源向量数据库Milvus,并实现基于元数据过滤的精准检索。
1) 环境搭建与索引建模
在 Milvus 中,针对中文向量检索,推荐的索引配置如下:
metric_type:"COSINE"(首选余弦相似度)index_type:"HNSW"(基于分层导航小世界图结构,检索速度极快)params:{"M": 16, "efConstruction": 128}
2) 索引构建脚本 (build_index.py)
Python
import os from langchain_community.document_loaders import TextLoader, DirectoryLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.embeddings import DashScopeEmbeddings from langchain_milvus import Milvus # 1. 实例化 Embedding 模型 embeddings = DashScopeEmbeddings( model="text-embedding-v4", dashscope_api_key=os.getenv("DASHSCOPE_API_KEY") ) # 2. 扫描并加载知识库文档 loader = DirectoryLoader( "./knowledge_base/", glob="**/*.*", show_progress=True ) raw_docs = loader.load() # 3. 语义切块 (Chunking) text_splitter = RecursiveCharacterTextSplitter( chunk_size=300, chunk_overlap=50 ) split_docs = text_splitter.split_documents(raw_docs) # 4. 写入 Milvus 向量数据库 URI = "http://localhost:19530" # Milvus 服务地址 vector_store = Milvus.from_documents( documents=split_docs, embedding=embeddings, collection_name="enterprise_knowledge_base", connection_args={"uri": URI}, index_params={ "metric_type": "COSINE", "index_type": "HNSW", "params": {"M": 16, "efConstruction": 128} }, drop_old=True # 重新构建时清理旧 Collection ) print(f"成功导入 {len(split_docs)} 个文档切块到 Milvus 数据库!")7. 高级检索技巧:相似度打分与元数据过滤(Metadata Filtering)
在实际业务场景中,我们经常遇到需要按部门、产品线、权限隔离查询的情况,这就需要用到元数据过滤。
带打分与表达式过滤的检索 (search_knowledge.py)
Python
import os from langchain_community.embeddings import DashScopeEmbeddings from langchain_milvus import Milvus embeddings = DashScopeEmbeddings( model="text-embedding-v4", dashscope_api_key=os.getenv("DASHSCOPE_API_KEY") ) # 连接现有 Collection vector_store = Milvus( embedding_function=embeddings, collection_name="enterprise_knowledge_base", connection_args={"uri": "http://localhost:19530"} ) query = "申请退款需要满足什么条件?" # 使用 Milvus expr 表达式进行元数据精确过滤 # 仅在 customer_service 类的文档中检索 Top 2 结果 results_with_score = vector_store.similarity_search_with_score( query=query, k=2, expr='category == "customer_service"' # 元数据过滤表达式 ) for doc, score in results_with_score: print(f"【相似度得分 (Distance/Score)】: {score:.4f}") print(f"【来源元数据】: {doc.metadata}") print(f"【匹配内容片段】: {doc.page_content}\n" + "-"*50)8. 避坑指南:检索效果不佳的排查清单
当 RAG 系统出现“答非所问”或“召回失败”时,建议按照如下优先级排查:
Embedding 模型版本一致性:建库时的 Embedding 模型与检索时的 Embedding 模型必须绝对统一。若模型不一致,向量分布空间完全不同,检索必然失败。
切片粒度(Chunk Size)调优:
切片过小:丢失上下文完整语义。建议适当调大
chunk_size。切片过大:单块混合了多个主题,稀释了关键信息的向量权重。
重叠窗口(Chunk Overlap):必须保留 10%~20% 的 overlap,避免关键实体或句式在切分点断开。
不要硬编码相似度阈值:不同数据库(Milvus, Chroma)和不同距离度量方式(Cosine, L2, IP)输出的 score 含义与区间完全不同,建议结合具体业务评估集(Evaluation Harness)设定动态召回策略。
小结
本文探讨了 RAG 系统中处理“语义理解”与“高速检索”的核心技术:
Embedding:负责将文本转化为捕捉了深层语义的高维数字向量。
向量数据库(Milvus):负责高效存储向量及元数据,并在千万级规模下实现毫秒级余弦相似度匹配。
在下一篇文章中,我们将把本章检索出的相关文本片段,组装为上下文(Context)输入给DeepSeek,完成最终“基于企业私有知识库的精准问答系统”开发!
