Embedding 模型选型指南:维度、成本与中文效果对比

2 阅读8分钟

做 RAG 系统时,很多人把时间花在调整 Prompt 上,却忽视了 Embedding 模型的选择——而后者对检索质量的影响往往更大。本文系统对比主流国产 Embedding 方案,帮你找到适合自己场景的那一个。

Embedding 的使用场景

在深入对比之前,先明确 Embedding 能用在哪里:

  • RAG 知识库:最典型的场景,把文档和查询都向量化,用相似度匹配找相关片段
  • 语义搜索:替代关键词搜索,"笔记本电脑" 能搜到 "便携式计算机"
  • 文本分类:把文本向量化后训练轻量分类器(比直接微调大模型便宜得多)
  • 聚类分析:对大量文本做 K-Means 或 DBSCAN,发现主题分布
  • 去重和相似检测:合同、工单、工单等文档的近似匹配

不同场景对 Embedding 的要求不一样。RAG 更看重检索召回率,语义搜索更看重跨语义泛化能力,分类/聚类更看重向量空间的线性可分性


国产 Embedding 模型全景

通义千问 text-embedding-v3

阿里云 DashScope 提供,目前是国内 API 方案里综合表现最好的之一。

核心参数

项目参数
最大输入长度8192 tokens
输出维度1024 维(默认)/ 2048 维(可选)
计费¥0.0007 / 千 token
API 端点https://dashscope.aliyuncs.com/compatible-mode/v1

支持动态指定输出维度(Matryoshka 技术),在维度降低时性能衰减很小,对存储敏感的场景很友好。

BAAI/bge-large-zh-v1.5

智源研究院开源,是目前中文 Embedding 开源方案的标杆

核心参数

项目参数
最大输入长度512 tokens
输出维度1024 维
部署要求GPU 推荐(CPU 也能跑,较慢)
许可证MIT(可商用)

在 MTEB 中文榜单上长期排名前列。缺点是最大输入只有 512 tokens,长文档需要先切分再向量化。

智谱 embedding-3

智谱 AI 提供,随 GLM 系列一起发布。

核心参数

项目参数
最大输入长度8192 tokens
输出维度2048 维
计费¥0.0005 / 千 token
API 端点https://open.bigmodel.cn/api/paas/v4

2048 维的高维度对复杂语义场景有帮助,价格也有竞争力。

关于 DeepSeek Embedding

DeepSeek 目前没有独立的 Embedding 模型对外提供服务。如果你的主要 LLM 是 DeepSeek,Embedding 环节可以选用上述任意一个方案,它们之间没有绑定关系——Embedding 模型和生成模型是独立调用的。


维度和成本对比

模型类型维度最大长度价格中文支持
text-embedding-v3API1024/20488192 tokens¥0.0007/千 token优秀
bge-large-zh-v1.5开源1024512 tokens服务器成本优秀
embedding-3(智谱)API20488192 tokens¥0.0005/千 token良好
bge-m3开源10248192 tokens服务器成本优秀

bge-m3 是 bge 系列的升级版,支持 8192 tokens 长度,多语言效果也更好,如果要自部署首选它。


评测方法:Recall@K

选型不能只看参数,要用自己的数据测。标准做法是构建一个查询-文档对测试集,计算 Recall@K(前 K 个检索结果中包含正确答案的比例)。

构建测试集

从你的知识库文档里,人工或半自动地标注 50-100 组查询-正确文档对:

# 测试集格式
test_cases = [
    {
        "query": "年假申请需要提前多少天?",
        "relevant_doc_id": "hr_policy_001_chunk_3"  # 正确答案所在的文档块 ID
    },
    {
        "query": "差旅费报销的发票要求是什么?",
        "relevant_doc_id": "finance_policy_002_chunk_7"
    },
    # ...
]

评测脚本

# evaluate.py
from embedder import get_embeddings  # 你的 embedding 封装
import chromadb

