#2338·slime

[Question] realign 使用本轮 response 长度判断

Author: stivory0Created Aug 31, 2026Updated Sep 5, 2026
Labelsquestion

Your Question

问题1: REALIGN 使用的是本轮 output_ids 的长度进行判断。但 REALIGN 是对上一轮的 response 进行 loss_mask 值零的操作,应该用上一轮的 len(response_ids) 吧? classify_token_drift(slime/agent/trajectory.py:188) 中:

python
start = self.last_response_start_idx
if start is not None and realign_at >= start and len(turn.output_ids) < self._fork_threshold:
    return DriftKind.REALIGN
return DriftKind.FORK

问题2:REALIGN 从 last_response_start 覆盖,而非从分歧点 realign_at _align_to_prompt(slime/agent/trajectory.py:216):

python
def _align_to_prompt(self, prompt_ids: list[int]) -> None:
    response_start = self.last_response_start_idx
    tail = prompt_ids[response_start:]
    self.tokens[response_start:] = tail
    self.loss_mask[response_start:] = [0] * len(tail)
    self.logprobs[response_start:] = [0.0] * len(tail)

这里从 realign_at 开始覆盖比较好吧?


我的实际运行例子(fork_threshold 默认 1024): 一个 3 轮片段:第 3 轮是 61 token 的小过渡句,第 2 轮的 output + tool_response 跨度 28404 token:

TMPDEBUG_DRIFT  kind=realign realign_at=39662 drift=18896 held_len=58558 prompt_len=58652
                resp_start=30154 held_div=47724(QQ) prompt_div=126724(QQ音乐)
TMPDEBUG_REALIGN resp_start=30154 zeroed_resp_tokens=28404 new_resp_len=61 builder_len_before=58558
  • zeroed_resp_tokens = 58558 - 30154 = 28404 == 上一轮 output + 其后 tool_response。
  • new_resp_len = 61(触发轮)< 1024 → REALIGN。
  • 分歧是单点 BPE 切分差异:模型侧 QQ(id 47724)vs 回传侧 QQ音乐(id 126724)。同文本、decode 一致。但 BPE 贪心合并使其后所有 token 边界错位,产生 drift=18896
  • _align_to_promptprompt_ids[30154:] 覆盖 [30154:58558],该范围包含分歧点之前的上一轮 output → 连带清零。

我在 builder 里统计了 REALIGN 清零范围内原有loss_mask 分布。4 次 REALIGN:

REALIGN resp_start=20503 zeroed=610  new_resp=730  realign_at=20740 innocent=237  killed_train_lm1=610  killed_masked_lm0=0
REALIGN resp_start=16167 zeroed=4435 new_resp=585  realign_at=16351 innocent=184  killed_train_lm1=4435 killed_masked_lm0=0
REALIGN resp_start=34362 zeroed=654  new_resp=175  realign_at=34694 innocent=332  killed_train_lm1=654  killed_masked_lm0=0
REALIGN resp_start=31995 zeroed=1061 new_resp=54   realign_at=32378 innocent=383  killed_train_lm1=1061 killed_masked_lm0=0
  1. killed_masked_lm0 == 0(全部)→ 清零范围里没有工具结果/历史,全是 loss_mask=1 模型生成,不是「工具调用结果长度」。
  2. killed_train_lm1 == zeroed_resp_tokens(完全相等)→ 清零区间就是上一轮完整 output 的一整段。
  3. pre_divergence_innocent = 184/237/332/383,均 > 0 → 清零从 resp_start 而非 realign_at 开始,分歧点前还有 184~383 个无辜 token 被一并清掉。

What I've Tried

我进行的改动: 问题1:

python
start = self.last_response_start_idx
if start is not None and realign_at >= start:
    sacrificed_len = len(self.tokens) - start
    both_short = (
        len(turn.output_ids) < self._fork_threshold
        and sacrificed_len < self._fork_threshold
    )
    kind = DriftKind.REALIGN if both_short else DriftKind.FORK
else:
    kind = DriftKind.FORK

加入了上一轮 response 长度的判断


问题2:

python
def _align_to_prompt(self, prompt_ids: list[int], realign_at: int) -> None:
    tail = prompt_ids[realign_at:]
    self.tokens[realign_at:] = tail
    self.loss_mask[realign_at:] = [0] * len(tail)
    self.logprobs[realign_at:] = [0.0] * len(tail)

Environment (if relevant)

  • slime version:
  • Python version:
  • PyTorch version:
  • CUDA/ROCm version:
  • GPU type and count:
  • OS:

Additional Context

No response

Pre-submission Checklist