LlamaIndex 本地密集向量嵌入实战:FastEmbedEmbedding 集成指南与源码解析
本文围绕 LlamaIndex 官方 API 参考中 embeddings/fastembed.md 所记录的 llama_index.embeddings.fastembed.FastEmbedEmbedding 展开,讲解如何在 LlamaIndex 索引与检索流程中接入 Qdrant FastEmbed,以本地 ONNX 推理方式生成文本嵌入向量。读完本文,你将掌握该集成包的安装方式、全部构造参数与默认值、文档/查询双路编码与异步调用能力,并能基于源码理解其底层实现、测试保障及常见问题排查方法。
1. FastEmbedEmbedding 在 LlamaIndex 中的定位
FastEmbed 是 Qdrant 开源的一个轻量、快速的 Python 嵌入生成库,基于 ONNX Runtime 做本地推理。FastEmbedEmbedding 是 LlamaIndex 官方对它的适配器,其完整实现位于 base.py,以独立的 llama-index-embeddings-fastembed 包分发(当前仓库中版本为 0.6.0,见 pyproject.toml)。
它在 LlamaIndex 中承担的是**密集向量(dense embedding)**生成职责,与同样基于 FastEmbed 的稀疏向量集成(对应 sparse_embeddings/fastembed.md)互相独立:一个面向语义稠密表示,一个面向 BM25 式稀疏加权表示。从类继承关系看(见下方源码与测试),FastEmbedEmbedding 直接继承自核心库的 BaseEmbedding(定义于 llama-index-core 的 base.py),因此能无缝接入 LlamaIndex 的索引构建、检索器与查询引擎体系。
由于推理完全在本地发生,模型下载完成后不再依赖任何远程 API,非常适合追求低延迟、低成本或数据不出内网的 RAG 场景。
2. 安装与引入
集成包本身只声明对 llama-index-core 的依赖,真正的推理库 fastembed 需要在运行时按需安装(源码在导入时才做检查,详见第 4 节)。安装方式:
# 1. 安装 LlamaIndex 的 FastEmbed 集成包
pip install llama-index-embeddings-fastembed
# 2. 安装推理后端:CPU 版
pip install fastembed
# 3. 若需 GPU 加速,安装 GPU 版(源码中的导入提示即推荐此方式)
pip install fastembed-gpu
安装后通过如下方式导入并实例化(代码与仓库中的示例笔记本 fastembed.ipynb 保持一致):
from llama_index.embeddings.fastembed import FastEmbedEmbedding
embed_model = FastEmbedEmbedding(model_name="BAAI/bge-small-en-v1.5")
包的导出结构非常精简,init.py 仅公开 FastEmbedEmbedding 一个符号:
from llama_index.embeddings.fastembed.base import FastEmbedEmbedding
__all__ = ["FastEmbedEmbedding"]
环境约束提示:从 pyproject.toml 看,该包声明
requires-python = ">=3.10,<3.13",并要求llama-index-core>=0.13.0,<0.15;开发依赖中锁定fastembed>=0.2.2,可作为选型版本下限的参考。
3. 构造参数全解析(字段与默认值)
FastEmbedEmbedding 的所有核心字段在 base.py 中通过 pydantic Field 声明,下面对照源码逐项说明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model_name |
str |
"BAAI/bge-small-en-v1.5" |
使用的 FastEmbed 模型名称。可在构造时显式更换,具体可选模型清单以 FastEmbed 项目维护的“Supported Models”列表为准 |
cache_dir |
Optional[str] |
None |
模型缓存目录;为 None 时使用系统临时目录下的 fastembed_cache |
threads |
Optional[int] |
None |
单个 onnxruntime session 可使用的线程数,默认交由运行时决定 |
doc_embed_type |
Literal["default", "passage"] |
"default" |
文档编码方式:"default" 调用普通 embed,"passage" 调用 passage_embed(详见第 5 节) |
providers |
Optional[List[str]] |
None |
指定 ONNX 执行提供者(如 ["CUDAExecutionProvider"]),默认使用 ONNX Runtime 可用提供者 |
此外,__init__ 接收 **kwargs 并透传给父类 super().__init__(...) 与底层 TextEmbedding(...)(见 base.py),这意味着所有 BaseEmbedding 标准字段都可以直接在构造时传入,其中最有实操价值的是:
embed_batch_size:批处理大小,核心库默认值为10(见 constants.py 的DEFAULT_EMBED_BATCH_SIZE),取值范围为1 < size <= 2048(定义于 BaseEmbedding);num_workers:异步批量编码时的并发 worker 数(None表示自动);embeddings_cache:可选的结果缓存,可传入带put/get接口的缓存实现。
一个配置了批大小与 passage 编码模式的典型实例:
embed_model = FastEmbedEmbedding(
model_name="BAAI/bge-small-en-v1.5",
embed_batch_size=24, # 覆盖核心库默认的 10
doc_embed_type="passage", # 对文档使用 passage_embed
cache_dir="/opt/models/fastembed_cache",
)
class_name() 方法固定返回 "FastEmbedEmbedding"(base.py),这在 LlamaIndex 的序列化、调试与可观测性场景中用于标识模型类型。
4. 懒加载机制:初始化时做了什么
FastEmbedEmbedding.__init__ 的一个关键设计是推迟导入并立即构建底层模型。源码(base.py)先尝试 from fastembed import TextEmbedding:
try:
from fastembed import TextEmbedding
except ImportError as e:
raise ImportError(
"Could not import FastEmbed. "
"Please install it with `pip install fastembed` or "
"`pip install fastembed-gpu` for GPU support"
) from e
self._model = TextEmbedding(
model_name=model_name,
cache_dir=cache_dir,
threads=threads,
providers=providers,
**kwargs,
)
这段代码带来的两个可观察行为:
- 缺失依赖时快速失败:若只装了集成包而没装
fastembed,构造对象就会抛出带明确修复提示的ImportError,而不是拖到真正编码时才报错。 - 模型下载/缓存发生在构造阶段:首次指定一个未缓存的模型名时,
TextEmbedding构造会触发模型文件下载(之后复用cache_dir缓存,默认落在系统临时目录的fastembed_cache),所以首次实例化通常比后续慢。
另外注意 _model 被声明为 pydantic PrivateAttr(base.py),不会参与字段序列化;model_config 中开启了 arbitrary_types_allowed=True 以允许持有非 pydantic 类型的运行时对象。
5. 文本编码 API:文档编码与查询编码的分工
FastEmbed 对「文档」与「查询」采用不同的编码入口,FastEmbedEmbedding 将这层差异封装为四个受保护方法(base.py):
| 方法 | 底层调用 | 语义 |
|---|---|---|
_get_text_embedding(text) |
_get_text_embeddings([text])[0] |
单条文本向量 |
_get_text_embeddings(texts) |
doc_embed_type=="passage" 时走 passage_embed,否则走 embed |
批量文档向量 |
_get_query_embedding(query) |
_model.query_embed(query) |
查询向量(始终用 query 专用入口) |
从源码可见,对文档/文本走的是 embed/passage_embed,由 doc_embed_type 决定;对查询则固定使用 query_embed。FastEmbed 中 passage 与 query 分开编码,本质上服务的是非对称检索场景(长文档 vs 短查询);如果你用较短、与查询风格相近的文本作为被检索内容,保持默认 doc_embed_type="default" 即可。
上述方法最终都会把底层 numpy.ndarray 转换为 float 列表(embedding.tolist()),对齐 LlamaIndex 的 Embedding = List[float] 类型约定(见 BaseEmbedding)。
对外,使用者一般直接调用 BaseEmbedding 提供的公开方法。例如仓库示例笔记本中的用法:
embeddings = embed_model.get_text_embedding("Some text to embed.")
print(len(embeddings)) # 向量维度
print(embeddings[:5]) # 前几维数值
批量场景推荐 get_text_embedding_batch(["文本 A", "文本 B", ...]),会自动按 embed_batch_size 分片并复用底层 session。
6. 异步调用与并发
FastEmbedEmbedding 同时提供异步 API。四个 _get_* / _aget_* 方法中,异步版本统一借助 asyncio.to_thread 把 CPU 密集的同步推理抛到线程池执行(base.py),从而避免阻塞事件循环:
import asyncio
async def main():
vecs = await embed_model.aget_text_embedding_batch(["hello", "world"])
return vecs
asyncio.run(main())
由于推理本身是本地 CPU/GPU 计算,异步收益主要在于「等待期间不阻塞其他协程」;若需要更高并发吞吐,可结合 num_workers 参数使用核心库内部的并发调度。
7. 接入 Settings 与索引构建的完整用例
在 LlamaIndex 新版 API 中,推荐通过全局 Settings 注入 embedding 模型,让索引、检索器与查询引擎自动复用:
from llama_index.core import Settings, VectorStoreIndex
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.fastembed import FastEmbedEmbedding
# 1) 配置全局嵌入模型
Settings.embed_model = FastEmbedEmbedding(
model_name="BAAI/bge-small-en-v1.5",
embed_batch_size=32,
doc_embed_type="passage",
)
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=20)
# 2) 构建向量索引(内部会自动调用嵌入模型)
documents = [
"LlamaIndex 是一个面向 LLM 应用的数据框架。",
"FastEmbed 基于 ONNX Runtime 在本地生成嵌入向量。",
]
index = VectorStoreIndex.from_documents(documents)
# 3) 查询(自动使用 query_embed 对查询编码)
query_engine = index.as_query_engine()
response = query_engine.query("FastEmbed 的推理基于什么运行时?")
print(response)
若配合外部向量库(如 Qdrant、Pinecone),只需将 index 替换为对应 VectorStoreIndex 构造即可,嵌入模型保持同一配置。
8. 测试用例如何验证契约
集成包的测试位于 tests/test_embeddings_fastembed.py,共两个用例,揭示了实现的两个契约:
@patch("fastembed.TextEmbedding")
def test_create_fastembed_embedding(mock_text_embedding):
cache = Path("./test_cache_2")
fastembed_embedding = FastEmbedEmbedding(
cache_dir=str(cache), embed_batch_size=24, doc_embed_type="passage",
)
assert fastembed_embedding.cache_dir == str(cache)
assert fastembed_embedding.embed_batch_size == 24
assert fastembed_embedding.doc_embed_type == "passage"
assert mock_text_embedding.call_args.kwargs["cache_dir"] == str(cache)
assert mock_text_embedding.call_args.kwargs["threads"] is None
assert mock_text_embedding.call_args.kwargs["providers"] is None
test_class断言FastEmbedEmbedding.__mro__中包含BaseEmbedding,保证它遵循 LlamaIndex 统一嵌入接口;test_create_fastembed_embedding(通过 mockTextEmbedding)验证:自定义字段cache_dir、embed_batch_size、doc_embed_type会被正确保存,同时cache_dir会原样透传给底层TextEmbedding,而threads、providers在未显式指定时为None。
这说明「集成包负责适配接口、fastembed 负责真实推理」的边界,也便于你验证自己传参是否正确。
9. 常见问题与排查建议
ImportError: Could not import FastEmbed:未安装推理后端。按第 2 节执行pip install fastembed(CPU)或pip install fastembed-gpu(GPU)后重试。- 首次实例化很慢 / 卡在下载:构造阶段会下载并缓存模型权重,属正常现象。将
cache_dir指向持久化目录(如/opt/models/fastembed_cache)可避免每次重复下载,也方便离线复用。 - 查询结果不理想:检查文档与查询是否使用了匹配的编码通道。默认配置下文档走
embed、查询走query_embed,二者在 FastEmbed 的“查询/文档”联合训练语义下协同;如果你手动把所有内容都当成查询风格处理,可考虑调整doc_embed_type并复测。 - CPU 推理慢:可通过
threads增大单个 ONNX session 线程数,或安装 GPU 后端并在providers中显式声明["CUDAExecutionProvider"]。 - 嵌入结果与向量库维度不匹配:更换
model_name会改变输出维度,需确保向量库的 collection/索引 schema 与模型维度一致;不要在已有索引中途切换不同维度的模型。
结语
FastEmbedEmbedding 是 LlamaIndex 体系中「去 API 依赖、纯本地嵌入」的代表性集成:构造参数透明、文档与查询编码分工清晰、同步与异步 API 齐备,并通过继承 BaseEmbedding 无缝融入索引与检索全链路。其完整源码与配套 API 参考位于 llama-index-embeddings-fastembed 集成目录(类定义见 base.py),官方 API 文档入口为 embeddings/fastembed.md,配合 fastembed.ipynb 示例可以快速上手并落地到实际 RAG 项目中。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00