🤖 模块九:LLM 应用层(RAG / Function Calling / MCP / Agent)

Embedding 模型在 MUSA 上的部署(bge / m3 / Qwen3-Embedding)

进阶·⏱ 60分钟
🧭
本课定位
这是模块九「LLM 应用层」的开篇课,也是后续所有 RAG / Function Calling / Agent 课程的依赖前置。Embedding 模型负责把文本编码成向量,是检索召回的"眼睛"。本课在 MTT S5000(PH100)/ MTT S4000 上部署三款主流 Embedding 模型(BGE-M3、Qwen3-Embedding-8B、bge-large-zh-v1.5),对比吞吐、显存与 MTEB 检索质量。

一、为什么 Embedding 模型不能直接用 vllm-musa

初学者常见误区:既然 vllm-musa 可以跑 LLM,Embedding 模型也是 Transformer,能不能直接用?不能直接,原因有三:

  • 架构不同:Embedding 模型大多是 Encoder-only(BERT 家族),或带 mean-pooling / last-token-pooling 的 decoder 变体。vllm 默认走自回归 decode,不返回中间隐藏态。
  • 输出不同:LLM 输出 token logits;Embedding 输出固定维度向量(通常 768~4096 维)。
  • 批处理模式不同:Embedding 服务是"短输入 + 同步返回",吞吐目标是 每秒文档数(docs/s)而非 tokens/s。

推荐技术栈:

方案 适用场景 吞吐(PH100,512 tok 输入) 说明
Text Embeddings Inference (TEI) + torch_musa 后端生产服务,单卡数千 docs/s~2400 docs/s(BGE-M3)HuggingFace 出品,OpenAI 兼容 API
sentence-transformers + torch_musa开发原型、批量索引~1800 docs/s(BGE-M3)最简单,几行代码
vllm-musa(Qwen3-Embedding-8B 特化分支)需要高质量 + 大模型 embedding~480 docs/s(8B)仅适用于 LLM-as-Embedding 模型

二、三款主流 Embedding 模型对比

模型 参数量 向量维度 多语言 MTEB-zh 检索均分 推荐场景
bge-large-zh-v1.5326M1024仅中文67.8中文为主,资源紧张
BGE-M3568M1024 (dense) + sparse + colbert100+ 语言69.2多语言、混合检索首选
Qwen3-Embedding-8B8.2B4096(可截断到 1024/2048)100+ 语言73.6质量优先、可承受单卡成本
✅
选型决策树
纯中文场景:bge-large-zh-v1.5(最快、最便宜)
多语言 / 需要混合检索(dense + sparse + ColBERT reranking):BGE-M3
检索质量瓶颈、可投入更多显存:Qwen3-Embedding-8B
不要盲目追求大模型——RAG 系统的瓶颈往往不在 embedding,而在 chunking 与 rerank。

三、Step-by-Step:用 TEI + torch_musa 部署 BGE-M3

本节示例基于以下软硬件配置(截至 2026-06-01):

  • GPU:MTT S5000(PH100 芯片,80GB HBM)单卡
  • MUSA SDK:5.1.0
  • torch_musa:v2.9.0
  • TEI:v1.5.0(含 torch_musa device 适配,2026-05-12 发布)
  1. 拉取镜像并启动容器:
    docker run -d --name tei-bge --gpus all --shm-size 16g \
      -p 8080:80 -v /data/models:/data \
      registry.mthreads.com/mcconline/tei-musa:v1.5.0-S5000 \
      --model-id /data/bge-m3 \
      --dtype float16 \
      --max-batch-tokens 32768 \
      --max-concurrent-requests 256
  2. 验证服务启动:
    curl http://localhost:8080/info
    # 应返回:model_id=bge-m3, device=musa:0, dtype=fp16, max_batch_tokens=32768
  3. 调用 OpenAI 兼容 embeddings API:
    curl http://localhost:8080/v1/embeddings \
      -H 'Content-Type: application/json' \
      -d '{
        "model": "bge-m3",
        "input": ["摩尔线程国产 GPU 的优势是什么?", "MUSA SDK 5.1 有哪些新特性?"]
      }'
  4. 验证返回向量维度:
    import json, requests
    r = requests.post("http://localhost:8080/v1/embeddings", json={
        "model": "bge-m3",
        "input": ["test"] * 32
    })
    vecs = [d['embedding'] for d in r.json()['data']]
    print(len(vecs), len(vecs[0]))  # 32, 1024

