2026-10-03 19:25:38 +08:00

enginex-bi100-compat

天数智芯 天垓100(Iluvatar_bi-100) · 文本生成 · vLLM 兼容增强引擎。

一句话:不改模型、不改卡,只把「引擎镜像层」的结构性不兼容在容器启动前修掉。 这类失败的特征是——换任何模型、换任何卡都会遇到,所以修在引擎层比修在提交层更普适。


1. 动机:失败不是均匀分布的

我在信创模盒累计 37 个验证失败。把 74 份失败日志 (log-extract/<taskId>/pod_runtime_log.txt / error_report_raw.json)聚合后发现, 失败高度集中在几类「模型没坏、卡也没坏、纯粹是引擎/配置层不匹配」的问题上。

按卡分布(我有日志的 42 例):

卡 失败数 主要缺陷
Iluvatar_bi-100 10 R2×4、R3×2、R7×1、R4×1
hygon_k100-ai 5 R4×2
Iluvatar_bi-150 4 其他×3
Iluvatar_mrv-100 4 R1×2(GGUF 需 llamacpp,属提交侧)、R2×2
Cambricon_mlu-370-x8 3 R2×3、R4×1

选 bi-100 首发,三个理由:

  1. 失败最集中(10 例,全平台第一);
  2. 该卡社区通过率约 51%(15 张卡里最低),修好收益最大;
  3. 它的原生 build-config 最简单(vllm serve /model --port 80 -tp 1),改动面小、风险低。

2. 修什么(五类,全部有日志实证)

编号 现象 根因 修法
R3 容器秒崩 AttributeError: 'list' object has no attribute 'keys' tokenizer_config.json 里 extra_special_tokens 写成 list,而 transformers 的 SpecialTokensMixin 会执行 ... + list(special_tokens.keys()) 生成 tokenizer 覆盖目录,把该字段规范成 dict,用 --tokenizer 指过去
R3b tokenizer 加载异常 tokenizer_class 是 TokenizersBackend / TiktokenTokenizer 这类镜像不认的类 按 fast/sentencepiece/bpe 归一化成正常类,并清掉 backend
R2 /v1/chat/completions 返回 400 或输出为空;启动参数里 chat_template=None 模型自身没带 chat_template 注入最小可用 Jinja 模板,用 --chat-template 指过去
R4 KeyError: 'xxx' / MODEL_NOT_SUPPORTED,配置加载阶段就挂 config.json 的 architectures 声明了引擎镜像没注册的类 用 --hf-overrides {"architectures":[...]} 把已证实的未注册类名映射到已注册类名
R7 ModuleNotFoundError: No module named 'ixformer.contrib.vllm.layers' 引擎镜像缺 MoE 实现依赖 shims/ 提供纯 PyTorch 回退,经 PYTHONPATH 注入

证据片段(均来自真实失败日志)

R3

self.SPECIAL_TOKENS_ATTRIBUTES = self.SPECIAL_TOKENS_ATTRIBUTES + list(special_tokens.keys())
AttributeError: 'list' object has no attribute 'keys'

R4

File ".../transformers/models/auto/configuration_auto.py", line N, in __getitem__
    raise KeyError(key)
KeyError: 'olmo...'

R7

File ".../vllm/model_executor/models/mixtral.py", line N, in forward
    from ixformer.contrib.vllm.layers import mixtral_decoder_layer_forward
ModuleNotFoundError: No module named 'ixformer.contrib.vllm.layers'

R2

startup args: ..., chat_template=None, ...
后续 MCQ 评测全部 HTTP Error 400 / 输出为空

3. 实现:沿用社区已验证的引擎模式

R3 / R3b 两条修复不是我发明的——社区开发者 sunruoxi 已在 EngineX-Sunrise/enginex-S2-vllm-fix-tokenizer(2026-05-28 上线,已注册到曦望 S2) 做过并跑在生产上。本引擎:

  • 完全沿用它的结构:Dockerfile + entrypoint.sh + 修补脚本 + detect_tokenizer.py + README, entrypoint.sh 也是 detect → fix → exec vllm serve "$MODEL_DIR" $EXTRA "$@" 这一套;
  • 合并它那两条修复(extra_special_tokens list→dict 用同形的 token→token, tokenizer_class 归一化用同一份坏类清单),保证两套引擎口径一致、可交叉验证;
  • 在其上补了 R2 / R4 / R7 三条,把「只修 tokenizer」升级成「修 tokenizer + 模板 + 架构 + 缺模块」。

extra_special_tokens dict 的构造方式与社区上线版一致:

cfg["extra_special_tokens"] = {token: token for token in orig_list}

