docs/design/local-embedding-llama-cpp-design.md
Date: 2026-04-11 Status: 已批准进入实现
为 OpenViking 增加内置的本地 dense embedding 能力,并满足以下产品行为:
embedding 时,OpenViking 默认使用本地 embedding backendbge-small-zh-v1.5-f16llama-cpp-python 加载 GGUF 模型最终效果是:在不改变“默认走本地 embedding”这一产品目标的前提下,让方案具备可实现性和可维护性。
本次设计包含:
embedding.backend = "local" backendllama-cpp-python 的 dense embedderbge-small-zh-v1.5-f16本次设计不包含:
OpenViking 采用以下组合策略:
llama-cpp-python 不进入主依赖,而是通过 openviking[local-embed] 之类的 optional extra 分发。bge-small-zh-v1.5-f16。这和 QMD 的做法不完全相同。QMD 是 Node CLI 产品,可以把 node-llama-cpp 作为主依赖;而 OpenViking 是 Python SDK 和服务组件,如果让原生依赖阻断主包安装,代价会更高。
调研结论和当前代码约束基本指向同一个方向:
因此,这个设计是在保留产品目标的前提下,尽量缩小原生依赖失败的影响范围。
如果配置中没有 embedding:
localbge-small-zh-v1.5-f16用户应感知到的行为是:“本地 embedding 是默认值”。
如果用户显式配置了 embedding,则始终以显式配置为准,包括:
backend: "local"openai、volcengine、vikingdbmodel_pathcache_dir不应存在覆盖用户配置的隐式重写。
基础安装:
pip install openviking
启用本地 embedding:
pip install "openviking[local-embed]"
如果用户依赖默认本地行为,但没有安装 local extra,系统必须在启动时给出可执行的错误提示,至少包含:
llama-cpp-python在 EmbeddingModelConfig 中新增 local 作为合法 backend。
本地 dense backend 支持的字段:
backend: "local"model: 逻辑模型名,默认 bge-small-zh-v1.5-f16model_path: 可选,显式指定 GGUF 文件路径cache_dir: 可选,缓存根目录,默认 ~/.cache/openviking/models/dimension: 可选,但通常应由内置模型注册表推导batch_size: 预留给后续批量 embedding建议配置示例:
{
"embedding": {
"dense": {
"backend": "local",
"model": "bge-small-zh-v1.5-f16",
"cache_dir": "~/.cache/openviking/models"
}
}
}
显式模型路径示例:
{
"embedding": {
"dense": {
"backend": "local",
"model": "bge-small-zh-v1.5-f16",
"model_path": "/data/models/bge-small-zh-v1.5-f16.gguf"
}
}
}
新增一个本地 dense embedder 实现,例如:
openviking/models/embedder/local_embedders.pyLocalDenseEmbedder其职责包括:
llama-cpp-python 是否可用close() 时释放本地资源需要修改:
EmbeddingModelConfig.validate_config() 以接受 backend == "local"EmbeddingConfig._create_embedder() 以支持 ("local", "dense")新增一个内置本地模型注册表。第一版可以先做成简单映射,按逻辑模型名索引:
首个内置模型为:
bge-small-zh-v1.5-f16这部分不是可选优化,而是本方案必须处理的设计点。
BGE/E5 一类模型是检索导向模型,通常需要区分:
OpenViking 当前只有 embed(text),这不足以表达这种语义差异。
设计上新增显式接口:
embed_query(text: str) -> EmbedResultembed_document(text: str) -> EmbedResult为了兼容现有代码,embed(text) 可以保留为一个薄封装,但内部必须带角色语义。新的检索代码应调用 embed_query(),新的入库代码应调用 embed_document()。
query/document 的格式规则必须封装在本地 embedder 内部,而不是散落在业务层拼装。
model_path,直接使用该路径。model。cache_dir。llama-cpp-python。默认目录:
~/.cache/openviking/models/行为要求:
第一版需要支持:
第一版暂不要求:
OpenViking 当前在 client 启动时就初始化 embedder,本地方案保持这一行为。
因此,下面这些问题都会在启动期直接暴露:
llama-cpp-python import 失败缺少本地依赖:
模型下载失败:
模型加载失败:
元数据不一致:
不允许静默回退:
openai、volcengine 或 vikingdb当前系统只在写入时校验向量维度,这在本地模型成为默认值之后是不够的。
需要至少持久化以下元数据:
embedding_backendembedding_modelembedding_dimensionembedding_model_identity其中 embedding_model_identity 用于区分“看起来模型名相同,但实际模型文件不同”的情况,可以采用:
只要以下任一项发生变化:
都应判定现有向量不可兼容。系统需要:
第一版建议采用显式 rebuild,而不是隐式迁移。
当前流程:
embed()改造后流程:
embed_document()当前流程:
embed()改造后流程:
embed_query()第一版可以先保证单条处理正确,但设计上必须明确批量优化路径。
阶段一:
LocalDenseEmbedder 中实现 embed_batch()阶段二:
如果没有批量能力,本地 CPU 索引构建吞吐大概率会明显低于模型 benchmark。
local backend 的配置校验和 factory 注册。llama-cpp-python 的 LocalDenseEmbedder。bge-small-zh-v1.5-f16。embed_query() / embed_document() 双路接口。embed_batch() 和 benchmark 基础设施。embedding 时自动生成隐式 local dense 配置model_path 能覆盖逻辑模型解析cache_dir 覆盖生效llama-cpp-python 时,启动错误信息正确embed_query() 与 embed_document() 走不同路径embed_batch() 的结果顺序和数量正确pip install openviking 可以在不安装本地依赖的情况下成功pip install "openviking[local-embed]" 可以启用本地 import 路径至少记录以下指标:
在 benchmark 出来之前,不应假设“默认本地 embedding”在所有环境里都同样合适。
推荐安装命令:
pip install "openviking[local-embed]"
如果用户想使用远程 embedding:
embedding.dense.backend