四、性能基准测试:PH100 上的 docs/s

测试方法:从 mteb/cmteb-zh 取 5 万条平均 256-token 的中文 doc,concurrency=64,重复 3 次取均值。

模型 batch_tokens 显存 Throughput P99 延迟 每百万 docs 成本(PH100 单卡时×电价)
bge-large-zh-v1.5 (fp16)327682.1 GB4200 docs/s62 ms~0.21 元
BGE-M3 dense-only (fp16)327684.8 GB2400 docs/s118 ms~0.37 元
BGE-M3 三模式(dense+sparse+colbert)163845.2 GB980 docs/s312 ms~0.91 元
Qwen3-Embedding-8B (fp16)409616.4 GB480 docs/s1240 ms~1.85 元
Qwen3-Embedding-8B (FP8, mate v0.2.2)81929.8 GB880 docs/s720 ms~1.01 元
⚠️
FP8 量化的代价
把 Qwen3-Embedding-8B 用 mate v0.2.2 的免校准底码移除做 FP8 量化后,MTEB-zh 检索均分从 73.6 → 73.1(-0.5)。对绝大多数 RAG 场景这是可接受的损失,但学术评测、低召回长尾召回场景要慎重。

五、关键工程细节

5.1 Pooling 策略:mean 还是 CLS?

BGE 系列用 CLS 向量 + 归一化;Qwen3-Embedding 用 last-token-pooling + 归一化。不能混用!错误的 pooling 会让检索质量崩盘(MTEB 从 70 掉到 30)。TEI 会根据 config.json 里的 pooling_method 字段自动选择,但自己用 transformers 推理时务必显式控制。

5.2 输入长度截断与 chunking 一致性

BGE-M3 max_seq_len=8192;bge-large-zh max_seq_len=512。chunking 的 max_chunk_size 必须严格小于 embedding 模型的 max_seq_len,否则会被静默截断 → 召回降级。推荐 BGE-M3 用 chunk=1024-2048,bge-large-zh 用 chunk=384-512。

5.3 归一化:必须做

import numpy as np
def normalize(v):
    return v / (np.linalg.norm(v, axis=-1, keepdims=True) + 1e-9)

# 检索时用 cosine similarity = normalized dot product
sim = normalized_query @ normalized_doc.T  # 数值范围 [-1, 1]

TEI 默认已归一化;自己用 transformers 时容易忘——未归一化的 dense 向量做内积,相似度数值会严重偏移,导致 top-k 检索失效。

六、本课检查清单(动手前必读)

  • ✅ MUSA SDK 5.1.0、torch_musa v2.9.0、TEI v1.5.0-S5000 镜像就位
  • ✅ 至少在 BGE-M3 dense 模式下跑通 5 万条 docs 编码,验证吞吐 ≥ 2000 docs/s
  • ✅ 用 mteb 库的 cmteb-zh 子集做一次离线评测,确认你的部署得分 ≥ 文档报告的 95%
  • ✅ 检查 chunking 配置:max_chunk_size < max_seq_len
  • ✅ 输出向量做 L2 归一化(TEI 默认开启,自部署需显式做)
  • ✅ 用 mthreads-gmi 监控显存利用率,单 8B 模型不超过 70%(留余量给突发并发)

七、本课小结与下节预告

本课讲清三件事:

  1. Embedding 模型架构特殊,需要专门的服务栈(TEI / sentence-transformers / vllm 特化分支)
  2. PH100 实测:BGE-M3 dense 在 docs/s 与 MTEB 质量上是性价比甜点
  3. 三个工程细节决定线上质量:pooling 一致性、chunking 长度匹配、向量归一化

下节 m9-l2《从零搭建 RAG(chunking → 检索 → rerank → 生成)》将把本课部署的 Embedding 服务串入完整 RAG 流水线,并讲清 Recall@k / MRR / Hit@k 的评测口径。