Embedding模型选型
**本文适合谁**:已经跑通基础 RAG,但发现中文场景下检索质量不够好,或者需要评估不同 Embedding 方案的成本和性能权衡的读者。
Embedding 模型选型:不只是 OpenAI
本文适合谁:已经跑通基础 RAG,但发现中文场景下检索质量不够好,或者需要评估不同 Embedding 方案的成本和性能权衡的读者。
在 RAG 系统中,Embedding 模型的选择直接决定了检索质量的上限。不同模型在不同语言、不同领域的表现差异显著,选型不当会导致语义相似的文档无法被检索到。
这个问题在处理中文内容时尤为突出。OpenAI 的 text-embedding-ada-002 对中文语义的表示质量明显弱于专门优化过的中文模型。例如查询"合同违约责任",知识库中明明有对应条款,但无法排进 top-3;换成英文同义词 "contract breach liability" 反倒能搜出来——这就是 Embedding 模型语言偏差的典型表现。
Embedding 模型干什么用
RAG 的第一步是把文档切块,把每个 chunk 转成一个向量,存进向量数据库。用户提问时,把问题也转成向量,用余弦相似度找最近的几个 chunk。
整个检索质量的上限,就是 Embedding 模型的语义理解能力。模型把语义相似的文本映射到向量空间里相近的位置,检索才准;如果模型对某种语言或领域的理解能力弱,相关文档和查询在向量空间里可能相距甚远,检索就失效了。
向量空间中的语义聚类——语义相近的词(动物/交通工具/食物/AI技术)在高维空间中距离更近
主流 Embedding 模型横向对比
OpenAI text-embedding-3-small / large
2024 年初 OpenAI 推出的新一代 Embedding 模型,取代了之前的 ada-002。
text-embedding-3-small 1536 维,$0.02 / 百万 token,性价比极高。text-embedding-3-large 3072 维,精度更高但价格翻倍。英文场景下这两个模型在 MTEB 榜单上表现一流。
缺点也很明显:API 调用,文档数据必须发送到 OpenAI 服务器,不适合有数据合规要求的项目。中文效果比英文差一档。
BGE(BAAI General Embedding)
北京智源研究院开源的 Embedding 模型系列,是国内开发者做中文 RAG 的首选。
bge-large-zh 专门针对中文优化,检索场景下的中文效果在开源模型里名列前茅。bge-m3 是多语言版本,支持 100+ 种语言,中英双语效果都很好,而且支持最长 8192 token 的输入,适合处理长文档。
可以用 sentence-transformers(一个专门用于加载和使用句子级别 Embedding 模型的 Python 库)本地加载,完全离线,数据不出境。
text2vec
国内开源,专门为中文设计,模型较小(几百 MB),推理速度快,适合资源有限的部署环境。效果比 BGE 略逊一筹,但轻量是它的优势。
Cohere Embed
商业 API,多语言支持是其强项,在非英语语言上的 MTEB 成绩不错。Cohere 有配套的重排序模型 Rerank,Embedding + Rerank 一套组合下来检索效果很好。不能本地部署,同样有数据出境问题。
Jina Embeddings
开源,最大的特点是支持 8192 token 的超长输入,普通 Embedding 模型通常只支持 512 或 1024 token。对于法律合同、学术论文这类需要整段长文本做 Embedding 的场景有价值。中文效果一般。
参考 MTEB 榜单
MTEB(Massive Text Embedding Benchmark,大规模文本嵌入基准,一个公认的评估 Embedding 模型性能的测试集合)是评估 Embedding 模型的标准基准,涵盖检索、分类、聚类、语义相似度等多个任务。
选模型之前先查看榜单:https://huggingface.co/spaces/mteb/leaderboard
有几点要注意:榜单上的总分是多任务平均,RAG 场景大概率是检索(Retrieval),要专门看检索子榜的成绩。另外,中文场景要看中文子集,不能只看英文或平均分。bge-m3 在中文检索子榜上长期排前几,这是榜单数据的客观反映。
维度与性能的权衡
维度越高,向量能表达的语义信息越丰富,精度通常更好。但高维向量也意味着更大的存储空间和更慢的相似度计算。
一个具体的数字感受:100 万个 1536 维的 float32 向量,存储占用约 6GB。如果换成 3072 维,就是 12GB。向量数据库在几千万条数据量级时,维度高低对检索延迟的影响很明显。
OpenAI 的 text-embedding-3 支持"维度截断"——可以指定输出 256、512、1024 维,精度略有损失但存储和速度大幅改善。这个功能在大规模部署时很有用。
代码示例:对比中文检索效果
import os
from openai import OpenAI
from sentence_transformers import SentenceTransformer
import numpy as np
from typing import List
# 初始化客户端
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# 本地加载 BGE-M3(首次运行会下载模型,约 2GB)
print("加载 BGE-M3 模型...")
bge_model = SentenceTransformer("BAAI/bge-m3")
# 测试数据:中文法律条款知识库
documents = [
"合同一方违约时,守约方有权要求违约方承担违约责任,包括继续履行、采取补救措施或者赔偿损失。",
"当事人可以约定一方违约时应当根据违约情况向对方支付一定数额的违约金,也可以约定因违约产生的损失赔偿额的计算方法。",
"不可抗力是指不能预见、不能避免并不能克服的客观情况,包括自然灾害和社会异常事件。",
"因不可抗力不能履行合同的,根据不可抗力的影响,部分或者全部免除责任。",
"当事人一方因第三人的原因造成违约的,应当依法向对方承担违约责任。",
]
query = "合同违约了要承担什么责任"
def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def get_openai_embeddings(texts: List[str]) -> List[np.ndarray]:
response = openai_client.embeddings.create(
model="text-embedding-3-small",
input=texts
)
return [np.array(item.embedding) for item in response.data]
def get_bge_embeddings(texts: List[str]) -> List[np.ndarray]:
# BGE 模型检索场景建议加前缀
return [bge_model.encode(text) for text in texts]
def retrieve_and_rank(query: str, documents: List[str], embed_fn, model_name: str):
print(f"\n{'='*50}")
print(f"模型:{model_name}")
print(f"查询:{query}")
print(f"{'='*50}")
# 计算所有文档的向量
all_texts = [query] + documents
all_embeddings = embed_fn(all_texts)
query_embedding = all_embeddings[0]
doc_embeddings = all_embeddings[1:]
# 计算相似度并排序
scores = [
(cosine_similarity(query_embedding, doc_emb), doc)
for doc_emb, doc in zip(doc_embeddings, documents)
]
scores.sort(key=lambda x: x[0], reverse=True)
for rank, (score, doc) in enumerate(scores[:3], 1):
print(f"\nTop {rank}(相似度:{score:.4f})")
print(f" {doc[:60]}...")
# 执行对比
retrieve_and_rank(query, documents, get_openai_embeddings, "OpenAI text-embedding-3-small")
retrieve_and_rank(query, documents, get_bge_embeddings, "BGE-M3(本地)")
运行这段代码,可以观察到 BGE-M3 对"合同违约责任"这个中文查询的排序更贴近人的直觉。OpenAI 的模型有时候会把不可抗力条款排进 top-3,因为它看到了"违约"这个词——但语义上那条是免责,不是违约责任。BGE 对中文法律语境的理解更准。
选型建议
纯英文场景:text-embedding-3-small 足够,性价比最高。有更高精度要求再升级 large。
中文或中英混合场景:bge-m3,本地部署,效果和成本都合适。
有数据合规要求、不能走外网:BGE 系列或 text2vec,本地部署,数据完全自控。
文档超长(单文档几千 token):Jina Embeddings 或 BGE-M3(支持到 8192 token)。
预算充足、追求多语言最佳效果:Cohere Embed + Rerank 组合,花钱省心。
评估要做,不要靠感觉
换模型之前,先建一个测试集:选 50-100 个典型查询,标注每个查询对应的相关文档。用 Recall@5(召回率指标,衡量前 5 个检索结果中有多少是真正相关文档的比例)来量化对比。
Embedding 模型选对了,RAG 检索质量能提升 20-30%,这个提升不需要任何 prompt 调优,就是换了个模型。值得花半天时间认真评估一下,而不是一直将就着用默认配置。