首页
/ LlamaIndex 本地密集向量嵌入实战:FastEmbedEmbedding 集成指南与源码解析

LlamaIndex 本地密集向量嵌入实战:FastEmbedEmbedding 集成指南与源码解析

2026-09-07 11:11:53作者:范靓好Udolf

本文围绕 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.pyDEFAULT_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,
)

这段代码带来的两个可观察行为:

  1. 缺失依赖时快速失败:若只装了集成包而没装 fastembed,构造对象就会抛出带明确修复提示的 ImportError,而不是拖到真正编码时才报错。
  2. 模型下载/缓存发生在构造阶段:首次指定一个未缓存的模型名时,TextEmbedding 构造会触发模型文件下载(之后复用 cache_dir 缓存,默认落在系统临时目录的 fastembed_cache),所以首次实例化通常比后续慢。

另外注意 _model 被声明为 pydantic PrivateAttrbase.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 中 passagequery 分开编码,本质上服务的是非对称检索场景(长文档 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(通过 mock TextEmbedding)验证:自定义字段 cache_dirembed_batch_sizedoc_embed_type 会被正确保存,同时 cache_dir 会原样透传给底层 TextEmbedding,而 threadsproviders 在未显式指定时为 None

这说明「集成包负责适配接口、fastembed 负责真实推理」的边界,也便于你验证自己传参是否正确。

9. 常见问题与排查建议

  1. ImportError: Could not import FastEmbed:未安装推理后端。按第 2 节执行 pip install fastembed(CPU)或 pip install fastembed-gpu(GPU)后重试。
  2. 首次实例化很慢 / 卡在下载:构造阶段会下载并缓存模型权重,属正常现象。将 cache_dir 指向持久化目录(如 /opt/models/fastembed_cache)可避免每次重复下载,也方便离线复用。
  3. 查询结果不理想:检查文档与查询是否使用了匹配的编码通道。默认配置下文档走 embed、查询走 query_embed,二者在 FastEmbed 的“查询/文档”联合训练语义下协同;如果你手动把所有内容都当成查询风格处理,可考虑调整 doc_embed_type 并复测。
  4. CPU 推理慢:可通过 threads 增大单个 ONNX session 线程数,或安装 GPU 后端并在 providers 中显式声明 ["CUDAExecutionProvider"]
  5. 嵌入结果与向量库维度不匹配:更换 model_name 会改变输出维度,需确保向量库的 collection/索引 schema 与模型维度一致;不要在已有索引中途切换不同维度的模型。

结语

FastEmbedEmbedding 是 LlamaIndex 体系中「去 API 依赖、纯本地嵌入」的代表性集成:构造参数透明、文档与查询编码分工清晰、同步与异步 API 齐备,并通过继承 BaseEmbedding 无缝融入索引与检索全链路。其完整源码与配套 API 参考位于 llama-index-embeddings-fastembed 集成目录(类定义见 base.py),官方 API 文档入口为 embeddings/fastembed.md,配合 fastembed.ipynb 示例可以快速上手并落地到实际 RAG 项目中。

登录后查看全文
热门项目推荐
相关项目推荐