Prhub

#48290 [ModelRunner v2] Enable MRV2 for pooling models by default

原始 PR 作者 taneem-ibrahim 合并时间 2026-08-19 12:36 文件变更 3 提交数 9 评论 35 代码增减 +26 / -6

执行摘要

pooling 模型默认启用 MRV2,全面切换执行路径

PR body 明确提出目标:"Enable Model Runner V2 by default for supported text-only and multimodal pooling models",覆盖 encoder embedding、classification、token embedding、token classification、decoder embedding、reranking、reward、PRM、Transformers 后端 embedding/classification、文本 ColBERT(Jina、LFM2)、多模态 dual-encoder、late-interaction(ColPali、ColQwen3)以及多模态 classification/rerank。此前 base 版本中 _is_default_v2_model_runner_modelrunner_type 有限制:只有 generate 类型才可能默认走 V2,pooling 类模型一律被排除在 MRV2 之外,导致新执行器的性能与功能收益无法覆盖这些模型,因此需要翻转该默认选择。

值得精读。虽然源码仅删除 3 行,但这是"以最小 diff 翻转大规模默认行为"的典范:通过参数化测试将各分支(文本/多模态、原生/Transformers 后端、encoder/decoder)拆成显式用例,再用渐进式 commit 逐步放宽范围,最后用完整测试矩阵兜底。值得关注的设计决策:删除整个 runner_type guard 而非添加 pooling 特判,让选择器回归"单一职责"(只关心 MoE/hybrid/attention-free 例外),以及 review 中 njhill 对 draft 模型检查的简化判断。

讨论亮点

