执行摘要
- 一句话:新增混合前缀 GSM8K 评估及 CPU 单元测试
- 推荐动作:值得精读。该 PR 展示了如何通过提取虚方法(
_setup_prefix_pool / _build_prefix)在测试框架中实现模板方法模式,使得扩展不同前缀策略变得简单。单元测试也写得清晰全面,可作为测试驱动开发的范例。
功能与动机
PR body 说明:mixed_prefix_gsm8k builds each question's few-shot prefix from a primary block plus a per-question random sample of a secondary pool, so an evaluation run exercises distinct, partially-shared cached prefixes (instead of one identical few-shot prefix shared by every request). This is the eval used to detect chunked-prefill / prefix-cache corruption: a clean run scores normally, a corrupting bug collapses the score.
实现拆解
-
重构 base class GSM8KEval(python/sglang/test/simple_eval_gsm8k.py)
- 将原本在 __init__ 中直接计算 _few_shot_prompt 的逻辑拆分为两个虚方法:_setup_prefix_pool 和 _build_prefix。
- _setup_prefix_pool 负责从原始数据行中划分训练/测试集并构建基础前缀;_build_prefix 返回特定索引的前缀字符串。
- 原先的 self._few_shot_prompt 改为通过 self._build_prefix(idx) 调用,默认行为不变。
- 同时将 num_examples 越界处理从隐式切片改为显式静默截断(切片行为保持不变,但避免硬 assert 被优化掉)。
-
新增子类 MixedPrefixGSM8KEval(python/sglang/test/simple_eval_mixed_prefix_gsm8k.py)
- 继承自 GSM8KEval,重写 _setup_prefix_pool 将数据集分割为 _primary_shots(固定前 N 条)和 _secondary_pool(接下来 secondary_pool_size 条)。
- 重写 _build_prefix:使用随机种子(种子 = 用户指定 seed + 问题索引)决定主块使用多少条(0~num_shots),并从辅助池随机采样 0~secondary_pool_size 条,拼接后返回。
-
集成到评测入口(python/sglang/test/run_eval.py)
- 在 run_eval 的 dispatch 中添加 elif args.eval_name == "mixed_prefix_gsm8k" 分支,实例化 MixedPrefixGSM8KEval。
- 添加两个 CLI 参数:--mixed-prefix-gsm8k-secondary-pool-size(默认 15)和 --mixed-prefix-gsm8k-seed(默认 42)。
-
添加 CPU 单元测试(test/registered/unit/bench/test_mixed_prefix_gsm8k.py)
- 使用 register_cpu_ci 注册到 CI 基础测试套件,不依赖 GPU。
- 生成合成数据集,验证 _build_prefix 的行为:
- 主段总是主 shots 的前缀;
- 剩余问题全部来自辅助池且无重复;
- 剩余大小在辅助池范围内;
- 不同查询的主深度和辅助大小具有多样性;
- 任意两个查询共享至少 0 个主 shots(即主段长度可不同)。
关键文件:
test/registered/unit/bench/test_mixed_prefix_gsm8k.py(模块 单元测试;类别 test;类型 test-coverage;符号 _write_synthetic_dataset, TestMixedPrefixGSM8KEval, setUpClass, tearDownClass): 新增的 CPU 单元测试文件,独立验证 MixedPrefixGSM8KEval 的前缀构造逻辑(合成数据集、多种断言),保证评估工具的可靠性,无需 GPU 即可运行。
python/sglang/test/simple_eval_mixed_prefix_gsm8k.py(模块 评估逻辑;类别 test;类型 test-coverage;符号 MixedPrefixGSM8KEval, init, _setup_prefix_pool, _build_prefix): 核心子类 MixedPrefixGSM8KEval,实现了混合前缀构造逻辑:从主 shots 中取前缀,再从辅助池随机抽样拼接。是整个评估功能的关键。
python/sglang/test/simple_eval_gsm8k.py(模块 基类重构;类别 test;类型 test-coverage;符号 _setup_prefix_pool, _build_prefix): 基类 GSM8KEval 重构,将前缀构建抽为虚方法 _setup_prefix_pool 和 _build_prefix,默认行为不变,但使子类可扩展。
python/sglang/test/run_eval.py(模块 命令行入口;类别 test;类型 test-coverage): CLI 入口增加 mixed_prefix_gsm8k 分支和两个参数(辅助池大小、随机种子),使用户可通过命令行直接调用。
关键符号:MixedPrefixGSM8KEval.init, MixedPrefixGSM8KEval._setup_prefix_pool, MixedPrefixGSM8KEval._build_prefix, GSM8KEval._setup_prefix_pool, GSM8KEval._build_prefix, TestMixedPrefixGSM8KEval._decompose, TestMixedPrefixGSM8KEval.test_primary_segment_is_strict_prefix_of_primary_shots
评论区精华
仅有一条来自 gemini-code-assist[bot] 的 review 评论,针对 simple_eval_gsm8k.py 中使用 assert 进行参数验证的风险。机器人建议改用 ValueError 以避免 Python -O 优化绕过。作者在后续 commit(722fd4b)中改为使用切片静默截断(slicing)而非断言,从而修复了该问题。讨论状态:已解决。
- 使用 assert 验证参数的风险 (correctness): 作者在 commit 722fd4b 中改为使用列表切片进行静默截断,避免 assert 被绕过,同时保持与上游调用者(如 test_basic_sanity_eagle3)的兼容。
风险与影响
关联脉络
- PR #27506 Add more testing for chunked prefill: 同为 chunked prefill 测试基础设施的扩展,引入了 scripted runtime 测试框架。本 PR 提供的 mixed_prefix_gsm8k 评估与 scripted 测试互补,共同检测 prefix-cache 一致性问题。
参与讨论