执行摘要
- 一句话:新增 Kimi-K3 B300 MMMU-Pro 精度 CI 覆盖
- 推荐动作:值得精读,尤其关注
run_eval.py 中 preset 与 legacy 参数分支的设计,以及 MMMUProMixin 的阈值校准方法论。对后续为其他推理模型添加 sgl-eval 评测有直接参考价值,也展示了如何通过实测数据驱动 CI 阈值设定。
功能与动机
Kimi-K3 是结合 DSPARK 线性 ReplaySSM 推测解码的推理模型,原 low-latency CI 使用 GSM8K 文本精度阈值把关,无法覆盖多模态长推理场景。PR body 明确指出:'Strengthen Kimi-K3 B300 low-latency coverage with the multimodal MMMU-Pro benchmark. The test should use the model-maintained sgl-eval preset so its generation settings stay aligned with Kimi-K3.' 首次 B300 运行也暴露了旧 GSM8K 阈值(accept length 4.5)对 MMMU-Pro 不再适用(实测 2.62),需要按新任务重新校准。
实现拆解
本 PR 分 5 步完成:
-
扩展精度测试套件(python/sglang/test/kits/eval_accuracy_kit.py):新增 MMMUProMixin,提供 test_mmmu_pro 方法;_run_accuracy_eval 将 num_threads 改为 Optional,并允许通过 eval_overrides 传递 model=None 与 load_preset_from_model_id,使模型和采样设置完全交给 sgl-eval preset。
-
扩展 run_eval.py 的 sgl-eval 桥:新增 --load-preset-from-model-id 参数;有 preset 时省略 --model、--num-threads、--temperature、--top-p、--max-tokens、--thinking 等参数,无 preset 时保留 legacy 默认(如 top_p=1.0、max_tokens=2048、temperature=0.0),避免影响既有 GSM8K 等约 47 个消费者。同时将用户输入的 mmmu-pro 归一化为 sgl-eval 注册名 mmmu_pro。
-
更新 sgl-eval 版本 pin:scripts/ci/utils/sgl_eval_ref.sh 更新到包含 Kimi-K3 preset 的修订版本。
-
拆分并改造 B300 测试:从 test/registered/models_e2e/test_kimi_k3_b300.py 中移出 TestKimiK3B300LowLatency,新建独立文件 test_kimi_k3_b300_low_latency.py,使用 MMMUProMixin(score_threshold=0.75、200 例、accept_length=2.4、preset 模型 moonshotai/Kimi-K3),并保留单请求 accept_length=4.0 和 300 token/s 速度断言;原文件保留 Balanced 和 MegaMoE 的 GSM8K 测试。注册 est_time=1800。
-
补充单元测试:在 test/registered/unit/test_eval_accuracy_kit_sgl_eval.py 和 test/registered/unit/bench/test_simple_eval_gsm8k.py 中新增命令构造、preset 优先级、legacy 默认值、任务名归一化和 mixin 阈值行为测试。
测试配套:新增/修改 6 个测试文件,无 serving 代码变更,未添加 workflow 或强制 bin 覆盖。
关键文件:
python/sglang/test/kits/eval_accuracy_kit.py(模块 精度测试套件;类别 test;类型 test-infra;符号 MMMUProMixin, test_mmmu_pro, _run_accuracy_eval): 新增 MMMUProMixin 和 test_mmmu_pro 方法,是 MMMU-Pro 评测的入口;同时调整 _run_accuracy_eval 的参数传递,使其支持 preset 模式。
python/sglang/test/run_eval.py(模块 评测脚本;类别 test;类型 test-infra;符号 _run_sgl_eval, run_eval, run_eval_once): 扩展 sgl-eval 桥,新增 --load-preset-from-model-id 支持和参数透传逻辑,是 MMMUProMixin 能工作的关键。
test/registered/models_e2e/test_kimi_k3_b300_low_latency.py(模块 B300 低延迟测试;类别 test;类型 test-coverage;符号 TestKimiK3B300LowLatency, setUpClass, tearDownClass, _stop_server): 新增的独立低延迟测试文件,使用 MMMUProMixin 替换 GSM8K,并设置校准后的阈值,是本次 CI 覆盖的核心载体。
test/registered/models_e2e/test_kimi_k3_b300.py(模块 B300 测试;类别 test;类型 test-refactor;符号 TestKimiK3B300Balanced): 移除低延迟测试类及相关导入,保留 Balanced 和 MegaMoE 的 GSM8K 覆盖,避免重复注册。
test/registered/unit/test_eval_accuracy_kit_sgl_eval.py(模块 精度套件单测;类别 test;类型 test-coverage;符号 _run_mmmu_pro, test_mmmu_pro_uses_kimi_preset_and_300_examples, test_mmmu_pro_score_threshold_gates_result): 为 MMMUProMixin 增加 Hermetic 单元测试,验证 preset 参数拼接和分数阈值 gating。
test/registered/unit/bench/test_simple_eval_gsm8k.py(模块 评测脚本单测;类别 test;类型 test-coverage;符号 test_model_preset_owns_model_and_sampling_defaults, test_non_preset_cli_keeps_legacy_top_p_default, test_run_eval_dispatches_hyphenated_mmmu_pro_name): 为 run_eval.py 的 preset 参数透传、legacy 默认值和任务名归一化增加测试,防止回归。
scripts/ci/utils/sgl_eval_ref.sh(模块 CI 脚本;类别 infra;类型 configuration): 更新 sgl-eval 版本 pin 到包含 Kimi-K3 preset 的修订,是评测可用的前提。
关键符号:MMMUProMixin.test_mmmu_pro, _run_accuracy_eval, _run_sgl_eval, run_eval, TestKimiK3B300LowLatency.setUpClass, TestKimiK3B300LowLatency.tearDownClass
关键源码片段
python/sglang/test/kits/eval_accuracy_kit.py
新增 MMMUProMixin 和 test_mmmu_pro 方法,是 MMMU-Pro 评测的入口;同时调整 _run_accuracy_eval 的参数传递,使其支持 preset 模式。
class MMMUProMixin:
"""基于 sgl-eval 的标准 10 选项 MMMU-Pro 评测 mixin。
模型 preset 提供 endpoint 模型和全部生成设置。将模型和采样参数
交给 sgl-eval 管理,对推理模型很重要——其推荐 token 预算和采样
设置与 run_eval 默认值不同。
"""
mmmu_pro_score_threshold: float = _THRESHOLD_NOT_SET
mmmu_pro_accept_length_thres: Optional[float] = None
mmmu_pro_num_examples: Optional[int] = 300
mmmu_pro_num_threads: Optional[int] = None
mmmu_pro_load_preset_from_model_id: Optional[str] = None
def test_mmmu_pro(self):
# 必须显式指定 preset,否则无法获得与模型对齐的生成配置
assert self.mmmu_pro_load_preset_from_model_id, (
f"{type(self).__name__} 必须设置 mmmu_pro_load_preset_from_model_id"
)
_run_accuracy_eval(
self,
eval_name="mmmu_pro",
score_threshold=self.mmmu_pro_score_threshold,
num_examples=self.mmmu_pro_num_examples,
num_threads=self.mmmu_pro_num_threads,
accept_length_thres=self.mmmu_pro_accept_length_thres,
model=None, # 由 preset 决定模型
load_preset_from_model_id=self.mmmu_pro_load_preset_from_model_id,
)
python/sglang/test/run_eval.py
扩展 sgl-eval 桥,新增 --load-preset-from-model-id 支持和参数透传逻辑,是 MMMUProMixin 能工作的关键。
def _run_sgl_eval(eval_name, args) -> dict:
# ... 前面处理 out_dir 等
model_preset_id = getattr(args, "load_preset_from_model_id", None)
cmd = [
"sgl-eval", "run", eval_name,
"--base-url", base_url,
"--out-dir", str(out_parent),
]
if model_preset_id:
cmd += ["--load-preset-from-model-id", model_preset_id]
# 有 preset 时不传 model 与采样参数,交给 preset 统一决定;
# 无 preset 时保留 legacy 默认值,避免影响既有 GSM8K 调用方。
if getattr(args, "model", None):
cmd += ["--model", args.model]
if getattr(args, "num_examples", None) is not None:
cmd += ["--num-examples", str(args.num_examples)]
if getattr(args, "num_threads", None) is not None:
cmd += ["--num-threads", str(args.num_threads)]
if getattr(args, "temperature", None) is not None:
cmd += ["--temperature", str(args.temperature)]
elif not model_preset_id:
cmd += ["--temperature", "0.0"]
if getattr(args, "top_p", None) is not None:
cmd += ["--top-p", str(args.top_p)]
elif not model_preset_id and getattr(args, "_sgl_eval_from_cli", False):
cmd += ["--top-p", "1.0"]
# ... 其余参数(seed、max-tokens、thinking 等)
test/registered/models_e2e/test_kimi_k3_b300_low_latency.py
新增的独立低延迟测试文件,使用 MMMUProMixin 替换 GSM8K,并设置校准后的阈值,是本次 CI 覆盖的核心载体。
class TestKimiK3B300LowLatency(MMMUProMixin, SpecDecodingMixin, CustomTestCase):
"""TP8 Low Latency recipe with DSPARK linear ReplaySSM speculation."""
mmmu_pro_score_threshold = 0.75
mmmu_pro_num_examples = 200
mmmu_pro_load_preset_from_model_id = MODEL_PATH
# MMMU-Pro 的多模态长推理平均接受长度低于 GSM8K(首轮实测 2.62),
# 所以这里用任务专属回归门限;单请求速度门限保持更严格。
mmmu_pro_accept_length_thres = 2.4
# 单请求贪婪解码的结束位置会影响这两个指标,且速度是端到端的,
# 启动和 TTFT 摊到输出里,所以只作为粗粒度保护。
accept_length_thres = 4.0
bs_1_speed_thres = 300
@classmethod
def setUpClass(cls):
cls.model = MODEL_PATH
cls.base_url = DEFAULT_URL_FOR_TEST
cls.process = popen_launch_server(
cls.model,
cls.base_url,
timeout=SERVER_LAUNCH_TIMEOUT,
other_args=[
"--trust-remote-code",
"--tp-size", "8",
"--mem-fraction-static", "0.85",
"--model-loader-extra-config", MODEL_LOADER_EXTRA_CONFIG,
"--reasoning-parser", "kimi_k3",
"--tool-call-parser", "kimi_k3",
"--mamba-full-memory-ratio", "0.86",
"--speculative-algorithm", "DSPARK",
"--speculative-draft-model-path", DSPARK_DRAFT_MODEL,
"--speculative-dspark-block-size", "7",
"--enable-linear-replayssm-spec",
],
)
@classmethod
def tearDownClass(cls):
_stop_server(getattr(cls, "process", None))
评论区精华
本 PR 没有实质性的 review 评论。评论区仅有作者触发的 rerun-test 交互:第一次低延迟测试在 8-gpu-b300 上失败(run #32818230199),第二次通过(run #32896985069),拆分后新文件的 rerun 再次失败(run #32910692557,PR 中未说明原因)。作者在 PR body 中记录了关键校准过程:首轮 300 例 MMMU-Pro 评测约 29 分钟,因旧 GSM8K 阈值(accept length 4.5)误报失败,CustomTestCase 重试导致 workflow 60 分钟超时;第二轮 200 例通过,得分 0.83,平均接受长度 2.6096,据此将接受长度阈值从 2.6204 校准为 2.4。
风险与影响
关联脉络
参与讨论