核心 review 交锋集中在选择逻辑的简化:

  • njhill 在 vllm/config/vllm.py 上评论:"I don't think we need to consider draft models here so can simplify this check",并给出单行建议 if model_config.runner_type == "pooling" and model_config.is_multimodal_model: return False;同时针对 tests/test_config.py 新增的 draft 用例说 "I think we can remove this"。最终实现采纳了"简化"方向但更进一步——直接删除整个 runner_type guard,因为后续 commit 决定多模态 pooling 也默认启用,特判反而多余。
  • depthfirst-app[bot] 在中间 commit 检出 MEDIUM 级 SyntaxError:"The elif on line 684 has no indented body ... appears to be a failed merge of two code versions",说明当时存在合并冲突残留,后续 commit 已修复。
  • 该 PR 经历 8 轮 Buildkite CI(#82842、#83016、#83019、#83059、#83117、#83427、#84141、#84314)与多次 retry,yewentao256 确认部分失败与本次变更无关("Current failures are not related")。

实现拆解

实现拆解(按演进步骤):

  1. 放宽选择器准入条件:在 vllm/config/vllm.pyVllmConfig._is_default_v2_model_runner_model 中删除 if model_config.runner_type != "generate": return False 两行(+0/-3)。删除该 guard 后,pooling 配置不再提前返回,而是继续向下经过 hybrid、attention-free、MoE 三组既有资格检查,非 MoE 非 hybrid 非 attention-free 的 pooling 模型即默认启用 MRV2。这是对 "默认执行路径" 这一核心决策点的最小 diff 修改。

  2. 渐进式放开的演进过程(9 个 commit):最初提交直接对所有 pooling 模型启用;随后收紧为仅 encoder-only(commit dee0a06);再放开文本 pooling、多模态暂留 V1(commit 94e9880);期间应用 njhill 的 review 反馈简化选择逻辑(commit 23c8b28、3ed00ec);最终 commit b924830 决定多模态 pooling 也默认启用,形成最终形态;两次 merge main 引入的合并冲突曾被 bot 检出语法错误(见讨论区),后续修复。

  3. 测试配套tests/test_config.pytest_is_default_v2_model_runner_model 参数化用例新增 BertModel(sentence-transformers/all-MiniLM-L6-v2,期望 True)、ColQwen3(TomoroAI/tomoro-colqwen3-embed-4b,多模态,期望 True),并把 Qwen3ForCausalLM pooling(Qwen3-Embedding-0.6B)的期望从 False 翻转为 True,同时为既有用例补充 is_multimodal_model 字段;tests/models/language/pooling/test_colbert.py 中 jina、lfm2 两个 Transformers 后端的 ColBERT 参数从 use_v2=False 翻转为 True,验证了 MRV2 下与 HF 输出对齐。

  4. 验证矩阵(PR body 详列):test_config.py 21 个 selector 用例;MRV2/HF 对比 13 个用例(BERT、ModernBERT、Jina、LFM2 ColBERT、Qwen3 embedding、Jina reranker、PRM golden outputs);MTEB 34 个启用用例 + 93 个 expected skips;pooling entrypoint 回归 325 个用例;多模态 5 个用例(CLIP 文本/图像 embedding、ColPali MaxSim 与 late-interaction scoring、Nemotron-VL rerank)。ColQwen3 因 HF custom code 与 Transformers v5 不兼容,模型套件测试被跳过,仅验证了 selector 资格。

文件 模块 状态 重要度
vllm/config/vllm.py 配置模块 modified 5.68
tests/test_config.py 配置测试 modified 4.97
tests/models/language/pooling/test_colbert.py 池化模型 modified 3.64

关键符号

_is_default_v2_model_runner_model test_is_default_v2_model_runner_model test_colbert_hf_comparison

关键源码片段

vllm/config/vllm.py core-logic

核心决策点:删除 `_is_default_v2_model_runner_model` 中 `runner_type != "generate"` 的准入 guard,使 pooling 模型默认进入 MRV2 路径,同时保留 MoE/hybrid/attention-free 例外。

def _is_default_v2_model_runner_model(self) -> bool:
    """判断模型是否默认使用 Model Runner V2 执行。    本 PR 移除了 runner_type != "generate" 的提前返回(base 版本中)。
    此前 pooling 模型一律被排除在 MRV2 默认路径之外;移除后,
    embedding、rerank、reward 等 pooling 模型也能进入 MRV2,
    而 MoE、hybrid、attention-free 三组例外检查原样保留。
    """
    model_config = self.model_config
    if model_config is None:
        return False
​
    architectures = getattr(model_config, "architectures", [])
    default_architectures = default_v2_model_runner_architectures()
    is_default_v2_architecture = any(
        arch in default_architectures for arch in architectures
    )
​
    # hybrid 模型只有在架构命中白名单时才允许走 V2
    if getattr(model_config, "is_hybrid", False) and (
        not is_default_v2_architecture
    ):
        return False
​
    # attention-free 模型(如 Mamba)在 V2 上尚不支持,保持 V1
    if getattr(model_config, "is_attention_free", False):
        return False
​
    # 非 MoE 模型(含 pooling)默认走 V2;
    # MoE 模型仅当架构命中默认白名单时才启用 V2。
    return is_default_v2_architecture or not model_config.is_moe
tests/test_config.py test-coverage

参数化测试同步更新:新增 BERT、ColQwen3(多模态)用例,并把 Qwen3-Embedding 期望从 False 翻转为 True,锁定新默认行为。

# 参数化用例:验证不同模型配置在默认情况下是否走 MRV2。
# 本 PR 新增 / 修改的关键分支:
# 1. 纯文本 encoder-only pooling(BERT)默认启用 MRV2
# 2. decoder 型 embedding(Qwen3-Embedding)从 False 翻转为 True
# 3. 多模态 late-interaction(ColQwen3)默认启用 MRV2
(
    SimpleNamespace(
        model="sentence-transformers/all-MiniLM-L6-v2",
        architectures=["BertModel"],
        runner_type="pooling",
        is_multimodal_model=False,
        is_moe=False,
        is_quantized=False,
    ),
    True,
),
(
    SimpleNamespace(
        model="Qwen/Qwen3-Embedding-0.6B",
        architectures=["Qwen3ForCausalLM"],
        runner_type="pooling",
        is_multimodal_model=False,
        is_moe=False,
        is_quantized=False,
    ),
    True,
),
(
    SimpleNamespace(
        model="TomoroAI/tomoro-colqwen3-embed-4b",
        architectures=["ColQwen3"],
        runner_type="pooling",
        is_multimodal_model=True,
        is_moe=False,
        is_quantized=False,
    ),
    True,
),

评论区精华

简化 pooling 模型的多模态检查与 draft 处理 设计

njhill 评论:"I don't think we need to consider draft models here so can simplify this check",并建议用单行 `if model_config.runner_type == "pooling" and model_config.is_multimodal_model: return False` 替代原 guard,同时认为 draft 模型无需在此考虑。

结论:采纳 " 简化 " 方向但更进一步:最终实现直接删除整个 runner_type guard,多模态 pooling 也默认启用 MRV2,由 MoE/hybrid/attention-free 检查兜底;draft 相关测试用例被移除。 · 已解决

draft runner 测试用例是否保留 测试

njhill 针对 tests/test_config.py 新增的 runner_type="draft" 用例评论 "I think we can remove this",认为 draft 模型不参与默认选择器判定。

结论:接受建议,draft 用例从参数化列表中移除。 · 已解决

中间提交的 elif 语法错误 正确性

depthfirst-app[bot] 检出 MEDIUM 级问题:"The `elif` on line 684 has no indented body ... a failed merge of two code versions",判定为合并两个版本代码时的残留,会导致 vllm.config.vllm 模块整体加载失败。

结论:在后续 commit 中修复,最终 head 版本无此问题;暴露了多并行分支合并的冲突风险。 · 已解决

风险与影响

风险点如下:

  1. 默认行为翻转:所有 pooling 模型从 V1 runner 静默切换到 MRV2,embedding 数值、rerank 分数可能产生细微差异(数值精度、pooling 执行顺序),依赖旧行为的用户需要重新验收;这是最大的回归面。
  2. 多模态端到端覆盖缺口:ColQwen3 在 selector 层面默认启用 MRV2,但其模型套件测试因 HF custom code 与 Transformers v5 不兼容被跳过,实际推理路径未经端到端验证。
  3. 合并期语法错误:depthfirst-app[bot] 曾检出 elif 无体的 SyntaxError,说明多并行分支合并存在冲突残留风险,最终版本已修复,但提醒后续维护者注意类似合并。
  4. 性能回归可能:MRV2 预期带来性能收益,但个别多模态 late-interaction 模型(ColPali 等)在 V2 下的表现需要发布后观测;测试主要验证正确性而非性能。
  5. CI 稳定性:8 轮 CI 中多次失败与重试,虽多数与本次无关,但 pre-commit 检查也失败过,说明变更链路的 CI 验证成本较高。

影响范围:所有使用 pooling runner 的用户——embedding API、reranking、reward 模型、PRM、ColBERT/ColPali 检索等场景的默认执行路径全部切换到 MRV2,属于默认行为变更,影响面广但不涉及 API 接口变化。对系统而言,MRV2 作为新执行器在功能完整性上已覆盖 pooling 各子类型,切换后 V1 pooling 路径的维护压力将逐步下降。对团队而言,这是 MRV2 迁移路线图的关键里程碑,后续可推进 V1 pooling 路径的弃用清理;选择器逻辑随之简化(pooling 与 generate 不再隔离),未来新增 pooling 架构默认即走 V2。影响程度评级:中高(默认行为变化),但测试矩阵(325 个 entrypoint 回归、34 个 MTEB)提供了较强信心。

默认执行路径翻转 多模态模型端到端覆盖不足 合并期语法错误风险 V1/V2 输出差异

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论