Embedding 基础
什么是 embedding
Embedding(嵌入向量)把文字、图片、音频等对象表示成向量。在 RAG 里常用的单向量文本 embedding,会把一句话、一段文档、一个标题或一个用户问题转成固定长度的数字序列。
用于语义检索的模型会学习让相关内容的向量更接近。比如“猫”和“猫咪”通常比“猫”和“股票”更接近;问题与能够回答它的文档,也应有较高的匹配分数。
可以把向量理解为语义空间里的坐标:
文本 向量
“猫” -> [0.12, -0.08, 0.31, ...]
“猫咪” -> [0.11, -0.07, 0.30, ...]
“股票” -> [-0.42, 0.56, 0.02, ...]2
3
4
文本向量通常有几百到几千维,每一维一般没有“动物性”“宠物性”这样的固定含义。检索程序比较的是整个向量,把“按意思搜索”转成距离或相似度计算。
这个能力不只用于 RAG,还常用于:
- 语义搜索:用户说“自动猫粮机”,也能找到写着“宠物喂食器”的文档。
- 聚类:把一批客服问题自动分成“退款”“物流”“账号登录”等主题。
- 推荐:找和当前商品、文章、代码片段相似的内容。
- 长期记忆检索:从历史对话、用户偏好和任务记录中找回相关内容。
- 去重:发现两段标题不同但意思几乎一样的资料。
- 异常检测:找出一批文本里语义上特别离群的记录。
- 分类:把输入和几个类别描述做相似度比较,选最接近的类别。
embedding 是怎么来的
典型的文本 embedding 模型按以下过程生成向量:
- 切 token:先把输入文本拆成 token。
- 编码上下文:模型计算每个 token 在当前上下文中的表示。
- 汇总成向量:通过池化或特定 token 的表示,得到固定长度的文本向量。
- 可选归一化:有些模型或框架会把向量长度归一化,方便用 cosine / dot product 比较。
几个容易混淆的点:
- embedding 不是关键词列表。它不是简单记录“出现过哪些词”,而是学习到的语义表示。
- embedding 不是可逆压缩。拿到一串向量,通常不能还原原文。
- embedding 有信息损失。短句、长文、代码、表格、专有名词的压缩难度不同。
- 模型大小不能单独决定检索质量,还要看训练数据、语言覆盖和目标任务。
向量相似度:怎么算“近”
检索时会把用户问题也转成向量,然后和库里的文档向量比较,找最接近的几个。
常见距离 / 相似度:
| 名称 | 越大越相似? | 适合记法 | 说明 |
|---|---|---|---|
| Cosine similarity | 是 | 看方向 | 最常见,关心两个向量方向是否接近 |
| Cosine distance | 否 | 距离越小越近 | 很多向量库返回的是 distance,不是 similarity |
| Dot product / inner product | 是 | 点积越大越近 | 两个向量均经过 L2 归一化时,点积等于余弦相似度 |
| L2 / Euclidean distance | 否 | 直线距离 | 图像、某些传统向量场景常见;文本检索也可用但要看模型建议 |
设两个非零向量为 q 和 d,余弦相似度为:
cosine(q, d) = dot(q, d) / (norm(q) × norm(d))L2 归一化会把向量长度变成 1。归一化后,dot(q, d) = cosine(q, d),且 L2(q, d)² = 2 - 2 × cosine(q, d),因此精确搜索时这三种方式的排序一致。是否归一化、使用哪种距离函数,应遵循模型的检索配置。
例如,语义检索希望得到这样的匹配关系:
"怎么给猫买喂食器" vs "自动猫粮机推荐" -> 相似度高,应该命中
"怎么给猫买喂食器" vs "今天股市怎么样" -> 相似度低,应该过滤2
注意两点:
- 不同模型的分数不可直接比较。模型 A 的 0.82 和模型 B 的 0.82 不是同一个含义,分数也不表示相关概率。
- “最相似”不等于“能回答”。检索结果还要看是否包含足够事实、是否过期、是否有权限、是否被噪声污染。
维度:为什么常见是 384、768、1024、1536、3072
维度就是向量里有多少个数字。比如 1536 维,就是每条文本变成 1536 个浮点数。
384、768、1024、1536、3072 都是常见的输出维度,取决于模型设计;部分模型还支持配置输出维度。这些数字不能直接当作模型的质量等级。维度主要影响:
- 存储:float32 每维占 4 字节。100 万条 1536 维向量,原始向量约 6.1 GB;3072 维约 12.3 GB,均按十进制计算,还没算索引和元数据。
- 计算:其他条件相同时,向量越长,一次距离计算的开销越大。
- 表示容量:同一模型缩短维度可能损失部分检索质量;不同模型之间,不能仅按维度比较效果。
先用一组带正确证据标注的问题评测检索效果,再比较存储和查询成本。如果 768 或 1024 维已经满足要求,就没必要直接上 3072 维。缩短向量也应使用模型支持的配置,不能任意截取前几维。
常见 embedding 模型
下面列出几类常见选择,便于比较 API 服务和本地部署模型:
| 模型 / 系列 | 常见维度 | 适合场景 | 备注 |
|---|---|---|---|
text-embedding-3-small | 1536,可用 dimensions 缩短 | 通用 RAG、OpenAI 生态 | 可作为 API 方案的评测起点 |
text-embedding-3-large | 3072,可用 dimensions 缩短 | 通用 / 多语言检索 | 与 small 比较检索收益和调用成本 |
text-embedding-ada-002 | 1536 | 老项目维护 | 老一代模型,新项目通常优先看 text-embedding-3-* |
| Cohere embed-v4.0 | 默认 1536,可选 256 / 512 / 1024 | 多语言、长上下文、文本+图片混合检索 | 支持可调维度和多种 embedding 类型 |
| Voyage voyage-4 / large / lite | 默认 1024,可选 256 / 512 / 2048 | 通用、多语言检索 | voyage-code-* 系列面向代码检索 |
BAAI bge-m3 | 1024 | 中文 / 多语言、本地或私有化部署 | 支持 dense、sparse、multi-vector 等检索形态 |
BGE small/base/large-en-v1.5 | 384 / 768 / 1024 | 英文检索、本地实验 | 轻量到中等规模开源选择 |
all-MiniLM-L6-v2 | 384 | 英文语义搜索、聚类、本地原型 | 模型较小,中文任务需另选或单独评测 |
选模型时,不要只看排行榜。更实用的判断顺序:
- 语言:中文、多语言、代码、法律、金融等领域差异很大。
- 任务:问答检索、聚类、分类、推荐、代码搜索,不一定同一个模型最强。
- 部署方式:API 省心但要考虑数据合规;本地模型可控但要管推理服务。
- 成本与延迟:分别测量批量入库、查询编码和向量检索;API 计费不一定随输出维度变化。
- 可复现性:模型版本、维度、归一化策略、chunk 规则都要记录,否则换模型后难排查。
查询与文档的编码配置
入库与查询需要使用配套的编码配置,输出向量必须处于同一空间。通常会用同一个模型,但查询和文档可能要求不同的前缀或 input_type;也有采用成对训练编码器的检索模型。
不同模型学出来的是不同“坐标系”。即使输出维度相同,直接比较两个不兼容模型的向量,也不能得到可靠的检索排序。
换模型时通常要做这些事:
- 重新切分或至少重新确认 chunk 规则。
- 用新模型把所有文档重新 embedding。
- 新建 collection / index,避免新旧向量混在一起。
- 用同一批评测问题比较旧库和新库的召回质量。
- 记录
embedding_model、dimension、chunk_version、created_at等元数据。
Dense、Sparse、Multi-vector:三种检索表示
RAG 里常说的 embedding 通常指 dense embedding,也就是一整串稠密浮点数。但实际检索不止这一种。
| 类型 | 代表 | 优点 | 短板 |
|---|---|---|---|
| Dense embedding | OpenAI / Cohere / Voyage / BGE dense | 擅长语义相似,能跨同义词、改写表达 | 对精确编号、人名、型号、否定条件有时不够稳 |
| Sparse 表示 | BM25、SPLADE、BGE-M3 sparse | 保留词项权重,适合术语、编号等匹配 | BM25 对同义改写较弱;学习式稀疏模型可通过词项扩展改善 |
| Multi-vector / late interaction | ColBERT、BGE-M3 multi-vector | token 级匹配更细,质量潜力高 | 存储和计算更贵,系统复杂度更高 |
BM25 根据词频等统计量打分,SPLADE 等模型则学习稀疏权重,两者的实现不同。工程上可以把 dense 检索和 BM25 结合,再用 reranker 重排,分别处理语义匹配和精确词匹配。
语义搜索与关键词搜索如何配合
关键词搜索匹配字面:搜“汽车”可能找不到只写了“轿车”的文档。 语义搜索匹配意思:能找到“轿车”“新能源车”“家用车”等相关表达。
但语义搜索也有短板。遇到这些内容时,关键词搜索常常更准:
- 订单号、合同号、SKU、错误码:
A1002、ERR_CONN_RESET - 人名、公司名、地名、专有名词
- 精确版本:
Python 3.12、Milvus 2.5 - 表格里的短字段:状态、枚举值、金额、日期
否定和例外条件需要另外检查。例如“不支持退款”和“支持退款”可能同时出现在候选中,单靠词项命中或向量分数都不足以判断适用规则。
例如,客服知识库里有“错误码 TS-999 的处理步骤”和“常见连接错误排查”两条记录。用户问“TS-999 怎么办”时,向量检索可能同时召回两条;关键词匹配可以保留错误码这个精确条件。混合检索(hybrid search)通常按以下流程组织:
用户问题
-> 两路召回:dense 向量检索找语义相关,BM25 / 全文检索找精确词
-> 合并候选
-> reranker 重排
-> 选择证据片段,交给 LLM 生成答案2
3
4
5
向量索引:为什么能在百万数据里快速找最近
如果有 1000 万条向量,每次查询都和所有向量逐一算距离,成本会很高。 向量库通常使用 ANN(Approximate Nearest Neighbor,近似最近邻)索引,在召回率、延迟和内存占用之间做取舍。
常见索引思路:
| 索引 | 核心直觉 | 适合场景 | 代价 |
|---|---|---|---|
| Flat / brute-force | 全量逐个比较 | 小数据、评测基线、最高召回 | 慢,数据大后不可接受 |
| HNSW | 建一张“近邻图”,搜索时沿图跳转 | 低延迟、高召回、通用生产场景 | 内存占用高,构建成本高 |
| IVF | 先把向量分桶,查询只搜相近桶 | 大规模、可调速度和召回 | 需要训练/建桶,参数不当会漏召回 |
| PQ / Quantization | 把向量压缩成更小表示 | 超大规模、内存受限 | 有精度损失,调参复杂 |
| Disk-based index | 把部分向量或索引放磁盘 | 数据大到内存放不下 | 依赖 SSD / NVMe,延迟更敏感 |
调参时主要看三个指标:
- ANN Recall@k:相对于相同距离函数和过滤范围下的精确 top-k,近似搜索找回了多少结果。
- Latency:一次查询多久返回。
- Memory / storage:索引和向量占多少资源。
ANN 召回率衡量索引是否漏掉近邻;业务召回率衡量是否找到了能回答问题的证据。前者很高时,仍可能因为模型或分块不合适而漏掉关键内容。
向量数据库
向量数据库专门存 embedding,并支持“快速找最近邻”。一般不只存向量,还会存文档内容和元数据。
一条 RAG chunk 记录通常包含原文、向量和元数据,下面只列出向量的前三个分量:
{
"id": "handbook:p12:chunk-03",
"text": "报销单需要在发票开具后 30 天内提交...",
"embedding": [0.012, -0.034, 0.087],
"metadata": {
"source": "employee-handbook.pdf",
"page": 12,
"section": "财务制度",
"updated_at": "2026-08-07",
"tenant_id": "company-a",
"embedding_model": "text-embedding-3-small",
"dimension": 1536,
"chunk_version": "v1"
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
常见选择:
| 工具 | 更准确的定位 | 适合场景 | 备注 |
|---|---|---|---|
| Chroma | 轻量向量库 | 本地 demo、小项目、教学 | 上手快,适合先把 RAG 跑通 |
| FAISS | 高性能向量检索库,不是完整数据库 | 本地批量检索、研究、需要 GPU/高性能索引 | 需要自己处理元数据、服务化、权限等 |
| Qdrant | 专用向量数据库 | 自托管 / 云服务、过滤条件多、RAG 生产 | payload 过滤、HNSW、dense/sparse/named vectors 生态成熟 |
| Milvus / Zilliz | 分布式向量数据库 | 更大规模、多集合、高吞吐 | 功能完整,部署和运维比轻量方案重 |
| Weaviate | 向量数据库 + 搜索能力 | 想要 schema、模块化向量化、hybrid search | 对混合检索和对象 schema 支持友好 |
| Pinecone | 托管向量数据库 | 快速上线、少运维、云上生产 | 省心,但要接受供应商成本和能力边界 |
| pgvector | PostgreSQL 扩展 | 已经有 Postgres、数据量中等、强事务/SQL/权限需求 | 业务表和向量放一起很方便,大规模要认真调索引 |
| Elasticsearch / OpenSearch | 搜索引擎 + 向量能力 | 已有全文检索体系,想做 hybrid search | 适合关键词搜索强、向量只是增强的系统 |
| Redis Vector Search | 内存数据库 + 向量检索 | 低延迟、缓存型语义检索 | 内存成本高,持久化和复杂检索要评估 |
简单选型:
- 学习 / 原型:Chroma 或 FAISS。
- 已经有 PostgreSQL:先试 pgvector,别急着引入新数据库。
- 过滤条件很多:Qdrant、Weaviate、Milvus 这类专用向量库更顺手。
- 不想运维:Pinecone、Weaviate Cloud、Zilliz Cloud、Qdrant Cloud。
- 超大规模 / 多租户 / 高吞吐:优先评估 Milvus、Qdrant、Pinecone 等生产型方案。
RAG 里 embedding 的完整位置
Embedding 为检索提供向量表示,检索结果中的原文再交给生成模型。两者可以独立选型:更换生成模型通常不需要重算文档向量,更换 embedding 模型则通常需要重建向量索引。
离线入库:
原始文档 -> 清洗 -> 切 chunk -> 生成 embedding -> 写入向量库
在线查询:
用户问题 -> 生成 query embedding -> 在权限及 metadata 约束下检索
-> reranker 重排 -> 取证据片段
-> 拼进 prompt -> LLM 生成答案 -> 返回引用来源2
3
4
5
6
7
几个关键参数:
| 参数 | 常见起点 | 影响 |
|---|---|---|
| chunk size | 300-800 token 起步 | 太小缺上下文,太大噪声多 |
| overlap | 10%-20% 起步 | 防止答案被切断,但会增加成本 |
| top_k | 5-20 个候选 | 太少漏资料,太多塞爆上下文 |
| rerank top_n | 3-8 条 | 最终给 LLM 的证据数量 |
| score threshold | 需要用评测集调 | 防止低相关内容混进答案 |
用评测集调整这些参数,观察正确证据是否进入候选、最终上下文是否保留了回答所需条件。分块和上下文组装的具体流程见 RAG 完整链路。
聚类:embedding 不只用来搜索
聚类(clustering)就是:不预先告诉模型“应该分成哪几类”,让算法自己按相似度把一批样本分成若干组。 你可以把它理解成“自动分堆”。比如一堆客服消息里,算法自己把“退款”“物流”“登录失败”“发票问题”分开。
它和分类不一样:
- 分类:你先告诉模型类别是什么,比如“退款 / 物流 / 登录”。
- 聚类:你先不给类别标签,让算法自己发现哪些东西像一类。
聚类不是 RAG 必需环节,但在这些任务里很有用:
- 给文档、工单、评论自动发现主题。
- 做语义去重,把重复 FAQ 合并。
- 从大量资料里抽样,保证覆盖不同主题。
- 给长期记忆分区,减少每次检索的候选范围。
- 做异常检测,找和常规数据不一样的输入。
常见聚类算法:
| 算法 | 是否要先给簇数 K | 适合场景 | 注意点 |
|---|---|---|---|
| K-Means | 要 | 数据量大、希望分成固定 K 个主题 | 假设簇比较“圆”,对 K 和初始化敏感 |
| MiniBatch K-Means | 要 | 更大数据量、速度优先 | 质量可能略低,但很适合百万级粗分组 |
| Agglomerative 层次聚类 | 可给 K,也可给距离阈值 | 小中规模、想看层次结构 | 数据大时慢,内存压力大 |
| DBSCAN | 不要 | 想自动发现密集区域和噪声点 | eps 很难调,高维文本向量上不一定稳定 |
| HDBSCAN | 不要 | 密度不均、希望识别离群点 | 仍需调 min_cluster_size 等参数,高维距离也可能难以区分样本 |
| Spectral Clustering | 通常要 | 非凸形状、小中规模图聚类 | 计算更贵,不适合直接上大规模 |
| Gaussian Mixture | 要 | 想要“软分类概率” | 假设数据近似高斯,高维文本上要谨慎 |
做文本 embedding 聚类时的实用建议:
- 按模型建议决定是否做 L2 归一化,并选用与距离度量匹配的聚类算法。
- 数据很多时,先用 MiniBatch K-Means 做粗分组,再人工看每组代表样本。
- 做可视化可以用 UMAP / t-SNE 降到 2D,但不要把 2D 可视化结果当最终真相。
- 聚类结果要人工抽检。
silhouette_score这类指标只能辅助,不能替代业务判断。 - 文本太短时聚类会漂。比如只有“好”“不行”“已处理”,缺上下文就很难分。
常见易忽略的细节
- 只存向量,不存原文和来源:最后 LLM 无法引用证据,也无法排查检索错在哪里。
- 不兼容模型的向量混在同一个 collection:维度可能相同,但距离无法反映内容的相关性。
- 维度和索引不匹配:例如库建的是 1536 维,查询传 1024 维,会直接报错或无法检索。
- 忽略权限过滤:先检索再过滤可能泄露候选信息;企业场景要把
tenant_id、ACL 等作为强过滤条件。 - 只用语义检索查编号:订单号、错误码、合同号优先走关键词或 metadata 精确过滤。
- chunk 切得太粗:一个 chunk 里混了多个主题,检索命中后 LLM 仍然找不到答案。
- chunk 切得太碎:召回的是半句话,缺少解释条件和上下文。
- 混淆召回与重排:reranker 可以改善候选顺序,但找不回未进入候选集的片段。
- 不记录版本:换模型、换切分策略后,很难解释线上效果为什么变化。
- 只看向量库返回分数:分数高不代表事实足够,必须看文本本身。