Prhub

#29645 Support real draft tokens to simulated acceptance

原始 PR 作者 weireweire 合并时间 2026-07-01 08:22 文件变更 3 提交数 3 评论 14 代码增减 +46 / -1

执行摘要

新增真实 draft token 模拟接受模式

原有模拟接受逻辑在验证后使用固定的 token ID 100 填充预测缓冲区,但该 ID 未必与所选 draft 路径的验证 token 一致,导致下游解码消费不一致的 token/KV cache 状态(PR body:"The fabricated token ID can therefore be paired with KV state produced for different draft tokens, making downstream decode consume inconsistent token/cache state")。需要提供一种保持 token/KV 一致性的选项,同时不破坏依赖固定 token 行为的性能基准测试。

值得快速阅读:PR 虽小但展示了在有兼容性约束下如何逐步改进模拟逻辑的设计思路。核心决策点是保持向后兼容的同时引入可选的正确性修复。建议后续跟进添加单元测试覆盖 generate_simulated_accept_index 的核心分支。

讨论亮点
  1. 默认行为争议:nvpohanh 提议默认使用 real-draft-token 模式("more correct"),允许用户通过设置 token ID 回退。weireweire 指出新方式不支持 topk>1,可能破坏旧配置。最终 Fridge003 决定保持 fixed 为默认值以保持向后兼容。
  2. 代码健壮性:gemini-code-assist[bot] 建议用 "target_predict" in locals() 判断替代依赖互补条件,但该建议未被采纳。
  3. 测试文件:Fridge003 明确要求移除单元测试文件("We don't need this unit test"),作者随即删除。
  4. 关联 PR 建议:nvpohanh 建议合并 PR#29320,但最终未合并。

实现拆解

  1. 新增环境变量:在 python/sglang/srt/environ.py 中声明 SGLANG_SIMULATE_ACC_TOKEN_MODE,默认值为 "fixed"(保留旧行为)。
  2. 导入配置:在 python/sglang/srt/speculative/spec_utils.py 模块级读取 SIMULATE_ACC_TOKEN_MODE,并在 generate_simulated_accept_index 函数中添加对应的参数和分支逻辑。
  3. 修改模拟函数:在 generate_simulated_accept_index 中添加 candidatestarget_predict 参数。当 mode="real-draft-token"simulate_acc_len > 1 时,使用 candidates 中的真实 draft token 填充预测缓冲区的前 simulate_acc_len-1 个位置,最后一个 bonus token 来自 target_predict(目标模型 argmax 结果)。若 mode="fixed",仍然填充 token ID 100,与原行为完全一致。
  4. 调用方适配:在 python/sglang/srt/speculative/eagle_utils.pyeagle_sample 函数中,传递 SIMULATE_ACC_TOKEN_MODE 和新增的 candidates / target_predict 参数。新增输入校验:mode="real-draft-token" 时要求 tree_topk=1;非贪婪采样时自动计算 target_predict(argmax)。
  5. 测试文件被移除:Review 中 reviewer 指出不需要专门的单元测试文件,最终移除了测试文件。
文件 模块 状态 重要度
python/sglang/srt/speculative/eagle_utils.py 推测解码 modified 6.65
python/sglang/srt/speculative/spec_utils.py 推测解码 modified 6.17
python/sglang/srt/environ.py 配置 modified 4.35

关键符号

generate_simulated_accept_index eagle_sample

关键源码片段

python/sglang/srt/speculative/eagle_utils.py core-logic

核心调用入口,在 eagle_sample 中添加了 SIMULATE_ACC_TOKEN_MODE 的校验、target_predict 的计算逻辑,并将新参数传递给 generate_simulated_accept_index。

# python/sglang/srt/speculative/eagle_utils.py
# 在 eagle_sample 函数中,模拟接受分支前新增校验if SIMULATE_ACC_TOKEN_MODE not in ("fixed", "real-draft-token"):
    raise ValueError(
        "Invalid SGLANG_SIMULATE_ACC_TOKEN_MODE "
        f"{SIMULATE_ACC_TOKEN_MODE!r}; expected 'fixed' or 'real-draft-token'."
    )if SIMULATE_ACC_TOKEN_MODE == "real-draft-token":
    if verify_input.tree_topk != 1:
        raise ValueError(
            "SGLANG_SIMULATE_ACC_LEN with real draft tokens currently "
            "requires speculative_eagle_topk=1."
        )
    # 对非贪婪采样,用 argmax 作为 target_predict
    if target_predict is None:
        target_predict = torch.argmax(next_token_logits, dim=-1).reshape(
            bs, verify_input.draft_token_num
        )accept_index = generate_simulated_accept_index(
    accept_index=accept_index,
    predict=predict, # mutable
    num_correct_drafts=num_correct_drafts, # mutable
    candidates=candidates,
    target_predict=target_predict,
    simulate_acc_len=SIMULATE_ACC_LEN,
    simulate_acc_token_mode=SIMULATE_ACC_TOKEN_MODE,
    bs=bs,
    spec_steps=verify_input.max_tree_depth - 1,
)
python/sglang/srt/speculative/spec_utils.py core-logic

