资讯动态

llama.cpp 新模型架构移植实战指南:add-new-model 工作流、常见陷阱与验证清单

发布时间:2026/9/7 3:59:33 来源:尧图企业网站定制
llama.cpp 新模型架构移植实战指南add-new-model 工作流、常见陷阱与验证清单【免费下载链接】llama.cppLLM inference in C/C项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp本文基于 llama.cpp 仓库内置的 add-new-model 技能文档 展开系统讲解向 llama.cpp 添加一个全新模型架构的完整流程从范围确认与去重检查、GGUF 转换、C 架构定义、GGML 计算图构建到可选的多模态编码器与 chat 模板支持并结合 HOWTO-add-model.md、AGENTS.md 与仓库源码覆盖每个步骤的真实触点touch point、历史 PR 评审中反复出现的陷阱以及提交 PR 前的验证清单。读完本文你可以独立完成一个新架构的移植并知道哪些做法会直接导致 PR 被拒。工作流总览与设计决策归属add-new-model 技能文档定位为一条引导式工作流guided workflow当贡献者想把某个新模型架构移植进 llama.cpp 时按步骤推进。项目明确允许使用 AI 生成代码因此 AI 可以替你写出完整实现而不只是指向模式示例——但必须全程遵循 AGENTS.md 的 AI 使用政策其核心要点包括贡献者对每一行代码负全责无论它如何产生。必须能向评审者解释并捍卫其中任何部分。AI 协作时要不断与贡献者确认不能默默生成完整个 diff 再交出去写代码前贡献者必须已经拥有这个架构的设计决策参考哪个既有实现、非标准部分如 RoPE 变体或 MoE 路由如何处理。AI 加速的是贡献者已做出的设计而不是替他们做设计披露是强制的任何 AI 有意义的贡献都必须按 PR 模板披露开 PR 前需提醒贡献者绝不代写PR 描述、commit message、GitHub issue/discussion 帖子或给评审者的回复如被要求代为提交commit 中用Assisted-by:绝不能用Co-authored-by:且仅在明确确认后如果改动看起来很大或引入了文档未覆盖的新模式暂停告知贡献者这类改动大概率需要先与维护者讨论保持 PR 自包含如果需要在新模型文件之外做大量非常规修改如触碰共享的 graph 构建代码、sampler 或核心 API停下来让贡献者先开 discussion/issue——侵入式或过度的改动会被直接关闭不要把无关工作打包进同一个 PR多模态和 chat 模板工作分别见 Step 4 / Step 5绝不用自定义 sin/cos 实现绕开 RoPE。历史上多个这样做的 PR 都被关闭了。如果现有的ggml_rope_ext见 Step 2 的 RoPE 技巧真的无法表达该模型的需求正确做法是先开 issue 与维护者讨论而不是提交带自定义 RoPE 实现的 PR。开始动手前技能文档要求先阅读 CONTRIBUTING.md、AGENTS.md 和 HOWTO-add-model.md如果还不在上下文中并运行git log --oneline -- src/models查看至少 3 个最近添加模型的 PR合并 commit/diff——这比文档更可靠地反映当前惯例因为文档可能落后于代码。从源码结构看src/models/ 目录下已有 150 余个模型实现文件如 llama.cpp、dbrx.cpp、qwen3.cpp、deepseek4.cpp 等这些就是近期惯例的活样本。Step 0范围确认与去重检查在写任何代码之前先向贡献者确认四件事是哪个模型HF 仓库 id 或名字纯文本还是带多模态视觉/音频编码器本地是否已有该模型的 HFconfig.json与权重是否查过已有的 PR/issue建议在ggml-org/llama.cpp仓库执行gh search issues model name和gh search prs model name。如果已有 PR 覆盖了它应该去那边评论协作而不是开重复 PR该模型最接近哪个已支持的架构例如Llama 风格带滑窗、类 DBRX 的 MoE、BERT 风格编码器如果贡献者说不清最接近的参考架构可以 grep conversion/ 目录下的*.py和 src/models/ 目录下的*.cpp按 config 形状层数、头数、MoE 专家数、norm 位置找相似的架构给出 1–2 个候选——但最终选择必须由贡献者确认。这是一个设计决策必须由贡献者拥有而不是替你挑。在贡献者回答完上述问题并点明参考架构之前不要进入 Step 1。Step 1把模型转换为 GGUF实际的触点清单在 HOWTO-add-model.md 第 1 节技能文档不重复推导而是要求你读它。简要概括其核心触点在 conversion/ 目录新建TextModel或MmprojModel子类用注册注解声明架构ModelBase.register(MyModelForCausalLM) ModelBase.example(user/model) class MyModel(TextModel): model_arch gguf.MODEL_ARCH.MYMODELexample应指向一个用于测试的有效 HF 模型可以配多个优先选非门控模型否则用极小的随机权重。在 gguf-py/gguf/constants.py 中定义 GGUF 张量布局在MODEL_ARCH加枚举项、在MODEL_ARCH_NAMES加人类可读名称、在MODEL_TENSORS加张量名。注意文档中的强调GGUF arch 字符串要一开始就慎重命名要与src/models/name.cpp的文件名对应因为一旦 GGUF 文件以某个 arch 字符串发布到社区事后改名会破坏所有人的存量文件这不是可以留给后续 PR 清理的事。在 gguf-py/gguf/tensor_mapping.py 中把原始张量名映射到 GGUF 标准名。重复层块用bid占位符例如transformer.blocks.{bid}.norm_1会映射为blk.{bid}.attn_norm。加新张量名前先确认标准名是否已存在。视模型情况覆写TextModel#set_gguf_parameters、MmprojModel#set_gguf_parameters、ModelBase#set_vocab、ModelBase#modify_tensors。张量名必须以.weight或.bias结尾这是约定quantize等工具依赖它。技能文档的补充要求对每个触点先给贡献者看参考架构中的等价代码再写新版本并确认他理解自己的模型哪里不一样非标准张量形状、额外 hparams而不是默默照抄模式。Step 2在 llama.cpp 中定义架构实际触点在 HOWTO 第 2 节技能文档同样要求直接读原文其中包括其 Tips and tricks 一节关于ggml_rope_ext的坑。核心触点为在 src/llama-arch.h 中定义新的llm_arch枚举值在 src/llama-arch.cpp 中把架构名加入LLM_ARCH_NAMES映射可能需要同步更新LLM_KV_NAMES、LLM_TENSOR_NAMES、LLM_TENSOR_INFOS在 src/llama-model-loader.cpp 的llama_model_loader构造函数中加入非标准元数据的加载如果模型有 RoPE在 src/llama-model.cpp 的llama_model_rope_type函数中加该架构的 case检查所有对每个llm_arch值做 switch/迭代的其它位置例如 src/llama-model-saver.cpp 和各类必填 hparams清单如哪些 arch 必须有 MoE 元数据。技巧grepLLM_ARCH_的全部用法。漏掉其中一处是添加新 arch 后 CI 测试如test-llama-archs失败的常见原因。另注意ggml 的维度顺序通常是 PyTorch 维度的逆序。RoPE 技巧结合 ggml/include/ggml.h 源码佐证PyTorch 实现倾向于显式计算freq_cis/sin/cos分量但 llama.cpp 中大多数 RoPE 都能用ggml_rope_ext见 ggml.h 的签名a张量、b位置、c频率张量、n_dims、mode、YaRN 相关参数处理不需要 sin/cos 矩阵——省内存且允许 GGML 的 RoPE 内核与其它算子融合。由于ggml_rope_ext只覆盖模型所用 RoPE 实现的一个子集移植时可能需要一些创造性适配。HOWTO 给出的真实案例包括libmtmd用GGML_ROPE_TYPE_NORMAL顺序把输入张量对半拆开、分别对两半调用ggml_rope_ext、再用ggml_concat拼回实现 2D RoPE部分模型需要缩放输入位置[0,1,2,...]变[0,0.5,1,...]可以通过freq_scale 0.5f提供学习式 RoPE 频率不依赖powf(freq_base, -2*i/n_dims)可以通过rope_freqs张量提供对应ggml_rope_ext的c参数并设freq_base 1.0f。注意 GGML 中rope_freqs存的是倒数theta pos[i] / rope_freqs可能需要在转换时求逆只旋转 head 的一部分nope 部分不要用 view ggml_concat硬拼两种布局都能用单个 RoPE 算子完成[rope|nope]布局传一个小于 head 尺寸的n_dims[nope|rope]布局对 RoPE 结果调用ggml_rope_set_offset(cur, n_offs)。仓库中的 src/models/deepseek4.cpp 正是后者的活例子——它对 query、key 和压缩 KV 张量整块做 RoPE 后调用ggml_rope_set_offset(cur, n_embd_head_nope)可看到该文件多处调用。n_offs必须为偶数n_offs n_dims必须容纳在行内且不支持 vision RoPE。例外如果 nope 部分还要叠加额外算子例如 deepseek32.cpp这类模型仍需要 view ggml_concat。技能文档在这一步再次强调绝不用自定义 sin/cos 绕开 RoPE。Step 3构建 GGML 计算图这是最有趣的部分。按 HOWTO 第 3 节新建一个继承自llama_model_base的结构体在其build_arch_graph方法中实现建图逻辑build_arch_graph返回基于llm_graph_context构建的 graph可参考 llama_model_llama、llama_model_dbrx、llama_model_bert 等现有实现在llama_model_mapping函数中为架构加 case实例化你的建图结构体。一些 ggml 后端不支持所有算子后端实现可以在单独的 PR中补充。调试推理图可以用 examples/eval-callback。技能文档的补充要求在写src/models/name.cpp之前至少通读 10 个src/models/下的其它文件混搭着读不要只读你点名的那一个参考架构确认你要写的结构体布局、命名与风格确实匹配当前惯例——模式会随时间漂移HOWTO 文档也可能落后于它。Step 4可选多模态编码器只有当贡献者在 Step 0 中声明了视觉/音频编码器才做这一步。实际触点在 HOWTO 第 4 节和 docs/multimodal.md在转换脚本中确认子类继承MmprojModel或同基类在clip.cpp中加编码器定义在mtmd.cpp中实现 preprocessor多数情况可复用现有的在 tools/mtmd/ 下实现编码器 GGML 图——如果模型确实与现有实现差异大就开独立文件否则复用现有实现如 siglip、pixtral、qwen再加模型专属 projector。技能文档在此处有一条必须认真读的规则多模态编码器能否与基础文本模型支持打包在同一个 PR 里取决于改动是否常规。如果编码器支持是常规的——即不需要任何新基础设施或新逻辑只是复用现有预处理/projector 机制的一个新 cgraph例如 siglip/pixtral/qwen 加一个新 projector——可以打包。但凡超出这个范围——需要新 preprocessor、非标准 projector 逻辑、或改动共享的libmtmd基础设施/逻辑——停下告诉贡献者这非常规让他先把文本模型落地编码器作为专门的后续 PR。这个决定不能悄悄放过在写任何clip.cpp/mtmd.cpp代码之前必须显式向贡献者点明。Step 5可选chat 模板 / 解析支持只有当模型需要新的内置 chat 模板src/llama-chat.cpp或新的输出解析器见 docs/development/parsing.md 和 docs/autoparser.md时才做。如果超出用户自带 Jinja 模板已能覆盖的范围就把这部分当作独立的后续 PR处理而不是基础模型支持 PR 的一部分——要显式向贡献者点明不能悄悄打包进去。常见陷阱来自历史 PR 评审技能文档归纳了一批在过往 add-model PR 的评审意见中反复出现的陷阱建议主动检查而不是等评审者来抓不要在 Python 转换脚本和 C 加载路径里校验同一个 hparam/config 假设——选定一层负责检查重复只是增加维护面真正在部分 config 中缺失的可选 hparams例如共享专家数应该用显式的可选/回退访问器读取而不是假定存在真正承重的 hparams缺失会导致模型输出错误或崩溃例如sliding_window_pattern、norm-eps必须缺失时硬报错不能静默回退到默认值不要把默认 chat 模板烧进 C 二进制——应该在转换时注入 GGUF因为一个llm_arch可能被多个不同模板的微调版复用烧进 C 的默认值对那些情况会静默失效在写专门的 tool-call/输出解析器之前先确认现有 autoparser 是否已经能处理该模板——test-chat-auto-parser jinja可以显示它检测到什么对应 tests/test-chat-auto-parser.cpp在转换时把自定义 EOS/闭合标签 token 标记为eot并不总是够——在长/agentic 生成中模型可能把闭合序列当作字面文本发出而不是 token导致生成永不终止于 EOG、原始文本泄漏到解析器之外。要验证这个字面文本路径而不是只验证 token 路径如果为了省事复用或别名了一个已有的分词器必须显式说明理由并测试该选择——静默复用是微妙 tokenizer bug 的常见来源注意层循环内部构建 per-layer view/index 张量导致的过度图分裂——把不随层变化的张量提升到循环外当你撞见GGML_SCHED_MAX_SPLIT_INPUTS时尤其相关传入 flash attention 的自定义 KQ mask必须匹配 FA 期望的 dtype——启用 FA 时先转成 F16 再传给build_attn_mha自定义 KV-cache 尺寸按对齐填充如GGML_PAD(..., 256)时填充要放在所有其它尺寸调整之后而不是之前——否则后续逻辑可能把它重新弄成非对齐对非标准的 cache/SWA滑窗注意力语义覆写专用 hook例如llama_model_n_swa()而不是篡改 hparams 来伪造行为——hparams 可能在其它地方被读取用于无关目的不要在基础模型 PR 里交付未完成/未验证的投机解码如 MTP脚手架——如果还没确认真正能工作就抽出来作为独立后续 PR转换代码应该调用基类现有的 hparams 逻辑例如super().set_gguf_parameters()而不是重新推导——大段复制TextModel/MmprojModel已提供内容的代码会被标记为冗余常量张量修改如norm(1 weight)和 permute/chunk 应该在转换时做而不是在图里做。HOWTO 的 Prefer conversion-time tensor modifications 一节给了实例Gemma 3 把norm(1 weight)中的1 折叠进转换时的权重图里只做普通 RMS normQwen3-Next 在modify_tensors中完成张量 permute。运行时在图里做这些大概率会以过度复杂被拒如果确实无法在转换时做先开 discussion 说明原因而不是直接实现进图里。例外常数 scale 的weight * scale通常更适合在推理时施加而不是在转换时折进权重。scale 在概念上作用于激活而非权重折进去可能损害数值稳定性并改变权重的取值范围从而让量化更差。此时应把 scale 作为独立元数据键写进 GGUF例如%s.attention.output_scale、%s.attention.value_scale、%s.embedding_scale在图中应用。配套的 code-review 技能 中New model / architecture一节还补充了几条评审时最常抓的约定不要在model.arch上分支当真实依赖是某个 config/能力值时应该基于 hparam/能力开关如果新模型只是现有架构的近似变体优先复用/子类化现有 arch 而不是复制近重复类会被要求合并新张量名走tensor_mapping.py而不是临时名称匹配QKV 应该用ggml_view切激活而不是切权重张量新图输入声明在图构建函数顶部而不是首次使用处测试量化 KV 路径-ctk/-ctv q8_0不要只测默认 f16。验证清单完整工具链参考 examples/model-conversion/README.md其中提供了一整套 Makefile 目标causal-convert-model、causal-verify-logits、causal-quantize-Q8_0、perplexity-run等支持通过MODEL_PATH环境变量或命令行参数传入模型路径。技能文档给出的七步清单转换为 GGUF然后检查/运行原始与转换后的张量运行 logits 校验原始 vs 转换后。如果该模型是某个已支持家族的新版本先验证上一个版本仍能通过 logits 校验——数值差异可能是本来就存在的而不是新工作引入的。完整的 logits 校验工具在 examples/model-conversion 中量化相关的 QAT 变体也要量化并重新验证。该 README 还提醒量化到Q4_0时 embedding 默认数据型为Q6_K上传到 ggml-org 时建议改用Q8_0——Q6_K更小但解包计算量更大输出 logits 需要解包整个 embedding 矩阵Q8_0质量几乎无损且计算效率更好运行困惑度评估simple 和 full 两种full 版先生成.kld数据集再评估在tools/cli、tools/completion、tools/imatrix、tools/quantize、tools/server上做一致性抽查先做 CPU 后端其它后端CUDA、Metal 等按 CONTRIBUTING.md 可以作为独立后续 PR对照 AGENTS.md以及 CONTRIBUTING.md 的 Coding guidelines/Naming guidelines 章节对每个改动文件做编码/命名规范的复查——这是与功能测试分开的独立一轮同样重要不强行折行、不用 Unicode 标点、注释最少化、snake_case命名文件名kebab-case、缩进/花括号风格与周边一致。开 PR 之前先对自己的 diff 跑一遍 code-review 技能skills/code-review/SKILL.md——它覆盖的正是评审者最常标记的约定与范围问题且推荐在 push 前本地完成注意其Scope and quick-reject gate一节列出的模式重复 PR、范围混杂、一次改多个 ggml 后端、新增量化类型缺少完整论证包、侵入式改动等是 PR 会被直接关闭的典型原因确认贡献者能向评审者解释每一行改动的变更并准备好被追问任何问题——无论代码有多少是 AI 生成的这都是硬性要求确认他做了完整 diff 的全面人工审查而不是扫一眼在 PR 模板 中如实填写 AI 披露部分不得省略或轻描淡写不要代写 PR 描述、commit message、issue/discussion 文本或任何评审回复——这些必须由贡献者本人完成。小结向 llama.cpp 添加新模型架构可以归纳为一条先收敛、再落地、后验证的主线Step 0 用去重检查和参考架构选择把设计决策收敛到贡献者手中Step 1–3 依次完成 Python 转换层conversion/ gguf-py、C 架构注册层src/llama-arch.h、src/llama-model-loader.cpp、src/llama-model.cpp与推理图实现层src/models/Step 4–5 把多模态编码器与 chat 模板/解析这类边界工作按常规与否拆分出去最后用 logits 校验、量化、困惑度和五大工具的一致性抽查闭环验证。贯穿全程的三条红线是不绕开ggml_rope_ext自造 RoPE、不把非常规改动混入基础 PR、不代写任何面向评审者的文字。【免费下载链接】llama.cppLLM inference in C/C项目地址: https://gitcode.com/GitHub_Trending/ll/llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价