# PR #25717 完整报告

- 仓库：`sgl-project/sglang`
- 标题：Move the retract-decode ratio estimation onto the new-token-ratio tracker
- 合并时间：2026-05-19 09:19
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/25717

---

# 执行摘要

- 一句话：将 retract-decode 比率计算移到新 token 比率跟踪器
- 推荐动作：可以审批。改动清晰且低风险。如果团队时间允许，建议为 `estimate_new_token_ratio_after_retract` 添加单元测试，以保证未来重构的安全性。

# 功能与动机

PR 说明中明确：该计算仅依赖于请求批次的已解码 token 数和采样参数 max_new_tokens，与重试状态机无关，且计算结果由 `NewTokenRatioTracker` 消费。将计算逻辑移到 tracker 中，使 new_token_ratio 的完整生命周期集中表达在同一类型上。

# 实现拆解

1. 在 `new_token_ratio_tracker.py` 中添加 `from __future__import annotations` 和 `TYPE_CHECKING` 导入，以避免运行时循环依赖。
2. 在 `NewTokenRatioTracker` 类中新增 `estimate_new_token_ratio_after_retract` 静态方法，接收 `Sequence[Req]` 参数，计算并返回新的 token 比率估计值。
3. 在 `schedule_batch.py` 的导入部分添加 `NewTokenRatioTracker` 的导入。
4. 将 `retract_decode` 方法末尾的 9 行内联计算替换为单行调用：`NewTokenRatioTracker.estimate_new_token_ratio_after_retract(self.reqs)`。

关键文件：
- `python/sglang/srt/managers/scheduler_components/new_token_ratio_tracker.py`（模块 调度器；类别 source；类型 core-logic；符号 estimate_new_token_ratio_after_retract）: 新增静态方法 estimate_new_token_ratio_after_retract，并添加 TYPE_CHECKING 导入以避免循环依赖。
- `python/sglang/srt/managers/schedule_batch.py`（模块 调度器；类别 source；类型 dependency-wiring）: 移除内联计算，替换为对 NewTokenRatioTracker.estimate_new_token_ratio_after_retract 的调用；新增导入 NewTokenRatioTracker。

关键符号：estimate_new_token_ratio_after_retract

## 关键源码片段

### `python/sglang/srt/managers/scheduler_components/new_token_ratio_tracker.py`

新增静态方法 estimate_new_token_ratio_after_retract，并添加 TYPE_CHECKING 导入以避免循环依赖。

```python
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, Sequence

from sglang.srt.environ import envs
from sglang.srt.server_args import ServerArgs

# 仅在类型检查时导入 Req，避免运行时循环依赖
if TYPE_CHECKING:
    from sglang.srt.managers.schedule_batch import Req


@dataclass(slots=True, kw_only=True)
class NewTokenRatioTracker:
    init: float
    min: float
    decay: float
    current: float

    @classmethod
    def from_server_args(cls, server_args: ServerArgs) -> "NewTokenRatioTracker":
        # 根据环境变量和 server_args 计算初始、最小和衰减值
        init = min(
            envs.SGLANG_INIT_NEW_TOKEN_RATIO.get()
            * server_args.schedule_conservativeness,
            1.0,
        )
        min_ratio = min(
            init * envs.SGLANG_MIN_NEW_TOKEN_RATIO_FACTOR.get(),
            1.0,
        )
        decay = (init - min_ratio) / envs.SGLANG_NEW_TOKEN_RATIO_DECAY_STEPS.get()
        return cls(init=init, min=min_ratio, decay=decay, current=init)

    def decay_step(self) -> None:
        """衰减当前比率"""
        self.current = max(self.current - self.decay, self.min)

    def reset(self) -> None:
        """重置为初始比率"""
        self.current = self.init

    @staticmethod
    def estimate_new_token_ratio_after_retract(reqs: Sequence[Req]) -> float:
        """
        根据请求批次的已解码 token 数和 max_new_tokens 估计 retract 后的新 token 比率。
        """
        # 计算批次内所有请求已解码 token 总数
        total_decoded_tokens = sum(len(r.output_ids) for r in reqs)
        # 计算批次内所有请求最大新 token 总数
        total_max_new_tokens = sum(r.sampling_params.max_new_tokens for r in reqs)

        # 估计比率 = ( 已解码 token + retract 步骤数 * 请求数 ) / ( 最大新 token + 1)
        new_estimate_ratio = (
            total_decoded_tokens + envs.SGLANG_RETRACT_DECODE_STEPS.get() * len(reqs)
        ) / (
            total_max_new_tokens + 1
        )  # 避免除零
        new_estimate_ratio = min(1.0, new_estimate_ratio)
        return new_estimate_ratio

```

# 评论区精华

该 PR 无 review 评论。由于是单一作者的纯粹重构，且逻辑完全等价，未引发讨论。

- 暂无高价值评论线程

# 风险与影响

- 风险：风险极低。变更本质上是纯抽取重构，计算逻辑完全等价。但需注意：如果未来 `NewTokenRatioTracker` 或 `Req` 类型发生变更，需确保 `estimate_new_token_ratio_after_retract` 方法同步更新。未添加专用测试，可考虑在后续补上。
- 影响：对用户无直接影响。对系统而言，`retract_decode` 的调用 `Scheduler` 行为不变。对团队来说，降低了 `schedule_batch.py` 的复杂度，提高了可维护性。
- 风险标记：缺少测试覆盖

# 关联脉络

- 暂无明显关联 PR