def evaluate_recall_at_k(
    test_cases: list[dict],
    collection,
    k_values: list[int] = [1, 3, 5, 10]
) -> dict:
    """
    计算不同 K 值下的 Recall
    Recall@K = 正确文档出现在 Top-K 结果中的查询数 / 总查询数
    """
    hits = {k: 0 for k in k_values}

    for case in test_cases:
        query_embedding = get_embeddings([case["query"]])[0]
        results = collection.query(
            query_embeddings=[query_embedding],
            n_results=max(k_values),
            include=["ids"]
        )
        retrieved_ids = results["ids"][0]

        for k in k_values:
            if case["relevant_doc_id"] in retrieved_ids[:k]:
                hits[k] += 1

    total = len(test_cases)
    return {f"Recall@{k}": round(hits[k] / total, 4) for k in k_values}

# 使用示例
results = evaluate_recall_at_k(test_cases, collection)
print(results)
# 输出: {'Recall@1': 0.62, 'Recall@3': 0.78, 'Recall@5': 0.85, 'Recall@10': 0.91}

对于企业知识库,Recall@5 达到 0.80 以上就算可用。低于这个值,需要调整 chunk size 或者换更好的 Embedding 模型。


代码示例:接入 Embedding API 并存入 ChromaDB

以通义千问 text-embedding-v3 为例,完整的接入和入库流程:

# embedding_pipeline.py
import os
import chromadb
from openai import OpenAI

