新渲染器的 8 个现有测试方法都通过了。可把旧版本的一条断言接过来,它就失败了:刚从助手输出中解析出来的当前轮 reasoning,在拼下一次模型输入时不见了。
2026 年 10 月 26 日,我复核了 SGLang 接入 deepseek-recipe 0.2.0 时的一个历史消息渲染问题。原问题由 05yuki 在 issue #46348 先行报告。我做的是固定版本复现、补充对照,以及检查旧回归测试能否继续发挥作用。
信息已经解析出来,为什么下次请求还会丢?
先看一段简化的对话流程。这里是结构示意,不是真实模型输出:
用户提出问题
助手返回 reasoning 字段,并调用 lookup
工具返回查询结果
助手继续处理同一个问题这中间有两次转换。输出解析器把助手生成的内容拆成字段,包括 reasoning、工具名称和参数;输入渲染器再把历史消息转换成下一次请求的 prompt。前一步正确,不能保证后一步也正确。
这次复核先用官方参考编码器构造合法的助手输出,再交给原版解析器处理。reasoning 字段、lookup 名称和 JSON 参数都能正确取出。接着把实际解析结果放回历史,追加工具结果,再分别交给参考编码器和新渲染路径。
差异出现在一个特定组合:开启 thinking、本次请求没有声明 tools、历史末尾是工具结果。参考编码器保留当前轮 reasoning,新路径却把它删掉了。
这里“本次没有声明 tools”,不等于历史中没有工具调用。按照该模型的编码约定,工具结果延续当前用户轮次,不能直接当成新一轮用户提问。
先排除那些本来就允许变化的情况
只看到两个 prompt 不同,还不能判定哪一个错了。因此我补了边界对照:如果真正追加一条新用户消息,删除上一轮 reasoning 符合约定;如果这次请求声明了 tools,当前轮内容也能正常保留。
测试覆盖 16 种历史配置,再分别按整段和逐 token 解析,得到 32 条解析到渲染的链路。输出解析全部正确;后续渲染有 4 条失败,来自两个受控内容在两种解析方式下的同一触发组合。它们不是四个独立 bug。
进一步追踪时,字段在 SGLang 的消息规范化之后仍然存在,直接调用原生转换和编码也得到相同的错误结果。范围因此缩小到后续原生转换、渲染阶段;这次没有定位到具体 Rust 函数,也没有验证修复。
新测试通过了,旧断言却能抓住问题
迁移后已经有专门的渲染器测试。我运行其中 8 个现有方法,全部通过,记录了 19 次 subTest 报告。
这个结果有明确范围:测试方法、断言和产品源码保持原样,测试基类、CI 登记和 tokenizer 下载入口做了离线适配。它不代表完整上游 CI 通过,19 次子测试也不是额外的独立案例。
更有用的对照来自旧版本。此前的 PR #44751 已经说明当前轮与新用户轮的区别,也有对应的旧测试方法。后端迁移增加了专用测试文件,同时从旧文件删除了这个方法。我保留它的输入和断言,在三个实现上运行:
| 实现 | 旧测试方法的结果 |
|---|---|
| 旧编码器 | 通过 |
| 官方 checkpoint 参考编码器 | 通过 |
| 新原生渲染器,经小型接口桥接入 | 在保留当前轮 reasoning 的断言处失败 |
接口桥只适配这个测试实际使用的参数和返回形式,没有替代编码算法。能发现问题的断言原本就存在。这次值得保留的经验是:后端替换后,除了重新验证接口,也要检查旧测试里有哪些关于对话含义的约定仍然适用。
下次换后端时,留下这几项检查
如果你也在替换模板、解析器或模型接入层,可以先沿着一段历史走完下面三步:
- 看到最终 prompt。 在接口返回、历史存储之外,再确认模型实际收到的内容。字段在中间对象里存在,不代表最后仍然存在。
- 成对保留轮次边界。 工具刚返回、用户真正问了新问题,是不同输入。一个应该保留当前轮内容,另一个允许丢弃旧内容;两边都测,才不会把“永远保留”误当成修复。
- 先搬断言,再适配接口。 找出旧测试在保护什么约定,只修改接入新实现所需的调用方式。若新规范确实改变了行为,就给出依据,别因为新测试已经通过而直接删掉旧预期。
同一轮复核还遇到了保留停止词时损坏相邻 Unicode 字符的情况。stream 与非 stream 的结果可以一致,却一起偏离预期文本。遇到这种问题,已有的直接内容断言就很管用,没必要先设计一套更复杂的检测方法。
想从一个更小的例子动手,可以试试文件名与列表的路径练习。它用一次 fsspec 修复,演示怎样同时检查出错输入和原本正常的输入,附可在本地运行的前后对照。
版本与结果
本文依据 2026 年 10 月 26 日的历史实验整理。SGLang 固定在 933853a3f2632b10e0bf10d7ffd2f6f853f574fa,recipe 为 0.2.0,模型参考固定在上文链接的 revision。使用受控输出和 CPU 原生组件,没有运行模型生成、GPU 推理或完整在线服务,也没有验证修复或测量回答质量。
可以下载历史测试结果摘录(JSON),查看 8 个方法名称、旧断言的三组结果、测试适配和版本。这是保存结果的摘录,未附原生重跑环境;整理本文时没有重新执行 SGLang 实验。完整项目的测试覆盖和最新版本是否仍受影响,需要另行验证。
后端迁移 PR #45315 和原 issue 保留了上游讨论。问题报告和旧断言都来自上游贡献者;这篇记录补充的是独立复核,以及迁移后怎样继续使用那条断言。