# PR #7433 完整报告

- 仓库：`verl-project/verl`
- 标题：[model] fix: add Qwen3.5 VL to _TEXT_TO_VL_FAMILY for unified checkpoint support
- 合并时间：2026-08-19 09:59
- 原文链接：http://prhub.com.cn/verl-project/verl/pull/7433

---

# 执行摘要

- 一句话：修复 Qwen3.5 VL 统一检查点构建 Continuous Token 崩溃
- 推荐动作：值得快速阅读，作为向 _TEXT_TO_VL_FAMILY 扩展新模型家族的参照；测试使用轻量 MockTokenizer / MockProcessor 验证升级路径，思路可复用。但该 PR 改动极小且无深入设计讨论，不建议作为精读案例。

# 功能与动机

PR body 明确指出：Qwen3.5 VL 的 HF model_type 为 qwen3_5 / qwen3_5_moe（与文本模型相同），wiring 会将其解析到文本家族 QWEN35；由于 QWEN35 未出现在 _TEXT_TO_VL_FAMILY 中，传入多模态 processor 会抛出 ValueError: Model resolved to the text Continuous Token family 'qwen35', but a multimodal processor was provided，使 Qwen3.5 VL 无法用于多模态 RL 训练。gxlvera 的评论进一步确认该问题源于 PR#6804 评论中报告的 bug。

# 实现拆解

1. 修改 wiring 映射：在 verl/utils/tokenizer/continuous_token_wiring.py 的 _TEXT_TO_VL_FAMILY 字典中新增 ContinuousTokenModelFamily.QWEN35 -> ContinuousTokenModelFamily.QWEN3_VL。这是完整升级链路的唯一源码改动，与既有 DEFAULT -> VL_DEFAULT、GEMMA4 -> GEMMA4_VL 模式一致。
2. 新增单元测试：在 tests/utils/test_continuous_token_on_cpu.py 的 TestWiringVLFactory 类中新增 test_qwen35_unified_with_processor_upgrades_to_vl 方法，使用带 encode / convert_tokens_to_ids 的 MockTokenizer 与带 image_processor 的 MockProcessor 调用 create_continuous_token_builder，断言返回 QwenVLContinuousTokenBuilder 且 supports_multimodal() 为 True。注意：PR body 声称该测试参数化覆盖 qwen3_5 与 qwen3_5_moe，但当前摘录仅见 qwen3_5_moe 用例，两者存在不一致，合并前应确认。
3. 配套与验证：无 API、配置、schema 或部署配套改动；提交历史显示测试用例经历了两次修订后定稿，最终由 wuxibin89 批准合并，CLA 已通过。

关键文件：
- `verl/utils/tokenizer/continuous_token_wiring.py`（模块 构建器路由；类别 source；类型 core-logic；符号 _TEXT_TO_VL_FAMILY, ContinuousTokenModelFamily）: 核心入口，通过一行映射修复 QWEN35 文本族到 QWEN3_VL 的升级路径，是多模态 Continuous Token 构建的关键接线点。
- `tests/utils/test_continuous_token_on_cpu.py`（模块 单元测试；类别 test；类型 test-coverage；符号 test_qwen35_unified_with_processor_upgrades_to_vl, MockTokenizer, encode, convert_tokens_to_ids）: 新增 CPU 单元测试覆盖 Qwen3.5 统一检查点升级 VL 的路径，防止后续回归，并验证错误提示不再触发。

关键符号：test_qwen35_unified_with_processor_upgrades_to_vl, create_continuous_token_builder

## 关键源码片段

### `verl/utils/tokenizer/continuous_token_wiring.py`

核心入口，通过一行映射修复 QWEN35 文本族到 QWEN3_VL 的升级路径，是多模态 Continuous Token 构建的关键接线点。

