多Embedding模型路由,短文本长文本选用不同Embed
多Embedding模型路由就是把不同长度的文本交给更适合的向量模型去处理,避免一个模型在短文本和长文本上两头损失精度。
本文用 Python 实现一个按文本长度切换 Embedding 模型的函数,并给出模型选型、避坑和验证方法,适合正在搭建 RAG 知识库的小型项目使用者。
为什么要按文本长度拆分路由
Embedding 模型对输入长度有很强的敏感度。
短文本例如搜索关键词,用面向长文档的大模型反而会稀释语义;
长文本例如知识库片段,用轻量小模型则容易丢失关键信息。
混用同一个模型,最直接的后果是向量相似度计算不准,RAG 检索结果跑偏。
多Embedding模型路由的价值,就是让短文本走短文本专用模型,让长文本走长文本专用模型,各取所长。
先准备两个可用的 Embedding 模型
这里使用 sentence-transformers,先安装依赖:
pip install sentence-transformers
然后加载两个模型:一个偏短文本,一个偏长文本。
示例选择两个中文友好的模型:
from sentence_transformers import SentenceTransformer
short_model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
long_model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
如果你的服务器内存有限,可以把模型换成更小的版本,也可以提前下载到本地再加载。
不要盲目追求大模型,能跑起来最重要。
写一个按长度分发的路由函数
最简单的方式是看字符数,比如超过 200 个字符走长文本模型:
def embed_text(text, max_chars=200):
model = long_model if len(text) > max_chars else short_model
return model.encode(text, normalize_embeddings=True)
但这不够严谨,因为中文的 token 数和字符数并不是一一对应。
更准确的做法是引入 tokenizer:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('BAAI/bge-large-zh-v1.5')
def count_tokens(text):
return len(tokenizer.tokenize(text))
def embed_text(text, max_tokens=256):
if count_tokens(text) > max_tokens:
return long_model.encode(text, normalize_embeddings=True)
return short_model.encode(text, normalize_embeddings=True)
路由函数拿到文本后先数 token,再决定交给哪个模型。
阈值可以按业务数据调整。
四个容易忽略的坑
- 字符数不等于 token 数。中文场景尤其明显,用
len(text)判断长度会导致长文本被误分到短模型,建议统一走 tokenizer。 - 长文本截断问题。如果输入超过模型最大长度,即使选了长文本模型也会被截断,造成向量信息缺失。需要提前做切片或分段编码。
- 频繁切换模型拖慢速度。每次调用都交替使用两个模型,会让显存或内存频繁加载释放。更好的是把请求按长度分组,批量交给同一个模型处理。
- 阈值写死不可取。不同知识库的短长文本分布差异很大,建议先记录文本长度日志,再确定一个合理边界值。
怎么验证路由是否真的生效
先做一个小测试,确认每个文本走的是哪个模型:
test_texts = ['云服务器', '这是一段用于测试的长文本,包含多个句子,用来验证长文本是否被正确路由到 long_model。']
for t in test_texts:
vec = embed_text(t)
print(len(t), vec.shape)
如果输出里短文本和长文本的向量维度一致,说明两个模型输出了相同维度的向量,可以继续算相似度。
更进一步的验证方法是计算同一对文本在两个模型下的语义相似度,看长文本在 long_model 下是否确实更合理。
也可以用日志记录每次路由命中的模型名,统计短长文本的比例,方便后续调阈值。
如果你正在处理多Embedding模型路由,建议先按本文步骤完整执行,再根据自己的环境做微调;
遇到异常时优先回看避坑和高频问题部分。