Mastra 本地语义召回实战:@mastra/fastembed 与 ONNX Runtime 本地 Embedding 模型集成指南
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
@mastra/fastembed是 Mastra 框架内置的本地 Embedding 模型集成包,基于 ONNX Runtime 在进程内直接运行小型嵌入模型,无需任何外部 API 调用。本文围绕该包的安装、与@mastra/memory的语义召回(Semantic Recall)集成、内置模型与初始化参数、底层推理与模型缓存原理展开,帮助你把它作为Memory的本地embedder,为 Agent 构建数据不出本机的语义检索能力。
包定位与设计背景
@mastra/fastembed(源码位于 packages/fastembed)提供了一套由 ONNX Runtime 驱动的本地嵌入模型方案。它的核心特点包括:
- 完全本地推理:模型以 ONNX 格式运行在
onnxruntime-node上,文本向量化不依赖远程 API,适合对数据隐私、延迟和成本敏感的场景。 - 维护分支而非上游依赖:该包是 fastembed-js),因此
@mastra/fastembed不再依赖已停止维护的fastembednpm 包,规避了上游停更带来的供应链风险。 - 为 Mastra Memory 而生:包的主入口导出的
fastembed对象实现了 Mastra Embedding Model 接口,可直接作为new Memory({ embedder: fastembed })的配置项,为语义召回提供本地向量化能力。
从 packages/fastembed/package.json 可以看到其运行时依赖:onnxruntime-node(1.26.0)、@anush008/tokenizers(分词器)、@huggingface/hub(Hugging Face 模型下载)、progress(下载进度条)与tar(压缩包解压),引擎要求 Node.js>= 22.13.0,包以 ESM 为主并同时导出 CommonJS 构建。
安装与环境要求
npm install @mastra/fastembed安装完成后,首次实际调用会触发模型下载(详见下文“模型获取与缓存”一节),因此请确保运行环境可以访问模型源(Qdrant FastEmbed 的 GCS 存储桶与 Hugging Face)。若你的项目使用 pnpm 管理依赖,可直接将包加入dependencies后重新安装即可。
与 Mastra Memory 集成:本地语义召回
默认用法(AI SDK v3)
README 给出的最小集成方式,是把它作为Memory的embedder传入:
import { Memory } from '@mastra/memory'; import { fastembed } from '@mastra/fastembed'; const memory = new Memory({ // ... other memory options embedder: fastembed, });Memory的构造器接收embedder选项后,会在两类场景中用到它(见 packages/memory/src/index.ts 中的校验逻辑):
retrieval: { vector: true }的向量检索;- 语义召回(semantic recall):即
semanticRecall配置为true(或{ scope: 'resource' }等)时,将历史消息向量化并做相似度检索,其中topK与messageRange控制召回数量与范围。
需要特别注意的是:语义召回要求同时配置 vector store 与 embedder。源码中对应的校验是:未配置 embedder 时抛出 “retrieval: { vector: true }requires an embedder” 与 “Subconscious semantic knowledge requires an embedder” 的错误(packages/memory/src/index.ts)。因此实际使用时通常是这样:
import { Memory } from '@mastra/memory'; import { LibSQLStore } from '@mastra/libsql'; import { LibSQLVector } from '@mastra/libsql'; import { fastembed } from '@mastra/fastembed'; const store = new LibSQLStore({ config: { url: 'file:memory.db' } }); const vector = new LibSQLVector({ config: { url: 'file:vectors.db' } }); const memory = new Memory({ store, vector, embedder: fastembed, semanticRecall: true, });此外,packages/memory/src/index.ts 中还有一处针对本包的专门处理注释:“for fastembed multiple initial calls to embed will fail if the model hasn't been downloaded yet”(使用 fastembed 时,模型尚未下载完成的首次多次调用可能失败),并会通过this.embedder.provider === 'fastembed'判断走对应的重试路径。也就是说,首次使用前模型下载是异步的,生产环境建议先用下文介绍的warmup()预热。
面向不同 AI SDK 版本的兼容层
@mastra/fastembed的入口(packages/fastembed/src/index.ts)同时注册了三种规范版本的 provider,保证不同 AI SDK 主版本都能对接:
| 规范版本 | 对应 AI SDK | 导出属性 |
|---|---|---|
| v1(legacy) | AI SDK v4 | smallLegacy、baseLegacy |
| v2 | AI SDK v5 | smallV2、baseV2 |
| v3 | AI SDK v6(默认) | small、base、multilingualE5LargeQuery、multilingualE5LargePassage及fastembed本身 |
fastembed对象本身即 v3 规范的bge-small-en-v1.5模型实例,同时挂载了上述命名属性便于显式选用具体模型。每个模型的maxEmbeddingsPerCall均为256,并声明supportsParallelCalls: true,允许并发嵌入调用。
内置模型与 API 一览
fastembed入口默认注册四个 v3 模型 ID:bge-small-en-v1.5、bge-base-en-v1.5、multilingual-e5-large-query、multilingual-e5-large-passage。而底层的EmbeddingModel枚举(packages/fastembed/src/fastembed.ts)则支持更完整的模型清单,配合listSupportedModels()可得到维度与说明:
| 枚举值 | 模型 ID | 向量维度 | 说明 |
|---|---|---|---|
BGESmallEN | fast-bge-small-en | 384 | 快速英文模型 |
BGESmallENV15 | fast-bge-small-en-v1.5 | 384 | v1.5 快速英文模型(默认) |
BGEBaseEN | fast-bge-base-en | 768 | 基础英文模型 |
BGEBaseENV15 | fast-bge-base-en-v1.5 | 768 | v1.5 基础英文模型 |
BGESmallZH | fast-bge-small-zh-v1.5 | 512 | 快速中文模型 |
AllMiniLML6V2 | fast-all-MiniLM-L6-v2 | 384 | Sentence Transformer MiniLM-L6-v2 |
MLE5Large | fast-multilingual-e5-large | 1024 | 多语言模型,官方推荐用于非英文场景 |
入口同时导出了以下类型与类,便于底层直接使用:
EmbeddingModel、FlagEmbedding(稠密向量模型类)SparseTextEmbedding、SparseEmbeddingModel、SparseVector(稀疏向量,SPLADE)ExecutionProvider(执行后端枚举)InitOptions、InitSparseOptions等初始化参数类型
底层实现:FlagEmbedding 推理流水线
FlagEmbedding(packages/fastembed/src/fastembed.ts)是稠密嵌入的核心实现,其工作流可拆解为四步:
- 模型获取:
init()中若指定标准模型,会调用retrieveModel(model, cacheDir, showDownloadProgress)从https://storage.googleapis.com/qdrant-fastembed/<model>.tar.gz下载模型压缩包(其中AllMiniLML6V2的 URL 命名略有特殊处理),解压到cacheDir并删除压缩包。若指定CUSTOM模型,则要求同时提供modelAbsoluteDirPath与modelName,直接加载本地目录中的 ONNX 文件。 - 加载分词器:
loadTokenizerFromDir()依次读取tokenizer.json、config.json、tokenizer_config.json、special_tokens_map.json,通过@anush008/tokenizers构造Tokenizer,按min(maxLength, model_max_length)设置截断与填充,并注册特殊 token。注意MLE5Large与AllMiniLML6V2默认加载model.onnx,其余模型默认加载model_optimized.onnx。 - 创建 ONNX Session:
ort.InferenceSession.create(modelPath, { executionProviders, graphOptimizationLevel: 'all' }),默认 CPU 执行并开启全量图优化。 - 批处理推理与归一化:
embed()为 AsyncGenerator,默认以 256 条文本为一批;每批内并行tokenizer.encode,组装input_ids、attention_mask、token_type_ids三个 int64 Tensor(MLE5Large会剔除token_type_ids),运行 session 后取last_hidden_state,按[batch, seq, hidden]维度切片还原每条文本的向量,最后做L2 归一化(normalize()中除以向量模长,并加了1e-12的 epsilon 防止除零)。
值得注意的细节是FlagEmbedding的语义前缀约定:
passageEmbed(texts)会给每条文本加passage:前缀;queryEmbed(query)会给查询加query:前缀。
这与入口层generatePrefixedEmbeddings()对 E5 模型的处理一脉相承:E5 系列是对称性较弱的模型,query 与 passage 必须使用不同前缀才能取得正确相似度。这一点在单元测试中有明确断言(packages/fastembed/src/index.test.ts):multilingualE5LargeQuery.doEmbed(['hello'])实际送入模型的是['query: hello'],而multilingualE5LargePassage送入['passage: hello'];bge系列则不加前缀(packages/fastembed/src/index.test.ts)。
SparseTextEmbedding:SPLADE 稀疏检索
除了稠密向量,包还提供SparseTextEmbedding用于稀疏检索(packages/fastembed/src/fastembed.ts):
- 内置模型
SpladePPEnV1(prithivida/Splade_PP_en_v1,词表大小 30522),模型文件从 Hugging Face 仓库按清单下载(onnx/model.onnx、tokenizer.json、config.json等 5 个文件)。 - 推理结果经过
log(1 + ReLU(logits))后处理,并对每个 token 维度取整条序列上的最大值,最终产出SparseVector:{ values: number[], indices: number[] },只保留正值项,天然稀疏。 queryEmbed/passageEmbed同样可用,测试中验证了稀疏向量的indices与values长度一致且值均大于 0(packages/fastembed/src/fastembed_splade.test.ts)。
稀疏向量可配合支持稀疏检索的向量库使用,作为稠密检索的补充。
模型缓存与预热:避免重复下载
入口层的模型访问全部经由 packages/fastembed/src/model-cache.ts 的getCachedModel()完成,其设计要点:
- 固定缓存目录:
~/.cache/mastra/fastembed-models(os.homedir()下的.cache/mastra/fastembed-models),首次访问时自动递归创建。 - Promise 级缓存:内部用
Map<FastEmbedModelType, Promise<FlagEmbedding>>缓存初始化中的 Promise。同一模型并发请求共享同一次FlagEmbedding.init,不会重复下载;初始化失败时会从 Map 中删除以便下次重试。相关单元测试验证了“重复调用只初始化一次”“并发请求共享初始化”“small/base 各自独立缓存”等行为(packages/fastembed/src/model-cache.test.ts)。
针对“并发测试/首次启动导致模型下载竞争”的问题,入口导出warmup():
import { warmup } from '@mastra/fastembed'; // 在启动阶段调用,只下载模型文件、不创建 ONNX session await warmup();warmup()底层调用warmupFastEmbedModels()(packages/fastembed/src/model-cache.ts),预先下载bge-small-en-v1.5与bge-base-en-v1.5两个默认模型。生产服务建议在启动阶段执行预热,避免首个请求承担下载延迟。
初始化选项详解
底层FlagEmbedding.init()/SparseTextEmbedding.init()的选项(InitOptions,见 packages/fastembed/src/fastembed.ts)默认值与说明如下:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model | EmbeddingModel/SparseEmbeddingModel | BGESmallENV15/SpladePPEnV1 | 要加载的模型 |
executionProviders | ExecutionProvider[] | ['cpu'] | ONNX 执行后端:cpu、cuda、webgl、wasm、xnnpack(见 ExecutionProvider 枚举) |
maxLength | number | 512 | 序列最大长度,会被tokenizer_config.json中的model_max_length进一步收紧 |
cacheDir | string | 'local_cache' | 模型缓存目录;入口层自动改用~/.cache/mastra/fastembed-models |
showDownloadProgress | boolean | true | 下载时是否显示进度条 |
modelAbsoluteDirPath | PathLike | '' | 仅CUSTOM模型必填:本地模型目录 |
modelName | string | '' | 仅CUSTOM模型必填:ONNX 文件名 |
选择自定义 ONNX 模型时,目录内须同时包含tokenizer.json、config.json、tokenizer_config.json、special_tokens_map.json与 ONNX 模型文件(测试中即从sentence-transformers/all-MiniLM-L6-v2下载这组文件后加载,见 packages/fastembed/src/fastembed_custom.test.ts)。选择 CUDA/XNNPACK 等非 CPU 后端时,需要确认运行环境的 ONNX Runtime 包含对应原生扩展。
测试与验证
包内测试分为两类:
- 单元测试(
npm test):通过 mock 验证入口层模型注册、E5 前缀、模型缓存语义(packages/fastembed/src/index.test.ts、packages/fastembed/src/model-cache.test.ts)。 - 真实模型测试(
npm run test:models,对应vitest run --project 'models:packages/fastembed'):每个内置模型一套测试(如 packages/fastembed/src/fastembed_bgebasev15.test.ts),覆盖init、单条/批量/小批量embed、queryEmbed、passageEmbed,并断言输出向量维度(bge-base 为 768、MiniLM 自定义模型为 384 等)。其中canonical values 测试固定了参考输入(如'hello world')的期望向量前若干维数值,用以锁定模型输出的一致性,防止依赖或实现变动悄悄改变向量结果。
使用注意事项
- 首次调用延迟:模型文件需要从 GCS/Hugging Face 下载(每个模型数百 MB 级别),首次
doEmbed可能耗时较长;并发场景下建议先调用warmup()。 - 磁盘与网络:模型缓存在
~/.cache/mastra/fastembed-models,可复用跨进程,但需保证目录可写且磁盘空间充足。 - 模型选型:英文场景默认
bge-small-en-v1.5(384 维,速度快、体积小);需要更高精度选bge-base-en-v1.5(768 维);非英文/多语言场景按官方建议选择multilingual-e5-large(1024 维,query/passage 分开注册)。 - E5 前缀不可混用:query 与 passage 必须使用对应角色模型(入口已分开注册为
multilingualE5LargeQuery与multilingualE5LargePassage),混用会显著降低检索质量。
小结
@mastra/fastembed把原本需要外部 API 的文本向量化能力完全下沉到本地:它以 vendored 的 fastembed-js 源码 + ONNX Runtime 实现了稠密(BGE / MiniLM / E5)与稀疏(SPLADE)两类嵌入模型,通过三套规范版本的 provider 无缝对接 MastraMemory的语义召回,并提供模型缓存、预热与批量推理等工程化细节。对希望在 Agent 应用中实现隐私友好、零 API 成本的语义检索的开发者而言,直接以embedder: fastembed接入即可快速落地。
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考