核心模拟逻辑所在文件,修改了 generate_simulated_accept_index 函数以实现真实 draft token 分支。

# python/sglang/srt/speculative/spec_utils.py
# 新增常量
SIMULATE_ACC_TOKEN_MODE = envs.SGLANG_SIMULATE_ACC_TOKEN_MODE.get()# generate_simulated_accept_index 函数关键分支(精简)
def generate_simulated_accept_index(
    accept_index,
    predict,
    num_correct_drafts,
    candidates,
    target_predict,
    bs,
    spec_steps,
    simulate_acc_len: float = SIMULATE_ACC_LEN,
    simulate_acc_method: str = SIMULATE_ACC_METHOD,
    simulate_acc_token_mode: str = SIMULATE_ACC_TOKEN_MODE,
):
    use_real_draft_tokens = simulate_acc_token_mode == "real-draft-token"
    # ... 采样 simulate_acc_len ...
    num_correct_drafts.fill_(simulate_acc_len - 1)
​
    if not use_real_draft_tokens:
        predict.fill_(100) # 旧行为:固定 token ID 100
        return sim_accept_index
​
    # 真实 draft token 模式:用 candidates 填充 predict 的前 simulate_acc_len-1 个位置
    if simulate_acc_len > 1:
        draft_node_indices = sim_accept_index[:, : simulate_acc_len - 1].long()
        predict[draft_node_indices] = candidates[:, 1:simulate_acc_len].to(
            dtype=predict.dtype
        )
        # 最后一个 bonus token 来自 target_predict
        bonus_node_indices = sim_accept_index[:, simulate_acc_len - 1].long()
        predict[bonus_node_indices] = target_predict[:, simulate_acc_len - 1].to(
            dtype=predict.dtype
        )
    return sim_accept_index

评论区精华

默认行为选择:fixed vs real-draft-token 设计

nvpohanh 提议默认使用 real-draft-token(更正确),weireweire 指出新方式不支持 topk>1 可能破坏旧配置。Fridge003 最终决定保留 fixed 为默认值。

结论:保持默认 fixed 以向后兼容,real-draft-token 作为可选模式。 · 已解决

代码健壮性:target_predict 初始化方式 style

gemini-code-assist[bot] 建议使用 `'target_predict' in locals()` 替代依赖互补条件,但作者未采纳。

结论:未采纳,保持原有条件分支逻辑。 · 已解决

单元测试文件必要性 测试

Fridge003 明确要求移除测试文件 "We don't need this unit test",作者随即删除。

结论:移除测试文件,无自动化测试覆盖新逻辑。 · 已解决

合并关联 PR #29320 other

nvpohanh 建议合并 PR#29320 到此 PR,但最终未合并。

结论:未合并,独立处理。 · 已解决

风险与影响

  1. 向后兼容风险低:默认 fixed 模式完全保留旧逻辑,用户无感知。
  2. real-draft-token 模式范围限制:该模式要求 topk=1,若用户在未调整配置的情况下直接设置环境变量,会触发 ValueError。文档或用户沟通需要明确提示。
  3. 无测试覆盖:原有的测试文件被删除,新逻辑缺少自动化验证,回归风险由人工验证和后续 CI 承担(Fridge003 说明 "CI tests don't cover simulated acc len")。

影响范围:仅影响使用 SGLANG_SIMULATE_ACC_LEN 环境变量开启模拟接受的用户。
影响程度:中等。新增了可选的正确性改进(token/KV 一致性),默认行为不变,对现有工作流无影响。
用户/团队:需要使用模拟接受进行基准测试或调试的工程师可以启用 real-draft-token 模式获得更准确的模拟结果。代码维护者需注意新增的环境变量和校验逻辑。

缺少测试覆盖 功能受限 (topk=1 only)

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论