LocalAI 模型库扩展实战:把 HuggingFace GGUF 模型发布进 Gallery(含 variants 变体机制全解析)
【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI
本篇技术指南以仓库内的维护指南 adding-gallery-models.md 为核心,系统讲解如何把 HuggingFace 上的 GGUF 权重发布为 LocalAI Model Gallery 中的一个条目:从获取 SHA256、编写 embedding / chat 条目 YAML,到理解variants变体自动选择、dflash/mtp特性标签规则与引擎偏好排序的底层实现。读完本文,你将既能独立把一个新模型合规地写入gallery/index.yaml,也能解释"多个量化包、多引擎构建的同一模型,最终该装哪一个"这一本地化问题的完整答案。
LocalAI 模型库是什么:index.yaml 与模板配置的分工
在动手之前,先理解模型库的两层结构(这与之后每一处字段都有关系):
- 条目目录:
gallery/index.yaml是唯一的模型清单。指南明确写着 "All models are defined ingallery/index.yaml",新增模型时应把条目放到合适的分区——embedding 模型靠近其他 embedding 模型,chat 模型靠近同类型的 chat 模型,并保持整体风格统一。 - 模板配置:
gallery/目录下还存在大量以模型架构命名的.yaml(如chatml.yaml、gemma.yaml、virtual.yaml、deepseek.yaml)。它们不是"另一个模型清单",而是条目可引用的基础配置——每个文件内嵌一段config_file:,定义后端引擎、上下文长度、stopwords、提示词模板(chat/chat_message/completion/function)等。以真实存在的 chatml.yaml 为例,它把backend: llama-cpp、context_size: 4096、f16: true与一整套 ChatML 风格的chat_message模板绑定在一起;gemma.yaml 则预设了 8192 上下文与 Gemma 家族的<start_of_turn>/<end_of_turn>模板。
条目通过url:字段声明自己"继承"哪份模板配置。条目本体上的overrides:再对模板做局部覆盖(换parameters.model文件名、追加options等)。下载、校验、安装的行为由 core/gallery 下的解析逻辑驱动,用户侧的一键安装入口(WebUI 的Models页、POST /models/apply、CLI 与 MCP)统一消费这份清单,详细用户文档见 docs/content/features/model-gallery.md。
第一步:获取 GGUF 文件的 SHA256(利用 x-linked-etag)
HuggingFace 上的 GGUF 文件会在 HTTP 响应头x-linked-etag中暴露其 SHA256,无需下载整个权重即可取得:
curl -sI "https://huggingface.co/<org>/<repo>/resolve/main/<filename>.gguf" | grep -i x-linked-etag返回值的去除引号部分即为 SHA256。指南给了一个完整的可验证示例:
curl -sI "https://huggingface.co/ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/resolve/main/embeddinggemma-300m-qat-Q8_0.gguf" | grep -i x-linked-etag # x-linked-etag: "6fa0c02a9c302be6f977521d399b4de3a46310a4f2621ee0063747881b673f67"这个例子并非虚构——gallery/index.yaml#L27782-L27804 中真实的embeddinggemma-300m条目,其files[].sha256正是6fa0c02a9c302be6f977521d399b4de3a46310a4f2621ee0063747881b673f67。
务必注意文件名的精确大小写:HuggingFace 文件名区分大小写(例如Q8_0与q8_0是两个不同的对象),同一仓库路径 URL 中也可能同时出现大小写混合(上例仓库名是qat-q8_0,文件名却含大写Q8_0)。发布前一定要先核对仓库的文件列表,照抄真实文件名,而不是凭记忆或按其他量化命名规则推断。
第二步:选择正确的模板配置
根据模型的架构与用途选择gallery/下的模板(这些模板文件通过url:被条目引用):
| 模板 | 适用模型 | 备注 |
|---|---|---|
| gemma.yaml | Gemma 家族(gemma、embeddinggemma、medgemma 等) | Gemma 的<start_of_turn>/<end_of_turn>对话模板,8192 上下文 |
| chatml.yaml | ChatML 格式模型(大量 Mistral / OpenHermes 微调) | <|im_start|>/<|im_end|>模板,4096 上下文 |
| deepseek.yaml | DeepSeek 系列 | 按 DeepSeek 对话格式定制 |
| virtual.yaml | 最小基座(很适合不需要对话模板的 embedding 模型) | 只含name/description/license,几乎不预设任何行为 |
可以看到 virtual.yaml 仅声明了一个空壳定义,embeddinggemma-300m这类纯 embedding 模型正是继承它、再由overrides注入 engine 与 embeddings 行为的。
条目通用字段速查
在逐个格式展开前,先建立字段心智模型。下面是对照真实条目(见下文)归纳的字段含义:
| 字段 | 作用 |
|---|---|
name | 全局唯一标识,用户据此安装(local-ai models install <name>、/models/apply),也是variants的引用键 |
url | 所继承模板配置的仓库路径,形如github:mudler/LocalAI/gallery/<template>.yaml@master |
urls | 展示给用户的来源页列表:原始模型页 + GGUF 转换仓库页,通常两行都要写 |
icon/license | 展示与许可声明;官方 gallery 只收录许可允许的模型 |
description | 一段 Markdown,说明模型规模、能力、量化方式 |
tags | 检索与过滤用;同时是variants排序读取特性的唯一信号(见下文 dflash/mtp 规则) |
overrides | 对模板config_file的合并覆盖,如backend、embeddings、parameters.model、options、context_size |
files | 每个需要下载并校验的权重:filename+sha256+uri(huggingface://或完整 HTTPS URL) |
variants | 可选;声明本模型的"其他构建"条目名列表(不同量化 / 不同引擎) |
编写 embedding 模型条目
Embedding 模型以 virtual.yaml 为基座,并设置embeddings: true。指南给出的标准模板如下(字段被完整保留,供直接套用):
- name: "model-name" url: github:mudler/LocalAI/gallery/virtual.yaml@master urls: - https://huggingface.co/<original-model-org>/<original-model-name> - https://huggingface.co/<gguf-org>/<gguf-repo-name> description: | Short description of the model, its size, and capabilities. tags: - embeddings overrides: backend: llama-cpp embeddings: true parameters: model: <filename>.gguf files: - filename: <filename>.gguf uri: huggingface://<gguf-org>/<gguf-repo-name>/<filename>.gguf sha256: <sha256-hash>对照仓库中真实的embeddinggemma-300m条目 gallery/index.yaml#L27782-L27804,可以看到完全相同的手法:url指向virtual.yaml@master,overrides里声明backend: llama-cpp、embeddings: true、known_usecases: [embeddings]并把parameters.model指到实际 GGUF 文件名,files用huggingface://URI 与 sha256 锁定权重。真实条目还展示了若干实操细节:
- embedding 模型可以继续沿用
llama-cpp引擎,不需要对话模板,这正是选virtual.yaml的原因; uri与filename不必同构——URI 决定下载源,filename决定落地文件名与加载引用;tags帮助 embedding 模型在检索与 WebUI 里归类。
编写 chat / LLM 模型条目
对话类模型通常会引用一个定义好提示词格式的模板配置(例如gallery/gemma.yaml、gallery/chatml.yaml)。指南给出的基础形态:
- &model-anchor url: "github:mudler/LocalAI/gallery/<template>.yaml@master" name: "model-name" icon: https://example.com/icon.png license: <license> urls: - https://huggingface.co/<org>/<model> - https://huggingface.co/<gguf-org>/<gguf-repo> description: | Model description. tags: - llm - gguf - gpu - cpu overrides: parameters: model: <filename>-Q4_K_M.gguf files: - filename: <filename>-Q4_K_M.gguf sha256: <sha256> uri: huggingface://<gguf-org>/<gguf-repo>/<filename>-Q4_K_M.gguf注意两点:锚点&model-anchor是为同一模型的不同量化变体准备的;name里通常带量化后缀(如granite-4.2-3b-q4),因为一个模型会有多个量化条目。真实例子可看 gallery/index.yaml#L312-L356 的granite-4.2-3b-q4:它继承virtual.yaml,但overrides写得更完整——context_size: 131072、function.automatic_tool_parsing_fallback、options: [use_jinja:true]、template.use_tokenizer_template: true,说明模板只是起点,真正决定运行时行为的细节都在overrides中按需补齐。
用 YAML merge 添加量化变体
同一模型的第二个量化版本,用 YAML 合并语法!!merge <<:从锚点派生出最小差异条目:
- !!merge <<: *model-anchor name: "model-name-q8" overrides: parameters: model: <filename>-Q8_0.gguf files: - filename: <filename>-Q8_0.gguf sha256: <sha256> uri: huggingface://<gguf-org>/<gguf-repo>/<filename>-Q8_0.gguf合并只覆盖需要变化的字段(name、overrides.parameters.model、files),其余字段(url、urls、tags、license)自动从锚点继承。真实仓库中的granite-4.2-3b-q8正是这条派生路径的产物 gallery/index.yaml#L357-L381:它从&granite-4-2-3b派生出Q8_0版本并替换 description 与文件清单。
用variants把同一模型的多个构建组织起来
当一个模型同时以多种量化发布、或还能被其他引擎服务时(例如同一个权重既有 llama.cpp GGUF 构建,又有 vLLM / MLX 构建),就把每个构建各自写成普通条目,然后让其中一个条目用variants:指向其他条目:
- !!merge <<: *chatml name: "nanbeige4.1-3b-q4" # ... the usual urls / overrides / files for the Q4 build ... variants: - model: nanbeige4.1-3b-q8仓库中nanbeige4.1的真实条目与之一致:nanbeige4.1-3b-q4(声明者)的variants指向nanbeige4.1-3b-q8(被引用者),见 gallery/index.yaml#L11494-L11568。
variants 的完整规则
以下是指南明确定义的规则,发布时逐条核对:
- 声明者是完整、普通的条目。它保留自己的
files/overrides,在任何主机上、被任何旧版 LocalAI 都可独立安装——旧版本解析到variants字段时直接忽略即可。 - variant 按
name引用另一个条目。被引用的条目必须真实存在,且它自己不能再声明variants。 - 被引用的条目默认保留自己的 gallery 行。只有在折叠列表(
collapse_variants=true,WebUI 默认请求该参数)中它才被隐藏、由声明者代表展示。折叠状态下搜索仍能命中被引用条目,并返回声明它的条目——因此引用一个条目永远不会让它"搜不到";关闭折叠后它回归自己的名字。 - 顺序无意义。不要试图用列表顺序表达偏好,按可读性排列即可。
- variant 可以比声明者更小。为小内存主机提供降级档(如大模型提供更小量化)是正常形态;声明者自己的构建照常参与竞争,大主机自然选中大构建。
- 不要手动描述硬件适配。安装时 LocalAI 先剔除"后端在当前主机跑不起来"的 variant,再剔除"装不进可用内存"的 variant;声明者自身的构建对两个过滤器都豁免,因此选择总能终止于某个可安装项。权重体积在安装时从模型实测并缓存,条目里无需书写。
- 引擎偏好优先于体积。在通过上述过滤的候选中,先按主机偏好的引擎取胜,引擎相同时才比"更大占用 = 更高质量的量化"。例如:NVIDIA 主机上 vLLM 构建胜过更大的 llama.cpp 构建;Apple silicon 上 MLX 构建胜过更大的 GGUF 构建;对两种引擎都没有偏好的主机则大构建胜出。各能力维度下的引擎顺序表位于
engineNamePreferenceRules(pkg/system/capabilities.go);一个后端如何进入该表见 adding-backends.md。 - 服务特性偏好排在引擎与体积之间。在引擎同等偏好的构建中,能"每步投机/预测多个 token"的构建胜过同权重的普通构建(DFlash 配对胜过 MTP,二者都胜过普通构建)。该顺序位于
servingFeaturePreferenceTokens(pkg/system/capabilities.go),并且只按条目声明的tags匹配、绝不看其他字段(不看条目名、不看overrides.options),详见下节 dflash / mtp 标签规则。引擎刻意排在它前面:服务特性只是让"正确的引擎"更快,并不会把"错误的引擎"变正确。适配性(体积过滤)依旧排在两者之前——严格大于普通构建的 drafter 配对,在小主机上会先于这套排序被剔除。 - variant 只是一个名字,没有 per-variant 内存字段。当某构建的实测体积错误时,应去被引用的条目上修正它自己的
size:(例如size: "20GiB")。估算逻辑优先采纳声明值而非自己的猜测,因此修正会作用于所有展示与比较体积的地方,而不只影响 variant 选择。
安装时的选择覆盖权
自动选择不是不可推翻的。用户可以在以下入口显式指定想要的构建:
POST /models/apply携带variant参数;- CLI
local-ai models install --variant <model-name>——--variant的帮助文本明确说明:安装声明了 variants 的条目时用 variant 的模型名指定具体构建,留空则由 LocalAI 自动选择(先剔除跑不了/装不下的,再按主机偏好引擎,最后按体积),见 core/cli/models.go#L44; install_modelMCP 工具。
variants的选择、折叠与描述逻辑对应仓库 core/gallery 下的resolve_variant.go、collapse_variants.go、describe_variants.go与各自的*_test.go;WebUI 侧的折叠请求可参见 core/http/routes/ui_api.go 与 React 前端 core/http/react-ui/src/pages/Models.jsx。折叠与按名引用的行为在collapse_variants_test.go、variants_lint_test.go中都有规格化测试——因此指南要求:只要加了variants列表,就必须跑core/gallery的测试套件(可执行go test ./core/gallery/...)来验证 lint 规则与选择行为。
引擎偏好表与体积排序的实现依据
上面的规则并非黑盒——pkg/system/capabilities.go 中保留了极其详细的注释来解释三张偏好表,以及为什么它们"讲三种不同的语言":
backendBuildTagPreferenceRules(构建标签:cuda/rocm/metal…)匹配的是已安装后端的构建目录名,服务于后端别名解析;engineNamePreferenceRules(引擎名:vllm/llama-cpp/mlx…)以子串匹配条目backend:值,服务于core/gallery/resolve_variant.go的 variant 自动选择。子串匹配是有意为之且"承重"的:vllm同时覆盖vllm-omni,mlx覆盖mlx-vlm/mlx-audio,llama-cpp覆盖ik-llama-cpp。真实的引擎顺序为:NVIDIA/AMD/Intel 上vLLM → sglang → llama-cpp;metal(Apple silicon)上MLX → llama-cpp(vLLM/sglang 无 metal 构建故不列出);纯 Vulkan 与无加速器的 "default" 上llama-cpp居首(GPU 服务引擎排在后面但仍可被点名安装);Intel Mac(darwinX86)上llama-cpp → vLLM → sglang且 MLX 故意不列出(MLX 需要 Apple silicon)。servingFeaturePreferenceTokens(服务特性:dflash、mtp)只与条目的tags做整词、不区分大小写的匹配。
注释还解释了"为什么引擎优先于体积、为什么服务特性不能上探到选项层"等关键设计决策,是阅读本主题时最有价值的第一手资料。
体积与特性如何参与排序
在 pkg/system/capabilities.go 的注释中,作者进一步点破了排序决策的优先级:安装时先过滤(后端不兼容 → 剔除;装不下 → 剔除),再排序(同引擎偏好下,服务特性高者先;引擎表在特性表之前,且两者都在体积之前决定胜负)。这也解释了一个看似反直觉的结论——"预测下一个 token 的构建为何能胜过体积更大的普通构建":因为特性表排序发生在"已通过适配过滤的候选集"内部,能进入排序的构建都已经能装进当前主机。
dflash/mtp标签规则
只有当条目确实配置了该特性时,才给它打dflash或mtp标签。variant 排序只读标签、不读任何其他字段。
判断依据是该条目实际配置了什么,使用其后端自己的词汇表:
| 后端 | 配置了该特性,当它声明…… |
|---|---|
llama-cpp | overrides.options含spec_type:draft-dflash或spec_type:draft-mtp |
ds4 | overrides.options含mtp_path:/mtp_draft: |
sglang | 引用的gallery/*.yaml设置了speculative_algorithm: |
该检查仅在策展(curation)时执行。spec_type是 llama.cpp 的配置词汇,跨后端的排序决策绝不能依赖某一个后端的选项写法——这正是排序器读标签而非读 options 的根本原因(此设计决策在 pkg/system/capabilities.go 的注释中被原样记录下来)。
规则要防的两个典型错误:
- 带头的权重 ≠ 启用特性的条目。NVFP4 GGUF 条目携带了 MTP 权重,但只设置了
use_jinja:true,并未启用任何投机解码,因此绝不能打标签——给它们打标签会在毫不更快的情况下赢得特性轴(源码注释即以qwen3.6-27b-nvfp4-mtp这类条目为例)。 - 名字不是声明。名字拼作
-mtp却什么都没配置的条目不该有标签;真正配置了特性的条目即使名字毫无提示(注释中举的hy3、glm-5.2例子)也要打标签。排序从不读名字,因此一个确实启用特性却没打标签的构建只会按普通构建参与排名。
对照真实条目验证上述词汇表:GLM-5.3 的 llama.cpp 条目在overrides.options中声明了spec_type:draft-mtp及spec_n_max:6、spec_p_min:0.75等投机解码参数,见 gallery/index.yaml#L44-L48;sglang 侧的 MTP 声明则集中在模板里,例如 sglang-gemma-4-e2b-mtp.yaml 设置了speculative_algorithm: NEXTN、speculative_num_steps: 5等 SGLang 参数(注释还说明了NEXTN会被 SGLang 归一化为 EAGLE)。
提交前自查清单
指南以一张清单收尾,逐条核验后再合入:
- 找到 GGUF 文件——记下精确文件名(区分大小写);
- 用
curl -sI+x-linked-etag方法取得 SHA256; - 根据模型架构从
gallery/选择正确的模板配置(gemma / chatml / deepseek / virtual…); - 把条目加入
gallery/index.yaml,放在相近模型附近; - embedding 模型必须设置
embeddings: true; - 同时给出两个 URL——原始模型页与 GGUF 仓库页;
- 撰写 description——说明模型规模、能力与量化类型。
如果条目携带variants列表,务必补上最后一项:运行 core/gallery 的测试套件,让 variants 的 lint 规格与选择逻辑测试在本地通过(参见variants_lint_test.go、collapse_variants_test.go、resolve_variant_test.go),因为这套测试正是把上文所有规则固化为可执行断言的地方。
【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考