# 初始化客户端
embed_client = OpenAI(
    api_key=os.environ["DASHSCOPE_API_KEY"],
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

chroma_client = chromadb.PersistentClient(path="./vector_db")
collection = chroma_client.get_or_create_collection(
    name="docs",
    metadata={"hnsw:space": "cosine"}
)

def embed_and_store(texts: list[str], ids: list[str], metadatas: list[dict] = None):
    """向量化并批量写入 ChromaDB"""
    # 分批调用 API(每批 25 条,API 限制)
    all_embeddings = []
    for i in range(0, len(texts), 25):
        batch = texts[i:i+25]
        resp = embed_client.embeddings.create(
            model="text-embedding-v3",
            input=batch,
            dimensions=1024
        )
        all_embeddings.extend([item.embedding for item in resp.data])

    collection.upsert(
        ids=ids,
        documents=texts,
        embeddings=all_embeddings,
        metadatas=metadatas or [{} for _ in texts]
    )
    print(f"写入 {len(texts)} 条向量,当前总数:{collection.count()}")

def semantic_search(query: str, top_k: int = 5) -> list[dict]:
    """语义检索"""
    resp = embed_client.embeddings.create(
        model="text-embedding-v3",
        input=[query],
        dimensions=1024
    )
    query_vec = resp.data[0].embedding

    results = collection.query(
        query_embeddings=[query_vec],
        n_results=top_k,
        include=["documents", "metadatas", "distances"]
    )
    return [
        {
            "text": doc,
            "meta": meta,
            "similarity": round(1 - dist, 4)
        }
        for doc, meta, dist in zip(
            results["documents"][0],
            results["metadatas"][0],
            results["distances"][0]
        )
    ]

切换到 bge-large-zh-v1.5(自部署)只需替换 embedding 调用部分(在我们的 TheRouter 网关中,切换不同 embedding 模型只需改 model 参数,无需改动任何接入代码):

# 自部署 bge 方案(使用 sentence-transformers)
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("BAAI/bge-large-zh-v1.5")

def embed_local(texts: list[str]) -> list[list[float]]:
    """本地推理,无 API 调用"""
    embeddings = model.encode(
        texts,
        normalize_embeddings=True,  # 必须归一化,否则余弦相似度不准
        batch_size=64,
        show_progress_bar=True
    )
    return embeddings.tolist()

存入 ChromaDB 的代码完全一样,只需把 API 调用换成本地推理。


中文效果专项测试

中文 Embedding 面临的特殊挑战:

  1. 多义词:"苹果" 是水果还是手机品牌?上下文决定
  2. 口语 vs 书面语:"这个怎么弄" 和 "操作步骤是什么" 语义相同
  3. 缩写和俚语:"996"、"内卷"、行业专有名词
  4. 繁简混合:部分企业文档繁简混用

针对这些场景,可以构建专项测试用例:

# 中文专项测试用例
chinese_test_cases = [
    # 口语 vs 书面语
    {"query": "怎么请假", "doc": "员工请假申请流程:首先登录 HR 系统..."},
    # 缩写
    {"query": "OA 系统在哪", "doc": "办公自动化系统访问地址为 oa.company.com..."},
    # 跨语义
    {"query": "年终奖什么时候发", "doc": "绩效奖金发放时间为次年 1 月..."},
]

实测下来,bge-large-zh-v1.5 在纯中文场景下表现最稳定,text-embedding-v3 在需要长文档支持(>512 tokens)时占优,智谱 embedding-3 在多轮语义理解场景表现不错。


开源自部署 vs API 方案的 Trade-off

API 方案优势

  • 零运维:没有 GPU 服务器、模型加载、版本管理等问题
  • 快速上手:几行代码就能跑起来
  • 自动更新:模型迭代升级不需要你操心
  • 弹性扩容:并发高了 API 自动扛,不用手动加机器

自部署优势

  • 数据不出境:对数据安全有严格要求的场景(金融、医疗、政务)
  • 边际成本为零:入库一次,查询 N 次,不按 token 计费
  • 可定制:可以在私有数据上继续微调,提升领域内效果
  • 离线可用:不依赖外部网络

我的建议

数据量 < 500 万 token/月  →  API 方案(成本可控,省心)
数据量 > 500 万 token/月  →  算一下 ROI,可能值得自部署
数据安全要求高           →  必须自部署,用 bge 系列
快速验证 MVP             →  先用 API,跑通后再考虑迁移

以 text-embedding-v3 为例:500 万 token × ¥0.0007/千 = ¥3.5/月。这个量级完全不值得为了省这点钱去维护一套 GPU 推理服务。

而当你的日均 Embedding token 超过 500 万时(约等于每天处理 1 万份中等长度文档),一台配 A100 的推理服务月租 ¥3000 左右,服务起来就有成本优势了。


选型快速决策

├── 数据出境敏感?
│   ├── 是 → bge-large-zh-v1.5512token)或 bge-m3(8192token)
│   └── 否 → 继续往下
│
├── 文档单段普遍超过 512 token?
│   ├── 是 → text-embedding-v3 或 embedding-3(支持 8192 token)
│   └── 否 → bge-large-zh-v1.5 也够用(开源免费)
│
├── 主要语言是中文?
│   ├── 是 → text-embedding-v3 或 bge-large-zh-v1.5 均可,实测决定
│   └── 多语言 → bge-m3(支持 100+ 语言)
│
└── 快速上线优先?
    ├── 是 → text-embedding-v3(API,零部署)
    └── 否 → bge 系列(开源,需要部署)

小结

Embedding 选型没有银弹,关键点回顾:

  1. 先用自己的数据测,用 Recall@K 量化,别光看官方 benchmark
  2. 中文场景首推 bge-large-zh-v1.5(开源)或 text-embedding-v3(API),两者中文效果都很好
  3. 长文档支持是刚需:如果文档块普遍 > 500 字,选 text-embedding-v3 或 embedding-3
  4. 数据安全先于性能:数据不能出内网的场景,自部署 bge 是唯一选择
  5. 先 API 后自建:量小时用 API,量大了再评估迁移成本

下一篇会讲如何用 Reranker 进一步提升 RAG 检索精度,敬请关注。


有什么想补充的 Embedding 选型经验?欢迎评论区交流。

作者:TheRouter 开发者,专注 AI 模型路由网关。项目主页:therouter.ai