```python
# 统一检查点的文本族 -> 视觉语言族升级映射。
_TEXT_TO_VL_FAMILY: dict[ContinuousTokenModelFamily, ContinuousTokenModelFamily] = {
    ContinuousTokenModelFamily.DEFAULT: ContinuousTokenModelFamily.VL_DEFAULT,
    ContinuousTokenModelFamily.GEMMA4: ContinuousTokenModelFamily.GEMMA4_VL,
    # Qwen3.5 的文本与 VL 版本共用 qwen3_5 / qwen3_5_moe 根类型，
    # 因此这里新增 QWEN35 -> QWEN3_VL 升级项，与前面两条规则保持一致。
    ContinuousTokenModelFamily.QWEN35: ContinuousTokenModelFamily.QWEN3_VL,
}

```

### `tests/utils/test_continuous_token_on_cpu.py`

新增 CPU 单元测试覆盖 Qwen3.5 统一检查点升级 VL 的路径，防止后续回归，并验证错误提示不再触发。

```python
def test_qwen35_unified_with_processor_upgrades_to_vl(self):
    '''Qwen3.5 统一检查点 + 多模态 processor -> Qwen VL builder。'''
    from verl.utils.tokenizer.continuous_token import QwenVLContinuousTokenBuilder
    from verl.utils.tokenizer.continuous_token_wiring import create_continuous_token_builder

    class MockTokenizer:
        name_or_path = 'Qwen/Qwen3.5-35B-A3B'

        def encode(self, text, add_special_tokens=False):
            # 仅模拟换行符的编码结果，其余文本返回固定 token 序列
            return [198] if text == '\n' else [1, 2, 3]

        def convert_tokens_to_ids(self, token):
            # 覆盖 VL 专用标记的 token id，供 builder 生成视觉填充符
            mapping = {
                '<|im_end|>': 151645,
                '<|vision_start|>': 151652,
                '<|vision_end|>': 151653,
                '<|image_pad|>': 151655,
            }
            return mapping.get(token, 0)

    class MockProcessor:
        # 模拟多模态 processor，触发文本族向 VL 族的升级逻辑
        image_processor = type('IP', (), {'merge_size': 2})()

    builder = create_continuous_token_builder(
        MockTokenizer(),
        hf_model_type='qwen3_5_moe',
        processor=MockProcessor(),
    )
    assert isinstance(builder, QwenVLContinuousTokenBuilder)
    assert builder.supports_multimodal() is True

```

# 评论区精华

全程没有对抗性技术讨论。gxlvera 评论确认：'This PR fixed the bug mentioned here: <PR#6804 评论链接 >. I have reviewed.' 这为本 PR 提供了与历史 PR 的直接关联证据；wuxibin89 最终 APPROVED。唯一留意的点是 PR body 与源码摘录在测试参数化上的不一致，未在评论中提及。

- PR#6804 提到的 Qwen3.5 VL bug 修复确认 (other): 确认修复目标并已审阅，无新增疑虑

# 风险与影响

- 风险：风险较低，核心变更仅一行映射且沿袭既有模式。但 continuous token wiring 位于 agent loop 等训练入口的关键路径，任何映射变化都会影响模型家族解析；若 QWEN35 纯文本模型误传 processor，现在会被升级为 VL 构建器，可能掩盖配置错误。测试方面，摘录仅覆盖 qwen3_5_moe 场景，qwen3_5 文本变体未被直接验证，若参数化描述不实则存在覆盖缺口。
- 影响：受益方为使用 Qwen3.5 VL 统一检查点的多模态 RL 训练用户，builder 创建阶段的崩溃被消除；纯文本 Qwen3.5 用户在不传 processor 时行为不变。改动仅影响连续 token 构建器的家族解析，对其他模块无直接影响，团队可按既有模式接入后续新模型。
- 风险标记：核心路径变更 , 测试覆盖存疑

# 关联脉络

- PR #6804 [BREAKING][rollout] feat: Add Multimodal Continuous Token: 本 PR 直接引用 PR#6804 评论中报告的 bug，且两者都围绕 continuous token 构建器家族解析，本 PR 是其后续修复。