关于 import-model
指导开发者将 Hugging Face 模型移植到 MAX ModuleV3 的工作流,涵盖检查配置与权重、选择参考架构、实现差异,并在服务模型前验证输出。
主要功能
- 01检查 Hugging Face 配置和权重
- 02识别原生架构及移植障碍
- 03比较模型并选择参考架构
- 04生成 ModuleV3 移植骨架
- 05实现架构差异并验证输出
适用场景
- 01评估模型是否需要移植到 MAX
- 02基于参考架构规划 ModuleV3 实现
- 03对照 Hugging Face 输出验证移植结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ modular/ skills --skill import-model安装后在 Claude Code 中输入/import-model确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.gitattributes.gitignorepixi.tomlREADME.mdreferences/distributed-transformer.mddivergences.mdimplement-graph.mdlayer-by-layer-debugging.mdmap-to-max.mdmax-vs-port-isolation.mdnative-arch-check.mdpitfalls-config.mdpitfalls-graph.mdpitfalls-serving.mdpitfalls-weights.mdpitfalls.mdplan-and-veto.mdread-config-json.mdread-modeling-code.mdrecognize-walls.mdrename-weights.mdrewrite-donor-docstrings.mdserve-and-iterate.mdstate-dict-audit.mdtemplate.mdtests-and-ci.mdvalidation-tiers.mdscripts/__init__.pycheck_port.pycheck_walls.py详细介绍
将模型导入 MAX
输入: Hugging Face 模型 ID($ARGUMENTS)。
将 references/template.md 复制一份,在各个阶段推进时跟踪此移植工作。其中的文件引用是相对于此技能根目录的路径,因此复制到其他位置后仍然有效。
将模型移植到 MAX,意味着编写一个 ModuleV3 模型,使其执行与 Hugging Face transformers 中的 modeling_<type>.py 相同的计算。然后,使用发布的权重编译模型,并验证输出是否匹配。ModuleV3 是 MAX 的模型 API。层是 max.experimental.nn 中 Module 的子类,包含对 Tensor 值进行处理的 forward()。算子来自 max.experimental.functional(导入为 F)。流水线会在 F.lazy() 下构建模块,并使用 compile(weights=...) 编译。单 GPU、张量并行和专家并行模型都使用 ModuleV3。多 GPU 模型通过 DeviceMesh 分片。
工作流分为决定与规划、实现和验证三个阶段。第 1 阶段是阅读和规划。第 2 阶段是移植:在模块中实现每个存在差异的子层。第 3 阶段是验证,仅在实现完成后进行。守卫(会中止工作流的前置条件)用于控制活动之间的转换。它们本身不是步骤。
反模式: 运行 scaffold.py,调整 arch.py,并启动服务,而 <slug>.py 仍实现供体模型(llama3_modulev3、olmo3 等)。由于 <slug>.py 仍运行供体架构,logit 验证会在该移植上失败。只有在满足 implement-graph.md 的完成标准后,才能运行验证脚本。
每个阶段都链接到包含详细信息的参考文档。只阅读当前活动对应的参考文档,不要一开始就全部阅读。
环境: 通过已安装 MAX 的 pixi 环境运行所有命令(pixi run python …、pixi run max serve …)。请在包含 pixi.toml 的技能根目录中运行命令,不要使用 shell PATH 中的裸 python 或 max:
cd <path-to-skill>
pixi install
pixi run python scripts/inspect_hf.py <HF_MODEL_ID>
# Or: pixi run test-scripts # smoke-test all scripts (no GPU)
默认环境可安装在 Linux(x86-64 和 ARM64)及搭载 Apple 芯片的 macOS 上,无需 CUDA。其 PyTorch 会在 CPU 或 Apple 芯片 GPU 上运行 HF 参考模型。在 NVIDIA 主机上,pixi run -e cuda … 会使用 CUDA PyTorch 运行相同的命令,因此 HF 参考模型也会在 GPU 上运行。
辅助脚本位于此技能的 scripts/ 目录中(请将它们复制或 vendoring 到你的仓库中)。所有辅助脚本也可通过统一分发器调用,其参数名称和退出代码相同:
pixi run python scripts/import_model.py inspect <HF_MODEL_ID>
pixi run python scripts/import_model.py list-archs --donors
pixi run python scripts/import_model.py scaffold <HF_MODEL_ID> --start-from olmo3 --output-dir ./
pixi run python scripts/import_model.py list-archs --match LlamaForCausalLM
pixi run python scripts/import_model.py check-walls <HF_MODEL_ID>
pixi run python scripts/import_model.py list-keys <HF_MODEL_ID> --summary
pixi run python scripts/import_model.py gates <HF_MODEL_ID> --port-dir <port_dir>
pixi run python scripts/import_model.py compare <HF_MODEL_ID> --port 8000
移植目录布局:
<port_dir>:包含arch.py和__init__.py中ARCHITECTURES的 slug 文件夹(通常为<output_dir>/<slug>,末尾不带斜杠)。将此路径传给--custom-architectures和run_oss_gates.py --port-dir。
MAX 会通过将 dirname(<port_dir>) 添加到 sys.path,并将 basename(<port_dir>) 作为模块导入,来解析 --custom-architectures <port_dir>。传入父目录会导入错误的模块名(例如,你的 slug 所在目录为 custom-arch)。
如果编辑时遇到导入或 API 错误,请从已安装 MAX 软件包中 max/pipelines/architectures/<donor>/ 下的供体架构复制导入语句。请参阅
pitfalls-config.md § Import and config API traps。
阶段 1:决定与规划
守卫:该架构是否已在 MAX 中注册? 编写任何代码之前,请检查 MAX 是否已注册模型
config.json::architectures[0]中的架构类。如果pixi run python scripts/list_native_archs.py --match <Class>返回一个 slug,请运行pixi run max serve --model-path <HF_MODEL_ID>并停止。无需移植。完整流程请参阅: native-arch-check.md。
读取 config.json
获取配置并读取每个字段:
pixi run python -c "from transformers import AutoConfig; \
print(AutoConfig.from_pretrained('<HF_MODEL_ID>', trust_remote_code=True))"
也可以使用辅助脚本:它会从 Hub 获取原始的 config.json,运行原生架构检查,并打印映射到 MAX API 的每个键:
pixi run python scripts/inspect_hf.py <HF_MODEL_ID>
然后列出 safetensors 元数据(键、形状和数据类型,不下载权重):
pixi run python scripts/list_checkpoint_keys.py <HF_MODEL_ID> --summary
摘要会统计张量和数据类型的数量。在编写权重适配器前,再运行一次,不带 --summary,以列出键名。
inspect_hf.py 表格中的每一行都会将一个 config.json 键映射到
pipeline_config.model.huggingface_config(architectures 和 torch_dtype 则映射到 arch.py 中的 SupportedArchitecture)。选定一个供体后,使用 --start-from <donor_slug> 重新运行:它会标出供体配置来源中从未提及的每个键。这些键就是你需要实现的配置差异,包括配置字段及使用该字段的模块代码;但如果没有模型代码读取它们(bos_token_id、initializer_range),则无需实现。字段含义和常见差异见:read-config-json.md。
在确定移植方案前,先检查是否存在阻碍:
pixi run python scripts/check_walls.py <HF_MODEL_ID>
退出码为 0 → 继续。退出码为 1 → 查阅 recognize-walls.md。退出码为 2 → 先停止,直到解决阻碍或将其排除在范围之外。
阅读模型卡
打开 https://huggingface.co/<HF_MODEL_ID> 并阅读模型卡,重点关注:
- 论文或博客文章。 浏览其中的架构部分。作者会在该部分列出他们的修改(QK-norm、MLA、滑动窗口注意力、MoE 路由)。
- 模型卡中提到的“技巧”。 “我们引入”“不同于以往模型”和“这是首个……”等表述都意味着存在差异。若现在忽略这些内容,实施时就会引入 bug。
如果模型卡表明该模型属于某个已知系列(Llama、Mistral、Qwen、Gemma),下方的供体比较环节会从该系列中最接近且已完成移植的变体开始。
某些模型卡信号需要检查是否存在阻碍:自定义 CUDA 内核、没有公开参考实现的自定义注意力、仅发布 FP8/FP4 权重、ALiBi,以及循环或状态空间层。如果模型卡提及其中任何一项,请先阅读 recognize-walls.md,再继续。有些模型仅凭公开的 MAX 接口无法移植。
提出计划并接受否决
编写任何代码之前,先用一小段文字说明默认方案,然后等待用户确认或否决。根据目前已阅读的内容,说明分发方式、供体、量化变体、验证深度和硬件目标。如果模型需要 ModuleV3 尚不具备的组件(层、分布式原语、量化路径),请指出该组件,并说明你会将其添加到 max.experimental.nn,还是需要用户决定。不要提出宽泛的问题。先说明默认方案,让用户提出异议。
完整指南和示例段落见: plan-and-veto.md。
如果估算的权重字节数无法放入一块 GPU,请先阅读
distributed-transformer.md,再选择 --start-from:分发方式比单独的注意力系列更重要。
与其他 MAX 架构进行比较
你要选择最适合复制的 ModuleV3 架构。“最适合”是指在以下方面匹配:
- 注意力形态(dense、GQA 或 MLA)
- MLP 形态(门控或非门控、dense 或路由)
- 头部布局(共享或不共享、单个 Linear 或多步)
- 分布形态
列出已安装的 MAX 中包含的 ModuleV3 架构(不要硬编码 slug 列表):
pixi run python scripts/list_native_archs.py --donors
HF 信号到 donor slug 的启发式提示见 map-to-max.md。简要版本:
| 你的模型 | 从以下架构开始 |
|---|---|
| 类似 Llama 3(GQA、RoPE、SwiGLU MLP) | llama3_modulev3 |
| QK-norm、混合滑动窗口和全注意力 | olmo3 |
类似 Gemma(1 + weight RMSNorm、双重 norm) | gemma3_modulev3 |
类似 Phi(融合 qkv_proj 和 gate_up_proj) | phi3_modulev3 |
| 类似 Granite(MuP 标量) | granite_modulev3 |
| MoE(稀疏专家、top-k 路由) | gpt_oss_modulev3 |
| MLA(潜在 KV)、单 GPU | deepseekV2_modulev3 |
| MLA + MoE、多 GPU(TP、DP + EP) | deepseekV3_modulev3 |
| 混合注意力和状态空间层 | nemotron_h_modulev3 |
视觉语言(使用 --full-copy 搭建脚手架) | gemma3multimodal_modulev3、kimik2_5_modulev3 |
打开所选架构的目录,阅读其模型文件(即 model.py 在 _instantiate_module() 中实例化的模块,例如
olmo3/olmo3.py 或 llama3_modulev3/llama3.py)及其 layers/。你要回答一个问题:移植时哪些模块会改变,哪些模块保持不变?
现在阅读对应的 Hugging Face 建模文件:
pixi run python -c "from transformers.models.<model_type> import modeling_<model_type>; \
print(modeling_<model_type>.__file__)"
阅读 __init__、注意力 forward、MLP forward、block 类和最终 head。将它们与 MAX 中的对应实现逐一比较。参考
read-modeling-code.md 了解各部分需要检查的内容。在开始服务之前,立即对照 divergences.md 中的症状表检查 RoPE 布局、norm 公式和激活函数:名为 rotate_half 的函数仍可能旋转相邻元素对(ERNIE 4.5),而 donor 的默认实现读取的是错误的元素对。
此项活动的输出:一份差异清单,每个 HF 与 donor MAX 架构之间的实际差异占一行(注意力、MLP/MoE、block 连接、head、RoPE、掩码)。你要在第 2 阶段实现其中的每一项。结构性差异不超过 3 项 → donor 选择不错。差异很多 → 换一个更接近的 donor,或计划重写整个模块。不要带着空的或“看起来像 Llama”的差异清单进入验证阶段。
第 2 阶段:实现
搭建文件结构
scaffold.py 只会写入骨架,不会实现你的模型。
pixi run python scripts/scaffold.py <HF_MODEL_ID> --start-from <donor_slug> --output-dir <output_dir>
该脚本会从 Hub 的 config.json 中读取 architectures[0],以供
arch.py::name 使用,然后将继承所选 ModuleV3 架构的骨架写入
<output_dir>/<slug>/。slug 默认为仓库名称,包含模型规模后缀
(ernie_4_5_0_3b_pt)。传入 --slug,可使用架构系列名称作为包名。生成的类为
<Short>Model 和 <Short>Config(Ernie4_5Model),名称可能与 Hugging Face 自带的类重复,因此并排查看时请注明它们各自所属的类。
arch.py:注册框架,逐字保留 donor 设置的所有SupportedArchitecture关键字(memory_planner=、batching=、reasoning_parser=等),包括已配置的规划器,例如PagedMemoryPlanner.with_activation_reservation(...)。脚手架会写入name=、仓库 ID、编码、权重格式,以及该移植版本自己的类和适配器(请核对name=、编码和memory_planner=)。 如果 donor 未声明规划器,则省略该字段并附上 TODO。脚手架无法追溯到导入来源的值会标记为 TODO 并发出警告。model_config.py:donor 配置子类,带有from_donor()钩子,用于处理 donor 不读取的字段(实现期间必须重新接线)model.py:pipeline 模型框架。_create_model_config()会将 donor 的配置重新指定为你的配置类型,而_instantiate_module()会构建你的根模块,并按照 donor 的放置方式将其放到设备或网格上。它继承 donor 的批处理器,因此无需batch_processor.py也能继续处理输入。weight_adapters.py:委托给 donor 的重命名逻辑(必须针对你的检查点重写)<slug>.py:donor 模块(实现期间必须编辑,使其与 HF 一致)
搭建脚手架后,<slug>.py 仍会执行 donor 的数学运算。在模块实现完成前,不要提供服务。
脚手架会使用 ruff format 格式化移植代码,并将 donor 中对 huggingface_config.rope_theta 的读取改写为 get_rope_theta(huggingface_config);该函数可读取 Transformers 4 和 Transformers 5 中的值。使用此技能早期版本搭建脚手架的移植代码,仍可能在 model.py 中读取 huggingface_config.rope_theta,并导致 check_port.py 报错:AttributeError: ... has no attribute 'rope_theta'。请重新搭建脚手架,或手动进行相同修改,并从 max.pipelines.lib.pipeline_variants.utils 导入 get_rope_theta。
复制 donor 代码时,donor 的文档字符串和代码注释会一并保留。 默认情况下,脚手架会为生成的文件写入新的文档字符串。--full-copy 模式会保留 donor 的文本,手动复制或通过子类化再编辑的任何 donor 文件也一样。重命名类不会修改那些描述文件声称用途的文本。这些文件开头的文档字符串仍会描述 donor,并声称具备你的移植版本可能并不具备的行为(单 GPU 支持、QK 归一化、注意力后归一化等)。重写这些文档字符串是实现过程中的必做步骤。请参阅
rewrite-donor-docstrings.md,了解每个模块文档字符串必须遵循的模式。其中还包含你必须在宣布实现完成前运行的审查清单。
实现模块
第 1 阶段生成了配置映射和差异列表。实现阶段会将它们转化为代码。
完整清单、工作顺序、ModuleV3 API 范围、反模式和完成标准: implement-graph.md。
按以下顺序:
model_config.py:供体的配置会读取它已知的键。对于 Phase 1 /inspect_hf.py中每个其他config.json键,都要在移植版配置中声明一个字段,并在from_donor()中设置该字段;model.py::_create_model_config()会使用供体的完整配置调用它。确认construct_kv_params()中的头数和 head_dim 与 HF 一致。流水线会根据你的配置中的construct_kv_params()为 KV 缓存分配空间,但from_donor()会从供体配置复制kv_params,而模块读取的是这份副本。如果你覆盖了construct_kv_params()、get_head_dim()或get_num_layers(),也要在from_donor()中根据你的覆盖实现设置kv_params,确保缓存与模块一致。weight_adapters.py:将检查点的 safetensor 键映射到根模块通过parameters返回的参数名称,包括根模块的包装器前缀(olmo3中为language_model.)。先运行list_checkpoint_keys.py。参见 rename-weights.md。接入 state-dict-audit.md 中的覆盖率审计:compile()会拒绝缺失或形状不匹配的参数,审计则会捕获未被任何参数使用的检查点张量。<slug>.py:针对差异列表中的每一项,编辑或替换供体模块,使 MAXforward()与 HFforward()保持一致:- Attention(Q/K/V、RoPE、mask、GQA、softcap,……)
- MLP 或 MoE(激活函数、路由、共享专家,……)
- Decoder block(归一化顺序和残差连接,不能与 Llama 互换)
- 最终归一化和 LM head(权重绑定、logit 缩放、softcap)
arch.py:确认name=与architectures[0]匹配,且default_encoding与 Hub 的torch_dtype匹配。保留脚手架生成的memory_planner=(从供体复制而来)。没有它,MAX 不会为 KV 缓存预留激活内存,也会跳过max_batch_size推断。如果脚手架留下了memory_plannerTODO,说明供体没有设置它。上线服务前解决此问题:KV 缓存移植版需要设置memory_planner=PagedMemoryPlanner,只有自行进行内存估算的架构(diffusion、embedding)才应保持未设置。脚手架还会保留供体的batching=。只有移植版的批处理方式与供体不同时才修改它。model.py:_instantiate_module()会构造你的根模块并调用.to(self.devices[0]),或者在多 GPU 移植版中于default_device(mesh)内构建模块。仅当 HF 以不同方式包装骨干网络(VL、多模态)时才进一步编辑。
编辑时阅读 HF 的 modeling_<type>.py,不要等验证失败后再读。只有在 HF 与供体一致时才继承供体。差异列表指出不同之处时,重写相应模块。
以下各项全部勾选后,才算实现完成:implement-graph.md 中的每一项,尤其是:
- 每项差异都有对应的代码更改。
- 权重编译时没有孤立键。
- 你已运行 rewrite-donor-docstrings.md 中的脚手架注释审计,并将每项匹配结果分类为 OK、Wrong 或 Stale。
声明实现完成前运行审计。max serve 和 compare_layers.py 不会读取文档字符串,因此后续步骤无法发现审计遗漏的供体声明。
快速 grep 示例(完整分类规则见 rewrite-donor-docstrings.md):
grep -rniE 'qwen|llama|mistral|cohere|gemma|phi|deepseek|olmo|granite|qwen3|mixtral|single-GPU|single GPU|RMSNorm|QK-norm' <port_dir>/
在子类模式的移植版中,大多数匹配项都是供体导入和脚手架生成的血缘信息行。这些内容是准确的。请阅读匹配项中的文档字符串和注释。
实现完成消息必须明确说明已完成审计
(例如:"docstrings 已按 rewrite-donor-docstrings.md 中的模式重写,且 rg 返回 N 处匹配,均为合理的 血缘引用")。没有这项说明,请勿报告实现完成。
预检(Hub 配置 + 架构注册,在首次启动服务前运行):
pixi run python scripts/run_oss_gates.py <HF_MODEL_ID> --port-dir <port_dir>
防护:本地冒烟测试门槛(Phase 3 前必须通过)。
max serve会先编译整个模型,再响应请求,因此参数名称或形状有误时,需要等完整编译完成才能发现。先运行端口检查。它会像max serve一样导入端口,在不编译的情况下,通过F.lazy()构建根模块,并列出适配后的检查点未填充的所有参数、所有形状或 dtype 不匹配项,以及所有没有参数读取的张量:pixi run python scripts/check_port.py <HF_MODEL_ID> --port-dir <port_dir>如果
compile()会失败,该命令会以状态码 1 退出。它会读取真实检查点,因此首次运行时会下载权重。每次检查前都要格式化并检查端口,因为你的编辑会添加脚手架未格式化的代码:pixi run ruff format <port_dir> pixi run ruff check <port_dir>
ruff check不得报告任何错误。run_oss_gates.py会检查墙面、检查点元数据,以及arch.py的名称和编码。它不能替代本检查。权重格式预检请参阅 serve-and-iterate.md。
Phase 3:验证
检查是否能生成连贯文本
前提条件: 模块实现已完成。实现期间不要启动服务来“看看会怎样”。应先修复配置、适配器和模块。
先对 HF 参考模型进行健全性检查。 在使用 MAX 之前,先按照模型卡指定的提示词模板单独运行 HF。如果 HF 本身生成的是乱码,那么参考模型就有问题,与其进行比较会发现一些无法通过修改端口解决的不匹配。
然后启动服务:
pixi run max serve --model-path <HF_MODEL_ID> --custom-architectures <port_dir>
max serve 会为 KV 缓存预留设备内存的 --device-memory-utilization 比例(默认值为 0.9)。如果启动时分配失败(内存不足,或缓冲区大于设备允许的大小),请降低该比例,例如使用 --device-memory-utilization 0.6。如果 HF 参考模型与服务运行在同一台机器上,也应降低该比例,因为两者会争用内存:否则,float32 参考模型可能会溢出到磁盘,导致每次比较耗时数分钟。
通过 /v1/chat/completions 探测经过指令微调的模型,该接口会应用其聊天模板;通过 /v1/completions 和纯提示词探测基座模型。PrefixLM 需要使用模型卡中展示的提示词格式。对于回答前会进行推理的模型(默认会生成 <think> 块),请在两端都关闭推理:MAX 使用 chat_template_kwargs,HF 的 apply_chat_template() 使用相同的关键字(enable_thinking=False),否则比较内容只会涵盖推理文本。在 HF 侧对经过聊天模板处理的文本进行分词时,请传入 add_special_tokens=False,因为模板已经添加了这些特殊标记。根据结果采取下一步操作:
- 服务在加载期间崩溃 → 修复配置和适配器。
- 服务成功启动但返回乱码 → 开始排查差异。
- 服务返回看似合理的文本 → 使用
max_tokens=64+运行,然后通过验证门槛。即使端口缺少某个增量,也仍可能生成流畅的回答。
完整的 HF 参考模型健全性检查、编码器/嵌入模型 slug 服务流程,以及修复-测试循环规范: serve-and-iterate.md。
一致性失败(调用 debug-model)
服务可以启动,但输出不正确:乱码、索引 K 处的贪心选择错误、logit 与错误 argmax 的余弦相似度较高,或文本连贯地生成了 N 个 token 后开始偏离。发生这种情况时,停止添加标量输出探针,并加载 debug-model 技能。
该技能用于处理能够加载并运行的模型所产生的错误输出。其工作流要求完成以下步骤:
- 在相同提示词和检查点上进行 HF 健全性检查
- 逐层运行 HF 与 MAX 张量转储比较器(而不是靠
F.print肉眼检查) - 通过并行调查 Agent 在重新编译前进行数值验证
- 如果转储结果一致但生成文本不一致,则对服务与管线进行二分排查
使用 import-model 搭建初始框架并设置检查门槛。使用 debug-model 本身来排查偏差。
快速 logit 探测(仅限前 5 分钟)
在构建转储工具之前,先运行快速健全性检查:
pixi run python scripts/compare_layers.py <HF_MODEL_ID> \
--port 8000 \
--prompt "The capital of France is"
需要在同一端口上运行 pixi run max serve,并指定 --custom-architectures <port_dir>。该探测会读取 logprobs,而重叠调度器会拒绝此请求。MAX 默认启用该调度器,除非 arch.py 设置了 supports_overlap_scheduler=False;因此,如果 arch.py 保持默认设置,请在 serve 命令中添加 --no-enable-overlap-scheduler --force。此脚本会打印仅 HF 的逐层统计,并比较提示词处的 top-1 logprob。标志详情请参阅 layer-by-layer-debugging.md。
如果 logprob 不一致或输出是乱码,请切换到 debug-model。不要只靠手动输出探针反复迭代。比较器定位出故障层后,divergences.md 中的症状目录仍然适用。
与 Hugging Face 对照
使用预训练权重端到端运行模型,然后在相同提示词上以贪心采样运行 HF。在 MAX 端,使用与模型支持的权重编码匹配的数据类型(大多数模型以 bfloat16 发布)。如果可行,HF 端使用 float32:bfloat16 参考本身会引入舍入误差,而正确的 bfloat16 移植版本也可能在几个 token 内与其产生偏差。
在输出首次不同时,查看 HF 的两个最高 logprob。如果它们相差约 0.1 nat 以内,那么模型在此处不分胜负,任一 token 都是正确的。如果前几个 token 就出现偏差,且 HF 有明显更优的选择,通常意味着分词器或聊天模板不匹配、数据类型与发布的权重不匹配,或 MAX 采样值非零。
当输出一致,或首次差异出现在平局处时,该移植版本就算完成了 贪心文本 部分。其他完成标准取决于规划阶段所选的验证深度。选择一个等级,从冒烟测试到 logit 一致性。
完整的 HF 对比流程、偏差分类和验证等级表,请参阅:validation-tiers.md。
常见陷阱
将 pitfalls.md 用作索引:找到症状后,加载对应类别的文件(配置、权重、模块或服务);文档字符串专项审查请参阅 rewrite-donor-docstrings.md。最常见的陷阱:
- 为脚手架提供服务。 在模块实现
<slug>.py中的所有差异之前,不要提供服务或进行验证,因为脚手架生成的模块仍在运行供体的计算逻辑。 - 重命名后供体文档字符串仍未修改。 在复制的供体文件中(使用
--full-copy或手动复制),类名会被重命名,但文档字符串和注释仍描述供体。请重写它们,并在宣布实现完成前运行审计 grep。
测试与 CI
为移植后的模型添加 pytest 测试时,应尽量减少每个文件的编译次数。通过模块级 fixture 编译一次,并在 @pytest.mark.parametrize 用例之间复用。对于必须编译不同模块的文件,使用 Bazel shard_count 并行运行,以保持文件完整。完整模式和示例请参阅:tests-and-ci.md。