4. 三条设计红线

  1. 绝不写模型目录——平台挂载的 /model 可能只读。所有修补只通过 「临时覆盖目录 + 命令行参数」实现;软链优先,软链不可用时只拷贝 tokenizer 白名单文件, 权重文件(.safetensors/.bin/.gguf/.pt/...)永远不碰(自测专门盯这条)。
  2. 修补失败绝不阻断启动——entrypoint.sh 里 preflight 非 0 退出也只打日志然后按原命令跑; preflight 内部每步都有 try/except。最坏情况=没修上,不会比不装本引擎更差。
  3. 干净模型零改动——模型自带 chat_template、架构已注册、无 list 字段时, preflight 不产出任何额外参数(自测用例 5 专门盯这条)。

5. 自测(不需要 GPU / vLLM / Docker)

python3 test_engine.py
# 25 passed, 0 failed

覆盖:R3 list→dict 且原目录零写入、R3b 坏类名归一化、R2 模板注入与 --api completion 时跳过、 R4 别名映射与「已注册类名不动」、干净模型不误补、模型目录不存在/空目录/坏 JSON 都不崩、 R7 shim 可 import 且 MoE forward 2D/3D 形状与数值有限(torch 2.13 cpu 实测)、 entrypoint.sh bash 语法、覆盖目录不含权重文件。


6. 使用方式

Dockerfile 以 ENTRYPOINT ["/opt/entrypoint.sh"] 结尾,平台会把 GPU 数、端口、 --max-model-len 等参数作为 "$@" 传进来,原样透传给 vllm serve:

exec vllm serve "$MODEL_DIR" $EXTRA "$@"

修补结果打进容器日志,事后可归因:

[entrypoint] preflight extra args: --tokenizer /tmp/mhxc_compat_xxx/tokenizer --hf-overrides {...}
[entrypoint] preflight: [preflight] patches=3 R3: ... | R4: ... | R7: ...

7. 如何注册为平台可用框架(本轮挖出的完整链路)

平台侧的「上传驱动」按钮在当前 UI 里不存在,但引擎注册的完整链路藏在社区引擎的 CI 里。 照 EngineX-Sunrise/enginex-S2-vllm-fix-tokenizer 的 .gitea/workflows/ 复刻即可:

.gitea/workflows/docker-build-push.yml   平台官方引擎 CI(原样复制,勿改)
.gitea/workflows/task_info.env           声明三要素:FRAMEWORK / GPU_TYPE / TASK_TYPE

推送一个 v* 标签即触发,CI 会依次:

  1. 读 task_info.env 取 FRAMEWORK/GPU_TYPE/TASK_TYPE(FRAMEWORK 必填);
  2. 调 GET https://modelhub.org.cn/adminApi/image-verify/validate?gpuType=…&taskType=… 校验元数据;
  3. docker build + docker push 到平台 registry;
  4. 调 POST https://modelhub.org.cn//adminApi/image-verify 回填 {framework, gpuType, imageUrl, taskType, createBy, repoUrl, tag} —— 这一步就是注册。

本仓的取值:FRAMEWORK=vllm_compat、GPU_TYPE=Iluvatar_bi-100、TASK_TYPE=text-generation, 并已推送标签 v1.0.0(commit d78a92b3)。 CI 运行记录:actions/runs/1,9 个步骤(Clone / Set metadata / Load Task Info / Validate Metadata / Login / Build / Push / Notify / Complete)全部 success,其中 Build 2s、Push 1m1s。

注册生效后,提交适配任务时的「框架」下拉里会出现 vllm_compat(只对 Iluvatar_bi-100 × 文本生成)。

8. 已验证 / 未验证(分清楚,不把做了当成了成了)

已验证

  • python3 test_engine.py → 25 passed, 0 failed(不需要 GPU / vLLM / Docker)
  • CI 9 步全部 success;image-verify/validate 返回 code:0, data:true(平台认可元数据)

尚未验证

  • 框架还没出现在平台侧:Iluvatar_bi-100 × text-generation 目前仍只有 transformers / vllm / vllm-patch-tokenizer(个人开发者 zhengzhongwei) / vllm_fix_tokenizer 四个, vllm_compat 未出现,「我的驱动列表」也仍显示「暂无驱动」。 → 注册是异步或需平台审核,尚未生效,需要继续观察(不能声称已注册成功)。
  • 没在真卡上跑过:本机没有天垓100,也拉不到该基础镜像,Dockerfile 未经真机验证。 风险已压到最小:基线镜像与官方基线一致、结构照搬生产引擎、修补全失败安全。

9. 已知边界

  • R4 别名表只收已证实的 4 条(Qwen3_5 系列 + Gemma3 早期类名),不靠猜扩表。
  • R7 shim 是兜底不是优化:保证「能出结果」,不追求吞吐。
  • 提交任务的「框架」下拉要等平台注册生效后才看得到 vllm_compat。
Description
Iluvatar bi-100 vLLM text-generation engine with preflight compatibility patches (R2/R3/R3b/R4/R7)
Readme 76 KiB
Languages
Python 86.9%
Dockerfile 8.6%
Shell 4.5%