快速结论:在 transformers 5.16.1 中使用 Phi3ForCausalLM + LongRoPE 生成时,一旦输入长度跨过 original_max_position_embeddings 这个“短/长”切换点,prepare_inputs_for_generation 会清空 KV cache,但随后仍用已经被切到单 token 的 input_ids 前向,导致前缀丢失、生成结果与不带 cache 的全前缀结果不一致(报错特征:Phi3 LongRoPE generation loses the prefix when clearing KV at the short/long crossing)。优先排查 Phi3 的生成输入准备逻辑(清 cache 与 input_ids 切片的执行顺序)。
适用环境:Python 3.12.0、PyTorch 2.14.0+cpu、Transformers 5.16.1(复现)与 5.19.0.dev0(在 main 3758eba846d7d4211fba1ad68ffa971da5b4ccf4 上复现)。CPU 即可复现,无需下载模型权重;GPU/CUDA 环境在 Issue 中未验证。
最快修复方案:暂无确认的一步修复方案。Issue 中仅确认了 bug 真实存在,并接受了修复 PR 的意向;未给出已合并或已验证的补丁版本。
注意事项:在清空 cache 后必须让整个已有前缀按 long factor 重新做一次前向,而不是只喂当前单 token;修复应控制改动范围,避免引入与生成输入准备无关的逻辑。相关 Issue #47530(logits_to_keep=None、rank-4 logits)与本问题无关,不要按那条路径排查。
问题场景
运行 transformers 的 Phi3ForCausalLM.generate,模型配置使用 LongRoPE(rope_type=”longrope”)并设置了 original_max_position_embeddings 与 sliding_window。当生成长度跨过 original_max_position_embeddings 这个短/长 RoPE 切换点时,Phi3ForCausalLM.prepare_inputs_for_generation 会清空 past_key_values(代码注释为 “enforce re-compute cache”),但 generation 在此之前已经把 input_ids 切片到当前 token,于是接下来的一次 forward 只拿到一个 token、没有 cache、却带着原始绝对位置。Issue 提供的 CPU 复现无需下载 checkpoint。
报错原文
Phi3 LongRoPE generation loses the prefix when clearing KV at the short/long crossing
last generation call: ([[32], [59]], [[8], [8]], None)
generated next token: [2, 26]
uncached full-prefix next token: [32, 26]
原因分析
最可能的原因:切换点处的 cache 重建顺序错误。prepare_inputs_for_generation 检测到已跨过原始上下文后清掉 8-token 的 cache,但基类 prepare_inputs_for_generation 仍旧只生成一个位于 position 8 的单 token 输入,旧前缀在 reset 之后没有被重新计算。因此这次 forward 相当于“丢掉了 8 个 token 的前缀、只用第 9 个 token 的位置 8 做推理”,得到的下一个贪心 token 为 [2, 26],而按完整前缀(不带 cache)重算的参考结果是 [32, 26]。Issue 中评论进一步指出:prepare_inputs_for_generation 实际收到了完整的九 token 前缀和 next_sequence_length=1,清 cache 后基类仍产出单 token 输入。低于阈值(未跨界)的对照用例结果与参考一致,说明问题只出现在短/长切换的边界上。
环境排查
- 确认 transformers 版本:Issue 在 5.16.1 复现;评论者在 main 3758eba846d7d4211fba1ad68ffa971da5b4ccf4(5.19.0.dev0)上同样复现。
- 确认 Python 版本:评论复现使用 Python 3.12.0。
- 确认 PyTorch 版本:评论复现使用 PyTorch 2.14.0+cpu。
- 确认是否使用 LongRoPE 配置:rope_type=”longrope”、original_max_position_embeddings 小于当前生成序列长度、存在 short_factor/long_factor。
- 确认 attn 实现:Issue 复现脚本设置 config._attn_implementation = “eager”,可先在 eager 路径下验证。
- 确认是否混淆 Issue #47530:该问题涉及 logits_to_keep=None 与 rank-4 logits,本问题使用普通 forward 且 logits 为 rank-3,两者无关。
解决步骤
- 先用 Issue 中的最小 CPU 脚本确认现象:构造小规模 Phi3Config(longrope、original_max_position_embeddings=8),注册 forward_pre_hook 记录每次调用的 input_ids、position_ids 与 past 长度,对比 generate 的最后一次调用与不带 cache 的全前缀 forward。
- 定位触发点:检查 src/transformers/models/phi3/modeling_phi3.py 中 v5.16.1 的 498-533 行(尤其是 512-521 行清 cache 与 523-533 行委托基类准备输入)的执行顺序,确认清 cache 后基类是否仍把 input_ids 切成单 token。
- 可优先尝试的修复方向:在清空 past_key_values 并要求重算时,不要只把当前 token 交给基类准备,而应让这一步使用完整已有前缀(或等价地确保 input_ids/position_ids 对应整个前缀)重新构建 cache;随后再按新 cache 继续生成。
- 保持改动最小、专注:按 Issue 中维护者意见,修 Phi3 的模块化生成输入准备并补边界回归测试,避免无关重构,避免引入冗余的”agent slop”代码。
- 若准备提交修复:先确认没有正在进行中的重叠 PR;Issue 中已表示欢迎 PR,但需人工复核后再提交。
验证方法
用 Issue 的复现脚本重跑:切换点处的最后一次 generation forward 应基于完整前缀重建 cache,而不是 ([[32], [59]], [[8], [8]], None) 这样的单 token、无 cache 输入;最终 generated next token 应与 uncached full-prefix 参考一致(示例中为 [32, 26])。同时保留一个低于切换点的对照用例(例如五个新 token)作为回归测试,确认未跨界时行为不受影响。
参考来源
huggingface/transformers #49334
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[Feature]: Integer token IDs for logprobs in `/inference/v1/generate` responses (`GenerateLogProbs`)](https://www.chat-gpts.plus/wp-content/uploads/2026/10/57574-ffe54962-768x403.jpg)

