一、先给结论
在真实的企业 RAG 项目中,Embedding 服务通常处理内部制度、合同、工单、代码、页面截图、图表和用户问题。数据不能随意出网,文档入库量大且需要反复重建,因此本文默认采用本地模型和私有化推理服务。线上 Embedding API 只作为特定合规场景下的补充,不再作为主要选型方向。
生产设计首先要判断检索对象:
- 可解析正文、FAQ、制度条款、代码和日志,优先使用文本 Embedding;
- 图表、流程图、页面布局、商品图片和扫描件视觉结构,增加多模态 Embedding;
- 复杂 PDF 同时保留解析文本和页面图像,分别建索引后融合召回;
- 视频只有在确实需要画面检索时才抽帧编码,不能把完整视频直接当作普通文档处理。
如果需要一组可落地的候选模型,可以从下面几类开始:
| 业务场景 | 首批候选 | 原生输出 | 选择原因 |
|---|---|---|---|
| 中文短文本、FAQ、制度条款,资源有限 | bge-base-zh-v1.5、bge-large-zh-v1.5 |
Dense | 模型相对轻量,中文检索成熟,适合建立低成本基线 |
| 中英混合、长文档,或准备做稠密与稀疏混合检索 | bge-m3 |
Dense、Sparse、Multi-vector | 一个模型提供三种检索表示,最长输入为 8192 Token |
| 中文、跨语言或代码检索,对效果要求更高 | Qwen3-Embedding-0.6B、4B |
Dense | 支持任务指令和可变输出维度,模型规模覆盖轻量到高质量场景 |
| 需要长文本、多语言或 Dense + Sparse 的轻量方案 | gte-multilingual-base |
Dense、Sparse | Encoder-only,768 维,推理成本低于数十亿参数模型 |
| 中文和中英知识库,希望配套 Reranker | bce-embedding-base_v1 |
Dense | 接入成熟,可作为独立中文基线 |
| 页面、截图、图片和视频跨模态检索 | Qwen3-VL-Embedding-2B |
多模态 Dense | 支持文本、图像、截图、视频及混合输入 |
| 轻量图文检索或商品以图搜图 | BGE-VL-base、BGE-VL-large |
多模态 Dense | CLIP 路线较轻,适合图文共享空间 |
这张表只是候选集,不是最终答案。生产选型必须满足以下条件:
- 在自己的文档和问题上评测,而不是只看公开榜单;
- 在目标推理引擎、精度和并发参数下压测,而不是只比较参数量;
- 明确模型是只输出稠密向量,还是原生输出稀疏向量;外接 BM25 不能算成模型具备稀疏输出;
- 把模型、输出类型、指令、维度、图像预处理和索引当成同一个版本发布,而不是允许运行时随意切换;
- 稠密、稀疏与视觉通道分别评测,证明融合召回带来收益后再承担额外计算、存储和运维成本。
二、原有选型思路为什么不贴合生产
2.1 生成模型不等于 Embedding 模型
qwen3:4b 是生成模型,qwen3-embedding:4b 才是专门用于向量表示的模型。Mistral、Qwen 等名称描述的是模型家族,不能因为推理框架能加载它们,就默认它们都适合生成 Embedding。
模型服务和模型本身也不是一回事:
- Ollama、vLLM、Text Embeddings Inference 是推理或服务工具;
- Qwen3-Embedding、BGE-M3 才是决定向量空间的模型;
- LangChain、LlamaIndex 等框架只是调用方,不应决定底层模型契约。
2.2 向量维度越高不代表检索一定越准
维度首先影响存储、内存、网络传输和相似度计算成本。模型训练方式、业务语料、指令和分块质量通常比单纯的维度更重要。
以一千万条向量、Float32、不计算索引和副本开销为例:
| 维度 | 原始向量数据量 |
|---|---|
| 768 | 约 30.72 GB |
| 1024 | 约 40.96 GB |
| 2560 | 约 102.40 GB |
| 4096 | 约 163.84 GB |
真实容量还要叠加向量索引、Payload、元数据、WAL、分片和副本。选择 4096 维不仅是模型推理问题,也可能把向量数据库的成本放大数倍。
Qwen3-Embedding 支持 Matryoshka Representation Learning(MRL),可以输出较低维度,但低维度仍然要经过业务评测,不能把“支持裁剪”理解为“裁剪无损”。其他不支持 MRL 的模型更不能直接截断向量。
2.3 “支持混合检索”不等于“模型输出稀疏向量”
稠密向量通常是固定维度的浮点数组,主要表达整体语义;稀疏向量通常是少量 Token ID 与权重的映射,主要保留产品名、错误码、版本号等词项信号。两者解决的问题不同,选型时不能只记录一个笼统的“支持向量检索”。
还要区分两种常见的混合检索:
Dense + BM25:Embedding 模型只输出稠密向量,关键词分数由检索引擎独立计算;Dense + Model Sparse:同一个模型同时输出稠密向量和学习得到的稀疏词项权重,例如 BGE-M3、gte-multilingual-base。
因此,“可以与 BM25 组合”不是模型能力指标,因为任何稠密模型都能在系统层与 BM25 组合。真正需要记录的是:模型官方推理接口能否输出稀疏表示、当前服务框架能否返回该输出、检索后端能否建立相应索引。
2.4 最大上下文长度不等于推荐 Chunk 长度
Qwen3-Embedding 的模型规格支持 32K 输入,BGE-M3 支持 8192 Token,但这只是模型可以接收的上限。把整章甚至整份文档直接编码,通常会稀释局部证据,也会显著降低吞吐。
Chunk 应先保持标题、段落、表格和代码结构,再通过真实问题评测长度与重叠。模型的最大输入长度只负责设置安全上限,不能替代分块策略。
2.5 “切换模型”不是修改一个配置项
只要模型、模型修订版本、输出类型、向量维度、归一化方式或指令模板发生变化,查询向量与旧文档向量就可能不再处于同一空间。即使新旧模型输出维度恰好相同,也不能把两者混在一个索引中搜索;稀疏词项权重的生成方式变化后,同样需要重建稀疏索引。
正确做法是创建新索引、重新生成文档向量、验证后切换读流量,并保留旧索引用于回滚。
2.6 云端模型并不天然更适合生产
线上 API 省去了模型部署,但引入了数据出境、供应商锁定、网络抖动、调用限额和持续费用。对于内部知识库,大规模离线入库往往比在线查询消耗更多 Token,本地部署的成本和可控性通常更符合实际。
只有在数据允许出网、本地资源无法满足需求,并且已经完成供应商、成本和可用性评估时,才需要把线上 API 纳入候选。
2.7 多模态 Embedding 不是 OCR 的替代品
OCR 和文档解析负责产生可阅读、可引用、可审计的文字;多模态 Embedding 负责发现版面、图形、颜色、空间关系等视觉相似性。只保留页面向量会失去精确文字、页码和引用位置,只保留 OCR 文本又可能漏掉流程图、图例和布局信息。
生产上应根据内容类型保留两条证据链:
- 文本链:解析或 OCR → 结构化 Chunk → 文本向量;
- 视觉链:页面或区域截图 → 图像预处理 → 多模态向量。
两条链路通过同一个 document_id、page_no 和区域坐标关联,但使用独立模型指纹和索引。
2.8 “支持多模态”不等于适合所有媒体
图文 CLIP 类模型通常适合文本搜图、以图搜图和商品检索;视觉语言模型路线可以处理截图、视觉文档和图文混合输入,但计算更重。视频检索还涉及抽帧率、镜头切分和时间戳,音频检索则需要独立的音频编码或 ASR 文本链路。
因此,“文本、图像、视频都塞进一个模型”不是生产架构。应先明确需要解决的是文字检索、视觉文档检索、图片检索还是视频片段检索。
三、文本 Embedding 怎么选
3.1 先统一选型指标
文本 Embedding 不能只比较公开榜单和稠密向量维度。进入候选集前,至少记录以下指标:
| 指标 | 要回答的问题 | 生产影响 |
|---|---|---|
| 业务召回质量 | 在真实问题上,Dense、Sparse 及融合结果的 Recall@K、nDCG@K 是否达标 | 决定模型是否真的解决业务问题 |
| 稀疏向量输出 | 官方推理接口能否返回 Token ID 与权重,而不只是能否外接 BM25 | 决定能否使用模型原生词项召回 |
| 稠密向量规格 | 默认维度、是否支持 MRL 或弹性维度、是否归一化 | 影响索引 Schema、存储和距离度量 |
| 输入契约 | 最大 Token、查询指令、文档模板、截断规则 | 输入不一致会直接破坏检索效果 |
| 语言与任务 | 中文、多语言、跨语言、代码和长文档是否经过对应评测 | 决定公开能力能否迁移到业务语料 |
| 服务兼容性 | 目标引擎能否返回模型所需的全部输出 | “能加载模型”不代表能提供 Sparse 或 Multi-vector |
| 性能与资源 | 吞吐、P95/P99 延迟、显存、CPU 与全量重建时间 | 决定容量和部署成本 |
| 许可证与维护 | 权重许可、商用条件、Remote Code 和版本维护状态 | 决定能否在企业内长期运行 |
其中,“稀疏向量输出”应记录为模型级指标和服务级指标。模型支持 Sparse,但服务接口只返回 Dense,当前部署仍然不能算支持稀疏检索。
3.2 文本模型能力对比
下面的“稀疏输出”以模型官方推理接口为准,不把外接 BM25 计入模型能力:
| 模型 | Dense 默认维度 | 最大输入 | 稀疏输出 | 可变 Dense 维度 | Query 指令 | 核心定位 |
|---|---|---|---|---|---|---|
| Qwen3-Embedding-0.6B / 4B / 8B | 1024 / 2560 / 4096 | 32K | 不支持 | MRL | 推荐使用 | 多语言、代码与复杂语义检索 |
| BGE-M3 | 1024 | 8192 | 支持 | 不支持 | 默认不需要 | Dense、Sparse、Multi-vector 一体化检索 |
| BGE v1.5 中文系列 | 512 / 768 / 1024 | 512 | 不支持 | 不支持 | 可选 | 中文短文本与低成本基线 |
| gte-multilingual-base | 768 | 8192 | 支持 | Elastic Dense | 默认不需要 | 轻量、多语言、长文本与混合检索 |
| bce-embedding-base_v1 | 768 | 512 | 不支持 | 不支持 | 默认不需要 | 中英知识库与 BCE Reranker 配套 |
| multilingual-e5-large-instruct | 1024 | 512 | 不支持 | 不支持 | 需要 | 多语言和跨语言独立基线 |
| jina-embeddings-v3 | 1024 | 8192 | 不支持 | Matryoshka | 使用任务参数 | 多语言、长文本与任务适配;注意商用许可 |
“不支持”表示该模型当前官方用法只提供稠密表示,不表示它不能与 BM25 组成混合检索系统。模型版本和接口会变化,落地时仍应固定 Revision,并用一次真实推理检查返回字段,而不是只读取服务的模型列表。
3.3 Qwen3-Embedding
Qwen3-Embedding 官方仓库给出的模型规格如下:
| 模型 | 参数量 | 最大输入 | 默认向量维度 | 稀疏输出 | MRL | 任务指令 |
|---|---|---|---|---|---|---|
| Qwen3-Embedding-0.6B | 0.6B | 32K | 1024 | 不支持 | 支持 | 支持 |
| Qwen3-Embedding-4B | 4B | 32K | 2560 | 不支持 | 支持 | 支持 |
| Qwen3-Embedding-8B | 8B | 32K | 4096 | 不支持 | 支持 | 支持 |
Qwen3-Embedding 更适合以下场景:
- 中文与多语言混合检索;
- 代码、错误日志、技术文档等语义较复杂的内容;
- 需要用任务指令区分知识库问答、代码检索、相似问题匹配等任务;
- 希望通过 MRL 在效果和向量存储之间寻找平衡。
生产上不建议直接从 8B 起步。更稳妥的顺序是用 0.6B 建立吞吐和质量基线,再评测 4B;只有 8B 的业务收益足以覆盖 GPU、延迟和存储增量时才升级。
Qwen3-Embedding 的查询侧应带任务指令,文档侧不加同样的查询指令。例如知识库检索可以固定为:
1 | Instruct: Given a user question, retrieve relevant passages from the enterprise knowledge base |
指令应该由 Embedding Gateway 根据 profile_id 统一添加,不能散落在各业务系统中。指令内容一旦变化,就应产生新的配置版本并重新评测。
Qwen3-Embedding 当前输出稠密向量。若业务还需要错误码、产品型号等精确词项召回,应在检索层增加 BM25 或选择原生输出 Sparse 的模型,不能把 MRL 的低维稠密向量误认为稀疏向量。
3.4 BGE-M3
BGE-M3 官方模型说明中的 M3 分别代表:
- Multi-Linguality:支持多语言;
- Multi-Granularity:支持短句到最长 8192 Token 的文本;
- Multi-Functionality:同时提供 Dense、Sparse 和 Multi-vector 表示。
BGE-M3 的 Dense 向量维度为 1024。它适合中英混合、长文本和混合检索,但要注意:模型具备三种输出能力,不代表生产环境必须全部启用。
| 输出 | 用途 | 生产代价 |
|---|---|---|
| Dense | 常规语义召回 | 接入最简单,适合作为第一阶段基线 |
| Sparse | 关键词和术语匹配 | 需要向量库或检索引擎支持稀疏向量 |
| Multi-vector | Token 级细粒度交互 | 存储和检索成本高,需专项评测后再启用 |
如果当前向量数据库只支持单个稠密向量,就先使用 BGE-M3 Dense,不要为了“用满模型能力”增加一套无法运维的检索链路。需要混合检索时,再比较 Dense + BM25 与 Dense + BGE Sparse 的实际收益。
BGE-M3 在检索场景下不要求给查询额外添加指令。不要复用 Qwen3-Embedding 的查询模板,否则已经不是官方推荐的输入方式。
3.5 BGE v1.5 中文系列
BGE v1.5 仍然适合作为中文短文本检索的工程基线:
| 模型 | 向量维度 | 最大输入 | 稀疏输出 | 典型定位 |
|---|---|---|---|---|
| bge-small-zh-v1.5 | 512 | 512 Token | 不支持 | CPU、边缘设备或低吞吐服务 |
| bge-base-zh-v1.5 | 768 | 512 Token | 不支持 | 资源与效果平衡 |
| bge-large-zh-v1.5 | 1024 | 512 Token | 不支持 | 中文检索质量优先 |
BGE v1.5 不带查询指令也能工作,检索任务中使用官方中文指令可能获得更好的效果。是否启用仍应作为一个独立实验变量,不能在上线后随意变化。
3.6 GTE / mGTE
gte-multilingual-base 是值得加入生产候选集的 Encoder-only 模型:约 305M 参数、768 维、最长 8192 Token,并能生成 Dense 和 Sparse 表示。官方接口可通过 return_dense=True 和 return_sparse=True 显式选择输出,也支持弹性 Dense 表示。相较数十亿参数的 Decoder-only Embedding,它更适合资源有限但需要多语言、长文本或混合检索的环境。
GTE 家族中也有基于 Qwen 的型号。需要比较不同模型架构时,应把 gte-multilingual-base 与 gte-Qwen2-* 分开记录,不能只写“GTE”。
3.7 BCE、E5 与存量中文模型
| 模型 | 稀疏输出 | 适用场景 | 生产判断 |
|---|---|---|---|
bce-embedding-base_v1 |
不支持 | 中英文知识库,可搭配 BCE Reranker | 中文 RAG 的独立候选,仍需业务评测 |
multilingual-e5-large-instruct |
不支持 | 多语言和跨语言检索 | 适合作为非国产独立基线,注意 Query 指令格式 |
text2vec-base-chinese |
不支持 | 中文短句、相似度和存量系统 | 模型轻,但长文本和复杂检索能力有限 |
Piccolo |
不支持 | 中文短文本或存量项目 | 新项目需检查维护状态和业务收益 |
M3E |
不支持 | 中文及少量英文的旧项目 | 官方模型卡说明权重受训练数据限制,仅供研究,商业生产需重新确认授权 |
jina-embeddings-v3 |
不支持 | 多语言、长文本、可变维度 | 本地商业使用涉及额外授权,不应未经审查直接上线 |
这些模型的意义是提供独立基线和迁移选择,不是让每个项目都维护十套模型。通常选择 2~4 个代表性候选完成同条件评测即可。
模型升级也不是解决所有召回问题的万能手段。如果正确答案根本没有被解析进 Chunk,或者表格被错误拆分,换更大的 Embedding 模型不会修复数据链路。
四、多模态 Embedding 怎么落地
4.1 先判断是否真的需要视觉检索
| 内容 | 默认处理 | 何时增加多模态向量 |
|---|---|---|
| 普通 DOCX、Markdown、网页 | 解析文本并使用文本 Embedding | 页面布局本身具有业务含义时 |
| 可搜索 PDF | 解析文字、表格和标题 | 流程图、复杂表格、印章或版式需要召回时 |
| 扫描 PDF | OCR 文本 + 页面来源信息 | OCR 质量不稳定或视觉结构重要时 |
| 商品图、设计稿、截图 | 多模态 Embedding | 默认需要,同时保留标题和描述文本 |
| 图表、流程图 | 图像向量 + OCR/说明文字 | 通常两条链都需要 |
| 视频 | ASR、字幕和镜头切分 | 需要按画面或时间片检索时才增加视频向量 |
如果用户始终通过文字搜索,文档也能被可靠解析,先把文本链路做好通常更经济。多模态检索主要解决文本解析无法表达的视觉证据,不是所有 RAG 的必选项。
4.2 当前可私有化的多模态候选
| 模型 | 规模与输出 | 输入能力 | 生产定位 |
|---|---|---|---|
Qwen3-VL-Embedding-2B |
2B、2048 维、32K、支持 MRL | 文本、图像、截图、视频及混合输入 | 视觉文档和跨模态检索的首批候选 |
Qwen3-VL-Embedding-8B |
8B、4096 维、32K、支持 MRL | 同上 | 质量优先且 GPU、延迟与存储预算充足 |
BGE-VL-base |
约 149M、512 维 | 文本、图像、图文组合 | 轻量文本搜图、以图搜图和商品检索 |
BGE-VL-large |
768 维 | 文本、图像、图文组合 | 比 Base 更高容量的 CLIP 路线 |
BGE-VL-MLLM / BGE-VL-v1.5 |
数十亿参数路线 | 更通用的视觉语义任务 | 能力更广但部署更重,中文视觉文档需专项验证 |
GME-Qwen2-VL-2B/7B |
1536 / 3584 维、32K | 文本、图像、图文组合 | 已有 GME 系统的迁移基线;新项目优先同时评测 Qwen3-VL |
Qwen3-VL-Embedding 官方仓库同时提供 Embedding 和多模态 Reranker。Embedding 用于大规模召回,Reranker 接收查询与候选对做精排;不能用 Reranker 代替全库向量检索。
BGE-VL包含较轻的 CLIP 路线和更重的 MLLM 路线。CLIP 路线适合图片检索,但短文本编码器不应替代正文知识库的长文本模型。
4.3 按输出通道独立建索引
flowchart TD
Source["原始文档 / 图片 / 视频"] --> Parse["解析、OCR、字幕"]
Source --> Visual["页面渲染、图片区域、视频镜头"]
Parse --> TextChunk["结构化文本 Chunk"]
TextChunk --> TextEmbed["文本 Embedding"]
TextEmbed --> DenseIndex["Dense 文本索引"]
TextEmbed -->|"模型原生支持"| SparseIndex["Model Sparse 索引"]
TextChunk --> BM25Index["BM25 词项索引"]
Visual --> VisualPre["缩放、裁剪、抽帧与质量门禁"]
VisualPre --> MMEmbed["多模态 Embedding"]
MMEmbed --> VisualIndex["视觉向量索引"]
Query["文本 / 图片查询"] --> Route{"查询路由"}
Route -->|"文本查询"| TextQuery["文本查询"]
TextQuery --> QueryEmbed["Query Embedding"]
QueryEmbed --> DenseIndex
QueryEmbed -->|"模型原生支持"| SparseIndex
TextQuery --> BM25Index
Route -->|"文本 / 图片查询"| MMQuery["多模态 Query Embedding"]
MMQuery --> VisualIndex
DenseIndex --> Fuse["RRF 或标定后融合"]
SparseIndex --> Fuse
BM25Index --> Fuse
VisualIndex --> Fuse
Fuse --> Rerank["文本或多模态 Rerank"]
Rerank --> Evidence["原文、页码、区域和时间戳"]
Dense、Model Sparse、BM25 和视觉索引是不同的检索通道,可以使用不同的数据结构、距离度量和 Collection。默认先建立 Dense 基线;只有精确术语样本证明存在缺口时,才增加 BM25 或 Model Sparse。融合时优先使用 RRF 等基于名次的方法;如果要加权原始分数,必须先用标注数据校准各通道的分数分布。
4.4 多模态配置也必须版本化
除文本模型已有的配置外,多模态指纹至少还应包含:
- 模型和 Processor Revision;
- 图像缩放、裁剪、颜色空间和像素上下限;
- PDF 页面渲染 DPI、区域切分算法和版本;
- 视频镜头切分、抽帧率、最大帧数和时间片长度;
- 查询指令、输入模态组合和输出维度;
- 是否使用 OCR 文本、标题或其他文字与图像组成混合输入。
同一张图片经过不同裁剪或缩放后可能得到不同向量,因此缓存键不能只使用原文件哈希,还要包含视觉预处理指纹。
五、用业务评测决定模型
5.1 构造黄金评测集
评测数据应该来自真实搜索日志、客服问题、制度问答和专家补充,至少覆盖:
| 样本类型 | 要验证的问题 |
|---|---|
| 直接问法 | 正确段落能否进入 Top K |
| 同义改写、口语和缩写 | 语义变化后能否稳定召回 |
| 产品名、错误码、单号和版本号 | 精确术语是否被纯语义检索漏掉 |
| 跨语言问题 | 中文问题能否召回英文材料,反之亦然 |
| 长文档和表格 | 局部证据会不会被长上下文稀释 |
| 页面截图和扫描件 | 文本问题能否召回正确页面和区域 |
| 图表和流程图 | 视觉关系能否被召回,OCR 文本是否形成互补 |
| 图片与图片 | 相似主体、布局或商品能否被正确区分 |
| 视频片段 | 正确镜头能否命中,时间边界是否准确 |
| 相似但错误的难负例 | 模型能否区分容易混淆的制度或产品 |
| 无答案问题 | 系统是否会召回看似相关但不支持答案的内容 |
初期可以先整理数百条高质量问题作为起点,但数量不是质量标准。每条问题都要标注相关 Chunk 或文档、相关等级、业务域和难例类型,并保留独立测试集,避免反复调参后只对当前样本有效。
多模态评测不能只标注文档级相关性。页面、区域和视频检索应标注 page_no、边界框或时间区间,否则无法判断模型找对了文档还是找对了证据。
5.2 同时测质量和成本
| 维度 | 建议指标 |
|---|---|
| 召回 | Recall@K、Hit Rate@K |
| 词项召回 | 产品名、错误码、编号和版本号子集的 Recall@K、零结果率 |
| 排序 | MRR、nDCG@K |
| 端到端 | 引用正确率、可回答率、无依据回答率 |
| 跨模态 | Text→Image、Image→Image、Text→Page 的 Recall@K 和 nDCG |
| 视觉定位 | 页面命中率、区域命中率、视频时间片 IoU |
| 在线性能 | P50/P95/P99 延迟、超时率、排队时延 |
| 离线性能 | Tokens/s、每小时 Chunk 数、全量重建时间 |
| 资源 | GPU 利用率、显存峰值、CPU、内存和网络流量 |
| 稳定性 | OOM、空向量、NaN、维度错误和重试率 |
| 存储 | 单向量字节数、索引体积、构建时间和副本成本 |
稀疏能力不能只看“支持/不支持”这一列。对于需要混合检索的候选,至少比较以下实验组:
| 实验组 | 目的 |
|---|---|
| Dense Only | 建立语义召回基线 |
| BM25 Only | 判断业务对原词、编号和术语的依赖程度 |
| Model Sparse Only | 验证模型学习到的词项权重是否优于传统词频信号 |
| Dense + BM25 | 衡量通用混合检索收益 |
| Dense + Model Sparse | 衡量模型原生双路输出的收益与成本 |
如果模型不支持 Sparse,就不运行对应实验组,不能用 BM25 结果冒充模型 Sparse。不要只报平均延迟;输入长度分布和批量大小会显著影响结果,应按输出通道、短中长文本以及在线查询、离线文档分别统计。
5.3 保证对比公平
一次实验只改变一个主要变量,并记录完整配置:
- 固定文档版本、分块结果和评测问题;
- 使用模型对应的 Tokenizer,并显式记录是否截断;
- 分别固定 Dense、Sparse 和 BM25 的索引参数、候选数、过滤条件及最终 Rerank 预算;
- 查询与文档使用各自正确的指令格式;
- 明确记录模型返回的输出类型、稀疏词项数量、融合方法和权重;
- 固定页面渲染、图片尺寸、裁剪和视频抽帧策略;
- 在相同精度、相同硬件和目标并发下压测;
- 每个模型重新生成完整文档向量,禁止用新查询向量搜索旧索引。
量化权重也要作为新的实验候选。Ollama 中的 Q4/Q8 模型、官方 FP16/BF16 权重和其他量化格式可能具有不同的延迟、显存和召回表现,不能只对比模型名称。
公开榜单只适合筛选候选模型,不能直接代替这组实验。
六、生产架构:在线与离线必须分开
文档入库追求吞吐,用户查询追求低延迟。两者共享同一模型实例时,一次全量重建就可能拖慢所有在线请求。
flowchart LR
subgraph Offline["离线入库平面"]
TextQueue["文本 Chunk 队列"] --> TextBatch["按 Token 动态合批"]
VisualQueue["页面 / 图片 / 视频队列"] --> VisualBatch["按像素与帧数合批"]
TextBatch --> OfflineText["离线文本 Embedding 池"]
TextBatch --> BM25Staging["BM25 STAGING 索引"]
VisualBatch --> OfflineMM["离线多模态 Embedding 池"]
OfflineText --> DenseStaging["Dense STAGING 索引"]
OfflineText -->|"Profile 启用 Sparse"| SparseStaging["Sparse STAGING 索引"]
OfflineMM --> VisualStaging["视觉 STAGING 索引"]
BM25Staging --> Validate["完整性与黄金集验证"]
DenseStaging --> Validate
SparseStaging --> Validate
VisualStaging --> Validate
end
subgraph Online["在线查询平面"]
Query["用户问题"] --> Gateway["Embedding Gateway"]
Gateway --> OnlineText["在线文本 Embedding 池"]
Gateway --> OnlineMM["在线多模态 Embedding 池"]
Gateway --> BM25Active["BM25 ACTIVE 索引"]
OnlineText --> DenseActive["Dense ACTIVE 索引"]
OnlineText -->|"Profile 启用 Sparse"| SparseActive["Sparse ACTIVE 索引"]
OnlineMM --> VisualActive["视觉 ACTIVE 索引"]
BM25Active --> Fusion["结果融合"]
DenseActive --> Fusion
SparseActive --> Fusion
VisualActive --> Fusion
end
Registry["模型注册表 / 配置指纹"] --> Gateway
Registry --> TextBatch
Registry --> VisualBatch
Validate --> Release["CAS 发布索引集合"]
Release --> BM25Active
Release --> DenseActive
Release --> SparseActive
Release --> VisualActive
Metrics["指标、日志、Trace"] -.-> Gateway
Metrics -.-> OnlineText
Metrics -.-> OnlineMM
Metrics -.-> OfflineText
Metrics -.-> OfflineMM
至少要做到:
- 在线和离线使用独立队列、并发配额和资源池;
- 文本与多模态任务也要隔离,避免页面批处理占满文本查询资源;
- 在线服务优先保证延迟,离线任务允许暂停和断点续跑;
- 新向量先写入 STAGING 索引,验证通过后再切换 ACTIVE;
- Dense、Sparse、BM25 与视觉索引由发布清单按启用通道原子绑定,禁止只切换其中一部分;
- 模型不可用时只切换到同一配置指纹的副本,不能临时换另一个模型;
- 多模态服务故障时可以关闭视觉通道保留文本检索;文本 Embedding 故障时可以降级到 BM25;
- 任何情况下都不能用其他模型生成的查询向量搜索当前索引。
七、Embedding Gateway 要固定什么
业务系统不应该直接依赖某个推理框架的私有参数。统一 Gateway 至少要区分查询和文档:
1 | { |
input_type 决定是否添加查询指令;profile_id 指向不可变配置。客户端不能直接传任意模型名、维度或 Prompt,避免同一索引出现不同编码方式。
多模态请求使用受控资产标识,而不是允许推理服务下载任意 URL:
1 | { |
Gateway 从内部对象存储读取 asset_id,并校验租户、ACL、MIME、像素、文件大小和解码结果。外部 URL、Data URI 和本地路径不应直接透传给模型 Worker。
一个启用 BGE-M3 原生 Dense + Sparse 输出的配置指纹至少包含:
1 | profile_id: kb-bge-m3-hybrid-v1 |
Qwen3-Embedding 等只输出 Dense 的模型,其 output_types 只能配置为 [dense]。Profile 不能声明模型或当前服务接口无法返回的输出。多模态 Profile 还需要记录 processor_revision、image_preprocess_version、page_render_version、frame_sampling_version 和允许的输入模态。Dense、Sparse 与视觉索引分别绑定具体 Profile,不能共用一个模糊的 embedding_model 配置项。
还要把推理引擎及其版本记录到部署清单中。即使模型配置不变,升级推理引擎后也应运行一致性测试,检查输出维度、归一化、截断和数值漂移。
Gateway 返回结果时应附带 profile_id、配置指纹、实际 Token 数、是否截断和实际输出类型。写入索引前再次校验:
- 数量是否与输入一致;
- Dense 维度是否符合索引 Schema,是否存在空向量、NaN 或 Infinity;
- Dense 的 L2 范数是否符合归一化约定;
- Sparse 的 Token ID、权重类型、非零项数量和数值范围是否有效;
- Profile 要求的 Dense、Sparse 输出是否完整,未启用的输出是否被意外写入;
- 模型指纹是否与目标索引一致;
- 多模态结果是否能追溯到资产、页码、区域或视频时间片。
八、输入处理比调用 API 更容易出错
8.1 查询和文档不能使用同一模板
不同模型的输入约定不同:
| 模型 | Query | Document |
|---|---|---|
| Qwen3-Embedding | 添加稳定的任务指令 | 不添加查询指令 |
| BGE-M3 | 默认不加查询指令 | 原文或受控增强文本 |
| BGE v1.5 | 可评测官方检索指令 | 不添加查询指令 |
这也是模型配置必须版本化的原因。模型名相同但查询模板不同,检索结果仍可能变化。
8.2 保存两份文本
建议区分:
display_text:接近原文,用于答案上下文与引用;embedding_text:可以在正文前补充文档标题、章节路径和必要的字段名,用于生成向量。
补充元数据有助于召回,但不能把未出现在原文中的增强文本当成引用证据。租户、权限、时间和标签等过滤条件应保存为向量 Payload,不要全部拼进 Embedding 文本。
视觉记录还应保存 asset_id、原始文件哈希、页面号、区域坐标、视频起止时间、渲染产物地址和预处理指纹。每条视觉向量必须继承原文档的租户与 ACL,删除原文档时同步删除文本和视觉索引数据。
8.3 禁止静默截断
调用前必须使用目标模型的 Tokenizer 计数。超过 max_input_tokens 时,应返回明确错误或重新分块,不能默认截掉尾部后继续写入索引。
max_input_tokens 应是经过压测的服务上限,通常低于模型理论上下文长度。较短的上限能减少长尾延迟、显存波动和单个超长请求对整个 Batch 的拖累。
8.4 图像与视频也不能静默降质
图片超过像素预算时应按固定策略缩放或分区,不能由不同 Worker 随机处理。扫描页需要检测空白页、旋转、模糊和极端长宽比;视频需要先做镜头或时间片切分,再按固定策略抽帧,并保留帧到原视频时间戳的映射。
视觉预处理失败必须显式进入隔离队列。生成一张全黑缩略图后继续写入向量,会形成很难通过普通日志发现的静默错误。
8.5 相似度与归一化必须匹配
如果向量经过 L2 归一化,内积与余弦相似度的排序等价;如果没有归一化,两者含义不同。模型输出、Gateway 处理方式和向量库距离度量必须写入同一个配置指纹。
相似度阈值也不能从其他模型照搬。换模型、换维度或换指令后,要根据正负样本的分数分布重新标定。
九、吞吐、显存和批处理
9.1 按 Token、像素和帧数合批
“每 64 条一批”不是稳定策略,因为 64 条 FAQ 和 64 个长 Chunk 的计算量完全不同。生产合批应同时限制:
max_batch_items:单批最大条数;max_batch_tokens:单批总 Token;max_input_tokens:单条最大 Token;max_wait_ms:在线请求等待合批的最长时间。
多模态批次还要限制 max_image_pixels、max_batch_pixels、max_frames 和 max_batch_frames。只限制条数时,一批缩略图和一批高分辨率页面的显存占用可能完全不同。
可以按输入长度分桶,避免短查询被长文档 Padding 拖慢。离线任务优先提高 GPU 利用率,在线任务优先控制排队时间,两者使用不同参数。
9.2 不要照抄显存表
模型能否部署在某张 GPU 上,取决于模型规模、权重精度、输入长度、批量、推理引擎和并行方式。网上单请求得到的显存数字不能直接作为生产容量。
正确流程是:
- 使用线上真实 Token、图片分辨率和视频帧数分布回放;
- 从低并发逐步增加,记录吞吐、P95/P99 延迟和显存峰值;
- 注入超长文本和突发流量,观察是否 OOM;
- 预留故障迁移容量,验证少一个副本时仍能服务;
- 根据排队 Token、延迟和 GPU 利用率共同扩缩容。
只按请求数扩缩容会误判负载,因为不同请求的 Token、图片像素和视频帧数可能相差几十倍。
多模态容量不能只计算向量本身,还要计算页面渲染图、区域裁剪图、视频关键帧和缩略图。若一份文档同时生成页级与区域级向量,向量数量也不再等于 Chunk 数,应分别按 N_text、N_page、N_region 和 N_frame 估算。
9.3 缓存要带配置指纹
文档向量缓存键至少包含:
1 | hash(embedding_text + embedding_config_fingerprint) |
只使用文本哈希会在模型升级后错误复用旧向量。查询缓存还要考虑租户、规范化结果、查询指令版本和过期时间,不能缓存或记录不必要的敏感原文。
视觉向量缓存使用原始资产哈希、页码或时间片、区域坐标、预处理指纹和模型指纹。页面重新渲染、裁剪规则改变或抽帧策略改变,都必须生成新缓存键。
十、推理服务怎么选
| 方案 | 当前应按什么输出验收 | 适合阶段 | 注意事项 |
|---|---|---|---|
| Sentence Transformers | Dense | 评测、离线任务、稠密基线 | 能加载 BGE-M3 不代表调用路径会返回其 Sparse 和 Multi-vector |
| FlagEmbedding / BGE-M3 官方实现 | Dense、Sparse、Multi-vector | BGE-M3 完整能力验证 | 显式设置返回类型,并核对每批结果字段 |
| GTE 官方实现 | Dense、Sparse | gte-multilingual-base 混合检索验证 |
需要相应 Remote Code,必须固定 Revision 并审查代码 |
Ollama /api/embed |
Dense | 本地开发、小规模内网应用、快速验证 | 当前文档返回归一化向量数组;不能据此暴露 BGE-M3 Sparse |
| Hugging Face Text Embeddings Inference | 按标准 Embedding 接口验收 Dense | 标准化 Embedding 服务 | 支持某个模型架构不等于暴露该模型的全部自定义输出 |
| vLLM Pooling | 按实际 Pooling 接口验收 | 已使用 vLLM、需要统一服务体系 | 先检查模型实现与返回 Schema,再在目标硬件压测 |
| Qwen3-VL-Embedding 官方实现 | 多模态 Dense | 图像、视觉文档和视频检索 | 功能完整,但需要自行补齐网关、动态批处理、监控和高可用 |
| Sentence Transformers 多模态模型 | 多模态 Dense | BGE-VL、GME 等评测与服务封装 | 核对自定义代码、Processor 和图片输入格式,不能只验证纯文本 encode |
推理引擎的选择是工程决策,不是模型质量决策。同一个模型在不同引擎下仍要验证输出一致性和性能,不能仅凭“支持加载”就上线。
生产部署还应具备:
- 模型权重从内部制品库加载,固定 Revision 和校验和;
- 记录模型来源、许可证和安全审查结果,升级时重新核对;
- 启动时完成模型预热,Readiness 通过后才接收流量;
- 至少两个副本,支持滚动升级和优雅下线;
- 请求体、输入条数、Token 和并发均有限额;
- 离线环境默认禁止推理容器访问公网;
- 图片和视频由独立解码沙箱处理,限制格式、像素、帧数、时长、CPU、内存和超时;
- 使用
trust_remote_code的模型必须固定代码 Revision,并经过代码与依赖审查; - 日志记录哈希、长度和版本,不记录完整敏感文本;
- 模型加载失败、GPU 故障和队列堆积均有告警。
十一、失败处理与可观测性
11.1 错误不能全部重试
| 错误 | 处理方式 |
|---|---|
| 网络闪断、临时不可用 | 有上限的指数退避,重试仍计入并发与预算 |
| OOM | 拆分 Batch、降低 Token 预算;持续发生时隔离实例 |
| 输入超长 | 重新分块或拒绝,禁止静默截断后重试 |
| 图片损坏、像素超限、视频解码失败 | 拒绝或隔离,记录资产与预处理版本 |
| Dense 维度不符、NaN、空向量 | 阻止写入并告警,不可作为临时错误重试 |
| Sparse 缺失、Token ID 非法、权重异常 | 阻止写入对应稀疏索引,检查模型接口和 Profile |
| 模型或指纹不匹配 | 阻止请求,检查路由与发布配置 |
| 单条坏数据 | 隔离该条并记录原因,避免阻塞整个离线批次 |
文本任务使用 chunk_id + embedding_config_fingerprint 作为幂等键;视觉任务使用 asset_id + page/region/time_range + visual_config_fingerprint。Worker 崩溃后可以重新领取任务,但同一配置不能重复写出多份有效向量。
11.2 至少监控这些指标
- 请求量、输入条数和输入 Token 分布;
- 排队时延、推理时延和端到端 P50/P95/P99;
- Batch Item、Batch Token 和 Padding 比例;
- 图片像素、页面数、视频帧数、解码与预处理耗时;
- GPU 利用率、显存、功耗,CPU 和内存;
- Tokens/s、Chunk/s、缓存命中率;
- 超时、OOM、重试、截断拒绝、无效向量;
- Dense 与 Sparse 输出成功率、Sparse 非零项数量和生成耗时;
- 按模型指纹和输出类型统计的索引写入量及查询量;
- Dense、Sparse、BM25、视觉和融合通道各自的候选量、命中率及零结果率;
- 在线 SLO 与离线全量重建剩余时间。
Trace 应串联业务请求、Gateway、预处理 Worker、模型副本和向量检索,但只记录必要元数据。发生召回异常时,要能还原“哪个模型版本、哪条指令、是否截断、如何渲染或抽帧、搜索了哪个索引集合”。
十二、模型升级与回滚
模型升级应走完整的索引发布流程:
flowchart LR
Candidate["候选模型与配置"] --> OfflineEval["离线黄金集评测"]
OfflineEval --> LoadTest["目标硬件压测"]
LoadTest --> Build["创建 Dense / Sparse / BM25 / 视觉新索引并全量回填"]
Build --> Check["数量、输出类型、维度、词项权重与资产关联校验"]
Check --> Shadow["影子流量 / 双路对比"]
Shadow --> Canary["小流量灰度"]
Canary --> Switch["原子切换 ACTIVE 别名"]
Switch --> Observe["观察业务指标"]
Observe -->|"异常"| Rollback["切回旧索引"]
Observe -->|"稳定"| Retain["保留观察期后清理旧索引"]
影子阶段可以分别搜索新旧索引并比较结果,但不要直接混合不同模型的相似度分数。不同向量空间的分数分布未必可比,需要融合时应使用排序名次或经过标定的分数。
发布前应核对:
- Dense、Sparse 与 BM25 新索引记录数和有效 Chunk Manifest 一致;
- 每条 Dense 向量的维度和指纹一致;
- 启用 Sparse 时,每条记录的词项权重有效且查询侧使用同一模型与 Revision;
- 页面、区域和视频时间片能够回溯到原始资产;
- 删除文档、租户和 ACL 过滤在新索引中生效;
- 文本、视觉和融合检索分别达到项目门槛;
- 在线延迟和离线吞吐满足容量窗口;
- 灰度与回滚都经过演练;
- 旧索引未在切换后立即删除。
十三、推荐的落地顺序
第一阶段:建立可信基线
- 选择
bge-base-zh-v1.5或Qwen3-Embedding-0.6B,先建立 Dense 基线; - 固定分块、输出类型、指令、归一化和向量库参数;
- 建立黄金问题与难负例;
- 跑通离线入库、在线查询、监控和索引版本发布。
这一阶段先使用文本检索。除非核心业务就是图片搜索,否则不要在基线尚未稳定时同时引入视觉链路。
第二阶段:解决明确问题
- 中文短文本质量不足:比较 BGE Large 与 Qwen3 0.6B/4B;
- 中英跨语言或长文档:加入 BGE-M3;
- 专有名词、编号召回差:比较
Dense + BM25、BGE-M3 Dense + Sparse和GTE Dense + Sparse,不要默认原生 Sparse 一定优于 BM25; - Top K 中有答案但首位不准:优先增加 Reranker,而不是盲目增大 Embedding 模型;
- 向量库存储压力大:评测 Qwen MRL 低维输出,或选择较低维模型;
- 领域表达特殊:先扩充难负例,再考虑领域微调。
第三阶段:按需增加多模态
- 从流程图、扫描页、商品图片等文本链路的明确失败样本开始;
- 使用
Qwen3-VL-Embedding-2B与BGE-VL-base/large建立视觉基线; - 分别评测文本、视觉以及融合结果,确认增量收益;
- 建立页面、区域和时间片来源定位;
- 完成视觉索引的灰度、回滚与资源隔离后再扩大覆盖范围。
第四阶段:规模化
完成在线与离线资源隔离、多副本、限流、断点续跑、模型制品管理、影子评测、灰度和回滚。只有这些能力闭环,模型在测试集上的提升才真正具备生产价值。
十四、总结
生产环境中的 Embedding 选型,不是 OpenAI、Ollama、Qwen 和 BGE 之间的简单品牌比较。更准确的拆分是:
- Qwen3-Embedding、BGE、GTE、BCE 等文本模型决定文字检索的向量质量、输出类型和输入约定;
- Qwen3-VL-Embedding、BGE-VL 和 GME 等模型补充页面、图像和视频的视觉召回;
- Ollama、TEI、vLLM、FlagEmbedding 决定模型如何被服务;
- 稀疏输出是独立选型指标:BGE-M3 与
gte-multilingual-base可原生输出 Sparse,Qwen3-Embedding、BGE v1.5、BCE 和 E5 等候选需要外接 BM25 才能形成稀疏词项通道; - 输出类型、指令、维度、归一化、距离度量和视觉预处理共同定义索引契约;
- 分块、Dense、Sparse、BM25、融合和 Rerank 共同决定最终召回效果;
- 版本化、容量、监控、灰度和回滚决定系统能否长期运行。
对多数企业内部知识库,合理的起点不是线上 API,也不是直接部署最大的文本或多模态模型,而是先建立可复现的文本基线。只有真实失败样本证明文字无法表达页面、图表或画面证据时,才增加独立的多模态索引并进行受控融合。
参考资料
- Qwen3-Embedding 官方仓库
- Qwen3-VL-Embedding 官方仓库
- BGE-M3 官方模型说明
- BGE-VL 官方模型说明
- FlagEmbedding 官方仓库
- GTE Multilingual Base 模型说明
- BCE Embedding 模型说明
- Multilingual E5 Instruct 模型说明
- Text2Vec 中文模型说明
- Piccolo 中文模型说明
- M3E 模型说明
- Jina Embeddings v3 模型说明
- GME Qwen2-VL 模型说明
- Ollama Embeddings 文档
- Hugging Face Text Embeddings Inference 支持列表
- vLLM Pooling Models 文档